java - 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 不像在独立版本上那样工作?你能解释一下发生了什么以及如何解决吗?
先感谢您!
解决方案
在应用程序服务器环境中运行时,应用程序服务器向应用程序提供一个 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;
}
}
推荐阅读
- semantic-release - npm semantic-release - 指定版本
- c# - 如何在 Xamarin AppShell 中动态添加 MenuItem
- node.js - VueJS - 未捕获的 RangeError:超出最大调用堆栈大小
- google-apps-script - 可以在将替换多个值的谷歌表格中自动运行查找和替换脚本吗?
- spring - Spring Boot 从上下文路径重定向到 localhost
- angular - Observable:检查返回值是否为空
- css - 自定义 Angular Material 组件尺寸
- azure - Azure Function App 突然停止工作
- javascript - 清除搜索输入时将建议附加到搜索框
- clojure - S 无法解决。克洛朱尔。草书