Spring JPA Hibernate handle large databases - java

I am new to JPA, Hibernate as well as Spring. Currently I am creating a spring web service which work with a database with a large number of tables. To access those tables I have created separate class annotating #Entity. Then I created a generic DAO class as all my entities need similar operations.
#Transactional
public class GenericJpaDao<T, ID extends Serializable> {
private Class<T> persistentClass;
private EntityManager entityManager;
public GenericJpaDao(Class<T> persistentClass) {
this.persistentClass = persistentClass;
}
protected EntityManager getEntityManager() {
return entityManager;
}
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public Class<T> getPersistentClass() {
return persistentClass;
}
#Transactional(readOnly = true)
public T findById(ID id) {
T entity = (T) getEntityManager().find(getPersistentClass(), id);
return entity;
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true)
public List<T> findAll() {
return getEntityManager().createQuery("select x from " + getPersistentClass().getSimpleName() + " x").getResultList();
}
public T save(T entity) {
getEntityManager().persist(entity);
return entity;
}
public T update(T entity) {
T mergedEntity = getEntityManager().merge(entity);
return mergedEntity;
}
public void delete(T entity) {
entity = getEntityManager().merge(entity);
getEntityManager().remove(entity);
}
public void flush() {
getEntityManager().flush();
}
}
Now I tried to instantiate this GenericJpaDao in the code with relevant Entitiy Class as the persistentClass. But then I could not find a way to set the entitiyManager as I am configuring it via datasource-config.xml as
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml"></property>
<property name="persistenceUnitName" value="hibernatePersistenceUnit" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
So according to what I understood from the samples available I need to create separate DAO classes for all my entity classes and instantiate them in spring-ws-servlet.xml.
<bean id="testDao" class="com.sample.dao.TestDao" />
<bean id="service"
class="com.sample.service.DefaultService">
<property name="testDao" ref="testDao" />
</bean>
I think this will be a problem in the long run as I need to have two separated classes for each table in the database, instaintiating them in the xml, keep track of all of them at my service class. Is there any method to overcome this or any best practice available?

You wont need to create a specific DAO for each of your classes.
But you will have to remove the constructor and change your method signatures to include the needed persistentClass (or an instance where you call getClass on).
Basically you need to remove the persistentClass property and change the methods to use the class dynamically from the generic parameter of type T or Class.
That way you have ONE spring managed DAO which is able to handle all of your entities.

Related

How to resolve No Session found for current thread

I was trying to do generic way of implementation of DAO and I followed as per the Article
Following are my genericDaoImpl class
#SuppressWarnings("unchecked")
#Repository
public abstract class GenericDaoImpl<E, K extends Serializable>
implements GenericDao<E, K> {
#Autowired
private SessionFactory sessionFactory;
protected Class<? extends E> daoType;
/**
* By defining this class as abstract, we prevent Spring from creating
* instance of this class If not defined as abstract,
* getClass().getGenericSuperClass() would return Object. There would be
* exception because Object class does not hava constructor with parameters.
*/
public GenericDaoImpl() {
Type t = getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) t;
daoType = (Class) pt.getActualTypeArguments()[0];
}
protected Session currentSession() {
return sessionFactory.getCurrentSession();
}
#Override
public void add(E entity) {
currentSession().save(entity);
}
#Override
public void saveOrUpdate(E entity) {
currentSession().saveOrUpdate(entity);
}
#Override
public void update(E entity) {
currentSession().saveOrUpdate(entity);
}
#Override
public void remove(E entity) {
currentSession().delete(entity);
}
#Override
public E find(K key) {
return (E) currentSession().get(daoType, key);
}
#Override
public List<E> getAll() {
return currentSession().createCriteria(daoType).list();
}
}
GENERICDAO
public interface GenericDao<E,K> {
public void add(E entity) ;
public void saveOrUpdate(E entity) ;
public void update(E entity) ;
public void remove(E entity);
public E find(K key);
public List<E> getAll() ;
}
SERVICE CLASS
#Service
public class test {
#Autowired
TestPlanDao testPlanDao;
#Transactional(propagation = Propagation.REQUIRED)
public int saveTestPlan()
{
try
{
TestPlan tp=new TestPlan();
tp.setTestplan_version(1);
testPlanDao.saveTestPlan(tp);
logger.info("testplan saved");
return 1;
}
catch(Exception e)
{
e.printStackTrace();
logger.error(e.getMessage(),e);
return 0;
}
}
This is my daoImpl
#Repository
public class TestPlanDaoImpl extends GenericDaoImpl<TestPlan, Integer> implements TestPlanDao{
#Override
#Transactional
public void saveTestPlan(TestPlan tp) {
// TODO Auto-generated method stub
add(tp);
}
hibernate configuration xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://${mysqlHost}/${mysqldatabase}" />
<property name="username" value="${mysqlUserName}" />
<property name="password" value="${mysqlPassword}" />
<property name="removeAbandoned" value="true" />
<property name="initialSize" value="20" />
<property name="maxActive" value="30" />
<property name="maxIdle" value="-1" />
<property name ="testOnBorrow" value="true"/>
<property name ="validationQuery" value="SELECT 1"/>
</bean>
<bean id="sessionFactoryConf"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.test.model.TestPlan</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.transaction.auto_close_session">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
I am not able to find the cause of
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:988)
Did you try removing the following property :
<prop key="hibernate.transaction.auto_close_session">true</prop>
I believe that Hibernate will close the session too soon and resulting in your error. Since you use Spring TransactionManager, let it close the session.
you need to add following code in your hibernate configuration xml file
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<!-- property should be wired with a Hibernate SessionFactory in your case it is sessionFactoryConf -->
<property name="sessionFactory" ref="sessionFactoryConf" />
</bean>
You have to remove the #Transactional annotation from the Repository method , use only the #Transactional annotation on the service layer method.
#Repository
public class TestPlanDaoImpl extends GenericDaoImpl<TestPlan, Integer> implements TestPlanDao{
#Override
#Transactional //Remove annotation from here
public void saveTestPlan(TestPlan tp) {
// TODO Auto-generated method stub
add(tp);
}
Remove #Transactional annotation from function and use it on the class level.
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryConf" />
</bean>
This happens when you try to save changes on a Business Object after a transaction has already finished.
I think you should take a look to Spring Data JPA ? It has a lot more to offer than a generic DAO.

Using TransactionProxyFactoryBean with Spring and Hibernate

I have an old project that I'm trying to upgrade to spring 4 / hibernate 4.
We use this approach to configuring our transactions.
That is to say, in XML we define a transaction manager like:
<bean id="abstractTransactionProxy"
abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="hibernateTransactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
and
<bean id="MySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="configLocation" value="WEB-INF/hibernate.cfg.xml" />
</bean>
and then each of our service beans are defined like:
<bean id="customerService" parent="abstractTransactionProxy">
<property name="target" ref="customerTarget"/>
<property name="personDAO" ref="personDAO" />
</bean>
and the DAOs are defined like:
<alias name="personDaoImpl" alias="personDAO"/>
<bean id="personDaoImpl"
class="com.foo.bar.hibernate.PersonDaoImpl">
<property name="sessionFactory" ref="MySessionFactory" />
</bean>
PersonDaoImpl (and all the other DAOs) have many methods that look like this (all of the DAOs extend HibernateDaoSupport):
public List<Person> getPersonByCriteria(final String criteria) {
List<Person> results =
(List<Person>) this.getHibernateTemplate().executeFind(new HibernateCallback<List<Person>>(){
public List<Person> doInHibernate(Session session) throws HibernateException, SQLException {
List<Person> results = (List<Person>) session.createQuery(MYSQLHERE).setString("criteria", criteria).list();
return results;
}
});
return results;
}
Now, the problem I Face at this point is getting rid of our dependency on extending our DAOs from HibernateDaoSupport, and by extension, on using it's HibernateTemplate helper object.
Here's the steps I've taken:
Simply remove them from the DAO methods - don't extend HibernateDaoSupport, remove the HibernateTemplate callback. This looks like:
public List<Person> getPersonByCriteria(final String criteria) {
List<Person> results = (List<Person>) getSessionFactory().getCurrentSession().createQuery(MYSQLHERE).setString("criteria", criteria).list();
return results;
}
Of course this gives me compile-time errors because 'getSessionFactory' was part of the base class 'HibernateDaoSupport' which I just removed.
So I implemented a 'MyHibernateDaoSupport' Class, and had my DAO extend that instead. It looks like:
public class MyHibernateDaoSupport {
private SessionFactory sessionFactory;
protected Session session;
protected static Logger logger = Logger.getLogger(MyHibernateDaoSupport.class);
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
session = this.sessionFactory.getCurrentSession();
}
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
public Session getHibernateTemplate() {
session = this.sessionFactory.getCurrentSession();
return session;
}
public MyHibernateDaoSupport() {
}
}
Now I can build, deploy and run and I hit this error message:
No session available in this thread
How can I make this work?
In simple terms, what was the 'hibernateCallback()', and it's anoymous inner class actually doing?
How can I make my project's transactions work as before, without using HibernateDaoSupport and HibernateTemplate classes?
I've tried tagging my 'CustomerService' (the business logic object that calls 'PersonDao') with #transactional but (A) I see the same error regardless and (B) I'd really prefer to be able to do this programmatically, to reduce the scale of changes to the way the code works now. I also tried putting the transactions at the DAO level instead, and met the same issue.
Apologies if that was way too much detail for the Stack overflow format.
You need to inject the SessionFactory into your DAO classes:
#Repository
public class PersonDAOImpl implements PersonDAO {
#Autowired
private SessionFactory sessionFactory;
public List<Person> getPersonByCriteria(final String criteria) {
List<Person> results = (List<Person>) getSessionFactory().getCurrentSession().createQuery(MYSQLHERE).setString("criteria", criteria).list();
return results;
}
}
Add #Transactional to your service methods
Add HibernateTransactionManager
Instruct Spring to use declarative transactions:
<tx:annotation-driven transaction-manager="transactionManager"/>
Make sure you use LocalSessionFactoryBean for creating the SessionFactory
Here's a detailed step-by-step tutorial for configuring Spring and Hibernate 4

Why can't I retrieve the entities I've just persisted?

I've got this web service that basically queries the database and returns all persisted entities. For testing purposes, I've created a TestDataManager that persists 2 example entities after Spring context is loaded (BTW, I'm using JAX-WS, Spring, Hibernate and HSQLDB).
My TestDataManager looks like this:
#Component
public class TestDataManager {
#Resource
private SessionFactory sf;
#PostConstruct
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void insertTestData(){
sf.openSession();
sf.openSession().beginTransaction();
sf.openSession().persist(new Site("site one"));
sf.openSession().persist(new Site("site two"));
sf.openSession().flush();
}
}
My JAX-WS endpoint looks like this:
#WebService
public class SmartBrickEndpoint {
#Resource
private WebServiceContext context;
public Set<Site> getSitesForUser(String user){
return getSiteService().findByUser(new User(user));
}
private ISiteService getSiteService(){
ServletContext servletContext = (ServletContext) context.getMessageContext().get("javax.xml.ws.servlet.context");
return (ISiteService) BeanRetriever.getBean(servletContext, ISiteService.class);
}
}
This my Service class:
#Component
#Transactional(readOnly = true)
public class SiteService implements ISiteService {
#Resource
private ISiteDao siteDao;
#Override
public Set<Site> findByUser(User user) {
return siteDao.findByUser(user);
}
}
This is my DAO:
#Component
#Transactional(readOnly = true)
public class SiteDao implements ISiteDao {
#Resource
private SessionFactory sessionFactory;
#Override
public Set<Site> findByUser(User user) {
Set<Site> sites = new LinkedHashSet<Site>(sessionFactory.getCurrentSession().createCriteria(Site.class).list());
return sites;
}
}
This is my applicationContext.xml:
<context:annotation-config />
<context:component-scan base-package="br.unirio.wsimxp.dao"/>
<context:component-scan base-package="br.unirio.wsimxp.service"/>
<context:component-scan base-package="br.unirio.wsimxp.spring"/>
<bean id="applicationDS" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:sites"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="applicationDS" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.connection.release_mode">on_close</prop>
<!--<prop key="hibernate.current_session_context_class">thread</prop>-->
<prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
This is what's going on now:
when the app is deployed, TestDataManager#insertTestData kicks-in (due to #PostConstruct) and persist does not raise any exception. I should have 2 entities in the DB by now.
Afterwards, I invoke the endpoint by a SOAP client, and the request goes all the way up to the DAO. The Hibernate invocation does not raise any exception, but the returned list is empty.
The odd thing is, in TestDataManager, if I switch from sf.openSession() to sf.getCurrentSession(), I get an error message: "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here".
What I am doing wrong here? Why is the query "not seeing" the persisted entities? Why do I need to invoke sf.openSession() on TestDataManager although it's annotated with #Transactional?
I have done some tests with hibernate.current_session_context_class=thread in application.xml, but then I just switch problems in each class. I'd like not needing to manually invoke sf.openSession() and leave that for Hibernate to take care.
Thanks a lot for any help!
I think you need to commit the transaction on insertTestData:
#PostConstruct
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void insertTestData(){
Session session = sf.openSession();
session.persist(new Site("site one"));
session.persist(new Site("site two"));
session.flush();
session.close();
}
(I use hibernate in jpa mode)
I think your transactional annotations are not intercepted properly. Have you specified the HibernateVendorAdapter? In jpa+hibernate the integration is not fully setuped without it! Most likely you are missing this declaration.
After you should be able to autowire directly the Session instead of the Factory.
As a side note. If you use opensession in your code at least call it only once and keep the session in a variable. Else you are always opening a new one on each call i believe.
#PostConstruct
public void insertTestData(){
Obejct o = new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
//Your code here
}
});
}
source: http://forum.springsource.org/showthread.php?58337-No-transaction-in-transactional-service-called-from-PostConstruct&p=194863#post194863

EntityManager persist() not saving anything to database

My entityManager persist() gets id from sequence and puts it to my Image object but the Image object itself is not showing up in the database. EntityManager.flush() gives an error so I can't commit this way. Here is my code.
#Repository
public class ImageDaoImpl extends BaseDao implements ImageDao {
#PersistenceContext
protected EntityManager entityManager;
#Override
#Transactional
public void create(Image image) {
JpaTemplate jpaTemplate = getJpaTemplate(entityManager);
jpaTemplate.persist(image);
}
#Repository
public class BaseDao {
private JpaTemplate jpaTemplate;
public JpaTemplate getJpaTemplate(EntityManager entityManager){
if(jpaTemplate == null)
jpaTemplate = new JpaTemplate(entityManager);
return jpaTemplate;
}
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p: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.PostgreSQLDialect" />
</bean>
</property>
<property name="persistenceUnitName" value="sample"></property>
</bean>
<!-- DataSource Setup -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/imageCapture" />
<property name="username" value="myusername" />
<property name="password" value="mypassword" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
This generally happens when Transaction in not applied.. I doubt #Transactional interceptor is not intercepting properly.
persist() means "add object to list of managed entries". To save object to data base you must call flush() method. But remember you must call in inside the transaction.
//Edit:
Example save method.
public void save(T t){
// begin transaction
em.getTransaction().begin();
if (!em.contains(t)) {
// persist object - add to entity manager
em.persist(t);
// flush em - save to DB
em.flush();
}
// commit transaction at all
em.getTransaction().commit();
}
This is not the best that you can make, but good enough.
Check your server logs. Are you creating new EntityManger? and have not begun the transaction. I think, Where you have begun that is another EntityManager object.
Check EntityManager object is not getting created twice. In my case I was initialize EntityManager object in one method and mistakenly calling that method twice during opening transaction and persisting data.
Incorrect code:
EntityTrasaction transaction = getEntityManager().getTransaction().begin();
getEntityManager().persist(customer); // This line is calling another object of EntityManager
transaction.commit();
Correct code:
EntityManager em = getEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(customer);
transaction.commit();
public EntityManager getEntityManager(){
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("punit");
return entityManagerFactory.createEntityManager();
}
Check your mvc-dispatcher-servlet.xml . Here in <context:component-scan base-package="pass"/> pass should equal to package where your controllers are
I faced this problem in when running test cases with SpringJUnit4ClassRunner
I solved it by
wrapping the test function with
#Autowired
private PlatformTransactionManager transactionManager;
// in your test funciton
// Declare a transaction programmatically to be able to rollback.
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
try {
// here test of Dao goes
} finally {
transactionManager.rollback(transaction);
}
hope this helps you

Transactions not working unless inside DAO

I have a problem with transactions in that annotating a service that calls a DAO with #Transactional throws an exception stating that the Session is not open. The only way I can get it working is by annotating the DAO with #Transactional. What on earth can be happening?
This is what I'd like to do but doesn't work:
class CustomerService {
private CustomerDao dao;
#Transactional
public void foo() {
int customerId = dao.getCustomer("fred");
}
}
class CustomerDao {
private HibernateTemplate hibernateTemplate;
public int getCustomer(String name) {
String sql = "SELECT {m.*} from Customers {m} where name=:name";
Query qry = getSession().createSQLQuery(sql).addEntity("m", Customer.class);
qry.setParameter("name", name);
qry.setCacheable(false);
List<Customer> list = qry.list();
return list.iterator().next().getId();
}
private Session getSession() {
return hibernateTemplate.getSessionFactory().getCurrentSession();
}
}
This is what I'm doing instead but would rather not have to:
class CustomerService {
private CustomerDao dao;
public Customer(CustomerDao dao) {
this.dao = dao;
}
public void foo() {
int customerId = dao.getCustomer("fred");
}
}
class CustomerDao {
private HibernateTemplate hibernateTemplate;
#Transactional
public int getCustomer(String name) {
String sql = "SELECT {m.*} from Customers {m} where name=:name";
Query qry = getSession().createSQLQuery(sql).addEntity("m", Customer.class);
qry.setParameter("name", name);
qry.setCacheable(false);
List<Customer> list = qry.list();
return list.iterator().next().getId();
}
private Session getSession() {
return hibernateTemplate.getSessionFactory().getCurrentSession();
}
}
The problem seems to be caused by the CustomerService being instantiated inside the constructor of a wrapper class, where the wrapper is declared in the Spring xml context file:
class AllServices {
private final CustomerService customerService;
private final OrderService orderService;
#Autowired
public AllServices(CustomerDao customerDao, OrderDao orderDao) {
this.customerService = new CustomerService(customerDao);
this.orderService = new OrderService(orderDao);
}
public CustomerService getCustomerService() {
return this.customerService;
}
public OrderService getOrderService() {
return this.orderService;
}
}
The spring file looks like this:
<context:annotation-config />
<import resource="classpath:db-spring-conf.xml"/>
<bean id="allServices" class="myPackage.AllServices" />
and the db-spring-conf:
<bean id="editorDatasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${versioning.db}" />
<property name="username" value="${versioning.user}" />
<property name="password" value="${versioning.pass}" />
</bean>
<tx:annotation-driven transaction-manager="editorTransactionManager"/>
<bean id="editorSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="editorDatasource"/>
<property name="exposeTransactionAwareSessionFactory">
<value>true</value>
</property>
<property name="annotatedClasses">
<list>
<value>myPackage.Order</value>
</list>
</property>
<property name="mappingResources">
<list>
<value>mappings/customer.hbm.xml</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">validate</prop>
<!-- Enable Query Cache -->
<prop key="hibernate.cache.use_query_cache">false</prop>
<!-- Enable 2nd Level Cache -->
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.connection.autocommit">false</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate3.SpringSessionContext</prop>
</props>
</property>
</bean>
<bean id="editorHibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="editorSessionFactory"/>
</bean>
<bean id="editorTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="editorSessionFactory" />
</bean>
<!-- DAOs -->
<bean id="customerDao" class="myPackage.CustomerHibernateDao" />
<bean id="orderDao" class="myPackage.OrderHibernateDao" />
I've now moved the instantiation of CustomerService up to the Spring config file and everything works a treat. Do all classes using #Transactional have to be in the context file? Also in order to make it work I had to create an interface for CustomerService to prevent an exception whilst loading the context file - Could not generate CGLIB subclass of class
So, you identified the cause of problem - Spring's #Transactional support is an aspect, and aspects in Spring are applied only to the components managed by the Spring contrainer (though it can be changed, but it's an advanced feature for complex cases).
If you don't like declaring services in XML, you may take a look at other options to delcare Spring-managed components:
Classpath scanning
Java-based configuration (since Spring 3.x)
Regarding the problem with CGLIB proxies see 7.6 Proxying mechanisms - probably you don't have CGLIB implementation in the classpath.

Categories