问题
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>
回答1:
<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>
回答2:
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!
回答3:
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);
来源:https://stackoverflow.com/questions/23938580/hibernate-update-problems-opensessioninviewfilter