如何在GlassFish中编程实现@Resource与@PersistenceContext的功能?
我在使用带有多个JDBC连接池的GlassFish服务器,想要为每个JDBC连接单独创建EntityManagerFactory。目前用以下代码可以成功创建EntityManagerFactory:
Map props = new HashMap(); props.put(PersistenceUnitProperties.JTA_DATASOURCE, <my jdbc datasource>); Persistence.createEntityManagerFactory(<persistence unit name>, props);
但没法结合UserTransaction实现事务的开启、提交和回滚,连entityManager.getTransaction().begin/commit/rollback也不正常——出错时记录还是会持久化到数据库。
用注入方式获取UserTransaction和EntityManager时:
@Resource UserTransaction utx1; @PersistenceContext(unitName = <persistence unit name>) private EntityManager em1;
utx1.begin()、utx1.commit()能完美控制EntityManager,但没法切换使用服务器上配置的多个JDBC连接池。我试着在@PersistenceContext里加properties指定数据源,也没生效:
@Resource UserTransaction utx1; @PersistenceContext( properties = {@PersistenceProperty(name=PersistenceUnitProperties.JTA_DATASOURCE, value="ORCLH_MARMA")}, unitName = "MINLO") private EntityManager em1;
我的问题是:能不能以编程方式实现上述注入代码的功能?
1. 优先通过多持久化单元配置实现(最稳妥)
GlassFish容器优先读取persistence.xml的持久化单元定义,你可以为每个JDBC连接池单独定义一个持久化单元,直接绑定对应的JTA数据源:
<!-- persistence.xml --> <persistence-unit name="MINLO_ORCLH_MARMA" transaction-type="JTA"> <jta-data-source>ORCLH_MARMA</jta-data-source> <class>com.yourpackage.YourEntity</class> <!-- 其他配置:提供商、缓存等 --> </persistence-unit> <persistence-unit name="MINLO_OTHER_DS" transaction-type="JTA"> <jta-data-source>OTHER_JDBC_POOL</jta-data-source> <class>com.yourpackage.YourEntity</class> </persistence-unit>
之后注入时直接指定对应单元名,就能同时保留事务控制和多数据源切换能力:
@Resource UserTransaction utx; // 使用ORCLH_MARMA连接池的EntityManager @PersistenceContext(unitName = "MINLO_ORCLH_MARMA") private EntityManager emOrcl; // 使用另一个连接池的EntityManager @PersistenceContext(unitName = "MINLO_OTHER_DS") private EntityManager emOther;
2. 编程式实现容器管理的JTA EntityManager
如果一定要用代码手动实现类似注入的效果,不能直接用Persistence.createEntityManagerFactory(这会创建本地事务型的EMF),而是要通过JNDI获取容器管理的JTA型EntityManagerFactory,再绑定UserTransaction:
// 获取容器的UserTransaction UserTransaction utx = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction"); // 通过JNDI获取对应持久化单元的EntityManagerFactory // GlassFish中JNDI格式通常为java:comp/env/persistence/[持久化单元名] EntityManagerFactory emf = (EntityManagerFactory) new InitialContext().lookup("java:comp/env/persistence/MINLO_ORCLH_MARMA"); // 创建JTA类型的EntityManager EntityManager em = emf.createEntityManager(); // 事务控制逻辑 try { utx.begin(); em.persist(yourEntity); utx.commit(); } catch (Exception e) { // 异常时回滚事务 if (utx.getStatus() == Status.STATUS_ACTIVE) { try { utx.rollback(); } catch (Exception rollbackEx) { rollbackEx.printStackTrace(); } } throw new RuntimeException("事务处理失败", e); } finally { // 手动关闭应用管理的EntityManager em.close(); }
3. 为什么@PersistenceContext加properties没生效?
GlassFish的@PersistenceContext注解的properties参数不支持覆盖persistence.xml中定义的jta-data-source——容器会优先读取持久化单元的静态配置,所以这种方式无法实现数据源切换,必须通过定义多个持久化单元解决。
内容的提问来源于stack exchange,提问作者Thanassis Diamantis

