Transaction and Rollback in Spring with JPA

2019-05-30 02:33发布

Hi friends of StackOverflow, I do not understand how to roll back I read the documentation of Spring, but I still do not understand. Basically I'm going to persist an object in the db (with primary key manually) all the way here all right, the object is inserted into the db. But when you persist the object again with the same primary key I have caused an exception, and rightly so, a violation of restriction of uniqueness. In this case I would get a Transaction rollback and warn you that there was the problem and continue running the program

This is my class:

public class ServiceDaoImpl{

    @PersistenceContext (unitName="fb-persistence")
    protected EntityManager em;

    public void setEntityManager(EntityManager entityManager) {
        this.em = entityManager;
    }

    @Transactional(readOnly=false)
    public void write(Service entity){
        try {
            em.persist(entity);
            em.flush();
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }

    /*
    * .. other method
    */
}

And this is the stack of error:

javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1214)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1147)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1153)
    at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:798)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
    at $Proxy27.flush(Unknown Source)
    at it.synclab.fb.jpa.dao.impl.GenericDaoImpl.write(GenericDaoImpl.java:236)
    at it.synclab.fb.jpa.dao.impl.EnteDaoImpl.write(EnteDaoImpl.java:1)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy34.write(Unknown Source)
    at it.synclab.fb.jpa.test.ConfigTest.insertEnte(ConfigTest.java:47)
    at it.synclab.fb.jpa.test.ConfigTest.main(ConfigTest.java:32)
Caused by: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:268)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
    at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:795)
    ... 21 more
Caused by: java.sql.BatchUpdateException: ORA-00001: violata restrizione di unicità (FLUSSIBATCH.SYS_C008896)

and my configuration files are (persistence.xml and applicationContext.xml):

This is the applicationContext.xml: ...

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="fb-persistence" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
        <property name="entityManagerFactory" ref="entityManagerFactory"/> 
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />


    <bean name="serviceDaoImpl" class="it.synclab.fb.jpa.dao.impl.ServiceDaoImpl" />
...

This is the persistence.xml:
<persistence>
    <persistence-unit name="fb-persistence" transaction-type="RESOURCE_LOCAL">  
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <mapping-file>META-INF/orm.xml</mapping-file>
        <class>it.entity.Service</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
            <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver"/>  
            <property name="hibernate.show_sql" value="true"/>  
            <property name="hibernate.connection.username" value="############"/>  
            <property name="hibernate.connection.password" value="############"/>  
            <property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521:XE"/> 
        </properties>
   </persistence-unit>
</persistence>
        at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:629)
        at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:9467)
        at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:211)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
        ... 27 more
    Exception in thread "main" org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
        at $Proxy34.write(Unknown Source)
        at it.synclab.fb.jpa.test.ConfigTest.insertEnte(ConfigTest.java:47)
        at it.synclab.fb.jpa.test.ConfigTest.main(ConfigTest.java:32)
    Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly
        at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:73)
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467)
        ... 9 more

Why have to be complicated? I just do not understand ... I hope some of you have had the same problem and solved

2条回答
贪生不怕死
2楼-- · 2019-05-30 03:20

Add a try catch block in your write method and throw an exception in case of error.

Put a rollbackFor or rollbackForClassname attribute in your @Transactional annotation for the exception that you rise, if you want to controll rollback events.

查看更多
叼着烟拽天下
3楼-- · 2019-05-30 03:21

I don't know if I understand your question correctly, but your problem might be with ServiceDaoImpl exception handling:

try {
     em.persist(entity);
     em.flush();
} catch(Exception ex) {
     ex.printStackTrace();
}

This is a really bad practice (tm). Don't catch the exception but let it pop up from your method. This way:

  1. Transaction demarcation mechanism will intercept the exception and mark transaction as rollback only
  2. You won't ignore the exception (yes, catching and logging is almost as bad as swallowing)
  3. Very likely the exception will be caught at some higher level and logged properly (using SLF4J or similar) and you won't have this boilerplate.

So to cut long story short:

public class ServiceDaoImpl{

    @PersistenceContext (unitName="fb-persistence")
    private EntityManager em;

    @Transactional(readOnly=false)
    public void write(Service entity){
        em.persist(entity);
        em.flush();
    }

}

Note that you don't need setter for EntityManager and the field can be private.

查看更多
登录 后发表回答