Failed to Catch Hibernate OptimisticLockException - java

I'm using EJB 3 with Hibernate.
I have a stateless session Bean. There is a method deleteItem in that bean.
When a client call the deleteItem method then delete occurred without any problem.
But If I'm trying to call the deleteItem method using a for loop and set the limit of that loop 5-10 times then sometimes the delete failed. But not always.
The delete operation actually delete data from 2 tables. The child table and the parent table.
Each Delete is committed by performing flush operation.
As I already mentioned that if i execute the delete one by one then no problem happen, it only happen when i try to run it concurrently. The exception I'm getting is below
Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key
constraint fails (`functionTest/MotherTable`, CONSTRAINT `FKBC6CB0E6D5545EFD` FOREIGN KEY
(`MotherTable_FieldId`) REFERENCES `ChildTable` (`childTableId`))
There is no way to happen concurrent delete operation here. And Item delete is not related with other Item delete operation. So if still concurrency happens it will not be a problem for deleting multiple item at the same time.
So, i came to a decision that - "May be the clients are accessing the Same Bean Instance in Multiple Thread" . In such situation two thread keep the same Entity Manager State in different state. One tries to flush the Persistence Context when the other is not yet completed remove of child item.
At that point the BatchUpdateException Occured. - It is my observation. I'm not 100% sure about it.
So to overcome this situation I have gone for Optimistic locking. I have created version column in the mother table. Now I'm getting the OptimisticLockException . But I'm not able to catch the exception. Below is the code which I'm using to catch the OptimisticLockException.
private boolean deleteItem(Item itemId) {
Item item= getItem(itemId);
removeChildTableData(item);
mEm.remove(item);
try
{
mEm.flush();
}
catch (OptimisticLockException e)
{
try {
Thread.sleep(1000);
}
catch (InterruptedException e1) {
e1.printStackTrace();
}
deleteItem(itemId);
}
catch(Exception ex)
{
if (ex.getCause() instanceof OptimisticLockException)
{
try {
Thread.sleep(1000);
} catch (InterruptedException x) {
}
deleteItem(itemId);
}
}
return true;
}
So my target is to catch the OptimisticLockException and reExecute the Delete Operation Again.
I have checked the Exception Class Name and it is EntityNotFound. But I see that in the stacktrace I'm getting OptimisticLockException as well as StaleObjectStateException.
So, Can anybody please guide me how I should catch this OptimisticLockException ?

You shouldn't. Also +1 to what JB said. This exception is trying to tell you something. You are trying to delete the parent row of a foreign key relation while a child is still referencing it. What are parent and child? Well:
a foreign key constraint fails (`functionTest/MotherTable`, CONSTRAINT `FKBC6CB0E6D5545EFD` FOREIGN KEY (`MotherTable_FieldId`) REFERENCES `ChildTable` (`childTableId`))
So MotherTable.MotherTableFieldId is referencing ChildTable.childTableId. And you're trying to delete a child while its mother is still pointing to it. That won't work.
I'm curious why you would have the relation this way, though. It seems that your model looks like this:
#Entity
#Table(name="MotherTable")
class Mother {
#Id
Long id;
#ManyToOne
#JoinColumn(name="MotherTable_FieldId")
Child child;
}
#Entity
#Table(name="ChildTable"
class Child {
#Id
#Column(name="childTableId")
Long id;
#OneToMany(mappedBy="child")
Set<Mother> mothers;
}
which is odd since now your child can have many mothers. Maybe you wanted this instead:
#Entity
class Mother {
#Id
Long id;
#OneToMany(mappedBy="mother")
Set<Child> children;
}
#Entity
class Child {
#Id
Long id;
#ManyToOne
#JoinColumn(name="mother_id")
Mother mother;
}
In this case, your DAO method would look like this:
#Transactional
public void deleteFamily(Mother mother) {
for (Child c: mother.getChildren()) {
em.remove(c);
}
em.remove(mother);
}
You could also use cascading:
#Entity
class Mother {
#Id
Long id;
#OneToMany(mappedBy="mother", cascading=CascadeType.ALL)
Set<Child> children;
}
which simplifies the DAO method to:
#Transactional
public void deleteFamily(Mother mother) {
em.remove(mother);
}
And even:
#Entity
class Mother {
#Id
Long id;
#OneToMany(mappedBy="mother", cascading=CascadeType.ALL, orphanRemoval=true)
Set<Child> children;
}
and now you don't have to em.remove() children:
#Transactional
public void deleteChild(Child child) {
Mother m = child.getMother();
m.getChildren().remove(child);
}
Also, you shouldn't try to commit transactions with em.flush(), that's wrong on several counts:
Transactions are committed with em.getTransaction().commit()
Think about what you're trying to do: is the deleteFamily supposed to happen in one transaction? Yes? Then implement it that way. Don't try to do a partial commit after deleting the children.
It's much more convenient to let someone else manage the transactions for you. Just mark the methods as #Transactional and let your JTA framework handle the details.
And DAO methods shouldn't even try to do transactions anyway. Think about this: you might want to implement a service later on that uses several DAO methods. If each of them tries to commit themselves in separate transactions the service call in toto cannot be a transaction. That's bad. So if you want to reuse your DAO methods, pull the transactional stuff into a separate layer above them.

Related

Spring Data domain events go missing (?)

I am having trouble publishing events from an aggregate-root in a Spring Boot application. What I basically want is to publish an "Update" event every time some information about a person is changed.
The code for this is pretty straightforward:
#Entity
public class Person {
#Transient
private final Collection<AbstractPersonRelatedEvent> events = new ArrayList<>();
Person(Person other) {
// copy other fields
other.events.foreach(events::add);
}
// other stuff
public Person updateInformation(...) {
Person updated = new Person(this);
// setting new data on the updated person
if (!hasUpdateEventRegistered()) {
updated.registerEvent(PersonDataUpdatedEvent.forPerson(updated));
}
return updated;
}
void registerEvent(AbstractPersonRelatedEvent event) {
events.add(event);
}
#DomainEvents
Collection<AbstractPersonRelatedEvent> getModificationEvents() {
return Collections.unmodifiableCollection(events);
}
#AfterDomainEventPublication
void clearEvents() {
events.clear();
}
}
I am managing Person instances through a manager:
#Service
#Transactional
class PersistentPersonManager implements PersonManager {
// other methods are omitted
#Override
public Person save(Person person) {
return personRepository.save(person);
}
}
However when I call the manager (manager.save(person.updateInformation(...)) the events seem to go "missing":
upon calling the save() method all events are still present but when Spring invokes getModificationEvents() the collection is empty. The events seem to have vanished somewhere in between (with only Spring-code being executed).
As this is pretty basic, I must be missing something essential but got stuck in a rut.
So how do I get back on track here?
I assume you are using JPA here.
For JPA the save operation actually does a merge on the JPA EnityManager.
For a detached entity merge loads/finds the entity with the same id from the database or the current session and copies all the (changed) fields over. This does ignore transient fields like the events.
You are dealing with detached entities because you are creating a new entity every time you call updateInformation.
So here is what is happening:
You load an entity (e1) from the database. It does not have any events registered.
By calling updateInformation you create a new detached entity (e2). You also register events with e2.
When calling save JPA finds the matching e1 and copies all changes from e2 into it, except the events. So e1 still has no events registered.
Events get triggered, but there aren't any because only e1 is used.
In order to fix this: Do not create new instances of the entity in updateInformation.

Start/end transaction in separate EJB methods

I developed a typical enterprise application that is responsible for provisioning customer to a 3rd party system. This system has a limitation, that only one thread can work on a certain customer. So we added a simple locking mechanism that consists of #Singleton which contains a Set of customerIds currently in progress. Whenever a new request comes for provisioning, it first checks this Set. If cusotomerId is present, it waits otherwise it adds it to the Set and goes into processing.
Recently it was decided, that this application will be deployed in cluster which means that this locking approach is no longer valid. We came up with a solution to use DB for locking. We created a table with single column that will contain customerIds (it also has a unique constraint). When a new provisioning request comes we start a transaction and try and lock the row with customerId with SELECT FOR UPDATE (if customerId does not exist yet, we insert it). After that we start provisioning customer and when finished, we commit transaction.
Concept works but I have problems with transactions. Currently we have a class CustomerLock with add() and remove() methods that take care of adding and removing customerIds from Set. I wanted to convert this class to a stateless EJB that has bean-managed transactions. add() method would start a transaction and lock the row while remove() method would commit transaction and thus unlocked the row. But it seems that start and end of transaction has to happen in the same method. Is there a way to use the approach I described or do I have to modify the logic so the transaction starts and ends in the same method?
CustomerLock class:
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class CustomerLock {
#Resource
private UserTransaction tx;
public void add(String customerId) throws Exception {
try {
tx.begin();
dsApi.lock()
} catch (Exception e) {
throw e;
}
}
public void remove(String customerId) throws Exception {
try {
tx.commit();
} catch (Exception e) {
throw e
}
}
}
CustomerProvisioner class excerpt:
public abstract class CustomerProvisioner {
...
public void execute(String customerId) {
try {
customerLock.add(customerId);
processing....
customerLock.remove(customerId);
} catch (Exception e) {
logger.error("Error", e);
}
}
...
}
StandardCustomerProvisioner class:
#Stateless
public class StandardCustomerProvisioner extends CustomerProvisioner {
...
public void provision(String customerId) {
// do some business logic
super.execute(customerId);
}
}
As #Gimby noted, you should not mix container-managed and bean-managed transactions. Since your StandardCustomerProvisioner has no annotation like "#TransactionManagement(TransactionManagementType.BEAN)" - it uses container-managed transactions, and REQUIRED by default.
You have 2 options to make it work:
1) To remove "#TransactionManagement(TransactionManagementType.BEAN)" with UserTransaction calls and run CMT
2) Add this annotation ("#TransactionManagement(TransactionManagementType.BEAN)") to StandardCustomerProvisioner and use transaction markup calls from this method, so all the invoked methods use the same transactional context. Markup calls from CustomerLock should be removed anyway.

Getting LazyInitializationException although using #Transactional(propagation=Propagation.REQUIRES_NEW)

I use jHipster with Spring Data JPA and have the following method:
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void doSomeWork(EntityA entityA) {
// some code
List<EntityB> entityBList = new ArrayList<EntityB>();
entityBList.add(new EntityB());
entityA.addAllEntityB(entityBList);
}
At the last line I get an org.hibernate.LazyInitializationException excption which I don't understand.
Why is it throwing this excption although the method is run in its own transaction?
Should it not just lazy load the list as the session is still open?
It might be that LazyInitializationException is thrown because a new transaction is started and entityA becomes "detached" as the result.
One can use something like this:
#Transactional
public void addEntityB(long entityAId, entityB) {
EntityA entityA = loadEntityA(entityAId);
addEntityBToEntityA(entityA, entityB);
saveEntity(entityB);
}
called from outside in this manner:
for (EntityB entityB : entityBList) {
try {
addEntityB(entityAId, entityB);
}
catch(Exception e){
log(e);
}
}
It is true that you load each time entityA, though.

hibernate 4 handle unique index exception

I have a system based on hibernate 4. I have an unique constraint in a table and need to handle it the following way:
try{
getMyService().create(myobj);
}catch(PersistenceException p){
//constraint fails
myobj.setConstraintColumn("new non unique value");
getMyService().create(myobj);//should save it
}
unfortunately I can't change the design of the system so I need just to figure it out this way.
EDIT
I get the following exception:
org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs)
The code of create method:
public E create(E entity) {
entityManager.persist(entity);
entityManager.flush();
entityManager.refresh(entity);
return entity;
}
It is not clear where your transaction boundaries are.
When the exception is thrown, you will need to:
1) Ensure that the first transaction is closed (it should be, but not sure - see if you get a nested transaction trying #2 alone)
2) begin a new transaction before you are able to persist/flush again (and subsequently commit that).
I finally figured out the issue. So, let me explain one by one.
First of all, take a look at
Propagation.REQUIRES_NEW does not create a new transaction in Spring with JPA
Then, for example we have a class and method like:
public class MyClass{
#Transactional
public void myMethod(){
....
}
}
So, first of all lets consider that myMethod is in it's own transaction completely, because transactions are AOP based, and it will be committed when appropriate aspect will fire, but only after method completes, throws exception, etc. So we can't partially commit, partially rollback, rollback incompletely,etc. So, we need to do the following:
Start big outer transaction
Start a new nested transaction, try to insert a record.
If nested transaction will fail, it will be rolled back, but the outer one will still be running.
If the first nested transaction failed, then start a new nested transaction and insert a record with new data, which will prevent ConstaintViolationException from being thrown.
So, in this case, we create a class:
public class ServiceHelper{
#Transational(proparation = **Propagation.REQUIRED_NEW**)
public void tryConstraint throws MyConstraintException{
try{
//insert
}catch(ConstraintViolationException e){
throw new MyConstraintException(e);
}catch(Exception ex){
throw new Exception(ex);
}
}
#Transational(proparation = **Propagation.REQUIRED_NEW**)
public void insertWithNoConflict throws Exception {
//Set new data
//insert, if still CVE or anything other, just throw it , or leave it for unchecked exceptions then
}
}
And our service:
public class MyService{
#Autowired
private ServiceHelper serviceHelper;
#Transactional(propagation = **Propagation.REGUIRED_NEW**)
public void createWithCheck(){
try{
serviceHelper.tryConstraint();
}catch(MyConstraintException e){
serviceHelper.insertWithNoConflict();
}
}
}
But there is still a weird situation because I need to use MyService methods for records creation in ServiceHelper, but I can't obtain them there because it will cause circular injections, so I have to get them via services factory like:
MyService service = (MyService)ServicesFactory.getInstance().getBean(MyService.BEAN_ID)
And I don't like it. But this approach works, I checked it today.
We should know 2 things: first of all we can't do anything with a transaction inside a method, we can't start there any new transaction, etc. A transaction context is relevant to method completely, it will still be there before a method ends. And the second thing is required_new doesn't work when we launch the method from the method of the same proxy class.

Spring+JPA+Hibernate: persist is updating the entity surprisingly. Please go through the details

In my code, I did as follows:
queried for a course entity
populate it with the given course data.
courseDao.update(entity) which internally calls persist(entity) method.
Surprisingly, the data is got updated successfully.
I am confused with this behaviour of persist method.
Please help me out.
code is as below:
//My Service......
#Service("myService")
#Transactional
public class MyServiceImpl implements MyService {
#Transactional(rollbackFor = { Throwable.class })
public void updateCourse(final Course course) throws MyServiceException {
------
------
CourseEntity courseEntity = courseDao.findById(course.getId());
populateCourseEntity(courseEntity, course);
courseDao.update(courseEntity);
}
}
//CourseDao.....
public class CourseDaoImpl implements CourseDao {
--------
public void update(final T entity) throws MyDaoException {
if (entity != null) {
this.entityManager.persist(entity);
}
else {
String errMsg = "Object to be updated cannot be null.";
throw new MyDaoException(errMsg);
}
}
}
When an entity is currently managed (attached to a session), all updates to it are directly reflected to the underlying storage even without calling persist().
In your case, you load your entity, so it's in the session. Then even if you don't call persist() it will be updated in the database on transaction commit.
The persist() description from the javadoc:
Make an entity instance managed and persistent.
This means that the method doesn't do anything in your case, since your entity is both persistent and managed.
P.S. Where I say "session", understand "entity manager"
JPA tries very hard to be a helpful API, such that anything you get from it (or save to it) will subsequently be tracked by JPA. This means than any further changes will be automatically handled for you by JPA without any additional work on your part.

Categories

Resources