Spring 中使用 JPA 进行事务和回滚

发布于 2024-12-06 15:20:11 字数 8026 浏览 1 评论 0原文

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

阪姬 2024-12-13 15:20:11

我不知道我是否正确理解您的问题,但您的问题可能与 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 的 setter,并且该字段可以是私有的。

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.

一口甜 2024-12-13 15:20:11

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文