【问题标题】:Unable to perform insert/update/delete无法执行插入/更新/删除
【发布时间】:2014-08-17 01:59:32
【问题描述】:

我想在使用 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(Category) 函数时,什么都没有发生 :( 如果我调用 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)

【问题讨论】:

  • 从配置中删除 &lt;prop key="current_session_context_class"&gt;thread&lt;/prop&gt; 会发生什么?
  • 你的代码在 GitHub 上吗?我想不出你可以尝试的其他任何东西,但我想尝试一下
  • 不,它没有在 Internet 服务器上进行版本控制。但我可以在某个地方做一个小拉链
  • 行得通!没问题
  • +1 表示完整的堆栈跟踪和 spring 配置 - 所有元素都在里面

标签: java spring hibernate jpa transactions


【解决方案1】:

你应该删除这个:

<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。

【讨论】:

    【解决方案2】:

    问题变得非常微妙!

    除了applicationContext.xml,您还有文件mvc-dispatcher-servlet.xml,您可以在其中配置Web 应用程序上下文。 问题是您在

    上启用了组件扫描的文件
    <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(当然,由于事务管理器的存在,将被代理)。

    【讨论】:

    • 感谢您的提示,即使它没有解决我的问题。
    • @AthaNor 我运行了它,它确实为我解决了问题
    【解决方案3】:

    堆栈跟踪提供元素:在事务之外更新,dao.impl.CategoryDaoJpa 直接注入 CategoryService 没有事务代理,CategoryService 直接注入 CategoryController。所以错误的原因是spring忽略了CategoryDaoJpaCategoryService上的@Transactional注解。

    首先,您应该将服务或 dao 设置为事务性的,但不能同时设置两者。更常见的服务是事务性的,因为单个服务方法可以在单个事务中调用 dao 中的多个方法。

    我真的不明白为什么 spring 会忽略你的 @Transactional 注释。 Spring 参考手册说:&lt;tx:annotation-driven/&gt; only looks for @Transactional on beans in the same application context it is defined in.。也许您应该尝试在applicationContext.xml 中明确定义应该表现出事务行为的bean。

    【讨论】:

    • 我从我的 Dao 中删除了 @transactionnal 并且它有效 :)
    【解决方案4】:

    我找到了解决办法,是因为我的过滤器不是@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);
        }
    
    
    }
    

    【讨论】:

    • 这是个好主意,但您只是在回避真正的问题!您在这里所做的事情使每个请求都具有事务性,这可能不是您想要做的事情。每一个请求都会导致一个事务被打开,无论它是否需要
    • 在我的网络应用程序上,我没有后台进程,因此每个请求都可以是事务性的。但我同意,我有没有查询或选择查询的请求,所以我不需要交易。
    • 您可能不会立即看到任何问题,但随着流量的增长,您可能会看到一些可扩展性问题
    • 我也支持@geoand 的建议,您应该尽量减少交易长度以更好地扩展。
    • 它适用于 Intranet 或 Extranet 网站,所以如果我达到 20 个用户,我应该重新考虑它。我已经失去了一些时间来解决这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-04
    • 2012-07-05
    • 2019-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多