I will describe the issue short:
I have a SessionLayer on which I want to manage transactions
I have servicelayer which actually performs data access logic
Servicelayer has the entityMgr injected using JPA annotation:
#PersistenceContext private EntityManager eMgr;
Session Layer does not have the entityMgr injected, but by using following spring config, it does correctly manage the transactions:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost/test" p:username="test" p:password="test" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:packagesToScan="com.model"> <!-- scans for entities (model) -->
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />
<context:component-scan base-package="com.session" />
<context:component-scan base-package="com.session.util" />
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="testSessionOperation" expression="execution(* com.session.TestSession.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="testSessionOperation" />
</aop:config>
The above works fine ... However what if i wanted a reference to the entitymgr in the SessionLayer.
I have tried adding the #PersistenceContext private EntityManager eMgr; annotation. This injects an entityMgr, but not the same one as the servicelayer, so the changes on servicelayer are not committed when using that enitymgr to commit.
What would be the way to go if I wanted the entityMgr used by the servicelayer also on the session layer (without passing it back and forth offcourse).
I though about moving enitymgr to session and passing it service, this works but i don't want to pass it everywhere it is required, it should be injected.
Thx in advance!
Use additional class that contains common entity manager
#Component
public class EntityManagerProvider implements IEntityManagerProvider
{
#PersistenceContext(unitName = "PU_NAME")
private EntityManager entityManager;
public EntityManager getEntityManager()
{
return entityManager;
}
}
your business bean
#Service
pucli class MyService
{
#Autowired
private IEntityManagerProvider entityManagerProvider;
//.. entityManagerProvider.getEntityManager() -- accessss to entity manager
}
Related
I am using spring JPA and have xml configuration like
<!-- additional datasource end -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager1" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory1" />
<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory2" />
<bean
id="org.springframework.context.annotation.internalPersistenceAnnotationProcessor"
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
<property name="defaultPersistenceUnitName" value="entityManagerFactory1" />
</bean>
<bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:persistenceUnitName="primary">
<property name="jpaPropertyMap">
<map>
<!-- Map values-->
</map>
</property>
</bean>
<bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:persistenceUnitName="primary">
<property name="jpaPropertyMap">
<map>
<!-- Map values-->
</map>
</property>
</bean>
I am declaring them in my repository class like this,
#Repository
public class MyDaoImpl implements MyDao {
#Autowired
#PersistenceContext(unitName = "primary")
private EntityManager em1;
#Autowired
#PersistenceContext(unitName ="secondary")
private EntityManager em2;
}
Where primary and secondry are datasources specified in persistance.xml file
I am using a method in which I am inserting in database1 and after that also updating in database2. For insertion I am using which em1 and updation using em2. I am doing it in same method one line after another like,
line-1 > totalUpdation = em1.createNativeQuery(INSERTION_QUERY).executeUpdate();
line-2 > totalUpdation = em2.createNativeQuery(UPDATION_QUERY).executeUpdate();
Insertion works fine but when line-2 is executed with em2 it gives TransactionRequiredException : For update/delete query.
Can anyone suggest how can I use single TransactionManager with 2 Entity Managers ?
When I execute the code, its showing org.springframework.transaction.UnexpectedRollbackException Transaction rolled back because it has been marked as rollback-only. But its not rolling back.
My Code:
Service level
public void checkTransaction(Users user) throws Exception {
adminDao.insertUser(user);
System.out.println("Transaction active :: " + TransactionSynchronizationManager.isActualTransactionActive());
throw (new Exception("Testing Transaction"));
}
xml
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" propagation="REQUIRED" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<tx:annotation-driven proxy-target-class="true"
transaction-manager="transactionManager" />
<aop:config>
<aop:pointcut id="serviceOperation"
expression="execution(* myapp.admin.service.AdminService.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
</aop:config>
POJO
My POJO (Users) Bean is not a annotated one, its mapped to hbm file. But it is registered in mapping resources.
<bean id="mysessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="mappingResources">
<list>
<value>myapp/admin/vo/Users.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>
</props>
</property>
</bean>
I was suffering from the same problem. What happens is that in the Service layer you can not make a query of type select and after it, an insert.
You need to separate the processes. In my case, declare a component class and, through it, call the processes of my service separately.
I hope it is useful for you ;)
I am creating a web application with spring-mvc, spring-security, spring-core, and hibernate.
But I always get this non-error from hibernate session factory.
It is actually throwing HibernateException, but I see on the log it is in debug level, and my application actually continues its execution with no problem.
But I am very curious to understand the reason of the exception raised.
The log shows the following lines.
2016-08-29 13:34:55,310 DEBUG [sg.com.diamond.express.base.dao.impl.HibernateDAOImpl].[doExecute]([328]) [http-nio-8888-exec-4] - Could not retrieve pre-bound Hibernate session
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:325)
at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308)
at org.springframework.orm.hibernate4.HibernateTemplate.get(HibernateTemplate.java:418)
at org.springframework.orm.hibernate4.HibernateTemplate.get(HibernateTemplate.java:411)
at sg.com.diamond.express.base.dao.impl.BaseHibernateDaoImpl.findById(BaseHibernateDaoImpl.java:160)
at sg.com.diamond.express.webadmin.service.impl.ClientProfileServiceImpl.searchByName(ClientProfileServiceImpl.java:48)
at sg.com.diamond.express.webadmin.controller.impl.ClientController.home(ClientController.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.servlet.mvc.multiaction.MultiActionController.invokeNamedMethod(MultiActionController.java:472)
at org.springframework.web.servlet.mvc.multiaction.MultiActionController.handleRequestInternal(MultiActionController.java:409)
at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:146)
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
This is the code that I have.
DAO Level
This is an abstraction level that I created to minimise the number of boiler-plate codes for DAO, no need to implement the basic c-r-u-d.
BaseDAO Interface
public interface BaseDAO <ID extends Serializable, M extends BaseModel> {
Session getCurrentSession();
M findById(ID id) throws DAOException;
List<M> findByCriteria(M criteria) throws DAOException;
List<M> listAll() throws DAOException;
ID saveObject(M object) throws DAOException;
void saveOrUpdate(M object) throws DAOException;
Query createQuery(String hql) throws DAOException;
Query createSQLQuery(String sql) throws DAOException;
Criteria createCriteria(Class clazz) throws DAOException;
List<M> runQuery(Query query) throws DAOException;
List<M> runQuery(String sql) throws DAOException;
}
BaseDAO Implementation
public class BaseHibernateDaoImpl<ID extends Serializable, M extends BaseModel> extends HibernateTemplate implements BaseDAO<ID, M> {
protected Class<M> modelClass;
#Override
public Session getCurrentSession() {
return this.getSessionFactory().getCurrentSession();
}
public ID saveObject(M object) throws DAOException {
try {
object.setCreator("Batch");
object.setCreatedAt(new Timestamp(new Date().getTime()));
object.setUpdater("Batch");
object.setUpdatedAt(new Timestamp(new Date().getTime()));
return (ID) super.save(object);
} catch (Exception e) {
logger.error(ExceptionUtil.getStackTraces(e), e);
throw new DAOException(e);
}
}
/** all other implementation methods **/
Spring XML Configuration
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Data Source Configuration
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="targetDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#192.168.170.36:1521:EXPT2" />
<property name="username" value="EXP_USER" />
<property name="password" value="passw0rd" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="targetDataSource"/>
</bean>
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Hibernate abstract session factory parent
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="baseSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" abstract="true">
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
</props>
</property>
</bean>
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Hibernate entity configurations
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="expressSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" parent="baseSessionFactory">
<property name="hibernateProperties">
<props merge="true">
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
<property name="mappingLocations" value="classpath*:hbm/express/*.hbm.xml"/>
</bean>
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Transaction Management
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="expressSessionFactory"/>
</bean>
<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(* sg.com.diamond.express.webadmin.service.*.*.*(..))"/>
<aop:advisor id="serviceTx" advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="search*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="list*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="is*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Base Dao Definitions
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="baseExpressDAO" class="sg.com.diamond.express.base.dao.impl.BaseHibernateDaoImpl" abstract="true">
<property name="sessionFactory" ref="expressSessionFactory"/>
</bean>
Question
Why does the error happen? and it doesn't break my app, the log level is DEBUG, not ERROR, my app is able to continue properly after that.
Is my configuration correct?
Note
Don't mind the full-xml config, for some reason, I need to use full-xml and none-annotations.
Answer
After mucking around with the aop pointcut expression, I noticed that on a simple batch program that I wrote, where I only have 1 context, I do not have this problem.
This leads me to the answer.
I originally structured my spring xml config as multiple layers.
At the top level, at the application context, I declare all the data sources, and the transaction managements.
At the second level, the servlet level, each servlet has its own context. At this level, I put all the controller-service-dao configurations.
There should be multiple of this "second-level" context, for example, 1 servlet context should handle all RESTful services, where another one handles web-ui.
I've structured it assuming it will take on the transaction management setup in the application context. Sort of like "all servlet context should be able to use the same data-source and transaction-management the same way"
But as it turns out, it doesn't.
When I moved the transaction management piece, including the data-source config, into the servlet context configuration, it all works fine, and the service is now wrapped correctly under transaction.
Thanks.
The error happens because your aop transaction configuration is not correct. It does not cover the service class from the stack trace. Tweak with the mapping until you stop getting the stack trace. Or maybe better use annotated transaction configuration if possible.
The answer to your second question is in the sources of hibernate template:
try {
325 session = getSessionFactory().getCurrentSession();
326 }
327 catch (HibernateException ex) {
328 logger.debug("Could not retrieve pre-bound Hibernate session", ex);
329 }
330 if (session == null) {
331 session = getSessionFactory().openSession();
332 session.setFlushMode(FlushMode.MANUAL);
333 isNew = true;
334 }
I have a spring bean that is created through factory-method and I also need to use #Transactional facility. So, when I create it as follows:
<bean id="myBean" class="pack.age.MyBean" factory-method="create">
<constructor-arg ref="someBean" />
</bean>
where
public class MyBean implements MyInterface{
private final SomeBean someBean;
public static MyInterface create(SomeBean someBean){
MyBean retVal = new MyBean(someBean);
//Create a thread and run it.
//Do some other job that doesn't suit for constructor
}
private MyBean(SomeBean someBean){
this.someBean = someBean;
}
}
Now, when I try to inject the bean into anothe bean:
public class MyAnotherBean{
private MyInterface myInterface;
public boid setMyInterface(MyInterface myInterface){
this.myInterface = myInterface;
}
}
declared as
<bean id="myAnotherBean" class="pack.age.MyAnotherBean">
<property name="myInterface" ref="myBean" />
</bean>
The actual myBean instance, not a proxy is injected. Since it's not a proxy, I can't use Spring #Transactional facility.
How can I inject a proxy when constructing an object through static factory-method?
In this case just enabling transaction annotation under the beans declaration should work:
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
But if not you can try enable the transaction declaratively:
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the MyInterface interface -->
<aop:config>
<aop:pointcut id="myBeanOperation" expression="execution(* x.y.service.MyInterface.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myBeanOperation"/>
</aop:config>
I have a situation where I need to setup a Proxy of a Pooled DataSource, my code is as follows:
<bean id="dataSourceBean" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="properties">
<props>
<prop key="c3p0.minPoolSize">0</prop>
<prop key="hc3p0.maxPoolSize">100</prop>
<prop key="hc3p0.timeout">60000</prop>
<prop key="c3p0.acquire_increment">10</prop>
<prop key="c3p0.max_statement">50</prop>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
</props>
</property>
</bean>
<bean id="dataSourceLockAdvice"
class="com.ndot2.datasource.DataSourceLockAdvice"/>
<bean id="dataSource" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="dataSourceBean"/>
<property name="interceptorNames">
<list>
<value>dataSourceLockAdvice</value>
</list>
</property>
</bean>
The problem that I'm having is that the connections aren't being closed anymore and it would seem that the destroy method of the proxied Datasource is no longer being called...
How would I call the Close method of the Proxied Bean? Or should I be implementing the Advice differently?
I've tried searching the Internet but I can't seem to find the answer to this, Help much appreciated!
EDIT:
As requested, here is my transaction management declarations (I'm using Appfuse)
<aop:config>
<aop:advisor id="userManagerTx" advice-ref="userManagerTxAdvice" pointcut="execution(* *..service.UserManager.*(..))" order="0"/>
<aop:advisor id="userManagerSecurity" advice-ref="userSecurityAdvice" pointcut="execution(* *..service.UserManager.saveUser(..))" order="1"/>
<aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* *..service.*Manager.*(..))" order="2"/>
</aop:config>
<!-- Enable #Transactional support -->
<tx:annotation-driven/>
<!-- Enable #AspectJ support -->
<aop:aspectj-autoproxy/>
<!-- Activates scanning of #Autowired -->
<context:annotation-config/>
<!-- Activates scanning of #Service -->
<context:component-scan base-package="com.ndot2.service"/>
<tx:advice id="txAdvice">
<tx:attributes>
<!-- Read-only commented out to make things easier for end-users -->
<!-- http://issues.appfuse.org/browse/APF-556 -->
<!--tx:method name="get*" read-only="true"/-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<tx:advice id="userManagerTxAdvice">
<tx:attributes>
<tx:method name="save*" rollback-for="UserExistsException"/>
</tx:attributes>
</tx:advice>
<bean id="userSecurityAdvice" class="com.ndot2.service.UserSecurityAdvice"/>
I don't have any #Transactional or #AspectJ driven Transaction management...
If you have the connection leaks in your application, the first step to do is to try to localize the place where the leaks occur, using the appropriate monitoring tools. As for c3p0, I believe it provides connection monitoring via JMX, as it's discussed in the related question.
So you can check with the debugging and monitoring tools if the leak occurs during some specific service call.
Then you should watch for different peculiarities: for example, in your configuration UserManager has more than one transaction advice, which could be the cause. In Spring container with annotated configuration a common error is that the bean in not being wrapped by transaction proxy, because the annotated transaction management is configured for the different IoC container. Another possible cause is that your method tries to manage transactions manually, and don't succeed in doing this correctly.