java 找到预先系结的JDBC联机!如果告知JpaTransactionManager要管理DataSourc,则JpaTransactionManager不支援在DataSourceTransactionManager内执行

oknwwptz  于 2022-10-30  发布在  Java
关注(0)|答案(1)|浏览(238)

我有与PostgreSQL数据库一起工作的Sping Boot 应用程序。
我只有一个数据源,用于Spring Data JPA操作和Liquibase迁移(我猜)。

  1. spring:
  2. datasource:
  3. driver-class-name: org.postgresql.Driver
  4. url: jdbc:postgresql://*************/*****
  5. username:*******
  6. password:*******
  7. type: com.zaxxer.hikari.HikariDataSource
  8. connection-test-query: SELECT 1;
  9. idle-timeout: 30000
  10. maximum-pool-size: 100
  11. minimum-idle: 7
  12. jpa:
  13. hibernate:
  14. ddl-auto: validate
  15. database-platform: org.hibernate.dialect.PostgreSQLDialect
  16. show-sql: false
  17. properties:
  18. hibernate:
  19. default_schema: public
  20. format_sql: true
  21. enable_lazy_load_no_trans: true
  22. generate_statistics: false
  23. jdbc.batch_size: 100
  24. order_inserts: true
  25. order_updates: true
  26. jdbc.batch_versioned_data: true
  27. query.fail_on_pagination_over_collection_fetch: true
  28. liquibase:
  29. liquibase-schema: public
  30. default-schema: public
  31. change-log: classpath:db/changelog/db.changelog-master.xml

我还有一个Sping Boot 配置类,它指定了我希望在应用程序中使用哪一个事务管理器。

  1. @Configuration
  2. public class TransactionManagerConfig {
  3. @Primary
  4. @Bean(name = "transactionManager")
  5. public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {
  6. JpaTransactionManager transactionManager = new JpaTransactionManager();
  7. transactionManager.setEntityManagerFactory(sessionFactory);
  8. return transactionManager;
  9. }
  10. }

当使用我的数据库时,我必须用不同的方式与我的数据进行通信。第一种是使用带有@Transactional注解的服务方法。例如,为了简单阅读,我使用下面的方法:

  1. @Transactional(readOnly = true, isolation = Isolation.READ_COMMITTED)
  2. public List<Dto> fetchAll() {...

另一种方式稍微复杂一点,但仍然很常见。例如,对于更新,我手动与事务交互:

  1. @Service
  2. public class EntityService {
  3. @Autowired
  4. private DocumentRepository documentRepository;
  5. @Autowired
  6. private PlatformTransactionManager transactionManager;
  7. public Long update(Long id, Entity entity) {
  8. DefaultTransactionDefinition transForCursor = new DefaultTransactionDefinition();
  9. transForCursor.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
  10. transForCursor.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
  11. TransactionStatus cursorTransactionStatus = transactionManager.getTransaction(transForCursor);
  12. try {
  13. //...
  14. //here I am interactiong with S3 File storage
  15. //...
  16. savedEntity = entityRepository.save(entity);
  17. } catch (Exception e) {
  18. //...
  19. //if I catch any exception I should delete files from S3 File storage
  20. //which I just uploaded and only then I should rollback database transaction
  21. //...
  22. if (!(e instanceof RuntimeException)) {
  23. transactionManager.rollback(cursorTransactionStatus);
  24. }
  25. throw e;
  26. }
  27. try {
  28. transactionManager.commit(cursorTransactionStatus);
  29. } catch (Exception e) {
  30. //...
  31. //if any exception happens when I was trying to commit I still should delete files
  32. //...
  33. throw new TransactionException();
  34. }
  35. return savedEntity.getId();
  36. }
  37. }

问题是,有时候在调用read或update方法时会出现以下异常:

  1. org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! JpaTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single JpaTransactionManager for all transactions on a single DataSource, no matter whether JPA or JDBC access.

但是有时候它运行的很好,没有任何错误,最有趣的是,上次我遇到这个问题的时候,每十次调用我的read方法,就会出现这个异常。
有没有人知道为什么会发生这种情况以及如何解决它?非常感谢您的时间和您的答案!
我试图通过在read方法中添加@Transactional注解来修复它。我还在定义事务管理器的配置类中添加了@Primay注解。但不幸的是,它对我没有帮助。我试图在这里找到相同的问题,但我没有满足相同的条件。

ohfgkhjo

ohfgkhjo1#

那个模式:

  1. TransactionStatus tx = transactionManager.getTransaction(...);
  2. try {
  3. ...
  4. } catch (Exception e) {
  5. if (!(e instanceof RuntimeException)) {
  6. transactionManager.rollback(tx);
  7. }
  8. throw e;
  9. }
  10. try {
  11. transactionManager.commit(tx);
  12. } catch (Exception e) {
  13. ...
  14. throw new TransactionException();
  15. }
  16. return savedEntity.getId();

肯定是不正确的,经验法则是:你必须在你开始的地方完成(提交或回滚)事务。2但是在你的情况下,这是不正确的,所以你遇到了资源泄漏和其他相关的后果。
如果需要在事务回滚时清除某些外部资源,可以使用以下选项:
1.春田道:

  1. TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
  2. @Override
  3. public void afterCompletion(int status) {
  4. if (STATUS_ROLLED_BACK== status) {
  5. // do something
  6. }
  7. }
  8. });

1.冬眠道:

  1. SessionImplementor session = entityManager.unwrap(SessionImplementor.class);
  2. session.addEventListeners(new BaseSessionEventListener() {
  3. @Override
  4. public void transactionCompletion(boolean success) {
  5. if (!success) {
  6. // do something
  7. }
  8. }
  9. });

1.另一个冬眠者道:

  1. SessionImplementor session = entityManager.unwrap(SessionImplementor.class);
  2. session.getActionQueue().registerProcess((success, sess) -> {
  3. if (!success) {
  4. // do something
  5. }
  6. });
展开查看全部

相关问题