具有应用程序管理的持久性上下文的 Spring Boot
Posted
技术标签:
【中文标题】具有应用程序管理的持久性上下文的 Spring Boot【英文标题】:Spring Boot with application managed persistence context 【发布时间】:2017-12-15 12:24:03 【问题描述】:我正在尝试从 EJB3 + JTA + JPA (EclipseLink) 迁移应用程序。目前,由于设计时数据库数量未知,此应用程序使用应用程序管理的持久上下文。
应用程序管理的持久上下文允许我们控制如何创建 EntityManager(例如,提供不同的数据源 JNDI 以在运行时为特定 DB 创建适当的 EntityManager)。
例如
地图属性 = new HashMap(); properties.put(PersistenceUnitProperties.TRANSACTION_TYPE, "JTA"); //数据源JNDI是通过配置而不事先知道数据库的数量 //目前,DB JNDI 存储在一个外部化文件中 //数据源由运营团队设置 properties.put(PersistenceUnitProperties.JTA_DATASOURCE, "datasource-jndi"); properties.put(PersistenceUnitProperties.CACHE_SHARED_DEFAULT, "false"); properties.put(PersistenceUnitProperties.SESSION_NAME, "xxx"); //创建适当的EntityManager以连接到运行时决定的数据库 EntityManager em = Persistence.createEntityManagerFactory("PU1", properties).createEntityManager(); //查询或更新数据库 em.persist(实体); em.createQuery(...).executeUpdate();当部署在 EJB 容器(例如 WebLogic)中时,具有适当的 TransactionAttribute(例如 TransactionAttributeType.REQUIRED),容器将负责事务的开始/结束/回滚。
现在,我正在尝试将此应用程序迁移到 Spring Boot。
我遇到的问题是即使我用@Transactional(propagation = Propagation.REQUIRED)
注释方法后也没有事务开始。
Spring 应用程序被打包为可执行 JAR 文件,并使用嵌入的 Tomcat 运行。
当我尝试执行这些更新 API 时,例如EntityManager.persist(..)
,EclipseLink 总是抱怨:
javax.persistence.TransactionRequiredException: '当前没有事务处于活动状态'
下面的示例代码:
//用于数据持久化 @服务 类 DynamicServiceImpl 实现 DynamicService //尝试开始一个事务 @Transactional(传播=传播。需要) 公共无效保存数据(DbJndi,EntityA) //这返回false,没有事务开始 TransactionSynchronizationManager.isActualTransactionActive(); //根据输入的DbJndi动态创建EntityManager //确定保存数据的DB EntityManager em = createEm(DbJndi); //保存数据 em.persist(EntityA); //安静的服务 @RestController 类休息控制器 @自动连线 动态服务服务; @RequestMapping(值 = "/saveRecord", 方法 = RequestMethod.POST) 公共 @ResponseBody 字符串 saveRecord() //保存数据 服务.saveData(...) //启动应用程序 @SpringBootApplication 类TestApp 公共静态无效主要(字符串[]参数) SpringApplication.run(TestApp.class, args); 持久性.xml ------------------------------------------ <persistence-unit name="PU1" transaction-type="JTA"> <!-- 评论 spring 来处理交易??? --> <!--property name="eclipselink.target-server" value="WebLogic_10"/ --> </属性> </持久化单元> ------------------------------------------ application.properties(只需 3 行配置) ------------------------------------------ spring.jta.enabled=true spring.jta.log-dir=spring-test #事务日志目录。 spring.jta.transaction-manager-id=spring-test ------------------------------------------我的使用模式不遵循大多数典型用例(例如,已知数据库数量 - Spring + JPA + multiple persistence units: Injecting EntityManager)。
谁能给我建议如何解决这个问题?
有没有人遇到过在设计时不知道数据库的情况?
谢谢。
【问题讨论】:
使用Persistence.createEntityManagerFactory
创建一个 EMF 会产生一个 JavaSE EMF,您负责在其中启动事务(RESOURCE_LOCAL 或 JTA UserTransaction)。也许 Spring 会希望您注入 EMF,从而通过其注释处理此类事务。
Neil,问题是我需要确定要连接到哪个数据库,spring INJECT 似乎无法为我动态注入适当的 EMF。你对此有什么想法吗?谢谢。
在让 JPA 工作之前,您将如何告诉 Spring 在事务中使用哪个数据源?看起来你最好自己控制交易。
【参考方案1】:
您自己已经回答了这个问题:“当部署在 EJB 容器(例如 WebLogic)中时,具有适当的 TransactionAttribute(例如 TransactionAttributeType.REQUIRED),容器将负责事务的开始/结束/回滚”。
WebLogic 符合 Java 企业版规范,这可能是它以前工作的原因,但现在您使用的是 Tomcat(嵌入式模式),而这不是。 所以你根本无法做你想做的事。 您的 persistence.xml 文件中的这条语句:
<persistence-unit name="PU1" transaction-type="JTA">
需要企业服务器(WebLogic、Glassfish、JBoss 等)
使用 Tomcat 你只能这样做:
<persistence-unit name="PU1" transaction-type="RESOURCE_LOCAL">
而且你需要自己处理交易:
myEntityManager.getTransaction.begin();
... //Do your transaction stuff
myEntityManager.getTransaction().commit();
【讨论】:
【参考方案2】:我终于搞定了:
-
启用 tomcat JNDI 并以编程方式为每个 DS 创建数据源 JNDI
添加交易内容
com.atomikos:transactions-eclipselink:3.9.3
(我的项目使用eclipselink而不是hibernate)
org.springframework.boot:spring-boot-starter-jta-atomikos
org.springframework:spring-tx
【讨论】:
以上是关于具有应用程序管理的持久性上下文的 Spring Boot的主要内容,如果未能解决你的问题,请参考以下文章
Spring 和 Flyway - 在应用程序上下文启动之前迁移
核心数据:具有多个上下文的独立持久存储与具有单个上下文的独立持久存储