@Transactional(readOnly = true) 未按 Spring 框架 5.2 版的预期工作

Moh*_*eem 5 hibernate spring-transactions spring-boot

Spring Framework 5.2 版开始,每当一个方法被标记为 @Transactional(readOnly=true) 我应该期待:

  • Session.setDefaultReadOnly(true).
  • flushMode 设置为MANUAL / NEVER但不是COMMIT(根据这篇文章和这个答案)。

但是,当我尝试使用 readOnly=true 时,上述两个条件都不符合预期。

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig {

    @Bean
    @Primary
    DataSource dataSource() throws PropertyVetoException {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/db_example");
        config.setDriverClassName(Driver.class.getName());

        config.setConnectionTestQuery("SELECT 1");
        config.setUsername("root");
        config.setPassword("root1234");



        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        config.addDataSourceProperty("useServerPrepStmts", "true");
        config.setMaximumPoolSize(11);
        return new HikariDataSource(config);
    }

    @Bean
    @Primary
    public EntityManagerFactory entityManagerFactory() throws PropertyVetoException {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(true);
        vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect");
        vendorAdapter.setShowSql(true);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan("com.example.hibernatedemo");
        factory.setDataSource(dataSource());
        Properties properties = new Properties();
        properties.setProperty("hibernate.generate_statistics", "true");
        factory.setJpaProperties(properties);
        factory.afterPropertiesSet();

        return factory.getObject();
    }

    @Bean
    PlatformTransactionManager transactionManager() throws PropertyVetoException {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory());
        txManager.setDataSource(dataSource());
        return txManager;
    }
}

     @Service
     public class PostService {
        .
        .
        .
        @Transactional(readOnly = true)
        public List<Post> getByTitles(List<String> titles) {
            List<Post> posts = postDao.getByTitle(titles);
            System.out.println(entityManager.getFlushMode());
            Session session = (Session) entityManager.getDelegate();
            System.out.println(session.isDefaultReadOnly());
            printLoadedState(posts);
            return posts;
        }
     }

     @Repository
     public class PostDao {
        .
        .
        .
        public List<Post> getByTitle(List<String> titles) {
           Session session = (Session) entityManager.getDelegate();
           return session.createQuery("select p from Post p where title IN :titles", Post.class)
                .setParameter("titles", titles).getResultList();
        }
     }
Run Code Online (Sandbox Code Playgroud)

以下是日志:

[2019-11-25 18:37:30] [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.getTransaction -  [  ] Creating new transaction with name [com.example.hibernatedemo.service.PostService.getByTitles]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
[2019-11-25 18:37:30] [http-nio-8091-exec-2] DEBUG o.s.jdbc.datasource.DataSourceUtils.prepareConnectionForTransaction -  [  ] Setting JDBC Connection [HikariProxyConnection@676721780 wrapping com.mysql.cj.jdbc.ConnectionImpl@66522ead] read-only
[2019-11-25 18:37:30] [http-nio-8091-exec-2] DEBUG o.h.e.t.internal.TransactionImpl.<init> -  [  ] On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
[2019-11-25 18:37:30] [http-nio-8091-exec-2] DEBUG o.h.e.t.internal.TransactionImpl.begin -  [  ] begin
.
.
.
flushMode: COMMIT
readOnly: false
.
.
.
[2019-11-25 18:37:33] [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.processCommit -  [  ] Initiating transaction commit
[2019-11-25 18:37:33] [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.doCommit -  [  ] Committing JPA transaction on EntityManager [SessionImpl(1945987926<open>)]
[2019-11-25 18:37:33] [http-nio-8091-exec-2] DEBUG o.h.e.t.internal.TransactionImpl.commit -  [  ] committing
[2019-11-25 18:37:33] [http-nio-8091-exec-2] DEBUG o.s.jdbc.datasource.DataSourceUtils.resetConnectionAfterTransaction -  [  ] Resetting read-only flag of JDBC Connection [HikariProxyConnection@676721780 wrapping com.mysql.cj.jdbc.ConnectionImpl@66522ead]
Run Code Online (Sandbox Code Playgroud)

pom.xml:

    <parent>
        <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
Run Code Online (Sandbox Code Playgroud)

如果需要更多信息,请添加评论。我可能错过了基本配置或其他东西。任何帮助表示赞赏。

Ken*_*han -1

好吧,我再看一遍源代码。HibernateJpaDialect如果显式设置为JpaTransactionManager(您已经通过设置HibernateJpaVendorAdapterLocalContainerEntityManagerFactoryBean)并且在 5.1 RC1 发布的此修复之后,它应该可以工作。

您测试的方法是否有可能被某些 bean 的@Transactional另一个方法调用@Transactional,而外部@Transactional方法是read-only=false?因为外部方法的设置@Transactional只会覆盖内部方法。(假设内层不设为PROPAGATION_REQUIRES_NEW)。

另一方面,你也可以尝试改变@Transactional你测试的方法,PROPAGATION_REQUIRES_NEW看看是否readOnly = true可以生效。