【问题标题】:Transaction and Rollback in Spring with JPA使用 JPA 在 Spring 中进行事务和回滚
【发布时间】:2011-09-26 09:04:13
【问题描述】:

StackOverflow 的朋友们好, 我不明白如何回滚我阅读了Spring的文档,但我仍然不明白。基本上我会一直在数据库中保留一个对象(手动使用主键),一直到这里,对象被插入到数据库中。但是,当您使用相同的主键再次持久化对象时,我导致了一个异常,这是正确的,违反了唯一性限制。在这种情况下,我会收到事务回滚并警告您存在问题并继续运行程序

这是我的课:

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
    */
}

这是错误堆栈:

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)

而我的配置文件是(persistence.xml 和 applicationContext.xml):

这是 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

为什么一定要复杂?我只是不明白......我希望你们中的一些人有同样的问题并解决了

【问题讨论】:

  • 我想处理ConstraintViolationException异常来启动自定义异常,所以需要回滚

标签: java hibernate spring jpa-2.0 java-ee-6


【解决方案1】:

我不知道我是否正确理解了您的问题,但您的问题可能与ServiceDaoImpl 异常处理有关:

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

这是一个非常糟糕的做法(tm)。不要捕获异常,而是让它从您的方法中弹出。这样:

  1. 事务分界机制将拦截异常并将事务标记为仅回滚
  2. 您不会忽略异常(是的,捕获和记录几乎和吞咽一样糟糕)
  3. 很可能会在更高级别捕获异常并正确记录(使用 SLF4J 或类似工具),并且您不会拥有此样板文件。

长话短说:

public class ServiceDaoImpl{

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

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

}

请注意,EntityManager 不需要设置器,并且该字段可以是私有的。

【讨论】:

  • 所以我必须在应用程序的顶层捕获异常
  • 不,如果您想要的行为是“如果发生运行时异常则回滚”而不是简单地什么都不做,让异常流动。毕竟,这为什么要使用自动事务管理?仅当您想在回滚之前执行一些额外的工作时才手动处理异常。但是请确保重新抛出异常 - 否则容器将无法发现事务应该回滚。
  • 在什么意义上?我应该检查什么?
  • 我没有关注...只是不要抓住异常,你会没事的:-)。
【解决方案2】:

在你的 write 方法中添加一个 try catch 块,并在发生错误时抛出异常。

如果您想控制回滚事件,请在您的 @Transactional 注释中为您引发的异常添加 rollbackForrollbackForClassname 属性。

【讨论】:

  • 这对他没有帮助,因为这个方法永远不会抛出异常(它正在被吞噬)。
  • 是的,他可以添加异常抛出并用回滚类控制它,我编辑我的帖子
  • 没关系,但如果我有另一种类型的异常会被吞下,正如@TomaszNurkiewicz 正确指出的那样
猜你喜欢
  • 2013-05-08
  • 2018-08-07
  • 1970-01-01
  • 1970-01-01
  • 2012-01-27
  • 2015-05-31
  • 2020-07-07
  • 2018-12-30
相关资源
最近更新 更多