2

I have a spring boot application and have a api called /student/update. This api includes one update native query, but it returns javax.persistence.TransactionRequiredException. I tried in both javax.transaction.Transactional and org.springframework.transaction.annotation.Transactional with org.springframework.data.jpa.repository.Modifying as per this instructions. But it returns the same exeption.

This is the student interface

@Service
public interface StudentService {

  Boolean updateStudentDetails() throws Exception;

  Boolean saveStudentDetails() throws Exception;

}

Implementation class

@Component
public class StudentServiceImpl implements StudentService {

@PersistenceContext
private EntityManager em;

@Overide
public Boolean updateStudentDetails() throws Exception {
    // some other operations
    return updateStudent();
}

@org.springframework.transaction.annotation.Transactional
private Boolean updateStudent() throws Exception {
    JSONObject obj = new JSONObject(); // some other details 
    String query = "UPDATE student SET updatedTime = CURRENT_TIMESTAMP(),previousData= "+obj.toString();
    Query query = em.createNativeQuery(query);
    int updateCount = query.executeUpdate();
    if (updateCount > 0) {
      return true;
    }
    return false;
}

public Boolean saveStudentDetails() throws Exception {
    Student student = new Student();
    student.setName("Tom");
    student.setAddress("....");
    studentRepo.save(student);
    return true;
}
}

Controller code

@RestController
@RequestMapping("student")
public class MarkupController {

@Autowired
private StudentService studentService;


@PostMapping("/update")
public String updateMarkup(@Valid @RequestBody Map<String,Object> request) {
    try {
        if(studentService.updateStudentDetails()) {
          return "Student Updated Successfully!!"; 
        } else {
          return "Student Updated failed!!"; 
        } 
    } catch (Exception e) {
      e.printStackTrace();
      return "Updation failed!!"
    }
}
}

while calling student/update api, it returns Exception on query.executeUpdate(); statement in StudentServiceImpl

javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1496)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:380)
at com.sun.proxy.$Proxy677.executeUpdate(Unknown Source)

Can anyone please help me to find the exact reason!! Thanks in advance!!

  • This might be related to @Transactional being added to a private method. Check out https://stackoverflow.com/questions/4396284/does-spring-transactional-attribute-work-on-a-private-method why that does not work. Does the error persist if you move the Transactional annotation to the public methods being called by the controller? – lugiorgi Aug 03 '20 at 11:28
  • 2
    @lugiorgi is right: you cannot use `@Transactional` in private methods because the way it works, Spring needs to create a proxy in order to apply transactionality (and other things), and it only can happen if the method is public. Also, you must use `@Transactional` in `updateStudentDetails` because, even being public, Spring cannot apply declarative transactionality if the method is called within the same class. – Lasneyx Aug 03 '20 at 11:34
  • is it possible to call the method `updateStudent` by CompletableFuture.runAsync?? – GameCoder007 Aug 03 '20 at 12:12
  • @GameCoder007 see the linked answer abowe. "If you use (default) Spring Proxy AOP, then all AOP functionality provided by Spring (like @Transational) will only be taken into account if the call goes through the proxy. -- This is normally the case if the annotated method is invoked from another bean." You have to annotate a public method `@Transactional` and call the method from a different bean for it to work. So yes, if you respect those points, you should be able to run it async. – lugiorgi Aug 03 '20 at 12:18

0 Answers0