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

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

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

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://*************/*****
    username:*******
    password:*******
    type: com.zaxxer.hikari.HikariDataSource
    connection-test-query: SELECT 1;
    idle-timeout: 30000
    maximum-pool-size: 100
    minimum-idle: 7
  jpa:
    hibernate:
      ddl-auto: validate
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    show-sql: false
    properties:
      hibernate:
        default_schema: public
        format_sql: true
        enable_lazy_load_no_trans: true
        generate_statistics: false
        jdbc.batch_size: 100
        order_inserts: true
        order_updates: true
        jdbc.batch_versioned_data: true
        query.fail_on_pagination_over_collection_fetch: true
  liquibase:
    liquibase-schema: public
    default-schema: public
    change-log: classpath:db/changelog/db.changelog-master.xml

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

@Configuration
public class TransactionManagerConfig {

    @Primary
    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(sessionFactory);
        return transactionManager;
    }
}

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

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

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

@Service
public class EntityService {
    @Autowired
    private DocumentRepository documentRepository;

    @Autowired
    private PlatformTransactionManager transactionManager;

    public Long update(Long id, Entity entity) {
        DefaultTransactionDefinition transForCursor = new DefaultTransactionDefinition();
        transForCursor.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transForCursor.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus cursorTransactionStatus = transactionManager.getTransaction(transForCursor);
        try {
            //...
            //here I am interactiong with S3 File storage
            //...
            savedEntity = entityRepository.save(entity);
        } catch (Exception e) {
            //...
            //if I catch any exception I should delete files from S3 File storage
            //which I just uploaded and only then I should rollback database transaction
            //...
            if (!(e instanceof RuntimeException)) {
                transactionManager.rollback(cursorTransactionStatus);
            }
            throw e;
        }
        try {
            transactionManager.commit(cursorTransactionStatus);
        } catch (Exception e) {
            //...
            //if any exception happens when I was trying to commit I still should delete files
            //...
            throw new TransactionException();
        }
        return savedEntity.getId();
    }
}

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

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#

那个模式:

TransactionStatus tx = transactionManager.getTransaction(...);
try {
    ...
} catch (Exception e) {
    if (!(e instanceof RuntimeException)) {
        transactionManager.rollback(tx);
    }
    throw e;
}
try {
    transactionManager.commit(tx);
} catch (Exception e) {
    ...
    throw new TransactionException();
}
return savedEntity.getId();

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

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
    @Override
    public void afterCompletion(int status) {
        if (STATUS_ROLLED_BACK== status) {
            // do something
        }
    }
});

1.冬眠道:

SessionImplementor session = entityManager.unwrap(SessionImplementor.class);
session.addEventListeners(new BaseSessionEventListener() {

    @Override
    public void transactionCompletion(boolean success) {
        if (!success) {
            // do something
        }
    }

});

1.另一个冬眠者道:

SessionImplementor session = entityManager.unwrap(SessionImplementor.class);
session.getActionQueue().registerProcess((success, sess) -> {
    if (!success) {
        // do something
    }
});

相关问题