I'm facing one problem, if there are two insert statement and there is exception in second insert query, rollback is not working properly.
Insert 1 for table PatientDemographics SUCCESS
Insert 2 for table EncounterHistory FAILS
Then result of insert 1 should not rollback (not revert from database automatically, it is to insert a record in the database).
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void addPatientDemographicsAndEncounterHistory(PatientDemographicsDTO patientDemographicsDTO, int count) throws Exception{
// save patient demographics data
PatientDemographics patientDemographics = new PatientDemographics();
patientDemographics.setFullName(patientDemographicsDTO.getFullName());
patientDemographics.setMedicalRecordNumber(patientDemographicsDTO.getMedicalRecordNumber());
mednetDAO.saveOrUpdate(patientDemographics);
if(count == 1){
throw new Exception(count+" Records Saved");
}
// save patient encounter history
EncounterHistory encounterHistory = new EncounterHistory();
encounterHistory.setCompanyID(patientDemographics.getDefaultCompanyID());
encounterHistory.setPatientID(patientDemographics.getPersonID());
encounterHistory.setDepartmentID(patientDemographics.getLastDepartmentID());
encounterHistory.setDoctorID(patientDemographics.getLastDoctorID());
encounterHistory.setLastOPDDate(patientDemographics.getLastOPDDate());
encounterHistory.setLastOPDFreeCount(patientDemographics.getLastOPDFreeCount());
encounterHistory.setLastNotesID(null);
encounterHistory.setLastIPDDate(null);
encounterHistory.setLastIPDFreeCount(patientDemographics.getLastIPDFreeCount());
mednetDAO.saveOrUpdate(encounterHistory);
}
In its default configuration, the Spring Frameworkâs transaction infrastructure code only marks a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass of RuntimeException. Checked exceptions that are thrown from a transactional method do not result in rollback in the default
configuration.
Remove throws from your method signature and throw RuntimeException :
if(count == 1){
throw new RuntimeException(count+" Records Saved");
}
Related
We have a Kafka consumer in a Java - Spring Boot service that has multiple instances in multiple clusters (all with same consumer group id). The consumer saves data to the database (SQL Server).
The code checks if a record exists in the ItemSet table before saving to the database. The actual data in the payload gets saved in a child table ItemValue and not the parent, ItemSet. The relational data table hierarchy is one to many in this order ItemSet -> ItemName -> ItemValue. ItemSet table has a unique constraint for department id, season combination to prevent multiple duplicate adds.
I need to do some processing after catching this exception to ensure that the incoming data still gets saved under the existing ItemSet and doesn't get lost. I am using Spring Data JPA and as soon as I catch the exception and try to retrieve the existing record I end up getting:
org.hibernate.AssertionFailure: null id in ItemSet entry (don't flush the Session after an exception occurs).
The getItemSet() in the catch block blows up.
What is the best way to overcome these race conditions?
ItemSet savedItemSet = null;
try
{
String seasonName = itemSet.getSeasonName();
Long seasonYear = itemSet.getSeasonYear();
Long departmentId = itemSet.getDepartment().getId();
List<ItemSet> itemSets = attributeSetRepository.findBySeasonNameAndSeasonYearAndDepartmentId(
seasonName, seasonYear, departmentId);
LOGGER.info("Found {} item sets corresponding to season name : {}, season year : {}, "
+ "department id : {}", itemSets.size(), seasonName, seasonYear, departmentId);
if(CollectionUtils.isEmpty(itemSets)) {
savedItemSet = itemSetRepository.save(itemSet);
}
else {
return new CreatedItemSet(itemSets.get(0).getId());
}
}
catch(PersistenceException | DataIntegrityViolationException e)
{
LOGGER.error("An exception occurred while saving itemSet set", e);
if (e.getCause() instanceof ConstraintViolationException)
{
String seasonName = itemSet.getSeasonName();
Long seasonYear = itemSet.getSeasonYear();
Long deptId = itemSet.getDepartment().getId();
LOGGER.info("A duplicate item set found in the database corresponding "
+ "to season name : {}, season year : {} and department : {}",
seasonName, seasonYear, deptId);
ExistingItemSet existingItemSet = getItemSet(seasonName,
seasonYear, deptId);
if(existingItemSet == null) {
LOGGER.info("No item set found");
return null;
}
return new CreatedItemSet(existingItemSet.getId());
}
}
You can't "continue". A transaction is marked for rollback and the persistence context is unusable after a constraint violation happens.
You can either try to avoid the constraint violation, by checking if the DB contains an entry before persisting/updating, or you run the rest of your code in a separate transaction if a constraint violation happens.
I need to insert records in multiple tables, but these tables are belonging to different schemas so transaction values are also different. But I want to insert records in all the tables atomically ( all or nothing). Following code I have tried to handle this.
#Transactional(value = "First")
public void insert(String f1, String f2, String f3, String f4){
try {
Entity1 entity1 = createEntity1(f1);
FirstTable1.insert(entity1);
insertInSecondSchema(f2, f3, f4);
}
catch (Exception e){
String errorMessage = "Error occur while inserting in first schema";
logger.error(errorMessage, e);
throw new CustomException(errorMessage,e);
}
}
#Transactional(value = "second")
private void insertInSecondSchema(String f2, String f3, String f4) {
try {
Entity2 entity2 = createEntity2(f2);
SecondTable2.insert(entity2);
Entity3 entity3 = createEntity3(f3);
SecondTable3.insert(entity3);
Entity4 entity4 = createEntity4(f4);
SecondTable4.insert(entity4);
}
catch (Exception e){
String errorMessage = "Error occur while inserting in second schema";
logger.error(errorMessage, e);
throw new CustomException(errorMessage,e);
}
}
In this scenario if some error occurs while inserting in SecondTable3. SecondTable2 is not rolled back but FirstTable1 is rolled-back. I have tried many propagation level in secondMethod, but none of them working for me. Please help how can I fix this using same code or some other code structure.
In this kind of situation I think is better to think as if this was a distributed system. In this case, you cannot make these insertions transactional, instead of that, you should use compensating actions in case one of the transaction succeeds and the other fails.
In your case if insertInSecondSchema fails then you have to call delete of the same record for the first schema, so the result is like the first insertion never happens
How to execute native query (create table) in java and also in return get information about is operation was successfull or not. Every method i did try always work the same. Query is working but i am getting errors about "how really bad that query was" but as i said it works.
try{
session.createNativeQuery("create table test (id number)").getResultList()
}
catch(Exception e){
// I am getting error "Could not extract result set metadata"
// Is there any way to execute getResultList() only if operation was select?
}
Summarizing, I need execute CRUD.
1. If "select" was executed i need resultList.
2. If "create" i don't want to execute getResultSet()
3. If "insert" was executed i need information about numbers of row affected.
etc... ... ...
And most important i always need information about eventual errors! If query had missing syntax or something i always need to get that information.
Guys can someone help me? I am fighting with this from several days...
A simple example using Native Query. You can determine the result of query from the affected rows value.
EntityTransaction entityTransaction;
EntityManager entityManager;
try
{
entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
// rowsUpdated - The number of "affected rows".
int rowsUpdated = entityManager.createNativeQuery("create table test (id nubmer)").executeUpdate();
entityTransaction.commit();
}
catch (HibernateException | IllegalStateException e)
{
// handle exceptions
if (entityTransaction != null && entityTransaction.isActive())
{
entityTransaction.rollback();
}
}
I have a table A_TABLE, through entityManger going to commit record using begin transction and try to commit. but in table we have combination of some value is there then throwing ORA-00001: unique constraint exception. And that transction is not active can't able to rollback.
So I deleted that record in DB, try to save same value record with new entityManger transction begin, but its try to saving the record two times, because old one got ORA-00001: unique constraint exception entity also going in this transction to DB and in console two insert is calling.
Need solution for this,
Code :
#Autowired
private EntityManager entityManger;
TableEntity entityObject = new TableEntity(); // Always creating new objects
entityObject .setYear(2018); // Combination of year and month
//if its availbe in DB its throw ORA-00001: unique constraint exception
entityObject .setMonth(01);
if (!entityManager.getTransaction().isActive()) {
entityManager.getTransaction().begin();
}
entityManager.persist(entityObject);
try{
if (entityManager.getTransaction().isActive()) {
entityManager.getTransaction().commit();
}
} catch (Exception e) {
if (entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback(); // Not come to this part when ORA-00001: unique constraint exception occur
}
throw e;
}
INSERT call twice above code after delete unique record from DB, but still throwing ORA-00001: unique constraint exception occur because old one got ORA-00001: unique constraint exception entity also going in this transction to DB and in console two insert is calling.
Here is my code:
EntityManager em = JPAUtil.createEntityManager();
try {
EntityTransaction tx = em.getTransaction();
try {
//do some stuff here
tx.begin();
List es = em.createNamedQuery("getMyEntities", MyEntity.class).getResultList();
for (MyEntity e : es) {
em.lock(e, LockModeType.OPTIMISTIC);
}
if (es.size() != 0) {
em.remove(es.get(0));
}
tx.commit
} finally {
if (tx.isActive()) {
tx.rollback();
}
}
} finally {
em.close();
}
When I'm executing that code I get :
...
..........
Caused by: javax.persistence.OptimisticLockException: Newer version [null] of entity [[MyEntity#63]] found in database
at org.hibernate.ejb.AbstractEntityManagerImpl.wrapLockException(AbstractEntityManagerImpl.java:1427)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1324)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:80)
... 23 more
Can anybody explain me why that?
I suppose that you have added the #Version-annotated column after you already had some entries in database, so that some null-values were created for the already-existing records.
Now hibernate can't compare the versions.
I would try to set the version column to 1 for all null-versioned entities.
I think this error is thrown due to the fact that I try to delete a record that has a lock on it. Trying to delete this row, will set the version to null, but the version in the database still remains set to former number. It seems that hibernate core perceive a null value to be not reliable for this kind of operation.
If I have to do this kind of operation, I have to release the lock first on this entity.
Anyone with better knowledge on it has to clarify that issue.