1

It's been about 5 hours trying to update an object through hibernate in my application using Spring MVC 3 and OpenSessionInViewFilter without any luck.

I've possibly gone through all the threads available in StackOverflow and other forums!

Hibernate doesn't throw any error, and says the object was updated but it doesn't reflect in my DB.

Any help would be massively appreciated.

So, here's my JSON request to update:

{
    "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"
}

Controller:

@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 class:

@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 method:

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;
    }

**Update 2: As suggested by Mr.Deinum, I've moved transaction manager config to o2-data.xml and now scanning only the controllers in the dispatcher while scanning other components in o2-data.xml

Data Configuration

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

Dispatcher Configuration:

   <?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>
Vishwas Shashidhar
  • 807
  • 2
  • 9
  • 23
  • Please add some configuration as I guess your setup is flawed. My guess is that you don't even have transactions and hence the need for a flush. I would expect that you will get an exception on `sessionFactory.getCurrentSession()` when you remove the OSIVF an exception telling you that no transaction is in progress. You have probably fallen into the trap everyone steps into which is duplicate bean instances due to component-scanning, you probably have a compoent-scan in both the root and servlet configuration both doing the same. – M. Deinum May 30 '14 at 06:37
  • I've added the configuration for spring and web.xml. And no sir, well before I started using OpenSessionInView, I was able to persist to the DB and retrieve content as well (and then I encountered LazyInitializationException due to which I added the filter). And, no, I've checked my logs, there are no duplicate bean instances. – Vishwas Shashidhar May 30 '14 at 07:46

3 Answers3

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

This <context:component-scan ... /> is located the configuration that is loaded by the DispatcherServlet.

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

The <tx:annotation-driven /> is located in the configuration that is loaded by the ContextLoaderLIstener.

A Bean(Factory)PostProcessor only operates on beans in the same ApplicationContext that it is loaded in. It doesn't do anything for beans in parent or child contexts. The <tx:annotation-driven /> registers an interceptor (or aspect) which is processed by an InfrastructureAdvisorAutoProxyCreator which is a BeanPostProcessor.

Solution

Either move your <tx:annotation-driven /> to your configuration of the DispatcherServlet or modify your component scan. The ContextLoaderListener should scan for anything but @Controller annotated beans, whereas the DispatcherServlet should scan only for @Controller annotated beans.

ContextLoaderListener configuration.

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

DispatcherServlet configuration

<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>
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • Thank you. I'll try this and update this thread. However, I fail to understand how the add and delete operations work without me having to flush the data manually. – Vishwas Shashidhar Jun 02 '14 at 12:15
  • insert is immediate as opposed to updates, due to the fact that an id is needed. – M. Deinum Jun 02 '14 at 12:17
  • Well, I did try with the above configuration without any luck unfortunately..Edited the question with the latest configuration... – Vishwas Shashidhar Jun 04 '14 at 18:16
  • 1
    Read again, your configuration is still flawed you missed the `use-default-filters="false"` for the component-scan in the servlet configuration. Currently you are still having multiple bean instances. – M. Deinum Jun 04 '14 at 18:27
  • Awesome, finally...guess I need to work through the documentation of Spring MVC thoroughly...thanks a lot :) – Vishwas Shashidhar Jun 04 '14 at 18:34
  • This solution just saved my life! Spent 2 whole days trying to figure out why certain entities weren't saving after I changed the package structure. I had updated the component and session factory scans accordingly, but some entities would save and some wouldn't. This inadvertently added non-controller classes to the dispatcher servlet and only including controllers fixed things! – AForsberg Aug 01 '18 at 20:06
1

Well apparently, so far, the only way I've figured out to work this out is by using the below code:

sessionFactory.getCurrentSession().flush();

Let's see how else this can be solved!

Vishwas Shashidhar
  • 807
  • 2
  • 9
  • 23
0

make sure you have:

@EnableTransactionManagement

and the Hibernate transaction manager aware of your session factory:

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

or

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

if you use xml configurations.

As long as you use the fault Spring proxy-based Transaction intercepting, it's easy to debug the service layer and check the current stack trace for the TransactionInterceptor between the Controller call and the actual service method.

If you have the TransactionInterceptor you should use transactions and the Hibernate transaction is called by the interceptor.

And you are calling Session.update for new objects. Merge is preffered since it works with both transient, detached and already attached instances.

So instead of:

sessionFactory.getCurrentSession().update(branch); 

you should have:

sessionFactory.getCurrentSession().merge(branch); 
erhun
  • 3,549
  • 2
  • 35
  • 44
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • I've already configured a transaction manager in my spring xml. The weird thing is that for a new object (i.e. saving), it is working flawlessly. Only for an update, I'm having to flush the data manually.. – Vishwas Shashidhar May 30 '14 at 05:26
  • replace session.update with session.merge and it will work every time. – Vlad Mihalcea May 30 '14 at 05:28
  • Tried that too :) I've tried merge, saveOrUpdate and update --> doesn't seem to work with any of these unless I flush it manually... – Vishwas Shashidhar May 30 '14 at 05:29
  • Check if there is a TransactionInterceptor in debug. If there isn't any you won't have any transaction. – Vlad Mihalcea May 30 '14 at 05:32
  • Been a while trying to figure out problem, still stuck though...and yes, there is a "TransactionInterceptor" in the logs...A line says transaction interceptor (a singleton) is being created: 23:06:17,162 DEBUG DefaultListableBeanFactory:215 - Creating shared instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0' – Vishwas Shashidhar Jun 04 '14 at 18:23