【问题标题】:Hibernate Update Problems - OpenSessionInViewFilter休眠更新问题 - OpenSessionInViewFilter
【发布时间】:2014-05-29 16:49:41
【问题描述】:

在我的应用程序中使用 Spring MVC 3 和 OpenSessionInViewFilter 尝试通过休眠更新对象大约需要 5 个小时,但没有任何运气。

我可能已经浏览过 StackOverflow 和其他论坛中的所有主题!

Hibernate 不会抛出任何错误,并表示对象已更新,但未反映在我的数据库中。

任何帮助将不胜感激。

所以,这是我要更新的 JSON 请求

{
    "id": "14",
    "name": "Whatever",
    "contactNumber": "918026754027",
    "manager": "Vishwas", --> I've updated this value
    "capacity": "222",
    "addressId": "31",
    "streetAddress": "1168, 1st Block, 17th Main, ABC",
    "countryId": "1",
    "stateId": "1",
    "cityId": "1",
    "area": "DEF",
    "pincode": "560050"
}

控制器:

@RequestMapping(value = "/branches/update", method = RequestMethod.POST, headers = {"Content-Type=application/json"})
    @ResponseBody
    public Map<String, Object> updateBranch(@RequestBody Map<String, String> requestMap) {
        boolean status = false;
        boolean branchStatus = false;
        Map<String, Object> statusMap = new HashMap<String, Object>();
        Branch branch = new Branch();

        Address address = new Address();
        address.setId(Long.parseLong(requestMap.get("addressId")));        
        address.setCountry(countryService.getCountryById(Long.parseLong(requestMap.get("countryId"))));
        address.setState(stateService.getStateById(Long.parseLong(requestMap.get("stateId"))));
        address.setCity(cityService.getCityById(Long.parseLong(requestMap.get("cityId"))));
        address.setType("BRANCH");
        address.setArea(requestMap.get("area"));
        address.setStreetAddress(requestMap.get("streetAddress"));
        address.setPincode(requestMap.get("pincode"));
        address.setModifiedBy("vishwas");
        address.setModifiedTimestamp(new Date());

        status = addressService.updateAddress(address);

        if (status) {
            branch.setId(Long.parseLong(requestMap.get("id")));
            branch.setName(requestMap.get("name"));
            branch.setAddress(address);
            branch.setContactNumber(requestMap.get("contactNumber"));
            branch.setManager(requestMap.get("manager"));
            branch.setActive(true);
            branch.setCapacity(Integer.parseInt(requestMap.get("capacity")));
            branch.setModifiedTimestamp(new Date());
            branch.setModifiedBy("vishwas");            
            branchStatus = branchService.updateBranch(branch);
        }

        if (branchStatus) {
            statusMap.put("status", branchStatus);
            statusMap.put("message", "Branch was updated successfully");
        } else {
            boolean delStatus = addressService.deleteAddress(address);
            statusMap.put("status", branchStatus);
            statusMap.put("message", "Problem updating branch. Please check with your system administrator");
        }
        return statusMap;
    }

服务类:

@Service("branchService")
@Transactional
public class BranchServiceImpl implements BranchService {

    @Autowired
    private BranchDAO branchDAO;
    private static Logger logger = Logger.getLogger(BranchService.class.getName());

    public boolean updateBranch(Branch branch) {
        logger.debug("Processing request to dao to update a branch --> " + branch.getId());
        return branchDAO.updateBranch(branch);
    }
}

DAO 方法:

public boolean updateBranch(Branch branch) {
        boolean status = false;
        try {
            logger.debug("Trying to update a branch --> " + branch.getId());                            
            sessionFactory.getCurrentSession().update(branch);            
            status = true;
        } catch (HibernateException exception) {
            logger.error("Problem updating a branch --> " + exception.getMessage());
        } catch (Exception exception) {
            logger.error("Problem updating a branch --> " + exception.getMessage());
        }
        return status;
    }

**更新 2:根据 Mr.Deinum 的建议,我已将事务管理器配置移至 o2-data.xml,现在仅扫描调度程序中的控制器,同时扫描 o2-data.xml 中的其他组件

数据配置

    <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
">
    <context:component-scan base-package="com.adwitiya.o2plus">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${database.driverClass}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
    </bean>

    <!-- Hibernate Session Factory -->
    <bean id="mySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="myDataSource"/>
        <property name="packagesToScan">
            <array>
                <value>com.adwitiya.o2plus.model</value>
            </array>
        </property>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.MySQLDialect
                hibernate.show_sql=true
            </value>
        </property>
    </bean>

    <!-- Hibernate Transaction Manager -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="mySessionFactory"/>
    </bean>

    <!-- Activates annotation based transaction management -->
    <tx:annotation-driven transaction-manager="transactionManager"/>


</beans>

调度程序配置:

   <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <context:component-scan base-package="com.adwitiya.o2plus">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <mvc:annotation-driven>
        <mvc:message-converters>
            <!-- Use the HibernateAware mapper instead of the default -->
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="com.adwitiya.o2plus.utilities.HibernateAwareObjectMapper"/>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <mvc:resources mapping="/gui/**" location="/gui/"/>

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:application.properties</value>
            </list>
        </property>
    </bean>

</beans>

web.xml

 <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <display-name>O2 Plus</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/o2-data.xml
            /WEB-INF/spring/o2-utils.xml
            /WEB-INF/spring/o2-security.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>hibernateFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
        <init-param>
            <param-name>sessionFactoryBeanName</param-name>
            <param-value>mySessionFactory</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>hibernateFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    <servlet>
        <servlet-name>o2-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/o2-dispatcher-servlet.xml</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>o2-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

【问题讨论】:

  • 请添加一些配置,因为我猜您的设置有缺陷。我的猜测是您甚至没有交易,因此需要刷新。我希望您在删除 OSIVF 时会在 sessionFactory.getCurrentSession() 上收到一个异常,一个异常告诉您没有正在进行的事务。由于组件扫描,您可能陷入了每个人都会陷入重复 bean 实例的陷阱,您可能在 root 和 servlet 配置中都有一个组件扫描,两者都在做同样的事情。
  • 我已经添加了spring和web.xml的配置。不,先生,在我开始使用 OpenSessionInView 之前,我能够持久保存到数据库并检索内容(然后我遇到了 LazyInitializationException,因此我添加了过滤器)。而且,不,我检查了我的日志,没有重复的 bean 实例。

标签: hibernate spring-mvc open-session-in-view


【解决方案1】:
<context:component-scan base-package="com.adwitiya.o2plus" />

&lt;context:component-scan ... /&gt; 位于DispatcherServlet 加载的配置。

<tx:annotation-driven transaction-manager="transactionManager"/>

&lt;tx:annotation-driven /&gt; 位于由ContextLoaderLIstener 加载的配置中。

Bean(Factory)PostProcessor 仅对加载它的 ApplicationContext 中的 bean 进行操作。它对父上下文或子上下文中的 bean 没有任何作用。 &lt;tx:annotation-driven /&gt; 注册了一个由InfrastructureAdvisorAutoProxyCreator 处理的拦截器(或方面),BeanPostProcessor

解决方案

要么将您的 &lt;tx:annotation-driven /&gt; 移动到您的 DispatcherServlet 配置中,要么修改您的组件扫描。 ContextLoaderListener 应该扫描除 @Controller 注释的 bean 之外的任何内容,而 DispatcherServlet 应该只扫描 @Controller 注释的 bean。

ContextLoaderListener 配置。

<context:component-scan base-package="com.adwitiya.o2plus">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> 
</context:component-scan>

DispatcherServlet配置

<context:component-scan base-package="com.adwitiya.o2plus" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> 
</context:component-scan>

【讨论】:

  • 谢谢。我会试试这个并更新这个线程。但是,我无法理解添加和删除操作是如何工作的,而无需手动刷新数据。
  • insert 是即时的,而不是更新,因为需要一个 id。
  • 好吧,不幸的是,我确实尝试了上述配置但没有任何运气。用最新配置编辑问题...
  • 再读一遍,你的配置仍然有缺陷,你错过了 servlet 配置中组件扫描的use-default-filters="false"。目前你仍然有多个 bean 实例。
  • 太棒了,终于……我想我需要彻底阅读 Spring MVC 的文档……非常感谢 :)
【解决方案2】:

显然,到目前为止,我想出解决此问题的唯一方法是使用以下代码:

sessionFactory.getCurrentSession().flush();

让我们看看还有什么办法可以解决这个问题!

【讨论】:

    【解决方案3】:

    确保你有:

    @EnableTransactionManagement
    

    Hibernate 事务管理器知道您的会话工厂:

    @Bean
    public HibernateTransactionManager transactionManager() {           HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(sessionFactory().getObject());
        return transactionManager;
    }
    

    <tx:annotation-driven transaction-manager="txManager" />
    

    如果你使用 xml 配置。

    只要使用故障 Spring proxy-based Transaction 拦截,就可以轻松调试服务层,查看当前堆栈跟踪,查找 Controller 调用与实际服务方法之间的TransactionInterceptor

    如果你有TransactionInterceptor,你应该使用事务并且拦截器调用休眠事务。

    您正在为新对象调用 Session.update。首选合并,因为它适用于瞬态、分离和已附加的实例。

    所以而不是:

    sessionFactory.getCurrentSession().update(branch); 
    

    你应该有:

    sessionFactory.getCurrentSession().merge(branch); 
    

    【讨论】:

    • 我已经在我的 spring xml 中配置了一个事务管理器。奇怪的是,对于一个新对象(即保存),它可以完美地工作。仅用于更新,我必须手动刷新数据..
    • 将 session.update 替换为 session.merge,每次都可以使用。
    • 也试过了 :) 我试过合并、saveOrUpdate 和更新 --> 除非我手动刷新,否则它们似乎都不起作用......
    • 检查debug中是否有TransactionInterceptor。如果没有任何交易,您将不会进行任何交易。
    • 试图找出问题有一段时间了,但仍然卡住了……是的,日志中有一个“TransactionInterceptor”……一行说正在创建事务拦截器(单例) : 23:06:17,162 DEBUG DefaultListableBeanFactory:215 - 创建单例 bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'的共享实例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-21
    • 2011-06-20
    • 2017-11-19
    • 2011-10-06
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    相关资源
    最近更新 更多