首页 > 解决方案 > Spring Boot / @Transactional 在独立模式下工作,但不在 Glassfish 中

问题描述

我有一个使用 JPA 存储库来操作 Postgres 数据库的服务 (JHipster4)。该服务正在导入 CSV 并创建大量实体。创建实体的公共方法用 @Transactional 注释(import org.springframework.transaction.annotation.Transactional)

独立启动应用程序(bootRun、OpenJDK 1.8、内部 tomcat 服务器)时,Import 以 700 个实体/秒的速度快速运行。导入完成后,我可以立即看到数据库中出现的数据。这使我确信事务正常工作。

一旦我将我的应用程序部署到 payara 4.1,@Transactional 注释似乎不像以前那样工作。我可以看到数据库正在逐行填充,而且速度很慢(20 个实体/秒),就像没有 @Transactional 一样。

在我的导入中,我使用 entityManager 和 createNativeQuery(..).executeUpdate(); 截断数据库表。

如果我删除@Transactional,我会得到

javax.persistence.TransactionRequiredException: Executing an update/delete

这很好。在带有 @Transactional 的 glassfish 上,我没有收到这样的错误(这也很好),但从数据库的角度来看,它看起来不像有一个事务正在运行(实体缓慢填充)。

payara 配置是完全默认的。spring 数据库配置也是最小的,com.zaxxer.hikari.HikariDataSource,database-platform:io.github.jhipster.domain.util.FixedPostgreSQL82Dialect,database:POSTGRESQL。

在 beans.xml 上我设置了 bean-discovery-mode="none" 因为 CDI 给了我问题。

那么在独立上下文和应用程序容器(glassfish)中使用 Spring Boot JPA 的主要区别是什么?

为什么@Transactional 不像在独立版本上那样工作?你能解释一下发生了什么以及如何解决吗?

先感谢您!

标签: javaspring-bootjakarta-eeglassfishpayara

解决方案


在应用程序服务器环境中运行时,应用程序服务器向应用程序提供一个 EntityManager。独立运行时,EntityManager 来自 spring。这意味着如果应用程序在 glassfish 中运行,则 glassfish 管理事务。

由于 Glassfisch 事务管理器不理解 Spring 的 @Transactional 注解,因此没有效果。

选项 1 是更改您的代码以使用应用程序服务器事务 API。我不想这样做,因为我会失去独立运行的能力(用于测试,即)。

选项 2 是实现自己的 EntityManager、TransactionManager 和 DataSource,像这样。我的目标是创建一个默认 TransactionManager 的替代品,它读取 spring boot 配置。

@Configuration
public class EntityManagerConfiguration {

    protected final static Logger log = LoggerFactory.getLogger(EntityManagerConfiguration.class);

    private final ConfigurableEnvironment env;

    @Autowired
    public EntityManagerConfiguration(ConfigurableEnvironment env) {
        this.env = env;
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean entityManager() {

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(
                "com.project.domain"
        );

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
        properties.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
        Map<String,Object> props = ConfigAccessService.getPropertiesStartingWith(
                env,
                "spring.jpa.properties"
        );

        for(Map.Entry<String,Object> config : props.entrySet()) {
            String configName = config.getKey().replace("spring.jpa.properties.","");
            properties.put(configName, config.getValue());
            log.info("setting " + configName + " = " + config.getValue());
        }
        em.setJpaPropertyMap(properties);
        log.info("EntitiyManager configured");

        return em;
    }

    /**
     * as we want to run stand-alone (tests) and in glassfish(JavaEE) the same, we setup our own connection
     * using the Hikari Connection-Pooling.
     * @return
     */
    @Primary
    @Bean
    public DataSource dataSource(){
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setDriverClassName("org.postgresql.Driver");
        hikariConfig.setJdbcUrl(env.getProperty("spring.datasource.url"));
        hikariConfig.setUsername(env.getProperty("spring.datasource.username"));
        hikariConfig.setPassword(env.getProperty("spring.datasource.password"));

        hikariConfig.setMaximumPoolSize(10);
        hikariConfig.setConnectionTestQuery("SELECT 1");
        hikariConfig.setPoolName("springHikariCP");

        HikariDataSource dataSource = new HikariDataSource(hikariConfig);

        return dataSource;
    }

    @Primary
    @Bean
    public PlatformTransactionManager transactionManager() {

        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                entityManager().getObject());
        return transactionManager;
    }


}

推荐阅读