1

I'm doing a crud operation project with Spring and Hibernate. Here's the model class.

Employee.java

@Entity
@Table(name="employee")
public class Employee {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private int id;
   private String name;
   private String password;
   private String gender;
   private String city;

   //getters and setters }

Here's the EmployeeDaoImpl.java:

import com.anand.model.Employee;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
//implementation of Dao Layer
public class EmployeeDaoImpl implements EmployeeDao {
   private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeDaoImpl.class);

   private SessionFactory sessionFactory;

   public SessionFactory getSessionFactory() {
      return sessionFactory;
   }

   public void setSessionFactory(SessionFactory sessionFactory) {
      this.sessionFactory = sessionFactory;
   }

   public Session getSession() {
      return getSessionFactory().openSession();
   }

   @Override
   public void addEmployee(Employee employee) {
      //Find out why the save is working but not persist
      getSession().save(employee);
      LOGGER.info("Employee " + employee + " added.");
   }

   @Override
   public void updateEmployee(Employee employee) {
      getSession().update(employee);
      LOGGER.info("Employee " + employee + " updated.");
   }

   @Override
   public Employee getEmployeeById(int id) {
      Employee employee = (Employee) getSession().load(Employee.class, id);
      LOGGER.info("Got Employee " + employee);
      return employee;
   }

   @SuppressWarnings({"unchecked", "JpaQlInspection"})
   @Override
   public List<Employee> listEmployees() {
      List<Employee> employeeList = getSession().createQuery("from Employee").list();
      for (Employee employee : employeeList) {
         LOGGER.info("Employee list " + employee);
      }
      return employeeList;
   }

   @Override
   public void deleteEmployeeById(int id) {
      Employee employee = (Employee) getSession().load(Employee.class, id);
      if (employee != null) {
         getSession().delete(employee);
      }
      LOGGER.info("deleted " + employee);
   }
}

Here's the EmployeeServiceImpl.java:

package com.anand.service;

import java.util.List;

import com.anand.dao.EmployeeDao;
import com.anand.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

public class EmployeeServiceImpl implements EmployeeService {
   @Autowired
   private EmployeeDao employeeDao;

   public EmployeeDao getEmployeeDao() {
      return employeeDao;
   }

   public void setEmployeeDao(EmployeeDao employeeDao) {
      this.employeeDao = employeeDao;
   }

   @Override
   @Transactional
   public void addEmployee(Employee employee) {
      getEmployeeDao().addEmployee(employee);
   }

   @Override
   @Transactional
   public void updateEmployee(Employee employee) {
      getEmployeeDao().updateEmployee(employee);
   }

   @Override
   @Transactional
   public Employee getEmployeeById(int id) {
      return getEmployeeDao().getEmployeeById(id);
   }

   @Override
   @Transactional
   public List<Employee> listEmployees() {
      return getEmployeeDao().listEmployees();
   }

   @Override
   @Transactional
   public void deleteEmployeeById(int id) {
      getEmployeeDao().deleteEmployeeById(id);
   }
}

Here's the controller class method in which employeeService is injected via dependency injection.

@RequestMapping(value = {"/","/home"})
   public ModelAndView homePage() {
      return new ModelAndView("home");
   }

@RequestMapping(value = "/register", method = RequestMethod.POST)
   public String registerEmployeePost(@ModelAttribute("employee") Employee employee, ModelMap modelMap) {
      System.out.println(employee);
      if (employee.getId() == 0) {
         System.out.println("adding employee");
         getEmployeeService().addEmployee(employee);
      } else {
         System.out.println("updating employee");
         getEmployeeService().updateEmployee(employee);
      }
      return "redirect:/register";
   }
   @RequestMapping(value = "/register", method = RequestMethod.GET)
   public String registerEmployeeGet(@ModelAttribute("employee") Employee employee, ModelMap modelMap) {
      modelMap.addAttribute("employeesList", getEmployeeService().listEmployees());
      return "result";
   }

   @RequestMapping(value = "/edit/{id}")
   public String editEmployee(@PathVariable("id") int id, Model model) {
      Employee employee = getEmployeeService().getEmployeeById(id);
      if (employee != null) {
         model.addAttribute("employee", employee);
      } else {
         System.out.println("####employee by this id not found####");
      }
      return "home";
   }

And following is the JSP file home:

<c:url var="addAction" value="/register"/>
<form:form action="${addAction}" commandName="employee" method="post"  cssClass="form-horizontal">
        <label for="id" class="col-sm-2 control-label">ID: </label>
            <form:input path="id" readonly="true" cssClass="form-control" disabled="true"/>
        <label for="name" class="col-sm-2 control-label">Name: </label>
            <form:input path="name" cssClass="form-control"/>
        <label for="password" class="col-sm-2 control-label">Password: </label>
            <form:input path="password" cssClass="form-control"/>
        <label for="gender" class="col-sm-2 control-label">Gender: </label>
            <form:input path="gender" cssClass="form-control"/>
        <label for="city" class="col-sm-2 control-label">City: </label>
            <form:input path="city" cssClass="form-control"/>
            <input type="submit" value="Submit" class="btn btn-lg btn-primary"/>
</form:form>

Finally, the spring-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <mvc:annotation-driven/>
    <context:annotation-config/>

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

    <bean class="org.apache.commons.dbcp.BasicDataSource" id="dataSource" destroy-method="close">
        <property name="driverClassName" value="org.postgresql.Driver"/>
        <property name="url" value="jdbc:postgresql://localhost:5432/test"/>
        <property name="username" value="postgres"/>
        <property name="password" value="abc@1234"/>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="annotatedClasses">
            <list>
                <value>com.anand.model.Employee</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
            </props>
        </property>
    </bean>

    <bean class="com.anand.dao.EmployeeDaoImpl" id="employeeDao">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <bean class="com.anand.service.EmployeeServiceImpl" id="employeeService">
        <property name="employeeDao" ref="employeeDao"/>
    </bean>

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

    <!--For @Transactional-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
</beans>

I have two problems, first, when I'm using the persist() instead of save(), the object doesn't get saved. Second, when I'm trying to update the model by hitting the /edit/{id} url and passing it's id(by which the controller fetch the employeeById), the form gets populated by right entries and after submitting, a new entry is added to list of employees with new ID. I've looked at the logs and found that the id is always coming in /register as 0. I can't figure out why! Please assist.

Edit: Now, the id is coming correctly but the object is still not updated even after I get the successful LOGGER message.

Anil Nivargi
  • 1,473
  • 3
  • 27
  • 34
आनंद
  • 2,472
  • 4
  • 21
  • 25
  • 1
    why do you want to use persist instead of save? for more info on difference between those refer https://stackoverflow.com/questions/5862680/whats-the-advantage-of-persist-vs-save-in-hibernate . For the second part where is the POST method in the controller which would update the entry? – Md Faisal Jun 09 '17 at 09:17
  • Please see the `registerEmployeePost` method. – आनंद Jun 09 '17 at 09:25
  • in that case you must have the id field in jsp side (make it hidden if you like) , at this point there is no id coming from the jsp to the controller (and default is 0) hence new registration.. – Md Faisal Jun 09 '17 at 09:29
  • @MdFaisal But I've already added the id field on jsp side. Could you please take a look at jsp file? – आनंद Jun 09 '17 at 09:32
  • yeah I have seen that.. then it must the be what @jcgarcia have answered. what you can do is have a hidden is in addition to what you already have in jsp. – Md Faisal Jun 09 '17 at 09:35

2 Answers2

3

You're setting your id input as disabled="true", so as you could see here, if the input element is disabled it cannot receive user input and its value will be not submitted with the form.

I recommend you to try to include a hidden css class instead of disable property. That should solve your problem, because the id will be sended to the server side and it will update the element instead of create a new one.

Hope it helps,

jcgarcia
  • 3,762
  • 4
  • 18
  • 32
  • I'm using it becuase I'd like to disable the user from entering another id. An object can't have different id if it needs to be updated, right? Also, I've removed the same and still it's not updating anything. – आनंद Jun 09 '17 at 09:28
  • 1
    Of course. I know that an user must not modify the identifier during an update. However, there're more efficient/elegant ways to ensure that the id will not be modified instead of restrict only the client side (think about an advanced user that edit your HTML from firebug or chrome-dev-tool)... Anyway, if you want to validate it only in the html side, you should check the parameters that you're sending in the PUT using firebug or chrome-dev-tools, etc.. are you sending the identifier? – jcgarcia Jun 09 '17 at 09:34
  • I understand, I'm using the ` – आनंद Jun 09 '17 at 09:38
  • 1
    At first, I've seen in your code that you are using a POST method to make the update. You must change this and use a PUT operation. Also, I've seen that you are delegating in the service layer to update the item. Could you provide more code info about the service implementation and repository (if exists)? – jcgarcia Jun 09 '17 at 09:42
  • 1
    @AnandTyagi and you are sure that `updateEmployee() ` is doing the right thing? – Md Faisal Jun 09 '17 at 09:43
  • do you get the log with `employee updated` ? (and is the logger running properly?) – Md Faisal Jun 09 '17 at 09:57
  • @MdFaisal Yes, I'm getting the logger message of success which was executed after update method. That's what puzzling me. – आनंद Jun 09 '17 at 10:27
  • Ok. After seen your code, I think that I know the problem. The received `Employee` in the `@ModelAttribute` is detached from the database because it's an instance that you create in the form. It has not been obtained from the DB. To make the update, use the received employee id and execute a `findOne` method to obtain the existing employee from the database. After obtain it, set the received properties from the form to the obtained element and send that obtained entity to the DAO to be updated. With that, it should persist. – jcgarcia Jun 09 '17 at 10:36
  • @jcgarcia I think `update()` also attaches the objects and updates the record.. @AnandTyagi did this solved the problem? – Md Faisal Jun 09 '17 at 11:02
  • @jcgarcia Unfortunately, this results in the `org.hibernate.HibernateException: illegally attempted to associate a proxy with two open Sessions` Exception. – आनंद Jun 09 '17 at 16:32
  • @MdFaisal No, unfortunately not. – आनंद Jun 09 '17 at 16:34
  • @jcgarcia I'm getting the object by it's ID with `Employee emp =getEmployeeService().getEmployeeById(employee.getId());`, where `employee` is the object that we got from `@ModelAttribute`. Then I'm setting all the props of `emp` by getters from `employee` and passing the `emp` as `getEmployeeService().updateEmployee(emp)`. – आनंद Jun 09 '17 at 16:39
  • Did you try to use an Spring Data JPA Repository instead of manage the hibernate session manually? Maybe this helps you a lot and simplify your code... http://projects.spring.io/spring-data-jpa/ – jcgarcia Jun 09 '17 at 17:04
  • 1
    @jcgarcia I found the bug! I was calling `sessionFactory().openSession()` which I never closed. Instead, I should've called the `sessionFactory().getCurrentSession()`. Thanks for helping me find the bug, and I'd certainly look at the spring-data-jpa. – आनंद Jun 09 '17 at 17:46
1

As it turned out, I was calling sessionFactory().openSession() which I never closed. Instead, I should've called the sessionFactory().getCurrentSession() to fetch the current session.

Please take a look here as well. Difference between openSession() and getCurrentSession()

आनंद
  • 2,472
  • 4
  • 21
  • 25