我应该将什么事务管理器用于JBDC模板使用JPA时?

我正在为JPA事务使用标准的JPA事务管理器。 但是,现在我想添加一些将共享相同“数据源”的JDBC实体。 如何使用spring事务使JDBC操作具有事务性? 我是否需要切换到JTA事务管理器? 是否可以将JPA和JDBC事务服务与相同的数据源一起使用? 更好的是,是否可以混合这两笔交易?

更新:@Espen:

我有一个从SimpleJdbcDaoSupport扩展的dao,它使用getSimpleJDBCTemplate.update插入数据库行。 当从服务代码抛出RuntimeException时,事务在使用JPATransactionManager时永远不会回滚。 它在使用DatasourceTransactionManager时会回滚。 我试图调试JPATransactionManager,似乎它永远不会在底层JDBCConnection上执行回滚(我想由于数据源不一定是JPA的JDBC)。 我的配置设置与您在此处解释的完全相同。

这是我的测试代码:

            <!--    -->              <!--  -->                    

这里是道:

 @Transactional public class CallRecordingScheduledProgramTriggerDAOJDBCImpl extends SimpleJdbcDaoSupport implements CallRecordingScheduledProgramTriggerDAO{ private static final Log log = LogFactory.getLog(CallRecordingScheduledProgramTriggerDAOJDBCImpl.class); @SuppressWarnings("unchecked") public CallRecordingScheduledProgramTrigger save( CallRecordingScheduledProgramTrigger entity) { log.debug("save -> entity: " + entity); String sql = null; Map args = new HashMap(); String agentIdsString = getAgentIdsString(entity.getAgentIds()); String insertSQL = "insert into call_recording_scheduled_program_trigger" + " ( queue_id, queue_id_string, agent_ids_string, caller_names, caller_numbers, trigger_id, note, callcenter_id, creator_id_string, creator_id) " + " values(:queueId, :queueIdString, :agentIdsString, :callerNames, :callerNumbers, :triggerId, :note, :callcenterId , :creatorIdString, :creatorId )"; args.put("queueId", entity.getQueueId()); args.put("agentIdsString",agentIdsString); args.put("callerNames", entity.getCallerNames()); args.put("queueIdString", entity.getQueueIdString()); args.put("callerNumbers", entity.getCallerNumbers()); args.put("triggerId", entity.getTriggerId()); args.put("note", entity.getNote()); args.put("callcenterId", entity.getCallcenterId()); args.put("creatorId", entity.getCreatorId()); args.put("creatorIdString", entity.getCreatorIdString()); sql = insertSQL; getSimpleJdbcTemplate().update(sql, args); System.out.println("saved: ----------" + entity); return entity; } } 

这是调用dao并抛出exception的客户端代码(spring服务)

 @Transactional(propagation=Propagation.REQUIRED) public void jdbcTransactionTest() { System.out.println("entity: " ); CallRecordingScheduledProgramTrigger entity = new CallRecordingScheduledProgramTrigger(); entity.setCallcenterId(10L); entity.setCreatorId(22L); entity.setCreatorIdString("sajid"); entity.setNote(System.currentTimeMillis() + ""); entity.setQueueId(22); entity.setQueueIdString("dddd"); String triggerId = "id: " + System.currentTimeMillis(); entity.setTriggerId(triggerId); callRecordingScheduledProgramTriggerDAO.save(entity); System.out.println("entity saved with id: " + triggerId ); throw new RuntimeException(); } 

注意:使用DatasourceTransactionManager时,代码按预期工作

更新 – 2:

好的,我找到了问题的根本原因。 感谢Espen。

我的实体管理器配置是这样的(从春季宠物诊所应用程序复制):

         

然后我把它改成这样:

             

现在一切似乎都在起作用! 谁能解释这两种方法之间的区别?

可以使用JpaTransactionManager在同一事务中混合JPA和JDBC代码。

来自Spring 3的JavaDoc的片段:

此事务管理器还支持在事务中直接访问DataSource(即使用相同DataSource的纯JDBC代码)。 这允许混合访问JPA的服务和使用普通JDBC的服务(不知道JPA)!

您应该知道,JPA会缓存查询并在事务结束时执行所有查询。 因此,如果您希望使用JPA在事务中保留一些数据,然后使用JDBC检索数据,那么在尝试使用JDBC代码检索之前,如果没有明确地刷新JPA的持久性上下文,它将无法工作。

使用JDBC代码声明JPA代码在事务中删除了一行的代码示例:

 @Test @Transactional @Rollback(false) public void testDeleteCoffeeType() { CoffeeType coffeeType = coffeeTypeDao.findCoffeeType(4L); final String caffeForte = coffeeType.getName(); coffeeTypeDao.deleteCoffeeType(coffeeType); entityManager.flush(); int rowsFoundWithCaffeForte = jdbcTemplate .queryForInt("SELECT COUNT(*) FROM COFFEE_TYPES where NAME = ?", caffeForte); assertEquals(0, rowsFoundWithCaffeForte); } 

如果您更喜欢使用JpaTemplate类,只需将entityManager.flush()替换为jpaTemplate.flush();

回应Sajids的评论:使用Spring,您可以配置一个支持JPA和JDBC的事务管理器,如下所示:

      

和注释驱动版本

 @Bean public JpaTransactionManager transactionManager(EntityManagerFactory emf) { JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(); jpaTransactionManager.setEntityManagerFactory(emf); return jpaTransactionManager; } 

为了使其工作,必须使用JdbcTemplate或SimpleJdbcTemplate类执行JDBC查询。 在使用扩展SimpleJdbcDaoSupport的DAO的情况下,您应该使用getSimpleJdbcTemplate(..)方法。

最后让两个DAO方法参与同一个事务,从@Transactional注释的服务类中调用两个DAO方法。 使用配置中的元素,Spring将使用给定的事务管理器为您处理事务。

在业务层:

 public class ServiceClass {.. @Transactional public void updateDatabase(..) { jpaDao.remove(..); jdbcDao.insert(..); } } 

编辑2:然后出了点问题。 它完全按照Javadoc中的规定对我有用。 您的实体管理器是否具有类似我的bean的数据源属性? 只有在将相同的数据源注入实体管理器和扩展的JpaDaoSupport类时,它才会起作用。

        hibernate.format_sql=true    

我还没有详细解决这个问题,因为我没有混合使用JDBC和JPA,但是如果你获得了XA数据源的JDBC连接,那么它们就是JTA事务。 因此,如果您在Stateless会话bean中运行代码,例如启用了事务,那么您将自动获得由JTA管理的实体和JDBC。

编辑这是来自Servlet的示例代码

 private @Resource DataSource xaDatasource; private @Resource UserTransaction utx; private @PersistenceUnit EntityManagerFactory factory; public void doGet(HttpServletRequest req, HttpServletResponse res) ... { utx.begin(); //Everything below this will be in JTA Connection conn = xaDatasource.getConnection(); EntityManager mgr = factory.createEntityManager(); //Do your stuff ... utx.commit(); } 

免责声明:代码未经测试。

只是意识到这不是spring,但无论如何我都会把它留下来