Estoy usando el administrador de transacciones estándar JPA para mis transacciones JPA. Sin embargo, ahora quiero agregar algunas entidades JDBC que compartirán el mismo 'origen de datos'. ¿Cómo puedo hacer que las operaciones JDBC sean transaccionales con la transacción de primavera? ¿Debo cambiar a los administradores de transacciones de JTA? ¿Es posible utilizar el servicio transaccional JDBC JPA & con el mismo origen de datos? Mejor aún, ¿es posible mezclar estas dos transacciones?¿Qué gestor de transacciones debería usar para la plantilla JBDC cuando uso JPA?
ACTUALIZACIÓN: @Espen:
Tengo un DAO se extendía desde SimpleJdbcDaoSupport que utiliza getSimpleJDBCTemplate.update para insertar una fila de base de datos. Cuando se arroja una RuntimeException desde el código de servicio, la transacción nunca retrocede cuando se utiliza JPATransactionManager. Se retrotrae al usar DatasourceTransactionManager. Traté de depurar el JPATransactionManager y parece que nunca realiza una reversión en JDBCConnection subyacente (supongo que debido al hecho de que el origen de datos no necesariamente tiene que ser JDBC para JPA). Mi configuración de configuración es exactamente como la que explicaste aquí.
Éstos son mis códigos de prueba:
<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>
y aquí está la DAO:
@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;
}
}
Aquí es el código de cliente que llama a la DAO y lanza una excepción (servicio de primavera)
@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();
}
NOTA: el código funciona como se esperaba cuando se utiliza DataSourceTransactionManager
ACTUALIZACIÓN - 2:
Ok he encontrado la causa raíz del problema. Gracias a Espen.
Mi configuración del gestor de entidad era así (copiado de la primavera de aplicación mascota-clínica):
<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>
Entonces me cambiaron a este aspecto:
<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>
Ahora todo parece estar funcionando! ¿Alguien puede explicar la diferencia entre estos dos enfoques?
relacionada quizá http://stackoverflow.com/questions/2650409/implement-custom-jta-xaresource-for-using-with-hibernate/2651580#2651580 – ewernli
Trate de eliminar la propiedad persistenceXmlLocation. Es una alternativa a la propiedad dataSource. Los requisitos para JpaTransactionManager para trabajar con consultas JPA y JDBC es que su entityManager usa el mismo DataSource que sus consultas JDBC y que usted especifica el dialecto JPA como ya lo ha hecho. – Espen