使用 @transactional注释在服务中回滚事务
晚上好,伙计们。
我有一种从端点调用的方法,可以将对象保存在数据库中,并且我想通过在服务内部发生任何例外情况,通过回滚交易来为此代码添加一些“保护”。
我将JAX-RS用于REST API部分,而OracleDB作为数据库。我的代码如下。
端点:
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response createAcordo(AcordoDTO acordoDTO) {
return acordosService.save(acordoDTO, token);
}
服务:
@Transactional(rollbackOn = Exception.class)
public Response save(AcordoDTO acordoDTO, String token) {
try {
... some bisiness logic and validation
Acordo acordo = acordoRepository.save(AcordoConverter.toAcordo(acordoDTO));
UserDTO usr = getUserInfoFromToken();
ApprovalDTO approvalDTO = new ApprovalDTO ();
approvalDTO .setUser(usr.getName()); // this method is throwing null pointer exception
// because usr is null
return Response.status(Response.Status.CREATED).entity(acordo).build();
} catch (Exception e) {
return new SystemErrorException().toResponse();
}
}
尽管例外和 @transactional Anotation上面的代码正在将新的acordo
对象保存到数据库中。但是,我期望发生的是完整的交易是回滚的,并且没有保存对象。 总而言之,我想知道的是:
- 我误解了有关 @transactional用法的任何概念吗?
- 如何修复此代码以实现所需的目标(回滚)?
ps.1 - 我尝试没有尝试/捕获
块,结果是相同的。保存()仍然将对象提交到数据库中。
ps.2 - acordorepository.save()具有@transaction注释。
ps.3 - 我尝试添加transactionsynchronizationRegistry.setRollbackonly()
在捕获块中,并且它工作。但是(对我来说)它看起来更像是作弊,而不是适当的解决方案。
ps.4 - 在我使用transactionsynchronizationRegistry.setrollbackonly()
交易状态为status_marked_rollback
,在所有其他测试中,它是status> status_active
status> status_active status_marked_rollback /代码>。
更新1
我的acordorepository.save()
如下。我试图检查是否有一个或两次转变。为此,我尝试(不确定是一个好主意)打印tsr.getTransactionKey()
均在我的service.save.save()
和上。 acordorePository.save()
,结果为相同的键,这使我相信只有一项交易。
@Resource
TransactionSynchronizationRegistry tsr; // I injected it here
// for testing purposes
@Transactional
public T save(T entity) {
if (entity.getId() == null) {
entityManager.persist(entity);
} else {
entityManager.merge(entity);
}
entityManager.flush();
return find(entity.getId());
}
更新2:
我的persistence.xml
如下。
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="namePU" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>java:/nameDS</non-jta-data-source>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<jar-file>lib/client-notification-1.30.jar</jar-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
<property name="hibernate.connection.autocommit" value="false"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.generate_statistics" value="false"/>
<!-- desabilita JSR-303 no save/update -->
<property name="javax.persistence.validation.mode" value="none"/>
</properties>
</persistence-unit>
</persistence>
Best regards,
Thomaz.
Good evening, guys.
I have a method called from an endpoint that saves an objects on my database, and I'd like to add some "protection" to this code by rolling back the transaction in the case of any exception occur inside the service.
I'm using JAX-RS for the REST API part and OracleDB as the database. My code is as follows.
Endpoint:
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response createAcordo(AcordoDTO acordoDTO) {
return acordosService.save(acordoDTO, token);
}
Service:
@Transactional(rollbackOn = Exception.class)
public Response save(AcordoDTO acordoDTO, String token) {
try {
... some bisiness logic and validation
Acordo acordo = acordoRepository.save(AcordoConverter.toAcordo(acordoDTO));
UserDTO usr = getUserInfoFromToken();
ApprovalDTO approvalDTO = new ApprovalDTO ();
approvalDTO .setUser(usr.getName()); // this method is throwing null pointer exception
// because usr is null
return Response.status(Response.Status.CREATED).entity(acordo).build();
} catch (Exception e) {
return new SystemErrorException().toResponse();
}
}
The code above, despite the exception and @Transactional anotation, is saving the new Acordo
object to the database. What I'd expect to happen, however, is that the full transaction was rolledback and no objects were saved.
In summary, what I'd like to know is:
- I'm misundertaing any concepts regarding @Transactional usage ?
- How to fix this code to achieve de desired goal (rollback) ?
PS.1 - I tried without try/catch
block and the result is the same. The save() still commits the object to the database.
PS.2 - The acordoRepository.save() has @Transaction annotation.
PS.3 - I tried adding TransactionSynchronizationRegistry.setRollbackOnly()
inside the catch block and it worked. However (for me) it looks like more like a cheat than a proper solution.
PS.4 - After I used TransactionSynchronizationRegistry.setRollbackOnly()
the transaction status was STATUS_MARKED_ROLLBACK
and in all other tests it was STATUS_ACTIVE
.
UPDATE 1
My acordoRepository.save()
is as follows. I tried to check whether there was one or two transacions. In order to do that, I tried (not sure it's a good idea) to print the tsr.getTransactionKey()
both on my Service.save()
and acordoRepository.save()
and the result was the same key, which led me to believe that there's only one transaction.
@Resource
TransactionSynchronizationRegistry tsr; // I injected it here
// for testing purposes
@Transactional
public T save(T entity) {
if (entity.getId() == null) {
entityManager.persist(entity);
} else {
entityManager.merge(entity);
}
entityManager.flush();
return find(entity.getId());
}
UPDATE 2:
My persistence.xml
is as follows.
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="namePU" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>java:/nameDS</non-jta-data-source>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<jar-file>lib/client-notification-1.30.jar</jar-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
<property name="hibernate.connection.autocommit" value="false"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.generate_statistics" value="false"/>
<!-- desabilita JSR-303 no save/update -->
<property name="javax.persistence.validation.mode" value="none"/>
</properties>
</persistence-unit>
</persistence>
Best regards,
Thomaz.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您想要
@transactional
回滚方法,则不得在该方法中捕获异常。从保存
中删除catch(异常)
,然后将其捕获在CreateAcordo中。那应该起作用。If you want the
@Transactional
method to rollback you must not catch the exception inside that method. Remove thecatch(Exception)
fromsave
and catch it in createAcordo instead. That should work.可能是你可以尝试
throwable.class而不是expcy.class。由于throwable是超级类
顺便说一句,拜迪福特弹簧将负责回滚,但即使我们明确声明,它也会检查检查/未检查的例外
特别 - rollbackfor = {anyexception.class}检查的异常
您可以看到此示例: https .com/spring/transactional-rollbackfor-example-using-spring-boot/
may be you can try with
Throwable.class instead Exception.class since throwable is super class of if with remove try/catch
by the way bydefault spring will take care of rollBack but even if we are declaring explicitly then it will check for checked/unchecked exception
specifically- rollbackFor = {AnyException.class} for checked exception becuse runtimeException will take care by spring itself
you may look this example: https://www.netsurfingzone.com/spring/transactional-rollbackfor-example-using-spring-boot/
我无法对代码发表评论后whell :)
我正在做出答案:
我认为您可以使用这样的例外:
Whell after I couldn't make comment with code :)
I'm making an answer:
I think that you can just use an exception like this: