I have used Hibernate successfully in past projects, but now I am trying to integrate Hiberante with a legacy system that uses JDBC. The intent is to gradually replace the plain JDBC code with Hibernate persistence, but the two will have to coexist for some period of time.
To that end, I want the Hibernate and JDBC code to share the same transaction, with errors in one forcing a rollback in the other. I am using Spring to manage all the code, but am having a problem where I create an object in Hibernate, but when I try to reference that same object for another insert operation in the plain JDBC code, I get an constraint violation because the newly created has not yet been committed to the database.
Here is my Spring configuration:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--====================-->
<!-- Hibernate Settings -->
<!--====================-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost:5432/test1"/>
<property name="username" value="*****"/>
<property name="password" value="*****"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>com/edusoft/model/Test.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.query.substitutions">true 1, false 0</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!--==============-->
<!-- DAO Mappings -->
<!--==============-->
<bean name="testDAO" class="com.edusoft.model.dao.TestDAOHibernateImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!--========================-->
<!-- Service Layer Mappings -->
<!--========================-->
<bean name="testServiceTarget" class="com.edusoft.service.TestServiceImpl">
<property name="testDAO" ref="testDAO"/>
</bean>
<bean id="testService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="testServiceTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="getTest">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="getAllTests">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="insertTest">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- This is the plain JDBC service -->
<bean name="questionServiceTarget" class="com.edusoft.service.QuestionServiceImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="questionService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="questionServiceTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="addQuestion">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
I should also note that I am explicitly starting a transaction with a Servlet filter at the beginning of the request to enforce the transactional scope to be over the entire request, and committing or rolling back at the end of the request. Thus, the session will not flush until the whole request is done. The doFilter() method looks something like this:
Code:
// start a transaction, using Spring transaction manager
PlatformTransactionManager txManager = (PlatformTransactionManager) this.context.getBean("transactionManager");
DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus tx = txManager.getTransaction(txDefinition);
try {
// now actually do something
filterChain.doFilter(request,response);
} finally {
// commit transaction and close everything down
if (tx.isRollbackOnly()) {
txManager.rollback(tx);
} else {
txManager.commit(tx);
}
}
I have a Spring controller object that does something like this:
Code:
Test test = (Test) command;
TestService service = getTestService();
Test result = service.insertTest(test);
// result contains the new object ID
QuestionService questionService = getQuestionService();
Question question = new Question("This is a sample question","This is the sample answer");
questionService.addQuestion(result,question);
When I get to the following code in QuestionService (the plain JDBC one)..
Code:
String text = question.getText();
String answer = question.getAnswer();
String testID = test.getId();
JdbcTemplate jt = new JdbcTemplate(getDataSource());
String newID = (String) jt.queryForObject("select nextval ('object_id_seq')",String.class);
String query = "insert into question(id,text,answer,test_id) values ('" + newID + "','" + text + "','" + answer + "','" + testID + "')";
jt.execute(query);
I get the following (totally understandable) exception:
Code:
org.springframework.dao.DataIntegrityViolationException: StatementCallback; SQL [insert into question(id,text,answer,test_id) values ('133','This is a sample question','This is the sample answer','132')]; ERROR: insert or update on table "question" violates foreign key constraint "fk_question_test_id"
My questions is: is it possible to do what I want to do? Namely, to share a transaction across Hibernate and JDBC code such that an object created in one is visibile to the other? I thought that with the above Spring configuration I would be sharing the same connection and transaction, but now I am not so sure. I have pured over the Spring and Hibernate docs, but have not anything that sheds any light. Any help is appreciated.
thanks.
--Deryl
Hibernate version: 3.0.5
Name and version of the database you are using: PostgreSQL 8.1