无法执行插入/更新/删除

时间:2014-06-26 09:18:42

标签: java spring hibernate jpa transactions

我想在我的应用程序中使用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)

4 个答案:

答案 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应用程序上下文的servicedao 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忽略了@TransactionalCategoryDaoJpa上的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);
    }


}