我有一个服务(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在独立版本上无法正常工作?您能解释发生了什么事以及如何解决吗?
提前谢谢!
答案 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;
}
}