How to force commit entity in #Transactional mode - java

I am using Spring data jpa and jdbc (using entityManager.unwrap(Session.class) ) connection.
My request flows through 3 method. 1st -> 2nd ->3rd.
1st and 3rd are annotated with #Transactional.
In 1st and 2nd method saving entity through JPA CRUD repository.
In 3rd method using basic jdbc query to retrieve values saved in 2nd method.
As we know in #Transaction entities are not committed to database until JPA commit the transaction.
I used saveAndFlush also in 2nd method but can not see retrieve updated values in method 3 using jdbc query.
1st Method - update()
#Transactional
#Override
public ApiResponse update(RequestInput input) {
ApiResponse response = doRestOfWork(input); // calling 2nd method
// .... some other entity save....
}
2nd Method - doRestOfWork() : setting status=true and calling saveAndFlush method
#Override
public ApiResponse doRestOfWork(Request request) {
Insight insight = insightRepository.findOne(request.getTypeId());
insight.setStatus(true);
insightRepository.saveAndFlush(insight);
processOperation(); // calling 3rd method
}
3rd method - processOperation() : retrieving updated status value through jdbc connection.
#Transactional
public void processOperation() {
Connection conn = null;
SessionImplementor sessionImpl = (SessionImplementor) entityManager.unwrap(Session.class);
try {
conn = sessionImpl.getJdbcConnectionAccess().obtainConnection();
Connection conn = null;
String stmt = "SELECT status from insight";
PreparedStatement ps = conn.prepareStatement(stmt);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
boolean value = rs.getBoolean(status); // returning false, i want this to return true as already set true in 2nd method and called saveAndFlush
}
} catch (SQLException ex) {
} finally {
JdbcUtility.closeResources(conn, ps, rs);
}
}
InsightRepository is extending JpaRepository
#Repository
public interface InsightRepository extends JpaRepository<Insight, Long> {}
I want updated value of status (which is boolean true - updated in method 2) in method 3.
How to achieve this ?
Update
I searched a lot and do not think that if a method is annotated with #Transactional then you can commit changes before completing JPA transaction. So the solution is to remove #Transactional annotation and use entityManager.getTransaction() if want to control over JPA transaction.

Create a method (4th method) for update with #Transactional(propagation = Propagation.REQUIRES_NEW) annotation in a bean different from the 2nd Method and change second method as calling 4th method like this :
#Component //or Service etc..
public class AnotherBean{
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void doUpdateWork(Request request){
Insight insight = insightRepository.findOne(request.getTypeId());
insight.setStatus(true);
insightRepository.save(insight);
}
}
and
#Override
public ApiResponse doRestOfWork(Request request) {
anotherBean.doUpdateWork(request);
processOperation(); // calling 3rd method
}

If you want to controll transaction, dont use #Transactional
em; //entityManager
Transaction tx=em.getTransaction();
try{
tx.begin();
//do work
tx.commit(); // comminted transaction
// start another transaction if you need to, doo more work etc.
...
}
This is most basic and fundamental block of code for using JPA for atomic operations.
Also there is no need to unwrap entity manager, use pure JPA. By unwrapping you are binding current implementation to JPA provider which is in contrary to JPA idea of independency from underlying JPA implementation provider. In other words, it will stop working if for some reasons Spring would change its JPA implementation from Hibrnate to eg. EclipseLink.

Related

How to perform another DB query in the same transaction in case of unique constraint violation in Java Spring #Transactional method?

I am struggling with handling exceptions during transactional methods with Spring #Transactional annotations. Currently, when my Dao insert method throws a unique contraint violation, I want to perform a read instead to get the existing item. However, this fails because the UniqueIdViolation causes the transaction to fail. How can I refactor this to work as intended whilst not doing a dodgy boolean return on the insert?
I have a DAO with an insert method, e.g.:
public class ItemDao {
public void insertItem(Item item) {
// insert into db via jooq
}
public Item fetch(String id) {
// fetch from db
}
...
}
If it violates a unique constraint I have an aspect that handles the DataAccessException and rethrows it as a UniqueIdException.
I have a service method that takes in some information and creates an Item, before returning that item.
If the dao throws a UniqueIdException, I want the service to catch this error and then call itemDao.find() to get the existing Item and return it.
public class MyServiceImpl implements MyService {
#Transactional(isolation = SERIALIZABLE)
public Item createItem(...) {
Item item = new Item(...);
try {
itemDao.insert(item);
return item;
} catch (UniqueIdException ex) {
return itemDao.find(item.getId());
}
}
}
The issue here is when itemDao.insert(item) throws an exception this causes the transaction to fail and therefore itemDao.find() doesn't run.
#Transactional is setup to rollback, by default, only when an unchecked exception is thrown.
Since DataAccessException is unchecked exception your transaction will be rolled backed.
A better strategy will be to first call fetch() if returned null then call insertItem

Spring / Hibernate #Transactional not flushing transaction if followed by a second #Transactional call

I have a weird behaviour in my Spring Boot App.
The app have the OpenSessionInView to false.
I have a Controller and a Service exposing 2 methods with annotation #Transactionnal.
Application.properties :
spring.jpa.open-in-view=false
My service :
#Service
public class MyService {
#Transactional(transactionManager = "myTx")
public void doA(Integer objectId) {
Object o = repo.findMyObject(objectId);
updateMyObject(o);
repo.save(o);
}
#Transactional(transactionManager = "myTx")
public void doB(Integer objectId) {
Object o = repo.findMyObject(objectId);
updateMyObjectDifferently(o);
repo.save(o);
}
}
My Controller (case 1) :
#RequestMapping("/do/{myId}")
public String do(Model model, HttpServletRequest request) {
service.doA(myId);
service.doB(myId);
return "page";
}
With SQL in debug, I see that the SELECT queries are performed during the call to the service.
But I see only 1 flush (several UPDATES), and it's done when service.doB() is finished and the TransactionInterceptor around the method launch the method invokeWithinTransaction which is weird.
As both method have #Transactional, I was hoping to see 2 flush : the first flush just at the end of service.doA() and a second flush at the end of service.doB().
What is more weird is that if I comment the second call, so
My Controller (case 2) :
#RequestMapping("/do/{myId}")
public String do(Model model, HttpServletRequest request) {
service.doA(myId);
//service.doB(myId);
return "page";
}
In case 1, it's like service.doA() knows that a second call will arrive just after on the same object, so it does not commit/flush the transaction and wait for the end of service.doB().
Why do I see only 1 flush ?
Is it because both calls are on the some DB object ?
I thought my knowledge of #Transactional were ok.
But now, I am lost.
Hibernate will detect if an object really is dirty and avoid sending an UPDATE statement if it's not necessary. I assume that your updateMyObject is simply not changing the state of the entity with respect to the state it had initially when it was loaded.

How to force #Transactional in JPA to commit two repository action not only just the last one?

Hi what i trying to achieve is to do "UPDATE" action and then do "DELETE" action with Spring Data JPA and #Transactional annotation, and i want both action is executed but if one of the action is failed, i need a rollback, how do i do this properly?
first here is my service class :
#Transactional
#Service
public class TransDeliveryPlanningService {
public ResponseRequest<TransDeliveryPlanning> deleteTransDeliveryPlanning(InputDeleteRequest<ViewAndDeleteRequest> request) {
String currentUser = request.getLoggedInUser();
String reasonDeleted = request.getReason();
Long id = request.getObject().getId();
ResponseRequest<TransDeliveryPlanning> response = new ResponseRequest<TransDeliveryPlanning>();
TransDeliveryPlanning transDeliveryPlanningOld = transDeliveryPlanningRepository.findById(id).orElseThrow(() -> new ResourceNotFound("Data "+ id +" not found!"));
transDeliveryPlanningOld.setIsDeleted(true);
transDeliveryPlanningOld.setReasonDeleted(reasonDeleted);
transDeliveryPlanningOld.setModifiedBy(currentUser);
TransDeliveryPlanning updatedTransDeliveryPlanning = transDeliveryPlanningRepository.save(transDeliveryPlanningOld);
transDeliveryPlanningRepository.delete(transDeliveryPlanningOld);
//NOTE delete transStuffing
List<TransStuffing> transStuffing = transStuffingRepository.findBydeliveryPlanningId(transDeliveryPlanningOld.getId());
Boolean deletePermit = false;
for(TransStuffing ts : transStuffing) {
if(ts.getStatus().equalsIgnoreCase("new")) {
deletePermit = true;
} else {
throw new ResourceIsDelete("Stuffing " + ts.getStuffingNo() + " Status already changed, delete is not permited!");
}
}
if(deletePermit){
transStuffingRepository.deleteAll(transStuffing);
}
//NOTE end of delete transStuffing
if(updatedTransDeliveryPlanning != null) {
response.setResponse("Sukses Hapus");
response.setObject(updatedTransDeliveryPlanning);
} else {
response.setResponse("Gagal Hapus");
}
return response;
}
}
as you can see, i do transDeliveryPlanningRepository.save and then the next line i do transDeliveryPlanningRepository.delete and the next repo i need to execute is transStuffingRepository.deleteAll
The goal i need to do save before delete is i use Hibernate Audit Envers to create an AuditLog, so i want log the delete reason into my audit table and then delete the record. But when i use **#Transactional** annotation the transDeliveryPlanningRepository.save (update) not executed, my function just execute transDeliveryPlanningRepository.delete and transStuffingRepository.deleteAll how i keep the save executed with #Transactional annotation?
UPDATE
As Morteza Bandi answer below suggestion, here is my updated code :
here is my repository :
#Repository
public interface TransDeliveryPlanningRepository extends RevisionRepository<TransDeliveryPlanning, Long, Integer>, JpaRepository<TransDeliveryPlanning, Long> {
#Modifying
TransDeliveryPlanning save(TransDeliveryPlanning transDeliveryPlanning);
}
when i do this, still it's not update before delete, what did i still miss here?
try putting #Transactional above a method inside service method. e.g.:
#Service
public class MyService {
#Autowired
private MyRepo myRepo;
#Autowired
private MyRepo2 myRepo2;
#Transactional
public void yourTransactionService(arguments){
*** your DML operations here ***
e.g.
myRepo.save(blabla);
myRepo.delete(blabla1);
...
myRepo2.save(blabla2);
myRepo3.delete(blabla3);
...
}
}
This way when calling yourTransactionService(arguments) either all the DMLs are persisted or none of them persisted.
There is a simple way to do this just by using query itself, you can use commit, transaction and roll back
For ex: you have to start the transaction at first, then you need to set autocommit = 0 so that every transaction that will take place will not get commit once you are ok with everything give commit else you can pass a statement as rollback this rolls back all the operations you have done after starting the transaction. For better understanding this use the following link

Spring #Transactional does not succeed or hangs up

I need help with my project since I am not familiar with the spring #transactional annotation. The question is why my application hungs up using the #transactional method but will not hung up without it. Also how do I solve this so that the transaction will suceed.
The scenario is that my application is set-up like this:
Uses declarative transaction Management
One method has the #transactional(rollbackFor=Exception.class) annotation and accesses the database multiple times.
The said method calls another method that returns a String and accesses the database multiple times.
Transaction does not suceed finishing causes a deadlock on my application. It does not return any exception.
The Code below is sample snippet
#Autowired
JdbcTemplate template;
#Transactional(rollbackFor = Exception.class)
public final void upsertData(){
insertTable1(); // insert query to insert in table 1
addReferencingData(); // was just called to update table but returns something which is not used;
//Hangs up before getting to next statement
somePreparedStatmentSQLmergeTable1(mergesql,template); // query to merge in table 1
}
public final String addReferencingData(){
updateTableA(); // update query to update values in table A
updateTableB(); // update query to update values in table B
mergeTable1(); // merge query to update or insert in table 1
return someString;
}
public static void somePreparedStatmentSQLmergeTable1(sql,template){
template.batchUpdate(sql, new BatchPreparedStatementSetter() {
public void setValues(final PreparedStatement ps, final int i){
// setting parameters to be used
}
public int getBatchSize(){
// returns size of data's
}
}
}
Also added the default Transaction manager on my application-context.xml file.Transaction already works based on logs. Only in this specific method it does not work.
Updated some information to be clearer.

Spring data : #Transactional and propogation

I have the following code:
public void method1(String id){
Object object = repository.findOne(id);
object.setState("running");
repository.save(object);
try{
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
}catch(Exception e){
object.setState("failed");
}
repository.save(object);
}
So, I change the state to "running" before calling a method that takes hours to execute. My object is a JPA Entity(with lazily loded collections) and method2() tries to load all the linked entities.
Now, in method2, I am getting
could not initialize proxy - no Session
error because it is outside of transaction (expected behavior). To prevent this, there are two solutions:
Annotate method1 with #Transactional. This would solve it, but then, the state won't be reflected to other transactions until the method execution finishes.
Change the fetch mode in Entity config and make it Eager. This would also solve it but I don't want eager fetching every time.
Is there any other way by which I can make it work?
How about this:
Option 1
1) Create a service method for status changing like following:
#Transactional( propagation = Propagation.REQUIRES_NEW)
public void changeStatusInNewTransaction(String id, String status){
Object object = repository.findOne(id);
object.setState(status);
repository.save(object);
}
2) Change the original method as follows:
#Autowired
Service service;
#Transactional
public void method1(String id){
service.changeStatusInNewTransaction(id, "running");
Object object = repository.findOne(id);
try{
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
}catch(Exception e){
object.setState("failed");
}
repository.save(object);
}
Thanks to this set-up, everything can be run under one #Transactional method, but when the state is to be changed to 'running' then :
The current transaction would be suspended
New one would be created
State would be changed and transaction commited
Parent transaction would continue and you can process with your big operation not having a problem that other users will wont see the status change for 2 hours..
Option 2
1) Create a service method for status changing like following:
#Transactional
public void changeStatusInNewTransaction(String id, String status){
Object object = repository.findOne(id);
object.setState(status);
repository.save(object);
}
2) Create transactional method just for long processing
#Transactional
public void performLongProcessing(String id){
Object object = repository.findOne(id);
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
repository.save(objects;
}
3) Mark the main method to run without transaction:
#Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method1(String id){
service.changeStatusInNewTransaction(id, "running");
try{
service.performLongProcessing(id);
}catch(Exception e){
service.changeStatusInNewTransaction(id, "failed");
}
}
Having a transaction around a method that executes for several hours, seems like a design mistake, so method1() should not have #Transactional! When you start a transaction, you need a connection and this connection will be allocated from you connection pool for the entire duration, which greatly limits scalability (and pisses of your DBA).
could not initialize proxy - no Session
You get this error because (without #Transactional on method1) your entity is detached after repository.save() has been called, and you can't load the lazy collections. A quick solution for this is to inject an EntityManager into object2 and call EntityManager.refresh() inside method2() this does not require a transaction, as you are only reading data.
There is no reason to use any sort of Transaction propagation to solve this issue.

Categories

Resources