Spring & Hibernate: non transactional service methods - java

I want my read methods not to use a transaction since this is just not needed at all, I only mark my create/update methods with #Transactional. But how do I do this? I have a pretty basic configuration of Spring with etc...
SessionFactory is injected in my DAO, and in each method I call sessionFactory.getCurrentSession().doQueryStuff();
This, however results in this error:
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
If you need my Spring configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/oxm
http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="be.howest.kidscalcula" />
<mvc:annotation-driven />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="myDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/kidscalcula" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
id="sessionFactory">
<property name="dataSource" ref="myDataSource" />
<property name="mappingResources">
<list>
<value>be/howest/kidscalcula/model/Foto.hbm.xml</value>
<value>be/howest/kidscalcula/model/Kindleerplanonderdeel.hbm.xml
</value>
<value>be/howest/kidscalcula/model/Klas.hbm.xml</value>
<value>be/howest/kidscalcula/model/Leerkracht.hbm.xml</value>
<value>be/howest/kidscalcula/model/Leerling.hbm.xml</value>
<value>be/howest/kidscalcula/model/Leerplan.hbm.xml</value>
<value>be/howest/kidscalcula/model/LeerplanOefenreeks.hbm.xml
</value>
<value>be/howest/kidscalcula/model/Leerplanonderdeel.hbm.xml</value>
<value>be/howest/kidscalcula/model/Niveau.hbm.xml</value>
<value>be/howest/kidscalcula/model/Oefenreeks.hbm.xml</value>
<value>be/howest/kidscalcula/model/Overgangsregel.hbm.xml</value>
<value>be/howest/kidscalcula/model/Rapport.hbm.xml</value>
<value>be/howest/kidscalcula/model/RapportLeerplanonderdeel.hbm.xml
</value>
<value>be/howest/kidscalcula/model/Schooljaar.hbm.xml</value>
<value>be/howest/kidscalcula/model/Subonderdeel.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.connection.pool_size">3</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
</props>
</property>
</bean>
<!-- Configure the multipart resolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="500000" />
</bean>
<!--
Transaction manager for a single Hibernate SessionFactory (alternative
to JTA)
-->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<tx:annotation-driven />
Does this error have anything to do with the fact that Propagation is standard Required?

Mark the method as transactional and read only to ensure the transaction does not modify any data:
#Transactional(readOnly=true)
This is an useful optimization when working with Hibernate. To read more on the implications of this annotation, here is a great post: Read-Only transactions with Spring and Hibernate

As the message clearly says, no session is bound to the thread and the configuration you have specified does NOT allow a "non-transaction" session to be created. I will try to explain each these of scenarios.
When Spring's transaction is enabled in your configuration(as you have done), Spring will wrap the SessionFactory object with a proxy that prevents creation of new sessions, if not already present, when SessionFactory.getCurrentSession is called. The proxy will only get the current session that is bound to the thread local and no ad-hoc session(non-transactional) creation is allowed by your code. This is how your configuration is preventing non-trasactional session creation.
When you annotate a method/class with #Transactional, Spring's TransactionInterceptor will create a session and bind it to the current thread when the method is called so that it is available later. Spring's OpenSessionInViewFilter, if enabled, will also bind a session to the current thread. Since you aren't doing any of these, no thread bound session is found.
What you should do is annotate your class with #Transactional(readOnly = True) and then annotate your create/update/delete methods with #Transactional(readOnly = False). It is important that you have a read-only transaction when you are reading data. This assures you that nobody can commit data during that call.

It is possible to start a session outside of a transaction even when using Spring transaction management. Call sessionFactory.openSession() to get hold of a new session. This session is local in that it is not bound to a thread and so won't be returned when you call sessionFactory.getCurrentSession(). You'll have to manage it and make sure it's properly closed at the the end of your session or if an error occurs.

You can not access the data without an (hibernate) session.
One way to provide such a session is a (when using spring):
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
or
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
(this depends on the way you use your persistence provider)
The positive side effect of this fiters is, that the session is still open when the JSP Views are rendered - so you will have no lazy loading problems when accessing such a not yet loaded property while rendering.
#see http://community.jboss.org/wiki/OpenSessioninView

Related

Spring EJB Integration

In my application we have the following architecture:
Web Layer: JSF+Rich Faces
Service Layer: EJB
DAO Layer: EJB Classes [consist of JDBC Queries]
We want to use Hibernate as ORM framework instead of JDBC in DAO layer. I want to use Spring ORM feature for integrating Hibernate in DAO layer. Now, challenges that we are facing:
DAO layer classes are stateless EJB classes. So,to use Spring DI inside EJB classes, I have to go for an interceptor as follows:
#Stateless(mappedName = "myAppDao")
#Interceptors(SpringBeanAutowiringInterceptor.class)
public class MyAppDaoImpl implements MyAppDaoRemote {
#Autowired
private SessionFactory sessionFactory;
#Override
public void getSession() {
if(sessionFactory!=null){
Session session = null;
try{
session = sessionFactory.getCurrentSession();
}catch (Exception e) {
session = sessionFactory.openSession();
System.out.println("Exception:"+e.getMessage());
}
}
With sessionFactory.getCurrentSession() I am getting this exception:
Exception:Could not obtain transaction-synchronized Session for current thread.
The reason behind this I am not able to use Spring transaction at Service layer since I have EJB classes in my service layer.
Moreover I don't want to use sessionFactory.openSession() as in that case I would have to manually close the session.
These are my Spring configuration files:
1) beanRefContext.xml -> Used by Interceptors
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<bean id="businessBeanFctory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg value="classpath*:daoConfig.xml" />
</bean>
</beans>
2) daoConfig.xml -> Hibernate Configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.myapp.model"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyAppDS" expected-type="javax.sql.DataSource"/>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
I know that there is no point in using EJB along with Spring since they are alternate to each other.But due to some restriction at project level I can't remove EJB for the time being.
So, is there any solution by which we can get the Hibernate current session inside an EJB class through Spring?
If you're using JBossAS/WildFly then you just need:
#Stateless(mappedName = "myAppDao")
public class MyAppDaoImpl implements MyAppDaoRemote {
#PersistenceContext
private Session session;
public void someDaoMethod(YourEntity e) {
// use session directly
// Transactions are automatically managed by the EJB container
// because that's one of EJB's raison d'ĂȘtre
}
}
Ensure that your (traditional) Hibernate configuration specifies JTA transactions.
See the WildFly JPA Reference Guide for more information.
If you're not using one of these JavaEE implementations then you can still use the Spring configuration that you have, but remember to include:
<property name="jtaTransactionManager" value="transactionManager"/>
in your sessionFactory bean.
Additionally, if you're using WebLogic or WebSphere you may need to specify platform dependent JTA transaction managers for these servers, such as WebLogicJtaTransactionManager or org.springframework.transaction.jta.WebSphereUowTransactionManager.
I have modified my daoConfig.xml in following way
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.oms.model"/>
<property name="jtaTransactionManager" ref="transactionManager"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/OMSDS" expected-type="javax.sql.DataSource"/>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
</beans>
I have added <property name="jtaTransactionManager" ref="transactionManager"/>
under sessionFactory bean definition.After this I am able to use
session = sessionFactory.getCurrentSession();in my dao layer class.

Unable autowire SessionFactory[null] into #Transactional bean

I have faced very strange issue with autowiring Hibernate SessionFactory bean into my Service.
I'm able to find SessionFactory bean in Spring Context object. So no issues with creating such bean.
But I can not autowire this bean into my service when it is marked with #Transactional annotation. Factory field is null.
Once I remove this annotation - everything works good.
Service Class:
#Service
#Transactional
public class ExampleRunner implements Runnable{
#Autowired
SessionFactory sessionFactory;
...
}
applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:property-placeholder location="classpath:application.properties"
ignore-resource-not-found="true" />
<context:component-scan base-package="org.edu" />
<context:annotation-config />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="${jdbc.driverClassName:org.hsqldb.jdbcDriver}" />
<property name="url" value="${jdbc.url:jdbc:hsqldb:mem:myAppDb}" />
<property name="username" value="${jdbc.username:sa}" />
<property name="password" value="$jdbc.password:}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="org.edu" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
${hibernate.dialect:org.hibernate.dialect.HSQLDialect}
</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto:create-drop}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql:true}</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager"
proxy-target-class="true" />
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
Usage:
public static void main(String[] args) {
Runnable runner = new ClassPathXmlApplicationContext("applicationContext.xml").getBean(ExampleRunner.class);
runner.run();
}
Seems like when I make #Transactional as class level annotation Spring creates CGLib proxy bean where SessionFactory field is null.
But when I use #Transational as method level annotation everything works good.
So I just want to understand such behavior.
What have I missed from Spring documentation?
I use Spring 4.1.4.RELEASE.
I got your error actually why you getting problem here because you not allowing container to auto-wired bean for you as you using java Main method it will not auto-wired bean that's why you getting error.

Getting save is not valid without active transaction error on using transaction annotation

I'm using spring + hibernate + jersey in my application. I want to use transactions so I used spring's #Transactional annotation in my service layer. This is my hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/db</property>
<property name="hibernate.connection.username">user</property>
</session-factory>
</hibernate-configuration>
I haven't used session_context here so spring can manage it. In my applicationCOntext.xml, I have defined transactionManager:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db"/>
<property name="user" value="username"/>
</bean>
<context:annotation-config/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="packagesToScan">
<list>
<value>com.hibernate.pojos</value>
</list>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
All urls matching /api/v1/* map to a servlet named jersey and servlet class used is com.sun.jersey.spi.spring.container.servlet.SpringServlet to which I have passed com.services as package to scan. In this package I have a class:
#Path("/app")
#Component
public class testApi() {
#Autowired
private DAOImpl daoimpl;
#POST
#Path("/create")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Transactional(rollbackFor = {Exception.class})
public Map<String,Object> testHello(user u) {
Map response = daoimpl.save(u);
return response;
}
}
The class daoimpl has the sessionFactory autowired and uses sessionFactory.getCurrentSession() method to get session. The method daoimpl.save(obj) just saves it in db. Since I have marked testHello as transactional, I expect a transaction to begin which is managed by spring and then control should go to daoimpl where the actual save happens. But I get save is not valid without active transaction. I have seen lot of posts where session_context is mentioned in hibernate config and because of that, spring is unable to handle transactions. But in my case, I have ensured that I dont provide any session_context. What am I missing? I even tried adding #transactional to DAO since in my sample app, Im just issuing one DB call for a service. But this didnt work either.
Well could be that you are specifying a session factory via hibernate.cfg.xml and then again in Spring which gets passed to the transaction manager.
Not sure to be honest however I have never used a hibenate.cfg.xml and then following works for me (with the transactional config added as you have specified). This also gives you the advantage of specifying your connection params as properties and allowing you to easily swap db configs via Spring Profiles or some other mechanism.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>uk.co.xyz.domain</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">${hibernate.showsql}</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.ddlauto}</prop>
<prop key="hibernate.cache.use_second_level_cache">${hibernate.enablecache}</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
</beans>
See also here which actually suggests is shouldn't matter:
applicationContext.xml with datasource or hibernate.cfg.xml. Difference?

Add extra package to scan to entity manager using Spring configuration?

I have a Spring Batch application that has a Spring context configuration that normally each batch job would reference. This way each batch job uses the same entity manager.
batch-context.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- ... -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="myPersistenceUnit" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="com.example.domain" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">100</prop>
<prop key="hibernate.jbc.batch_size">1000</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.use_sql_comments">false</prop>
</props>
</property>
</bean>
<!-- ... -->
</beans>
Now in my specific batch job context (call it ExampleBatch.xml) I want to add another package to scan to the already defined entityManagerFactory bean. Is this possible?
ExampleBatch.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">
<!-- ... -->
<import resource="classpath:batch-context.xml" />
<bean id="entityManagerFactoryWithExtraPackages"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
parent="entityManagerFactory">
<!-- How do I override the packagesToScan property on the already defined entityManagerFactory bean?-->
<property
name="packagesToScan"
value ="com.example.domain,com.example.domain.abstraction"
/>
</bean>
<!-- ... -->
</beans>
The way I have it right now will not work because it complains that "No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2"
Is trying to override the "packagesToScan" property the right approach to take in this scenario? Is there a better way to accomplish this behavior?
Edit:
I was able to accomplish what I needed using the property-override functionality. Below is the updated ExampleBatch.xml that I went with
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">
<!-- ... -->
<import resource="classpath:batch-context.xml" />
<context:property-override properties-ref="entityManagerOverride"/>
<bean id="entityManagerOverride"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<util:properties>
<prop key="entityManagerFactory.packagesToScan">com.example.domain,com.example.batch.module.domain</prop>
</util:properties>
</property>
</bean>
<!-- ... -->
</beans>
So far Spring does not yell at me that this is an invalid configuration. Have yet to determine if this is in fact producing the desired outcome.
Edit 2:
The property-override method does not appear to be sufficient. It is a valid configuration but after inspecting the entity manager at runtime like this:
for (EntityType<?> entity : manager.getMetamodel().getEntities()) {
String name = entity.getName();
System.out.println(name);
}
It only contains entities from the com.example.domain package.
Does anyone have any other ideas?
The way you have it now, you really define two separate beans - one called entityManagerFactory and the other one entityManagerFactoryWithExtraPackages.
There are several ways to solve your very request:
Just get rid of one of the beans - merge the definitions into one.
I only guess it's not an option for you, otherwise you wouldn't ask.
Define the entityManagerFactory as abstract, then you end up having one bean anyway.
Use the property override mechanism. This fits the scenarios, where you are not in control of the 'top' bean and despite that you want to re-configure (literally override the values of the properties of) beans defined there.
Just replace this:
<property
name="packagesToScan"
value ="com.example.domain,com.example.domain.abstraction"/>
with this:
<property name="packagesToScan">
<array>
<value>com.example.domain</value>
<value>com.example.domain.abstraction</value>
</array>
</property>
If it fits your packages organization, you may try
<property name="packagesToScan" value="com.example.domain.*" />
In your batch-context.xml, so your ExampleBatch.xml doesn't have anymore to "override" the parent.
Another way to dig is using placeholder; in batch-context.xml, you would use :
<property name="packagesToScan" value="${packageList}" />
while in ExampleBatch.xml you would declare the placeholder with the appropriate value, as explained for example here : http://www.mkyong.com/spring/spring-propertyplaceholderconfigurer-example/

which SessionFactory should be use for transactionManager?

<bean id="projectService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target">
<bean class="com.company.project.company.services.ServiceImpl" init-method="init">
<property name="HRappsdao" ref="HRappsdao"/>
<property name="projectdao" ref="projectdao"/>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="store*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="bulkUpdate*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
i have 2 datasource HRappsdao and projectdao, both are using different sessionFactory. in this case, my transactionmanager should be using which sessionfactory? (hrappsdao or projectdao) ?
editted
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" > //my HRappsdao using same
<ref local="sessionFactory"/>
</property>
</bean>
Actually, you're not showing the configuration of your transaction manager so I'm not really sure of what your are currently using but, quoting the documentation:
JTA (usually through JtaTransactionManager) is necessary for accessing multiple transactional resources within the same transaction.
With Spring 2.5, consider using the "new" <tx:jta-transaction-manager/> configuration element for automatic detection of the underlying JTA-based transaction platform (works with most app servers). See the chapter 9.8. Application server-specific integration for more details on this.
If you are using an older version of Spring, you'll need to configure your JtaTransactionManager manually. This will require some knowledge of your application server as the JNDI location of the JTA TransactionManager is specific to each J2EE server.
Please provide more details (like the version of Spring and the application server you are using if you need more guidance).
UPDATE: As I said, when using multiple datasources, you need to use the JtaTransactionManager and not the HibernateTransactionManager (see the javadoc). If you are using Spring 2.5, update your Spring configuration as below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<tx:jta-transaction-manager />
<!--
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
//my HRappsdao using same
<ref local="sessionFactory" />
</property>
</bean>
-->
...
</beans>
Note that you'll need something like JOTM with Tomcat or Jetty. You should maybe consider moving to a J2EE app server like JBoss or Glassfish.

Categories