Hibernate entity life cycle and session life cycle inside a Spring transaction - java

Im having some trouble understanding the way spring handles the hibernate entities, and does the lazy loading process.
So for this case we have to entities
#Entity
public class EntityA{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany(cascade={CascadeType.PERSIST, CascadeType.REFRESH})
private Collection<EntityB> bss= new ArrayList<EntityB>();
and the agregated entity
#Entity
public class EntityB{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany(mappedBy="bss")
private Collection<EntityA> ass= new ArrayList<EntityA>();
then I have a simple business class marked as transactional:
#Component
#Scope("session")
#Transactional(propagation=Propagation.TRIED_EVERY_SINGLE_ONE)
public class GenericTestBean {
private EntityA entityA;
#Autowired
private IGenericDAO genericDAO;
public GenericTestBean() {
System.out.println("bean creado!");
}
public void testQuery() {
entityA= genericDAO.get(EntityA.class, 1l);
System.out.println(TransactionIndicatingUtil.getTransactionStatus(true));
System.out.println("is element atached? :" + genericDAO.isAtached(entityA));
//this.loadData();
}
public void loadData(){
System.out.println(TransactionIndicatingUtil.getTransactionStatus(true));
System.out.println("is element atached? :" + genericDAO.isAtached(currentCompany));
System.out.println(entityA.getBss.size());
}
}
and a Simple test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "applicationContext.xml" })
#WebAppConfiguration
public class EntityQueryTest {
// #Autowired
// private SessionFactory sessionFactory;
#Autowired
GenericTestBean genericTestBean;
#Test
public void consultarCompania(){
genericTestBean.testQuery();
genericTestBean.loadData();
}
what Im guessin that should happen is:
1. GenericTestBean gets instantiated
2. testQuery is called from outside the proxy starting the transaction
2. loadData is called, the proxy sees the active transaction, and takes the existent resources
3. the data is retrieived
4. The transacion is closed...
But this doesn't happens, there seems to be two different transactions in each method call, and the entity gets detached between calls, issuing a lazy init exception.
the actual output log is this:
bean creado! --here the object get created
Hibernate: select company0_.companyId as ..... --here the first query get executed
[com.pe.controlLines.data.dao.GenericTestBean.testQuery] --here we check the name for the transaction
is element atached? :true --and see if the element is attached to the session
[com.pe.controlLines.data.dao.GenericTestBean.loadData] --this is in the second method call (with a different transaction name :O )
is element atached? :false --both now, the same element get detached
I tried re-attaching the entity, but this gives me a new query to the database (a new query to table EntityA is sent plus the query to get the objects, which I really don't like).
Im hoping to save one additional query just in order to have lazy loading, or does it have to be this way?, or maybe I have some configuration wrong?
Pdta: I think that the view filter option is even worst than the re-attaching option, it could lead to serious performance issues under high concurrency.
Can anyone please clarify the behavior of transaction contexts between methods calls, and how it get related to the session and the state of the entities?
the TransactionIndicatingUtil implementation is taken from here http://java.dzone.com/articles/monitoring-declarative-transac?page=0,1
and the generic dao is build after this idea
General or specific DAO to record delivery with information from multiple tables?
Update
in case is of some use, here is the spring config file
<context:component-scan base-package="xxxxxx" />
<context:annotation-config />
<context:spring-configured />
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Data Source Declaration -->
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/xxxx" />
<property name="username" value="xxxxx" />
<property name="password" value="xxxxx" />
<property name="initialSize" value="2" />
<property name="minIdle" value="0" />
<property name="minEvictableIdleTimeMillis" value="120000" />
<property name="maxActive" value="20" />
<property name="maxWait" value="5000" />
</bean>
<!-- Session Factory Declaration <bean id="SessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> -->
<!-- Session Factory Declaration -->
<bean id="SessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="packagesToScan">
<list>
<value>com.xx.xx.xx.xx</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.search.default.directory_provider">filesystem</prop>
<prop key="hibernate.search.default.indexBase">C:/DEVELOPMENT/lucene/indexes</prop>
</props>
</property>
</bean>
<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>
<!-- Transaction Manager is defined -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory"/>
</bean>

The Test class should be transactional too
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "applicationContext.xml" })
#Transactional // <--- add
public class EntityQueryTest {
I am assuming that your applicationContext.xml file is the XML code shown in your post

The JUnit test itself is not transactional. Each method of your GenericTestBean bean is transactional. So, each time the non-transactional test calls a method of the transactional bean, a transaction is started, the bean method is executed, and the transaction is committed. Since you call two transactional methods successively, so two separate transactions are started.
If the test method itself was transactional, then a transaction would be started for the test method, and (by default), the two bean methods would execute in the context of the existing transaction, started by the test. The transaction would commit after the test method has returned.

Related

#Transactional doesn't open another transaction

I'm trying to open multiple transactions​ on a method using #Transactional by Spring framework
And for some reason it's throws java.lang.IllegalStateException : Transaction already active
My method
#Transactional
Public void foo(Entity e){
entityManager.merge(e);
entityManager.getTransaction().begin();
entityManager.getTransaction().commit();
}
Any idea how to open multiple transaction without getting that error?
I think you may be confusing what the Spring #Transactional annotation means. When you use that annotation you're asking Spring to enrich your method invocation with transaction semantics.
You may want to take a look at the TransactionAspectSupport class, more specifically the method invokeWithinTransaction which is the place where all the magic happens for your transactional methods.
You will see there that before your code is invoked, a new transaction is created, then your code is executed, and after that, either a commit or rollback happens. In other words, Spring's aspect controls the transaction for you.
All these magic needs some configuration for it to happen: you need to make sure that Spring finds your #Transactional methods and enrich them. To do so you need to add a piece of configuration:
<tx:annotation-driven/>
And of course you need to define a transaction manager that can be used by your transactional methods. It seems you're using JPA so a JpaTransactionManager makes sense in this case:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="POSTGRESQL"/>
<property name="showSql" value="true"/>
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
<property name="prepareConnection" value="true"/>
</bean>
</property>
<property name="persistenceUnitName" value="test-api"/>
<property name="packagesToScan">
<list>
<value>com.company.humanresources</value>
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<constructor-arg name="emf" ref="entityManagerFactory"/>
</bean>
Finally, if you want to gain access to the entity manager being used by your current transaction, you can do something like:
#PersistenceUnit(unitName = "test-api")
EntityManagerFactory emf;
#Transactional
public Department addDepartment(String name) {
EntityManager em = EntityManagerFactoryUtils.getTransactionalEntityManager(emf);
Department department = new Department();
department.setName(name);
em.persist(department);
return department;
});
Once more, Spring will control the transaction semantics around your transactional method. You must also take into account that the default Spring behavior only works with public methods.

#Transactional questions and how to use with #Service

My service makes use of a generic DAO (which explicitly uses Hibernate session factory). I have spent some time before I discovered this error
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I annotated my service and all works perfectly. Now I want to use context path scanning, and remove this line from my configuration:
<bean id="societaService" class="it.trew.prove.services.SocietaService" />
So.... here's my final version:
#Service
#Transactional(readOnly = true)
public class SocietaService {
private Dao societaDao;
#Autowired
public void setSocietaDao(Dao societaDao) {
this.societaDao = societaDao;
}
public void salvaSocieta(Societa s) {
societaDao.save(s);
}
public List<Societa> listAll() {
return societaDao.getAll(Societa.class);
}
public void deleteById(Long id) {
societaDao.delete(getSocieta(id));
}
public Societa getSocieta(String id) {
return getSocieta(Long.parseLong(id));
}
public Societa getSocieta(Long id) {
return societaDao.get(Societa.class, id);
}
}
Adding #Service annotation makes my app give the awful hibernate error above. Why?
Removing #Service and configuring the service bean via xml = it works. WHY??
In addition:
does transactional annotating my service class makes all its methods to be executing in a transaction?
EDIT
Here's my context xml:
<context:annotation-config />
<context:component-scan base-package="it.trew.prove" />
<!-- Hibernate -->
<bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.google.appengine.api.rdbms.AppEngineDriver" />
<property name="url" value="jdbc:google:rdbms://xxx:xxx/xxx" />
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="annotatedClasses">
<list>
<value>it.trew.prove.model.beans.Scadenza</value>
<value>it.trew.prove.model.beans.Fornitore</value>
<value>it.trew.prove.model.beans.Societa</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<!-- <prop key="hibernate.hbm2ddl.import_files">/setup.sql</prop> -->
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
Did you enabled annotations ?
<context:annotation-config />
and
<context:component-scan base-package="org.example"/>
this will enable annotations like #Service, #Component and #Repository
First of all, you need to tell spring to enable Transactional annotation with something like that:
<tx:annotation-driven transaction-manager="transactionManager" />
Second, if you use #Transactional at class level, any call to a method of your Service will be transactional. Whether it starts a transaction or not depends on the "propagation" attribute. The default is start one if none started in this session (Propagation.REQUIRED). If you use #Transactional only at class level, all of your methods will inherit its attributes, i.e., if you set "readOnly = true", all of yours methods will be read only, thus, updates/saves/delete won't work. I would recommend you to read more the Spring Transaction Management Doc

Hibernate transaction manager configurations in Spring

In my project I use Hibernate with programmatic transaction demarcation.
Every time in my Service methods i write something similar to this.
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
.. perform operation here
session.getTransaction().commit();
Now I'm going to refactor my code with declarative transaction management. What I got now ...
<context:component-scan base-package="package.*"/>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="mySessionFactory"/>
</property>
</bean>
Service class:
#Service
public class TransactionalService {
#Autowired
private SessionFactory factory;
#Transactional
public User performSimpleOperation() {
return (User)factory.getCurrentSession().load(User.class, 1L);
}
}
And simple test -
#Test
public void useDeclarativeDem() {
FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("spring-config.xml");
TransactionalService b = (TransactionalService)ctx.getBean("transactionalService");
User op = b.performSimpleOperation();
op.getEmail();
When I try to get user email outside of Transactional method, I got lazy initialization exception, email is my case is a simple string. Hibernate does not even perform sql query, until I call any getters on my POJO.
what I am doing wrong here?
Is this approach valid?
Can you suggest any opensources project which works on spring/hibernate with annotation based configuration?
Update
For some reason if I replace getCurrentSession with openSession this code works fine. Can someone explain it please?
Ok, finally i realized what was the problem. In code above i used load instead of get. Session.load did not actually hit the databased. That's the reason why i get lazy-initialization exception outside of #Transactional method
If i use openSession instead of getCurrentSession, session is opened outside of scope spring container. As result session was not close and it allow me to read object properties outside of #Transactional method
The reason Hibernate does not perform any SQL queries until you call the getters is because I believe the FetchType is set to LAZY. To fix this problem you will need to change the FetchType to EAGER in your POJO:
#Entity
#Table(name = "user")
public class User {
/*Other data members*/
#Basic(fetch = FetchType.EAGER)
private String email;
}
I personally never had to specify Basic types to have an EAGER FetchType so I'm not entirely sure why your configuration requires this. If its only in your tests it could be due to the way you have your JUnit tests configured. It should have something like this on the class declaration:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/test-app-config.xml"})
#Transactional
public class UserServiceTest {
}
As for a good resource I always find SpringByExample to be helpful.
EDIT
So I'm not entirely sure what is wrong with your configuration. It does differ from the way I have mine set up so here is my typical configuration in hopes that it helps. The hibernate.transaction.factory_class could be a key property you are missing. I also use the AnnotationSessionFactoryBean:
<!-- DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost/dbname"
p:username="root"
p:password="password"/>
<!-- Hibernate session factory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource">
<property name="packagesToScan" value="com.beans"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>
</props>
</property>
</bean>
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
From Hibernate documentation https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch11.html#objectstate-loading:
Be aware that load() will throw an unrecoverable exception if there is no matching database row. If the class is mapped with a proxy, load() just returns an uninitialized proxy and does not actually hit the database until you invoke a method of the proxy. This is useful if you wish to create an association to an object without actually loading it from the database. It also allows multiple instances to be loaded as a batch if batch-size is defined for the class mapping.
From above documentation in your case bean is mapped with spring proxy so load just returns. Hence you need to use get() instead of load() which always hits the database.

Spring Transaction - automatic rollback of previous db updates when one db update failes

I am writing a simple application (Spring + Hibernate + PostgreSql db). I am just trying to construct a sample object and persist in db.
I run a simple java class main method where i have loaded the applicationContext and have got reference to the service class as below
TestService srv = (TestService)factory.getBean("testService");
Application Context - context :
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryVsm" />
</bean>
<bean id="testService" class="com.test.service.TestServiceImpl">
<property name="testDao" ref="testDao"/>
</bean>
<bean id="testDao" class="com.test.dao.TestDaoImpl>
<property name="sessionFactory" ref="sessionFactoryVsm"/>
</bean>
In TestService I have injected TestDao. In test service method I have constructed to employee objects
emp1 and emp2 and calling dao twice to update.
TestDaoImpl code:
public void saveOrUpdate(BaseDomainModel baseObject) {
Session session = null;
try {
session = getHibernateTemplate().getSessionFactory().openSession();
session.saveOrUpdate(baseObject);
session.flush();
} catch (Exception e) {
logger.error("Generic DAO:saveOrUpdate::" + e);
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
}
When emp2 update fails emp1 should also fail. How do I do that. Please advice
Thanks in advance
Updated :
Thanks Nanda. I tried Declarative transaction. But it is not working. emp1 gets persisted and not rolled back eveb second dao call fails. I have added transaction advice to the method.
to test if the transaction advice is applied or not i changed the propagation to "NOT_SUPPORTED". but still emp1 gets persisted. the expectation is we should have got Transaction Not Supported type of exception. please advice .
UPDATED
#seanizer - Thanks for the update. I have even tried adding
#Transactional(propagation=Propagation.NOT_SUPPORTED)
public void saveEmp(Employee emp)
to that service method. But it didn't work. Moreover iterating the collection<BaseDomainModel> hold good only if i need to call one dao. If in case i have to call two different dao to persist obj1 and obj2- this may not help. Just to check if the transaction is getting applied I get
#Transactional(propagation=Propagation.NOT_SUPPORTED).
But still obj1 got persisted.
I just doubt if the xml configuration/ annotation given is correct. please check
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryVsm" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="saveEmp" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="testServiceOperation" expression="execution(*com.test.service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="testServiceOperation"/>
</aop:config>
I am using org.springframework.orm.hibernate3.HibernateTransactionManager for the transactionManager. Is this correct ?
Updated
I have created my exception class myRuntimeExp extending from RuntimeException and throwing the same from Dao method to the service method. but still the rollback is not happening. I just doubt if I have correctly given the configurations in the applnContext.xml. Can someone help me how to check if the transaction advice / annotation is being applied to the method or not? is there any way of running it in a debugging mode and check
Issue :
I was using
session = getHibernateTemplate().getSessionFactory().openSession();
But it should be current session and it is working fine.
session = getHibernateTemplate().getSessionFactory().getCurrentSession();
If you use declarative transaction management, you can lose most of this boilerplate:
TestDaoImpl:
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory f){
this.sessionFactory = f;
}
public void saveOrUpdate(BaseDomainModel baseObject) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(baseObject);
}
And you can control the transaction handling from the service layer using #Transactional (or xml configuration)
TestServiceImpl:
private TestDao testDao;
public void setTestDao(TestDao d){
this.testDao = d;
}
#Transactional // one transaction for multiple operations
public void someServiceMethod(Collection<BaseDomainModel> data){
for(BaseDomainModel baseObject : data)
testDao.saveOrUpdate(baseObject);
}
Reference:
Implementing DAOs based on plain
Hibernate 3 API
By default, Spring only rolls back for unchecked exception. You have to provide the rollback-for attribute and specify what exception you are trying to catch.
From the Spring documentation:
However, please note that the Spring Framework's transaction
infrastructure code will, by default, only mark a transaction for
rollback in the case of runtime, unchecked exceptions; that is, when
the thrown exception is an instance or subclass of RuntimeException.
(Errors will also - by default - result in a rollback.) Checked
exceptions that are thrown from a transactional method will not result
in the transaction being rolled back.
Here, get these snippets and hope they will help you:
<bean id="abstractService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="add*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="update*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="modify*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="delete*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="save*">PROPAGATION_REQUIRED, -Exception</prop>
</props>
</property>
</bean>
<bean id="persistenceServiceTarget" class="com.blahblah.server.service.impl.PersistenceServiceImpl">
<property name="persistenceDAO" ref="persistenceDAO" />
</bean>
<bean id="persistenceService" parent="abstractService">
<property name="target" ref="persistenceServiceTarget" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="abstractDAO"
class="org.springframework.orm.hibernate3.support.HibernateDaoSupport"
abstract="true">
<property name="sessionFactory">
<ref bean="webSessionFactory" />
</property>
</bean>
<bean id="persistenceDAO" class="com.blahblah.server.dao.impl.PersistenceDAOImpl"
parent="abstractDAO">
</bean>
These should be the things that you need. They don't have to be in the same file, maybe split them between services and daos.
You can put the sessionFactory in TestServiceImpl and open the session there.
another point is to check if the database support the transaction

using AbstractTransactionalDataSourceSpringContextTests with Hibernate & DbUnit - inserted data not found

All,
I'm attempting to write a unit test for a Dao that uses Hibernate. I'm also using Spring, so I'm trying to extend AbstractTransactionalDataSourceSpringContextTests and also use DBUnit to insert data into the database before each test in the onSetUpInTransaction method.
From my logs, I can see that DbUnit is able to successfully insert the data in onSetUpInTransaction just fine. However, when I run a test method that uses the Dao (and therefore, Hibernate) to try to access that data (testGetPersonById2), the data isn't found, even though all this should be happening in the same transaction. After the test method finishes running (it fails), I see the log statement from the AbstractTransactionalDataSourceSpringContextTests that the transaction does get rolled back correctly.
It seems like the onSetUpInTransaction and Hibernate session must be using different transactions, but I can't figure out why. Does anyone have an example of something like this working? Advice on what I'm missing?
Here's what I've got so far:
public class PersonDaoTest extends AbstractTransactionalDataSourceSpringContextTests{
private Log logger = LogFactory.getLog(PersonDaoTest.class);
private PersonDaoImpl personDao;
#Override
public void onSetUpInTransaction() throws Exception {
// Load test data using DBUnit
super.onSetUpBeforeTransaction();
DataSource ds = jdbcTemplate.getDataSource()
Connection con = DataSourceUtils.getConnection(ds);
IDatabaseConnection dbUnitCon = new DatabaseConnection(con);
DatabaseConfig config = dbUnitCon.getConfig();
config.setFeature("http://www.dbunit.org/features/qualifiedTableNames",
true);
//This dataset contains a single entry in the Persons table,
// a new person with Id = 998877665, it gets inserted successfully
IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(
"./PersonDaoTest.xml"));
logger.warn("dataSet = " + dataSet);
try {
DatabaseOperation.REFRESH.execute(dbUnitCon, dataSet);
SessionFactoryUtils.getSession(getSessionFactory(), false).flush();
} finally {
DataSourceUtils.releaseConnection(con, ds);
}
}
//This test PASSES, because the Person with Id = 9 already
//exists in the database, it does not rely on the data being set up in the
// onSetUpInTransaction method
#Test
public void testGetPersonById() {
Person person = personDao.findById(9L);
assertNotNull("person should not be null", person);
}
//This test FAILS at the assertNotNull line, because
//no Person with Id = 998877665 exists in the database,
//even though that Person was inserted
//in the onSetUpInTransaction method - it seems
//that hibernate cannot see that insertion.
#Test
public void testGetPersonById2() {
Person person = personDao.findById(998877665L);
assertNotNull("person should not be null", person);
}
UPDATE: Here's my spring config:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="com.p6spy.engine.spy.P6DataSource">
<constructor-arg>
<bean id="basicDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.query_cache_factory">org.hibernate.cache.StandardQueryCacheFactory</prop>
</props>
</property>
</bean>
<!-- The Hibernate interceptor
<bean id="hibernateInterceptor" class="org.springframework.orm.hibernate3.HibernateInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>-->
<bean id="personDao" class="my.dao.PersonDaoImpl">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
I spent some more time with this, and never was able to find anything that worked when trying to use the onSetUpInTransaction method. I ended up switching over to using onSetUpBeforeTransaction and onTearDownAfterTransaction instead. It's not exactly ideal, because the onSetUpBeforeTransaction method does end up committing its data insertions to the database, and that data must then be cleaned up in onTearDownAfterTransaction. However, the tests themselves can still insert and update data all they want and have those changes all rolled back, since each test does still operate in its own transaction. So, I don't have to do anything special to clean up when a test inserts new data, which means I met one of my goals anyway!
I had a similar problem. here is how I solved it.
I added a code to my abstract testcase class
then I added a datasource with the name "dataSource" which allowed me to use it to insert my test data and create my test sqls using that data source.
The individual datasources datasource1 and datasource2 were correctly injected into my dao beans without any issue.
#Override
protected String[] getConfigLocations()
{
setAutowireMode(AUTOWIRE_BY_NAME);
setDependencyCheck(false);
return new String[] { "classpath:configuration/myappxxx-test-application-context.xml" };
}

Categories