Reputation: 955
I want to use a transaction manager in my application using Hibernate and JPA. I started by configuring applicationContext.xml (I also want to use c3p0 but i'll configure it later):
<?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: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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="dao,business" />
<!-- Configuration du transaction manager -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="entity" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="current_session_context_class">thread</prop>
<!-- <prop key="hibernate.cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</prop> -->
<!-- used for debug -->
<prop key="hibernate.show_sql">true</prop>
<!-- EhCache -->
<!-- <prop key="hibernate.cache.provider_configuration_file_resource_path"
value="classpath:ehcache.xml</prop> -->
<!-- <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.SingletonEhCacheProvider</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.generate_statistics">true</prop> -->
<!-- configuration pool via c3p0, see https://community.jboss.org/wiki/HowToConfigureTheC3P0ConnectionPool -->
<!-- <prop key="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</prop>
<prop key="hibernate.c3p0.acquire_increment">1</prop> <prop key="hibernate.c3p0.max_size">5</prop>
<prop key="hibernate.c3p0.max_statements">100</prop> <prop key="hibernate.c3p0.min_size">1</prop>
<prop key="hibernate.c3p0.timeout">100</prop> <prop key="hibernate.checkoutTimeout">1000</prop>
<prop key="hibernate.c3p0.idleConnectionTestPeriod">30</prop> <prop key="hibernate.c3p0.preferredTestQuery">SELECT
1</prop> -->
<!-- <prop key="c3p0.testConnectionOnCheckout">true</prop> -->
<!-- <prop name="eclipselink.jdbc.bind-parameters">false</prop> -->
</props>
</property>
</bean>
<!-- Configuration de la BDD -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/my_db?autoReconnect=true" />
<property name="username" value="user" />
<property name="password" value="password" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
My DAO look like this:
package dao.impl;
import dao.CategoryDao;
import entity.Category;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class CategoryDaoJpa implements CategoryDao {
@PersistenceContext
private EntityManager em;
@Override
public int save(Category entity) {
em.persist(entity);
return entity.getId();
}
@Override
public void remove(Category entity) {
em.remove(entity);
}
@Override
public void remove(int id) {
em.createQuery("DELETE FROM Category WHERE id=:id").setParameter("id", id).executeUpdate();
}
@Override
public void update(Category entity) {
em.merge(entity);
}
@Override
public Category load(int id) {
return em.find(Category.class, id);
}
@Override
public List<Category> loadAll() {
return em.createQuery("FROM Category ORDER BY type,sortIndex", Category.class).getResultList();
}
@Override
public void update(int id, String value) {
em.createNativeQuery("UPDATE category SET titre=:t WHERE id=:id")
.setParameter("t", value)
.setParameter("id", id)
.executeUpdate();
}
}
I call it in my business object:
package business;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import dao.CategoryDao;
import entity.Category;
@Service
@Transactional
public class CategoryService {
@Autowired
private CategoryDao dao;
public void update(int id, String title) {
Category c=dao.load(id);
c.setTitre(title);
dao.update(c);
}
}
When I call the update(Category) function, nothing is happening :( If I call update(int, String), I got this error:
Grave: Servlet.service() for servlet [mvc-dispatcher] in context with path [/my-app] threw exception [Request processing failed; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query] with root cause
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:71)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:333)
at com.sun.proxy.$Proxy156.executeUpdate(Unknown Source)
at dao.impl.CategoryDaoJpa.update(CategoryDaoJpa.java:53)
at business.CategoryService.update(CategoryService.java:53)
at controller.CategoryController.pageListe(CategoryController.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:175)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:446)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:434)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at filter.AuthFilter.doFilter(AuthFilter.java:36)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Upvotes: 4
Views: 3995
Reputation: 148880
Stack trace gives elements : update outside of a transaction, and dao.impl.CategoryDaoJpa
directly injected in CategoryService
without the transactional proxy and CategoryService
directly injected in CategoryController
. So the reason of the error is that spring ignores the @Transactional
annotation on CategoryDaoJpa
and on CategoryService
.
First, you should set either the service or the dao to be transactional, but not both. It is more common to have the service to be transactional, because an single method of service could call many methods in dao in a single transaction.
I do not really understand why spring ignores your @Transactional
annotations. Spring Reference Manual says : <tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in.
. Maybe you should try to explicitely define in applicationContext.xml
the beans that should exhibit the transactional behaviour.
Upvotes: 1
Reputation: 955
I found the solution, it was because my filter was not @Transactionnal
package filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Component //was missing
@Transactional(propagation=Propagation.REQUIRED) //was missing
public class AuthFilter implements Filter {
@Override
//no use so not implemented
public void destroy() {
//Ignore
}
@Override
//no use so not implemented
public void init(FilterConfig config) throws ServletException {
//Ignore
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
if(((HttpServletRequest)req).getSession().getAttribute("userAccount")==null) {
HttpServletResponse response=(HttpServletResponse)resp;
response.sendRedirect(((HttpServletRequest)req).getContextPath()+"/index.html");
return;
}
chain.doFilter(req, resp);
}
}
Upvotes: 0
Reputation: 63991
The problem turns out to be very subtle!
In addition to applicationContext.xml
you have the file mvc-dispatcher-servlet.xml
where you configure the Web application context.
The problem is that file you have enabled component scanning on
<context:component-scan base-package="dao" />
<context:component-scan base-package="business" />
in that file.
That is clearly wrong since those beans should only be present in the root application context (and they are since you have actually enabled component scanning for those packages in applicationContext.xml
).
What was causing the problem was that the service
and dao
beans from the Web application context where being used, and in that context there was no transaction manager, and there for no proxy was being created.
If you simply remove
<context:component-scan base-package="dao" />
<context:component-scan base-package="business" />
from mvc-dispatcher-servlet.xml
then everything will work as expected because the beans from the root application context will be used (and of course will be proxied due to the existence of the transaction manager).
Upvotes: 3
Reputation: 153710
You should remove this:
<prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
As you already provide a Datasource, of which Hibernate is already aware of:
<property name="dataSource" ref="dataSource" />
I don't see the TransactionInterceptor in your stacktrace which indicates you have no transaction available.
Also there is no proxying involved:
at dao.impl.CategoryDaoJpa.update(CategoryDaoJpa.java:53)
at business.CategoryService.update(CategoryService.java:53)
at controller.CategoryController.pageListe(CategoryController.java:31)
The Service is called by the controller directly without using any proxy, like it's the case with any Spring Bean. This is why you don't see the TransactionInterceptor in your stacktrace.
How are you injecting the service in the web Controller? You should make sure you use dependency injection and not instantiate the bean manually or something.
Upvotes: 3