Hi,
I am using cuba6.10, here is my case:
I have a screen and with it’s datasource, and a @Component with some other DB change logic(the other DB changes has no DB relationship to the datasource in screen).
For some user action, I have datasource changes and also call the @Component to perform other DB change。
In general , all works fine. But occasionally, I found that the change which in @Component does not happen(did not commit to database), but I did not find in which process flow that no db change will happens in @Component。
And recently,in two days, there are around 260 that actions performed, but about 250 has no DB change done in @Component, and other db changes of the screen datasource works fine. I also did not see any other error/warnings in logfile.
Do you have such experience or could you advice how can I debug/analyze this issue, please?
Below is the main code:
//screen controller
//other datasource operation here
// then, new an entity, and pass it to a service
CertSignLog certSignLog = (CertSignLog) metadata.create("cip$CertSignLog");
certSignLog.setContent(content);
certSignLogService.saveEntity(certSignLog);
//other datasource operation here
//in the service , call the component, and in componet, commit db changes:
public void saveEntity(CertSignLog entity) {
try (Transaction tx = persistence.getTransaction()) {
EntityManager em = persistence.getEntityManager();
em.persist(entity);
tx.commit();
} catch (Exception e) {
throw e;
}
}
Firstly, a few comments not directly related to possible causes:
Don’t use EntityManager until needed. Your code could be simpler and less error prone with DataManager:
CertSignLog certSignLog = (CertSignLog) metadata.create("cip$CertSignLog");
certSignLog.setContent(content);
// right in the screen controller, or in a service but without any transaction management
dataManager.commit(certSignLog);
When using programmatic transaction management with EntityManager, use createTransaction() in try-with-resources block or use lambda:
public void saveEntity(CertSignLog entity) {
// use createTransaction() to always create new transaction
try (Transaction tx = persistence.createTransaction()) {
EntityManager em = persistence.getEntityManager();
em.persist(entity);
tx.commit();
}
}
// or
public void saveEntity(CertSignLog entity) {
persistence.runInTransaction(em -> {
em.persist(entity);
});
}
As for a possible cause of your problem, try to add logging to your “save” method or just use DataManager which outputs debug messages on saving entities.
Hi Konstantin,
One more thing about this topic, similar “data lost” behavior also happened to AccessToken。
I am using cuba6.10, and have StoreTokensInDb set to true。
When the “data lost” happens, I noticed that in same time, sys$AccessToken did not stored the token。
The store token to db code is also using EntityManager+transaction, but without tx.end().
I guess some of my code leads some bad result after long running but I did not figure out yet, just keep you posted about this topic.
Every time when cuba before insert AccessToken , it try to delete it first.
From the logfile, the delete sql executed, but the insert sql right after it not executed, no error no warning.
BTW, maybe I should always use persistence.createTransaction() rather than persistence.getTransaction() for my own standalone logic, right?
Hi,
If you use persistence.getTransaction() - then the fact whether this data will be committed to database or lost - will depend on the outer code from which you call storeAccessTokenToDatabase() method. Because this method takes outer transaction from calling code.
So general rule #1 - don’t use persistence.getTransaction() unless you really need it and understand what it does.
maybe I should always use persistence.createTransaction() rather than persistence.getTransaction() for my own standalone logic, right?
Again, general rule #2, by Konstantin:
Manual transaction control is usually necessary only if one programs a complex long transaction where several business-logically or constraint-related entities MUST be committed to the database in the same transaction.
And even in this case DataManager can be used, like:
In modern versions of CUBA the preferred way to work with data is using a DataManager, TransactionalDataManager, EntityChangedEvent.
There are cases when EntityManager is still needed, like hard-deleting many entitites with a JPQL in database without loading entities to java heap. But the common rule is that DataManager should be used everywhere if it the task can be accomplished by using a DataManager.