Hi All,
i'm new to Spring but I like it very much. I started to use the DAO Support with Hibernate a while back and now converting from WebWork to Spring MVC since the Spring MVC offers a bit more flexibility - especially the FormWizard stuff. Now a very strange problems occurs while using OpenSessionInView Filter.
If an error occurs in my form (which uses a FormCommand and a nestest domain object), the changes are commited to the database when errors occur (which leads to re-display the form with error messages)!!! But an Hibernate update query is fired and it updates the database with the (in this case) empty form field value.
But in the error/redisplay case i do not call a save or update method of my service object (or underlying dao).
If i deactivate OpenSessionInView filter the error case behaviour is fine, but if i call the save-method of my DAO i get LazyInitException (no open session). Which is a very strange error, i can't find the cause. When i'm calling the save method of the dao the session is still open.
I'd prefer not having an open session in my view, but i don't know hot to do it properly. Init all those relations by manually calling getters seems to be kludgy to me.
Anyway, I can't get it work properly without OpenSessInViewFilter. I guess it would work if i would use the FormCommand object to update the domain object. but that's double work.
It would be very cool if you guys could give me a hint what's going wrong (why are updates to the database made when dao.update(obj) is NOT called. The DAOs/Service beans worked perfectly with WebWork btw!
Is there a "better" documentaion of the MVC implementation? I found it very hard to learn by the examples. I could use WebWork efficiently after 1 day - and after 3 days still trying to find out how the Spring MVC works.
Here's the code of the FormController:
Code:public class UserFormController extends SimpleFormController { private static final Log log = LogFactory.getLog(UserFormController.class); private UserService userService = null; protected Object formBackingObject(HttpServletRequest request) throws ServletException { log.error("formBackingObject called"); User user = userService.getUser(RequestUtils.getRequiredStringParameter(request, "username")); return new UserForm(user); } public ModelAndView onSubmit(Object command) throws ServletException { log.error("onSubmit called"); User user = ((UserForm) command).getUser(); userService.saveUser(user); return new ModelAndView(new RedirectView("/admin/user/view"), "username", user.getHandle()); } public void setUserService(UserService userService) { log.error("setUserService called called"); this.userService = userService; } }
FormBackingObject:
UserFormValidator:Code:public class UserForm { private User user; private String repeatedPassword; private boolean newUser; public UserForm(User user) { this.user = user; this.newUser = false; } public UserForm() { this.user = new User(); this.newUser = true; } public User getUser() { return this.user; } public boolean isNewUser() { return newUser; } public void setRepeatedPassword(String repeatedPassword) { this.repeatedPassword = repeatedPassword; } public String getRepeatedPassword() { return repeatedPassword; } }
UserServiceImpl:Code:public class UserFormValidator implements Validator { public boolean supports(Class clazz) { return UserForm.class.isAssignableFrom(clazz); } public void validate(Object obj, Errors errors) { ValidationUtils.rejectIfEmpty(errors, "user.fullname", "FIRST_NAME_REQUIRED", "First name is required."); ValidationUtils.rejectIfEmpty(errors, "user.email", "FIRST_NAME_REQUIRED", "Email is required."); } }
UserDaoImpl:Code:public class UserServiceImpl implements UserService { private UserDao userDao; private RoleDao roleDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void setRoleDao(RoleDao roleDao) { this.roleDao = roleDao; } // TODO: throw exception public boolean authenticate(String username, String password) { User user = getUser(username); if (user == null || !user.getPassword().equals(password)) return false; return true; } public User getUser(Long id) { return userDao.loadUserById(id); } public List getUsers() { return userDao.loadAllUsers(); } public User getUser(String username) { return userDao.loadUserByName(username); } public void saveUser(User user) { userDao.saveOrUpdateUser(user); } public void deactivateUser(User user) { //TODO: implement deactivate } public void reactivateUser(User user) { //TODO: implement reactivate } public List getRoles() { return roleDao.loadAllRoles(); } public Role getRole(String name) { return roleDao.loadRoleByName(name); } public Role getRole(Long id) { return roleDao.loadRoleById(id); } public List getUnassignedRoles(User user) { return userDao.loadUnassignedRolesForUser(user); } }
Formbean defininition:Code:public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public List loadUnassignedRolesForUser(User user) throws DataAccessException { List uaRoles = new ArrayList(); List roles = getHibernateTemplate().loadAll(Role.class); for (Iterator i = roles.iterator(); i.hasNext();) { Role current = (Role) i.next(); // TODO: using contains could be dangerous, try to use named query // but don't know how to obtain the hibernate dialect!? if (!user.getRoles().contains(current)) { uaRoles.add(current); } } return uaRoles; } public User loadUserById(Long id) throws DataAccessException { return (User) getHibernateTemplate().load(User.class, id); } public User loadUserByName(String username) throws DataAccessException { return (User) getHibernateTemplate().find("select u from User u where u.handle = ?", username).get(0); } public List loadAllUsers() throws DataAccessException { return (List) getHibernateTemplate().loadAll(User.class); } public void saveOrUpdateUser(User user) throws DataAccessException { getHibernateTemplate().saveOrUpdate(user); } }
Dao/Service Definitions:Code:<bean id="userValidator" class="com.thyrell.pm.user.web.UserFormValidator"/> <bean id="editUserForm" class="com.thyrell.pm.user.web.UserFormController"> <property name="userService"><ref bean="userService"/></property> <property name="validator"><ref local="userValidator"/></property> <property name="commandClass"><value>com.thyrell.pm.user.web.UserForm</value></property> <property name="commandName"><value>userForm</value></property> <property name="formView"><value>EditUserForm</value></property> <property name="successView"><value>EditUserFormRedirect</value></property> </bean>
Ah, yeah, the OpenSessionInViewFilter overridden (caus of the auto-flush thing):Code:<bean id="pmHibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor"> <property name="sessionFactory"><ref local="sessionFactory"/></property> </bean> <bean id="pmTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> <!-- <bean id="pmTransactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">--> <!-- <property name="sessionFactory"><ref local="sessionFactory"/></property>--> <!-- </bean>--> <bean id="pmTransactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager"><ref bean="pmTransactionManager" /></property> <property name="transactionAttributeSource"> <value> com.thyrell.pm.user.UserServiceImpl.*=PROPAGATION_REQUIRED,-DataAccessException com.thyrell.pm.user.UserServiceImpl.get*=PROPAGATION_REQUIRED,readOnly </value> </property> </bean> <bean id="userDaoTarget" class="com.thyrell.pm.user.dao.UserDaoImpl"> <property name="sessionFactory"><ref local="sessionFactory" /></property> </bean> <bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.thyrell.pm.user.dao.UserDao</value></property> <property name="interceptorNames"> <list> <value>pmHibernateInterceptor</value> <value>userDaoTarget</value> </list> </property> </bean> <bean id="roleDaoTarget" class="com.thyrell.pm.user.dao.RoleDaoImpl"> <property name="sessionFactory"><ref local="sessionFactory" /></property> </bean> <bean id="roleDao" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.thyrell.pm.user.dao.RoleDao</value></property> <property name="interceptorNames"> <list> <value>pmHibernateInterceptor</value> <value>roleDaoTarget</value> </list> </property> </bean> <bean id="userServiceTarget" class="com.thyrell.pm.user.UserServiceImpl"> <property name="userDao"><ref bean="userDao" /></property> <property name="roleDao"><ref bean="roleDao" /></property> </bean> <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.thyrell.pm.user.UserService</value></property> <property name="interceptorNames"> <value>pmTransactionInterceptor,userServiceTarget</value> </property> </bean>
Code:public class FlushingSpringSessionInViewFilter extends OpenSessionInViewFilter { public FlushingSpringSessionInViewFilter() { } protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); session.setFlushMode(FlushMode.AUTO); return session; } protected void closeSession(Session session, SessionFactory sessionFactory) throws CleanupFailureDataAccessException { if (session != null && session.isOpen() && session.isConnected()) { try { session.flush(); } catch (HibernateException e) { throw new CleanupFailureDataAccessException("Failed to flush session before close: " + e.getMessage(), e); } } super.closeSession(session, sessionFactory); } }
Hope the long code listings help you nailing down the problem.
Thanks a lot,
Andi


Reply With Quote

)