具有应用程序管理的持久性上下文的 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 ------------------------------------------ &ltpersistence-unit name="PU1" transaction-type="JTA"> &lt!-- 评论 spring 来处理交易??? --> &lt!--property name="eclipselink.target-server" value="WebLogic_10"/ --> &lt/属性> &lt/持久化单元> ------------------------------------------ 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的事务管理

Spring 和 Flyway - 在应用程序上下文启动之前迁移

核心数据:具有多个上下文的独立持久存储与具有单个上下文的独立持久存储

Spring Boot集成持久化Quartz定时任务管理和界面展示

具有应用程序管理持久性的 quarkus

Spring + Hibernate + JPA [关闭]