Spring Boot / @Transactional在独立模式下工作,但不在Glassfish中工作

时间:2018-11-01 16:50:47

标签: java spring-boot java-ee glassfish payara

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

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

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

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

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

javax.persistence.TransactionRequiredException: Executing an update/delete

这很好。在带有@Transactional的glassfish上,我也没有得到这样的错误(也可以),但是从数据库的角度来看,似乎没有事务正在运行(实体正在缓慢填充)。

payara配置完全是默认的。春季数据库配置也很少,com.zaxxer.hikari.HikariDataSource,数据库平台:io.github.jhipster.domain.util.FixedPostgreSQL82Dialect,数据库:POSTGRESQL。

在bean.xml上,我设置了bean-discovery-mode =“ none”,因为CDI给我带来了麻烦。

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

为什么@Transactional在独立版本上无法正常工作?您能解释发生了什么事以及如何解决吗?

提前谢谢!

1 个答案:

答案 0 :(得分:0)

在应用程序服务器环境中运行时,应用程序服务器将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;
    }


}