Error Springboot query Not supported for DML operations - java

I'm trying to assign office for employees with same department. I'm gettin this error. What's the problem here ?
"org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.example.macademiaproject.model.Employee u set u.office = :office where u.department = :department]; nested exception is java.lang.IllegalStateException: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.example.macademiaproject.model.Employee u set u.office = :office where u.department = :department]",
Query :
#Modifying
#Query("update Employee u set u.office = :office where u.department = :department")
List <Employee> assignOffice (#Param("office") String office,
#Param("department") String department);
ServiceImpl :
#Override
public List<Employee> assignOffice(String department,String office) {
List<Employee> employees = employeeRepository.assignOffice(office,department);
return employees;
}
Controller :
#GetMapping(path="/assign")
public ResponseEntity<List<Employee>> assignOffice(#RequestParam("office") String office,
#RequestParam("department") String department){
return ResponseEntity.ok(employeeService.assignOffice(office,department));
}

#Modifying(clearAutomatically = true)
can you try this annotation or you can take a look at this link.
Spring Boot Data JPA - Modifying update query - Refresh persistence context

Related

Hibernate Fetching with Multiselect in Criteria API not working

I am using Hibernate Criteria API to load the data from the DB, however when using multiselect and fetching the related entities with mapping #OneToOne, #ManyToOne & #ManyToMany, I am getting error.
Code to get the data
private Session getSession() {
return entityManager.unwrap(SessionImplementor.class);
}
#Override
public Account getGatewayAccount(Long appId, String accountNumber) {
Session session = getSession();
CriteriaBuilder criteria = session.getCriteriaBuilder();
CriteriaQuery<Account> query = criteria.createQuery(Account.class);
Root<Account> from = query.from(Account.class);
from.fetch(Account_.APP, JoinType.INNER);
query.multiselect(from.get(Account_.ID), from.get(Account_.ACCOUNT_NUMBER))
.where(criteria.equal(from.get(Account_.ACCOUNT_NUMBER), accountNumber),
criteria.equal(from.get(Account_.APP).get(App_.ID), appId));
try {
return session.createQuery(query)
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
I have also tried using EntityGraph to lad the data like below
private Session getSession() {
return entityManager.unwrap(SessionImplementor.class);
}
#Override
public Account getGatewayAccount(Long appId, String accountNumber) {
Session session = getSession();
RootGraph<Account> entityGraph = session.createEntityGraph(Account.class);
entityGraph.addAttributeNodes("app");
CriteriaBuilder criteria = session.getCriteriaBuilder();
CriteriaQuery<Account> query = criteria.createQuery(Account.class);
Root<Account> from = query.from(Account.class);
query.multiselect(from.get(Account_.ID), from.get(Account_.ACCOUNT_NUMBER))
.where(criteria.equal(from.get(Account_.ACCOUNT_NUMBER), accountNumber),
criteria.equal(from.get(Account_.APP).get(App_.ID), appId));
try {
return session.createQuery(query)
.setHint("javax.persistence.fetchgraph", entityGraph)
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
The exception that I am getting is
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.QueryException: query specified join fetching,
but the owner of the fetched association was not present in the select list
I am looking a way which can fetch the related objects with multiselect in select clause and get the data as POJO object, not like a Tuple.
What you want seems to be a constructor query
query.select(criteria.construct(Answer.class, from.get(Account_.ID), from.get(Account_.ACCOUNT_NUMBER))
This works by using a constructor on the defined class that accepts the two parameters define in the query, so bypasses all JPA hooks; What is returned is an unmanaged POJO. Since this isn't a managed entity fetch graphs will likely not have any meaning.
Otherwise, you can just return:
query.select(from)
This will return an Address instance that is managed and has all attributes described in your entityGraph loaded. Depending on your provider specifics, everything not defined in the entityGraph should be left as lazy and so unfetched.

Delete Query in Spring Boot with Optional Parameter

I am trying to implement Delete query in Spring Boot, however the parameters are optional. How do I write JPA query for same.
Here is how I have implemented for mandate Request Params:
#Transactional
#Repository
public interface ABCRepo extends CrudRepository<ABC, Long>{
public List<ABC> findByABCIdAndStartYrAndStartMonth(String pilotId, int startYr, int startMonth);
public long deleteABCByABCId(String pilotId);
}
Controller.class
#RequestMapping(value="", method= RequestMethod.DELETE)
public Response delete(#PathVariable("abc-id")String pilotId)
{
LOGGER.info("Trying to delete pilot bank using abc id : "+ abcId);
long deletedRecords=abcBiz.deleteABCByABCId(abcId);
if(deletedRecords==0)
{
throw new PilotNotFoundException("Entity not found "+abcId);
}
return Response.status(Response.Status.NO_CONTENT).entity(deletedRecords).build();
}
My new Controller.class after adding optional params
#RequestMapping(value="", method= RequestMethod.DELETE)
public Response delete(#PathVariable("abc-id")String abcId, #RequestParam(name = "bid-yr", required = false)
int bidYr, #RequestParam(name = "bid-month", required = false) int bidMonth)
{
LOGGER.info("Trying to delete pilot bank using abc id : "+ abcId);
long deletedRecords=abcBiz.deleteABCByABCId(a);bcId
if(deletedRecords==0)
{
throw new PilotNotFoundException("Entity not found "+abcId);
}
return Response.status(Response.Status.NO_CONTENT).entity(deletedRecords).build();
}
How do I handle this at JPA?
For optional parameters, you need to write the query. Something like below:
#Modifying
#Query("DELETE FROM ABC WHERE abcId=:pilotId AND (:otherOptionalParam IS NULL OR otherField=:otherOptionalParam)")
public long deleteABCByABCId(String pilotId, String otherOptionalParam);
If you want to create a complex query, with lot of optional parameters, then you can create custom repository, and develop native queries. Here I have already answered to how we can create custom repositories in Spring data JPA - https://stackoverflow.com/a/68721142/3709922
On Top of what Jignesh has said, don't forget to mark your parameters with Param annotation. Also jpa modification will return int/Integer but not long so I had to change return type too.
#Modifying
#Query("DELETE FROM ABC WHERE abcId=:pilotId AND (:otherOptionalParam IS NULL OR
otherField=:otherOptionalParam)")
public long deleteABCByABCId(#Param("pilotId")String pilotId, #Param("otherOptionalParam")String
otherOptionalParam);

Using a Hibernate filter with Spring Boot JPA

I have found the need to limit the size of a child collection by a property in the child class.
I have the following after following this guide:
#FilterDef(name="dateFilter", parameters=#ParamDef( name="fromDate", type="date" ) )
public class SystemNode implements Serializable {
#Getter
#Setter
#Builder.Default
// "startTime" is a property in HealthHistory
#Filter(name = "dateFilter", condition = "startTime >= :fromDate")
#OneToMany(mappedBy = "system", targetEntity = HealthHistory.class, fetch = FetchType.LAZY)
private Set<HealthHistory> healthHistory = new HashSet<HealthHistory>();
public void addHealthHistory(HealthHistory health) {
this.healthHistory.add(health);
health.setSystem(this);
}
}
However, I don't really understand how to toggle this filter when using Spring Data JPA. I am fetching my parent entity like this:
public SystemNode getSystem(UUID uuid) {
return systemRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException("Could not find system with id " + uuid));
}
And this method in turn calls the Spring supported repository interface:
public interface SystemRepository extends CrudRepository<SystemNode, UUID> {
Optional<SystemNode> findByUuid(UUID uuid);
}
How can I make this filter play nicely together with Spring? I would like to activate it programatically when I need it, not globally. There are scenarios where it would be viable to disregard the filter.
I am using Spring Boot 1.3.5.RELEASE, I cannot update this at the moment.
Update and solution
I tried the following as suggested to me in the comments above.
#Autowired
private EntityManager entityManager;
public SystemNode getSystemWithHistoryFrom(UUID uuid) {
Session session = entityManager.unwrap(Session.class);
Filter filter = session.enableFilter("dateFilter");
filter.setParameter("fromDate", new DateTime().minusHours(4).toDate());
SystemNode systemNode = systemRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException("Could not find system with id " + uuid));
session.disableFilter("dateFilter");
return systemNode;
}
I also had the wrong type in the FilterDef annotation:
#FilterDef(name="dateFilter", parameters=#ParamDef( name="fromDate", type="timestamp" ) )
I changed from date to timestamp.
This returns the correct number of objects, verified against the database.
Thank you!

Getting a list of results by using CRUD Repository

I am new to using CRUD Repository.
I have a database table with three columns:
course_id, name, course
I want to get a list of course_id give name, example,
SELECT id FROM table WHERE name='charmaine';
However, I do not want to do it with query but using crud repository.
There is an error shown in my controller.
May I know there is this error?
My controller
#GetMapping(value = "getting/{name}")
//#ResponseBody
public String getting(#PathVariable("name") String name) {
List<CourseMaster> getIds = CourseService.findIdByName(Name); —> error icon here
return getIds; —> error icon here
}
Service
public List<CourseMaster> findIdByName(String Name) {
return CourseMasterRepo.findByName(Name);
}
Repository
public interface CourseMasterRepo extends CrudRepository<CourseMaster, Long> {
List<CourseMaster> findByName(String Name);
}
You have to autowired service class in your controller like.
#Autowired
CourseService courseService;
#GetMapping(value = "getting/{name}")
public String getting(#PathVariable("name") String name) {
List<CourseMaster> getIds = courseService.findIdByName(Name);
return getIds;
}
if your code is done in java spring you must use hql language in hibernate, that is a interface of sql query.
hql query that use lambda expression is very simple and useful.
For example,
String hql = "FROM Employee E WHERE E.id = 10";
Query query = session.createQuery(hql);
List results = query.list();

Update single field using spring data jpa

I'm using spring-data's repositories - very convenient thing but I faced an issue. I easily can update whole entity but I believe it's pointless when I need to update only a single field:
#Entity
#Table(schema = "processors", name = "ear_attachment")
public class EARAttachment {
private Long id;
private String originalName;
private String uniqueName;//yyyy-mm-dd-GUID-originalName
private long size;
private EARAttachmentStatus status;
to update I just call method save. In log I see the followwing:
batching 1 statements: 1: update processors.ear_attachment set message_id=100,
original_name='40022530424.dat',
size=506,
status=2,
unique_name='2014-12-16-8cf74a74-e7f3-40d8-a1fb-393c2a806847-40022530424.dat'
where id=1
I would like to see some thing like this:
batching 1 statements: 1: update processors.ear_attachment set status=2 where id=1
Spring's repositories have a lot of facilities to select something using name conventions, maybe there is something similar for update like updateForStatus(int status);
You can try something like this on your repository interface:
#Modifying
#Query("update EARAttachment ear set ear.status = ?1 where ear.id = ?2")
int setStatusForEARAttachment(Integer status, Long id);
You can also use named params, like this:
#Modifying
#Query("update EARAttachment ear set ear.status = :status where ear.id = :id")
int setStatusForEARAttachment(#Param("status") Integer status, #Param("id") Long id);
The int return value is the number of rows that where updated. You may also use void return.
See more in reference documentation.
Hibernate offers the #DynamicUpdate annotation. All we need to do is to add this annotation at the entity level:
#Entity(name = "EARAttachment ")
#Table(name = "EARAttachment ")
#DynamicUpdate
public class EARAttachment {
//Code omitted for brevity
}
Now, when you use EARAttachment.setStatus(value) and executing "CrudRepository" save(S entity), it will update only the particular field. e.g. the following UPDATE statement is executed:
UPDATE EARAttachment
SET status = 12,
WHERE id = 1
You can update use databind to map #PathVariable T entity and #RequestBody Map body. And them update body -> entity.
public static void applyChanges(Object entity, Map<String, Object> map, String[] ignoreFields) {
map.forEach((key, value) -> {
if(!Arrays.asList(ignoreFields).contains(key)) {
try {
Method getMethod = entity.getClass().getMethod(getMethodNameByPrefix("get", key));
Method setMethod = entity.getClass().getMethod(getMethodNameByPrefix("set", key), getMethod.getReturnType());
setMethod.invoke(entity, value);
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
}

Categories

Resources