我想在我的应用程序中使用Hibernate和JPA的事务管理器。 我首先配置了 applicationContext.xml (我也想使用c3p0,但我稍后会配置它):
<?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>
我的DAO看起来像这样:
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();
}
}
我在我的业务对象中调用它:
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);
}
}
当我调用更新(类别)功能时,没有发生任何事情:( 如果我调用update(int,String),我收到此错误:
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)
答案 0 :(得分:3)
你应该删除它:
<prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
正如您已经提供了一个数据源,Hibernate已经知道了:
<property name="dataSource" ref="dataSource" />
我在堆栈跟踪中没有看到TransactionInterceptor,表明你没有可用的交易。
此外,没有涉及代理:
at dao.impl.CategoryDaoJpa.update(CategoryDaoJpa.java:53)
at business.CategoryService.update(CategoryService.java:53)
at controller.CategoryController.pageListe(CategoryController.java:31)
控制器直接调用服务而不使用任何代理,就像任何Spring Bean的情况一样。这就是您在堆栈跟踪中看不到TransactionInterceptor的原因。
您是如何在Web控制器中注入服务的?您应该确保使用依赖注入,而不是手动实例化bean或其他东西。
答案 1 :(得分:3)
问题变得非常微妙!
除了applicationContext.xml
之外,您还拥有配置Web应用程序上下文的文件mvc-dispatcher-servlet.xml
。
问题是您已启用组件扫描的文件
<context:component-scan base-package="dao" />
<context:component-scan base-package="business" />
在该文件中。
这显然是错误的,因为这些bean应该只出现在根应用程序上下文中(因为你实际上已经为applicationContext.xml
中的那些包启用了组件扫描)。
导致此问题的原因是来自Web应用程序上下文的service
和dao
bean正在使用,并且在该上下文中没有事务管理器,并且没有创建任何代理
如果你只是删除
<context:component-scan base-package="dao" />
<context:component-scan base-package="business" />
来自mvc-dispatcher-servlet.xml
然后一切都将按预期工作,因为将使用来自根应用程序上下文的bean(当然,由于事务管理器的存在,代理将被代理)。
答案 2 :(得分:1)
堆栈跟踪提供了以下元素:在事务之外进行更新,dao.impl.CategoryDaoJpa
直接注入CategoryService
而没有事务代理,CategoryService
直接注入CategoryController
。因此,错误的原因是spring忽略了@Transactional
和CategoryDaoJpa
上的CategoryService
注释。
首先,您应该将服务或dao设置为事务性,但不能同时设置两者。将服务设置为事务性更为常见,因为单个服务方法可以在单个事务中调用dao中的许多方法。
我真的不明白为什么春天会忽略你的@Transactional
注释。 Spring参考手册说: <tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in.
。也许您应该尝试在applicationContext.xml
中明确定义应该表现出事务行为的bean。
答案 3 :(得分:0)
我找到了解决方案,原因是我的过滤器不是@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);
}
}