How to rollback a series of persist statements in

2020-06-19 03:23发布

问题:

I have a situation where i a need to rollback a series of persist method. I have a method in my controller class, from where I am calling the persist method.

Controller class:

@EJB
private jpa.session.ClassMasterFacade ejbFacadeCM;
@EJB
private jpa.session.StudentMasterFacade ejbFacadeSM;
@EJB
private jpa.session.ParentsMasterFacade ejbFacadePM;
@EJB
private jpa.session.AddressMasterFacade ejbFacadeAM;

public String confirmData() {
    try {
        ejbFacadeSM;.create(selectedSM);
        ejbFacadeCM;.create(selectedCM)
        ejbFacadeAM;.create(selectedAM);
        ejbFacadePM;.create(selectedPM);
    } catch (Exception e) {
        //rollback all
        JsfUtil.addErrorMessage(e, ResourceBundle.getBundle ("/resources/Bundle").getString("PersistenceErrorOccured"));
        return null;
    }
 }

Example Facade class:

@Stateless
public class ClassMasterFacade extends AbstractFacade<ClassMaster> {

    @PersistenceContext(unitName = "sdjv_smsPU")
    private EntityManager em;

    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    public ClassMasterFacade() {
        super(ClassMaster.class);
    }

}

All other facade classes are similar to the above class. The abstract class is:

public abstract class AbstractFacade<T> {
    private Class<T> entityClass;

    public AbstractFacade(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    protected abstract EntityManager getEntityManager();

    public void create(T entity) {
        getEntityManager().persist(entity);
    }

    public void edit(T entity) {
        getEntityManager().merge(entity);
    }

    public void remove(T entity) {
        getEntityManager().remove(getEntityManager().merge(entity));
    }

    public T find(Object id) {
        return getEntityManager().find(entityClass, id);
    }
}

I want to rollback all the create method if any exception is found.

I searched and found some answers:

  • Roll back transaction after exception in JPA + Spring
  • How to prevent JPA from rolling back transaction?

I am new to JPA and just started exploring it. Am I missing some concept?

there is till a problem, dont know if it is related. In all the entity class, i have a autogenerated key as updated. the problem is that if the exception occurs in second create statement, the autogenerated key for the first create statement is generated but still not updated in mysql as excepted..But in case of success all statement, the key sequence is broken.

Entity Class:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "class_group_id")
private Integer classGroupId;

回答1:

Use a javax.transaction.UserTransaction, which is available to you in any standard Java EE environment.

Usage is fairly straightforward, inject it and use the following methods:

  • begin() to start a transaction
  • commit() if all operations were successful and you want to commit the results
  • rollback() if an error occurred and you want to roll back to the point in time when begin() was called

See: Oracle documentation on UserTransaction

@Resource
UserTransaction tran;

...

public void confirmData (){

    tran.begin();
    try {
        ejbFacadeSM.create(selectedSM);
        ejbFacadeCM.create(selectedCM)
        ejbFacadeAM.create(selectedAM);
        ejbFacadePM.create(selectedPM);

        // Create's succeeded, commit transaction.
        tran.commit();
    } catch (Exception e) {
        // Error occurred, rollback transaction
        tran.rollback();
    }
}


回答2:

The rollback can be done by a transaction automatically. Just tell your method that it has to run inside a transaction and a new transaction shall be created if non exists. You can do this by annotating your method with transaction attribute REQUIRES_NEW

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public String confirmData() {
    try{
        ejbFacadeSM.create(selectedSM);
        ejbFacadeCM.create(selectedCM)
        ejbFacadeAM.create(selectedAM);
        ejbFacadePM.create(selectedPM);
    }catch(PersistenceException e){
        //do some stuff
    }
}

If one of your statements fails with a PersistenceException, all actions will be rolled back when your transaction ends. The transaction ends with your method.

Be aware of one exception to this, as the specification (in JSR 338) says:

All instances of PersistenceException except for instances of NoResultException, NonUniqueResultException, LockTimeoutException, and QueryTimeoutException will cause the current transaction, if one is active and the persistence context has been joined to it, to be marked for rollback.

Example: If your 4th statement ejbFacadePM.create(selectedPM) fails with e.g. a LockTimeoutException, only your 4th statement is rolled back but your transaction won't be marked for rollback. Next you would reach the catch-block of your code. This gives you the chance to recover from the exception by maybe writing some code to try the 4th statement again. When you leave your confirmData() method, the transaction would still be committed to the DB with the changes you did in your previous ejbFacadeXX methods.

If you still say you've to rollback the full transaction including all statements issued no matter what kind of exception occured, you can still tell the transaction to rollback in your catch-block. Therefore either use rollback() for Bean Managend Transactions (BMT) or setRollbackOnly() for Container Managed Transactions (CMT). If you don't set your transaction to be bean managed (e.g. by using @TransactionManagement( TransactionManagementType.BEAN )), your transaction is most likely container managed which is default since JEE 5.

BMT is not recommended because it requires your application to do work that your server can already do for you.



标签: java jpa