Saj*_*jid 22 java spring jpa jdbc
我正在为我的JPA事务使用标准的JPA事务管理器.但是,现在我想添加一些将共享相同"数据源"的JDBC实体.如何使用spring事务使JDBC操作具有事务性?我是否需要切换到JTA事务管理器?是否可以将JPA和JDBC事务服务与相同的数据源一起使用?更好的是,是否可以混合这两笔交易?
更新:@Espen:
我有一个从SimpleJdbcDaoSupport扩展的dao,它使用getSimpleJDBCTemplate.update插入数据库行.当从服务代码抛出RuntimeException时,事务在使用JPATransactionManager时永远不会回滚.它在使用DatasourceTransactionManager时会回滚.我试图调试JPATransactionManager,似乎它永远不会对底层的JDBCConnection执行回滚(我想这是因为数据源不一定是JPA的JDBC).我的配置设置与您在此处解释的完全相同.
这是我的测试代码:
<context:property-placeholder location="classpath:*.properties"/>
<!-- JPA EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation"
value="classpath:/persistence-test.xml" />
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
-->
<!-- Database connection pool -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
<property name="testOnBorrow" value="${database.testOnBorrow}" />
<property name="validationQuery" value="${database.validationQuery}" />
<property name="minIdle" value="${database.minIdle}" />
<property name="maxIdle" value="${database.maxIdle}" />
<property name="maxActive" value="${database.maxActive}" />
</bean>
<!-- Initialize the database -->
<!--<bean id="databaseInitializer" class="com.vantage.userGroupManagement.logic.StoreDatabaseLoader">
<property name="dataSource" ref="storeDataSource"/>
</bean>-->
<!-- ANNOTATION SUPPORT -->
<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- JPA annotations bean post processor -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<!-- Exception translation bean post processor (based on Repository annotation) -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<!-- throws exception if a required property has not been set -->
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
<bean id="userService" class="com.rfc.example.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
<property name="contactDao" ref="contactDao"></property>
<property name="callRecordingScheduledProgramTriggerDAO" ref="com.rfc.example.dao.CallRecordingScheduledProgramTriggerDAO"></property>
</bean>
<bean id="userDao" class="com.rfc.example.dao.UserDaoJPAImpl" />
<bean id="contactDao" class="com.rfc.example.dao.ContactDaoJPAImpl"></bean>
<bean id="com.rfc.example.dao.CallRecordingScheduledProgramTriggerDAO" class="com.rfc.example.dao.CallRecordingScheduledProgramTriggerDAOJDBCImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
Run Code Online (Sandbox Code Playgroud)
这里是道:
@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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是调用dao和抛出异常的客户端代码(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();
}
Run Code Online (Sandbox Code Playgroud)
注意:使用DatasourceTransactionManager时,代码按预期工作
更新 - 2:
好的,我找到了问题的根本原因.感谢Espen.
我的实体管理器配置是这样的(从春季宠物诊所应用程序复制):
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation"
value="classpath:/persistence-test.xml" />
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
</bean>
Run Code Online (Sandbox Code Playgroud)
然后我把它改成这样:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation"
value="classpath:/persistence-test.xml" />
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
</bean>
</property>
</bean>
Run Code Online (Sandbox Code Playgroud)
现在一切似乎都在运作!谁能解释这两种方法的区别?
Esp*_*pen 26
可以使用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);
}
Run Code Online (Sandbox Code Playgroud)
如果你喜欢使用JpaTemplate
类,只需更换entityManager.flush()
与jpaTemplate.flush();
回应Sajids的评论:使用Spring,您可以配置一个支持JPA和JDBC的事务管理器,如下所示:
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa
.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
Run Code Online (Sandbox Code Playgroud)
和注释驱动版本
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(emf);
return jpaTransactionManager;
}
Run Code Online (Sandbox Code Playgroud)
为了使其工作,必须使用JdbcTemplate或SimpleJdbcTemplate类执行JDBC查询.对于扩展SimpleJdbcDaoSupport的DAO,您应该使用getSimpleJdbcTemplate(..)方法.
最后让两个DAO方法参与同一个事务,从@Transactional注释的服务类中调用两个DAO方法.使用<tx:annotation-driven>
配置中的元素,Spring将使用给定的事务管理器为您处理事务.
在业务层:
public class ServiceClass {..
@Transactional
public void updateDatabase(..) {
jpaDao.remove(..);
jdbcDao.insert(..);
}
}
Run Code Online (Sandbox Code Playgroud)
编辑2:然后出了点问题.它完全按照Javadoc中的规定对我有效.您的实体管理器是否具有类似我的bean的数据源属性?只有在将相同的数据源注入实体管理器和扩展的JpaDaoSupport类时,它才会起作用.
<bean id="entityManagerFactoryWithExternalDataSoure" primary="true"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor
.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<value>
hibernate.format_sql=true
</value>
</property>
</bean>
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
22156 次 |
最近记录: |