Проблемы с обновлением гибернации — OpenSessionInViewFilter

Прошло около 5 часов безуспешных попыток обновить объект через спящий режим в моем приложении с использованием Spring MVC 3 и OpenSessionInViewFilter.

Возможно, я просмотрел все темы, доступные на 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: по предложению г-на Дейнума я переместил конфигурацию диспетчера транзакций в 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>

person Vishwas Shashidhar    schedule 29.05.2014    source источник
comment
Пожалуйста, добавьте некоторую конфигурацию, так как я предполагаю, что ваша настройка ошибочна. Я предполагаю, что у вас даже нет транзакций и, следовательно, необходимость сброса. Я ожидаю, что вы получите исключение sessionFactory.getCurrentSession(), когда вы удалите исключение OSIVF, сообщающее вам, что транзакция не выполняется. Вы, вероятно, попали в ловушку, в которую каждый шагает, в которую дублируются экземпляры bean-компонентов из-за сканирования компонентов, у вас, вероятно, есть сканирование компонентов как в корневой конфигурации, так и в конфигурации сервлета, которые делают то же самое.   -  person M. Deinum    schedule 30.05.2014
comment
Я добавил конфигурацию для spring и web.xml. И нет, сэр, задолго до того, как я начал использовать OpenSessionInView, я мог сохраняться в БД и также извлекать контент (а затем я столкнулся с LazyInitializationException, из-за которого я добавил фильтр). И нет, я проверил свои журналы, нет повторяющихся экземпляров bean-компонентов.   -  person Vishwas Shashidhar    schedule 30.05.2014


Ответы (3)


<context:component-scan base-package="com.adwitiya.o2plus" />

В этом <context:component-scan ... /> находится конфигурация, загруженная DispatcherServlet.

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

<tx:annotation-driven /> находится в конфигурации, загруженной ContextLoaderLIstener.

Bean(Factory)PostProcessor работает только с bean-компонентами в том же ApplicationContext, в который он загружен. Он ничего не делает для bean-компонентов в родительском или дочернем контексте. <tx:annotation-driven /> регистрирует перехватчик (или аспект), который обрабатывается InfrastructureAdvisorAutoProxyCreator, который является BeanPostProcessor.

Решение

Либо переместите свой <tx:annotation-driven /> в свою конфигурацию DispatcherServlet, либо измените сканирование компонентов. ContextLoaderListener должен сканировать все, кроме @Controller аннотированных компонентов, тогда как DispatcherServlet должен сканировать только @Controller аннотированных компонентов.

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>
person M. Deinum    schedule 02.06.2014
comment
Спасибо. Я попробую это и обновлю эту тему. Однако я не понимаю, как операции добавления и удаления работают без необходимости сбрасывать данные вручную. - person Vishwas Shashidhar; 02.06.2014
comment
вставка выполняется немедленно, в отличие от обновлений, из-за того, что требуется идентификатор. - person M. Deinum; 02.06.2014
comment
Что ж, к сожалению, я попытался с вышеуказанной конфигурацией, но безуспешно.. Отредактировал вопрос с последней конфигурацией... - person Vishwas Shashidhar; 04.06.2014
comment
Прочтите еще раз, ваша конфигурация все еще ошибочна, вы пропустили use-default-filters="false" для сканирования компонентов в конфигурации сервлета. В настоящее время у вас все еще есть несколько экземпляров компонента. - person M. Deinum; 04.06.2014
comment
Отлично, наконец... думаю, мне нужно тщательно изучить документацию Spring MVC... большое спасибо :) - person Vishwas Shashidhar; 04.06.2014
comment
Это решение просто спасло мне жизнь! Потратил целых 2 дня, пытаясь понять, почему некоторые объекты не сохранялись после того, как я изменил структуру пакета. Я соответственно обновил сканирование фабрик компонентов и сеансов, но некоторые сущности сохранялись, а некоторые нет. Это непреднамеренно добавило классы, не являющиеся контроллерами, в сервлет диспетчера и включило только фиксированные вещи контроллеров! - person AForsberg; 01.08.2018

Ну, видимо, до сих пор единственный способ, которым я понял, это решить, используя приведенный ниже код:

sessionFactory.getCurrentSession().flush();

Посмотрим, как еще это можно решить!

person Vishwas Shashidhar    schedule 29.05.2014

убедитесь, что у вас есть:

@EnableTransactionManagement

и менеджер транзакций Hibernate знает о вашей фабрике сеансов:

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

or

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

если вы используете конфигурации xml.

Пока вы используете перехват транзакций на основе прокси-сервера Spring, легко отладить уровень службы и проверить текущую трассировку стека на наличие TransactionInterceptor между вызовом контроллера и фактическим методом службы.

Если у вас есть TransactionInterceptor, вы должны использовать транзакции, а транзакция Hibernate вызывается перехватчиком.

И вы вызываете Session.update для новых объектов. Слияние предпочтительнее, так как оно работает как с временными, отсоединенными, так и с уже присоединенными экземплярами.

Итак, вместо:

sessionFactory.getCurrentSession().update(branch); 

Вы должны иметь:

sessionFactory.getCurrentSession().merge(branch); 
person Vlad Mihalcea    schedule 30.05.2014
comment
Я уже настроил менеджер транзакций в своем spring xml. Странно то, что для нового объекта (т.е. сохранения) он работает без нареканий. Только для обновления мне приходится сбрасывать данные вручную.. - person Vishwas Shashidhar; 30.05.2014
comment
замените session.update на session.merge, и он будет работать каждый раз. - person Vlad Mihalcea; 30.05.2014
comment
Это тоже пробовал :) Я пробовал слияние, сохранение или обновление и обновление --> похоже, не работает ни с одним из них, если только я не сброшу его вручную... - person Vishwas Shashidhar; 30.05.2014
comment
Проверьте, есть ли в отладке TransactionInterceptor. Если их нет, у вас не будет никаких транзакций. - person Vlad Mihalcea; 30.05.2014
comment
Некоторое время пытался выяснить проблему, но все еще застрял... и да, в журналах есть TransactionInterceptor... Строка говорит, что создается перехватчик транзакций (одноэлементный): 23:06:17,162 DEBUG DefaultListableBeanFactory:215 — Создание общего экземпляра одноэлементного компонента «org.springframework.transaction.interceptor.TransactionInterceptor#0» - person Vishwas Shashidhar; 04.06.2014