I'm trying to work with Hibernate and Spring DataSourceTransactionManager to handle commit and rollback functions, but probably I dind't get something.
Before using Spring DataSourceTransactionManager, this was one of my DAO class
package com.springgestioneerrori.DAO;
public class UtenteDAO extends DAOBase{
public void salvaUtente(Utente utenteIn) throws DAOException{
Session session = getHibernateSession(); //from this method I get Hibernate SessionFactory
try{
session.beginTransaction();
session.saveOrUpdate(Object);
session.getTransaction().commit();
}
catch(Exception e){
session.getTransaction().rollback()
}
}
}
This is the class that give me the sessionFactory
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new AnnotationConfiguration().configure().buildSessionFactory();
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
getSessionFactory().close();
}
public Session getHibernateSession (){
Session session = HibernateUtil.getSessionFactory().openSession();
return session;
}
Now i'm trying to use DataSourceTransactionManager in a declarative way. Following some examples on the internet I wrote this:
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="salvaUtente"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="userDaoTxPointcut" expression="execution(* com.springgestioneerrori.DAO.UtenteDAO.salvaUtente(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="userDaoTxPointcut" />
</aop:config>
Now, what should I write inside the method salvaUtente() to perform many inserts, for example like these
session.saveOrUpdate(User);
session.saveOrUpdate(UserCredentials);
session.saveOrUpdate(UserOtherDetails);
and making Spring handle commint and rollback?
First you are using the wrong transaction manager. The DataSourceTransactionManager isn't for Hibernate but for plain JDBC. If you are using plain Hibernate use the HibernateTransactionManager. (Assuming that you are using Hibernate 4 here!).
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Now I would also suggest to use #Transactional instead of a <tx:advice /> and <aop:config /> block. Makes your configuration easier. Simply remove the 2 mentioned blocks and replace with <tx:annotation-driven />.
<tx:annotation-driven />
Now your code is wrong as you are starting transactions yourself. Annotate your method with #Transactional (after adding the thing above) and remove your transaction handling code.
#Transactional
public void salvaUtente(Utente utenteIn) throws DAOException{
Session session = getHibernateSession(); //from this method I get Hibernate SessionFactory
session.saveOrUpdate(Object);
}
Now I don't know what your getHibenateSession method does but make sure you don't use openSession on the SessionFactory to obtain a session. Use getCurrentSession instead.
protected Session getHibernateSession() {
return sessionFactory.getCurrentSession();
}
Your current BaseDAO class is flawed. Delete your buildSessionFactory and remove the static final. Let Spring configure and inject the SessionFactory.
public abstract class BaseDAO {
#Autowired
private SessionFactory sessionFactory;
protected Session getHibernateSession() {
return sessionFactory.getCurrentSession();
}
}
In your configuration add the configuration for the LocalSessionFactoryBean.
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
... Other Hibernate Properties
</bean>
That is basically all that is needed. This is also all explained in detail in the Spring Reference guide. I strongly suggest a read.
I would suggest
#Transaction(rollbackFor = {if you need to rollback on certain checked exception})
salvaUtente()
The transaction is only roll backed for unchecked exception until mentioned otherwise.
if there is an exception at any point salvaUtente in the transaction will be roll backed else the transaction will be committed once the method is existed
HibernateTransactionManager can also manage the transactions on the DataSource. We need to inject the DataSource in the HibernateTransactionManager and it should work for both, Hibernate and plain old SQL.
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager>
<property name="sessionFactory" ref="sessionFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
Hope this works for you.
(I know this is pretty late, but may help someone landing to this post)
Related
Is it possible to #Autowired a field
#Repository( "categoryDao" )
public class SomeDaoImpl implements SomeDao {
#Autowired
private SessionFactory sessionFactory;
...
}
without using setter/getter or *component scan?
I have a config
<bean id="categoryDao" class="com.example.dao.SomeDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
...
</bean>
You can use constructor injection instead, so something like
#Repository( "categoryDao" )
public class SomeDaoImpl implements SomeDao {
private SessionFactory sessionFactory;
#Autowired
public SomeDaoImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
...
}
There's a good discussion about the different types of injection, with a lot of refernce links available here
UPDATE after comment
Now that you explained, you should not change your code on accout of test. In your case you should use a test specific XML context, but reduce the packages being scanned to just enough
Also a handy construct is that inside your test context xml, you can redifine some injected beans by instantiating it first and using context:exclude-filter property, usefull for mocking, an example snippet
<!--Mock object -->
<bean id="beanDAO" name="beanDAO" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="your.package.BeanDAO"/>
</bean>
<context:component-scan base-package="your.package">
<context:exclude-filter type="regex" expression="your\.package\.Bean*"/>
</context:component-scan>
I am working on an existing project using Spring and Hibernate and am getting confused because I get a
org.springframework.dao.InvalidDataAccessApiUsageException: Write
operations are not allowed in read-only mode (FlushMode.MANUAL): Turn
your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker
from transaction definition.
error when trying to save objects but I still can't find what exactly is wrong.
There is a service layer that is annotated using #Service and a save method that it should be transactional so it is annotated with #Transactional(readOnly = false). To me that means that spring should handle transactions itself.
#Service
public class LadyService {
Logger log = Logger.getLogger(LadyService.class);
#Autowired
private PictureDAO pictureDao;
#Autowired
private LadyDAO ladyDao;
#Autowired
private AddressDAO addressDao;
#Transactional(readOnly = false)
public void save(Lady lady) {
Address a = addressDao.getExistingAddress(lady.getAddress());
if (a == null) {
a = addressDao.save(lady.getAddress());
}
lady.setAddress(a);
ladyDao.save(lady);
pictureDao.savePictures(lady.getPictures());
}
The error happens when doing a save in the AddressDAO. It is annotated as #Repository.
#Repository
public class AddressDAO extends HibernateDaoSupport {
public Address save(Address address) {
getHibernateTemplate().save(address); <-- write not permitted error happens here
return address;
}
#SuppressWarnings({ "unchecked" })
public Address getExistingAddress(Address address) {
DetachedCriteria cd = DetachedCriteria.forClass(Address.class);
cd.add(Restrictions.eqOrIsNull("administrative_area_level_1",
address.getAdministrative_area_level_1()));
cd.add(Restrictions.eqOrIsNull("administrative_area_level_2",
address.getAdministrative_area_level_2()));
List<Address> result = (List<Address>) getHibernateTemplate()
.findByCriteria(cd);
if (result.isEmpty()) {
return null;
} else {
return (Address) result.get(0);
}
}
}
What I thought would happen was that #Transactional makes spring create a session and a transaction for the save on the service layer, and that in the DAOs, the hibernate template would get the current session and transaction that spring manages and use it to save the objects.
The error message, though, makes me think that my service method and dao methods are not in the same transaction.
In the servlet-context.xml there are these statements:
<annotation-driven />
<context:component-scan base-package="com.kog.fable" />
<beans:bean id="mySessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="myDataSource" />
<beans:property name="packagesToScan">
<beans:array>
<beans:value>com.kog.fable.**.*</beans:value>
</beans:array>
</beans:property>
<beans:property name="hibernateProperties">
<beans:props>
<beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect
</beans:prop>
<!-- create, validate, update -->
<beans:prop key="hibernate.hbm2ddl.auto">create</beans:prop>
<beans:prop key="hibernate.show_sql">false</beans:prop>
<beans:prop key="hibernate.connection.pool_size">10</beans:prop>
<beans:prop key="hibernate.connection.autocommit ">false</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<beans:bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
<beans:bean id="addressDAO" class="com.kog.fable.dao.AddressDAO">
<beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
<beans:bean id="ladyDAO" class="com.kog.fable.dao.LadyDAO">
<beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
<beans:bean id="pictureDAO" class="com.kog.fable.dao.PictureDAO">
<beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
Here I don't understand why, if component scan is used, the DAO beans are still declared explicitly. Shouldn't the component scan feature be able to create those by itself since the DAO classes are annotated with #Repository?
Since I thought this configuration could create duplicate beans, I tried deleting the xml entries, but then I started getting:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'addressController': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.kog.fable.dao.AddressDAO
com.kog.fable.controller.AddressController.addressDAO; nested
exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'addressDAO' defined in file
[***\com\kog\fable\dao\AddressDAO.class]: Invocation of init method
failed; nested exception is java.lang.IllegalArgumentException:
'sessionFactory' or 'hibernateTemplate' is required
Here I thought that the extension of HibernateDaoSupport for my DAOs would make them inherit the sessionFactory and related methods so I don't understand what happens.
I have read that I could set the flush mode to AUTO or set the setCheckWriteOperations on the template to FALSE to solve that kind of problems and it seems to work, but I guess that this would not ensure the transaction coherence in all cases as I would like it.
Any help would be appreciated as I am quite new to Spring and Hibernate and am a bit stuck here.
When extending HibernateDaoSupport you will not benefit from autowiring, you will have to override the setSessionFactory method and put an #Autowired annotation on it. Else it won't work.
I would also expect a <tx:annotation-driven /> without that the #Transactional is pretty much useless and doesn't do anything.
if your app is Spring MVC based...
in application-context try this..
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="yourSessionFactory"></property>
</bean>
but in dispatcher-servlet (not in appContext !!!)
<tx:annotation-driven />
Dont forget namespaces for tx, and jar libraries spring-tx,spring-orm,hibernate-core
In configuration file
do the change:-
#Configuration
#EnableTransactionManagement <-----Put this line
public PersistenceConfig{
//your code
}
(OR)
#Bean
#Autowired
public HibernateTemplate getHibernateTemplate(SessionFactory session) {
HibernateTemplate hb = new HibernateTemplate();
hb.setCheckWriteOperations(false);
hb.setSessionFactory(session);
return hb;
}
I have a webapp using Hibernate 4.1 and Spring 3.1 and JSF 1.2 (myFaces).
I have this "LazyInitializationException" each time I try to access one of my pages
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at £org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:149)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:195)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at foo.data.bo.implementations.EOServiceType_$$_javassist_10.getTechKey(EOServiceType_$$_javassist_10.java)
at foo.converter.EOServiceTypeConverter.getAsString(EOServiceTypeConverter.java:36)
at org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils.getConvertedStringValue(RendererUtils.java:648)
at org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils.getSubmittedOrSelectedValuesAsSet(HtmlRendererUtils.java:362)
at org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils.internalRenderSelect(HtmlRendererUtils.java:337)
at org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils.renderMenu(HtmlRendererUtils.java:288)
at org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:57)
at org.apache.myfaces.renderkit.html.ext.HtmlMenuRenderer.encodeEnd(HtmlMenuRenderer.java:70)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:649)
... 50 more
I think I missunderstood something because I don't know How to give the "session" to my classes.
For info, here are some of my configuration files :
spring-config.xml:
<context:annotation-config />
<context:component-scan base-package="foo" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="namingStrategy">
<ref bean="oracleNamingStrategy" />
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<alias name="jndiDataSource" alias="dataSource" />
<bean name="oracleNamingStrategy"
class="org.hibernate.cfg.ImprovedNamingStrategy">
</bean>
<bean name="jndiDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/fooDS</value>
</property>
</bean>
My java class EOServiceType
#Entity
#Table(name="EOSERVICETYPE")
public class EOServiceType implements IEOServiceType {
#Id
#Column(name="EOSERVICETYPE_ID")
private long techKey;
#Column(name="H_PROPERTY")
private String property;
#Column(name="H_DESCRIPTION")
private String description;
//... + all getters and setters
}
My DAO implementation for Hibernate EOServiceTypeDaoHibernateImpl
#Repository("eOServiceTypeDao")
public class EOServiceTypeDaoHibernateImpl implements IEOServiceTypeDao {
#Autowired
private SessionFactory sessionFactory;
public void save(IEOServiceType serviceType) {
sessionFactory.getCurrentSession().save(serviceType);
}
public void update(IEOServiceType serviceType) {
sessionFactory.getCurrentSession().update(serviceType);
}
//... and some other CRUD operations...
}
My POJO Service implementation for Hibernate EOWebStaffServicesImpl
#Service
public class EOWebStaffServicesImpl implements IEOWebStaffServices {
#Autowired
private SessionFactory sessionFactory;
//...
#Autowired
private IEOServiceTypeDao eoServiceTypeDao;
public void saveOrUpdateEOServiceType(IEOServiceType eoServiceType) {
try {
eoServiceTypeDao.saveOrUpdate(eoServiceType);
} catch (DataIntegrityViolationException e) {
DuplicateKeyException exception= new DuplicateKeyException("Duplicate business key for " + eoServiceType,e);
throw exception;
}
}
public void deleteEOServiceType(IEOServiceType eoServiceType) {
eoServiceTypeDao.delete(eoServiceType);
}
My Hibernate Config file :
<hibernate-configuration>
<session-factory>
<property name="hibernate.mapping.precedence">hbm, class</property>
<property name="show_sql">false</property>
<property name="format_sql">true</property>
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="jdbc.batch_size">20</property>
<mapping class="foo.data.bo.implementations.EOServiceType"/>
<!-- ... and other mappings -->
</session-factory>
</hibernate-configuration>
Does any body have a tip to help me ? I read some articles and post but did not really find a solution to my problem.
Best regards,
Kamran
I had the same kind of problem several weeks ago. I obviously forgot to annotate my method which is interacting with Hibernate.
I recommend you the
#Transactional
annotation. It should fix your problem.
Otherwise here is the related Hibernate documentation:
Sessions and Transactions
I think this happens because you don't use transactions in your DAO classes. Hibernate will not work with Spring outside of the transaction. You can define declarative transactions with Spring (annotate necessary classes with #Transactional annotation). Here is a link to Spring reference documentation about Transactions.
Also you should inject SessionFactory to your bean before using it:
#autowired
private SessionFactory sessionFactory;
What are you planing to archive with the SessionFactory in your service class?
Anyway, i think the problem is that you are trying to access a detached object with lazy properties. (That's usually the case when you see that exception)
Is that all the code there is of your POJOs?
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
I'm trying to set up a Spring JPA Hibernate simple example WAR for deployment to Glassfish.
I see some examples use a persistence.xml file, and other examples do not.
Some examples use a dataSource, and some do not. So far my understanding is that a dataSource is not needed if I have:
<persistence-unit name="educationPU"
transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.coe.jpa.StudentProfile</class>
<properties>
<property name="hibernate.connection.driver_class"
value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url"
value="jdbc:mysql://localhost:3306/COE" />
<property name="hibernate.connection.username" value="root" />
<property name="show_sql" value="true" />
<property name="dialect" value="org.hibernate.dialect.MySQLDialect" />
</properties>
</persistence-unit>
I can deploy fine, but my EntityManager is not getting injected by Spring.
My applicationContext.xml:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="educationPU" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="StudentProfileDAO" class="com.coe.jpa.StudentProfileDAO">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="studentService" class="com.coe.services.StudentService">
</bean>
My class with the EntityManager:
public class StudentService {
private String saveMessage;
private String showModal;
private String modalHeader;
private StudentProfile studentProfile;
private String lastName;
private String firstName;
#PersistenceContext(unitName="educationPU")
private EntityManager em;
#Transactional
public String save()
{
System.out.println("*** em: " + this.em); //em is null
this.studentProfile= new StudentProfile();
this.saveMessage = "saved";
this.showModal = "true";
this.modalHeader= "Information Saved";
return "successs";
}
My web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
Are there any pieces I am missing to have Spring inject "em" in to StudentService?
Just to confirm though you probably did...
Did you include the
<!-- tell spring to use annotation based congfigurations -->
<context:annotation-config />
<!-- tell spring where to find the beans -->
<context:component-scan base-package="zz.yy.abcd" />
bits in your application context.xml?
Also I'm not so sure you'd be able to use a jta transaction type with this kind of setup? Wouldn't that require a data source managed connection pool? So try RESOURCE_LOCAL instead.
I'm confused. You're injecting a PU into the service layer and not the persistence layer? I don't get that.
I inject the persistence layer into the service layer. The service layer contains business logic and demarcates transaction boundaries. It can include more than one DAO in a transaction.
I don't get the magic in your save() method either. How is the data saved?
In production I configure spring like this:
<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/ThePUname" />
along with the reference in web.xml
For unit testing I do this:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:persistence-xml-location="classpath*:META-INF/test-persistence.xml"
p:persistence-unit-name="RealPUName" p:jpaDialect-ref="jpaDialect"
p:jpaVendorAdapter-ref="jpaVendorAdapter" p:loadTimeWeaver-ref="weaver">
</bean>
If anyone wants to use purely Java configuration instead of xml configuration of hibernate, use this:
You can configure Hibernate without using persistence.xml at all in Spring like like this:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean()
{
Map<String, Object> properties = new Hashtable<>();
properties.put("javax.persistence.schema-generation.database.action",
"none");
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect"); //you can change this if you have a different DB
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(adapter);
factory.setDataSource(this.springJpaDataSource());
factory.setPackagesToScan("package name");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setJpaPropertyMap(properties);
return factory;
}
Since you are not using persistence.xml, you should create a bean that returns DataSource which you specify in the above method that sets the data source:
#Bean
public DataSource springJpaDataSource()
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost/SpringJpa");
dataSource.setUsername("tomcatUser");
dataSource.setPassword("password1234");
return dataSource;
}
Then you use #EnableTransactionManagement annotation over this configuration file. Now when you put that annotation, you have to create one last bean:
#Bean
public PlatformTransactionManager jpaTransactionManager()
{
return new JpaTransactionManager(
this.entityManagerFactoryBean().getObject());
}
Now, don't forget to use #Transactional Annotation over those method that deal with DB.
Lastly, don't forget to inject EntityManager in your repository (This repository class should have #Repository annotation over it).
I have a test application set up using JPA/Hibernate & Spring, and my configuration mirrors yours with the exception that I create a datasource and inject it into the EntityManagerFactory, and moved the datasource specific properties out of the persistenceUnit and into the datasource. With these two small changes, my EM gets injected properly.
This may be old, but if anyone has the same problem try changing unitname to just name in the PersistenceContext annotation:
From
#PersistenceContext(unitName="educationPU")
to
#PersistenceContext(name="educationPU")