0

I'm having a problems updating an entity. Here are my annotated models: (note: there are many more fields that I think are irrelevant to the problem)

Employee

@Entity
public class Employee {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private int id;

   @OneToMany(cascade = CascadeType.ALL, mappedBy = "employee", fetch = FetchType.EAGER)
   private List<Paycheck> paychecks;

   // Note: this method does not ever seem to be called
   @Override
   public boolean equals(Object o) {
       System.out.printf("\n\n\nEquals requested!\n\n\n");

       if (o == null || !(o instanceof Employee)) {
           System.out.printf("\n\n\nNot equal! 1\n\n\n");
           return false;
       }

       Employee other = (Employee) o;

       if (id == other.getId()) {
          System.out.printf("\n\n\nEqual! id = id\n\n\n");
          return true;
       }

       // equivalence by id
       return id == other.getId();
   }

   @Override
   public int hashCode() {
       final int prime = 31;
       int result = 1;
       result = prime * result + (id ^ (id >>> 32));
       return result;
   }
}

Paycheck

@Entity
public class Paycheck {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private int id;


   @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
   private Employee employee;
}

My DAO update method :

@Override
public void update(T item) {
    Session session = sessionFactory.getCurrentSession();

    session.beginTransaction();
    session.saveOrUpdate(item);
    session.getTransaction().commit();
}

And the service method :

 public List<Paycheck> executePayroll(List<Employee> employees) {
    List<Paycheck> paychecks = new ArrayList<>();

    for(Employee employee : employees) {
        Paycheck paycheck = engine.processPay(employee, employee.getCurrentHours());
        paycheck.setEmployeeId(employee.getId());
        paycheck.setEmployee(employee);
        paycheck.setDate(today);
        paychecks.add(paycheck);
        employee.setCurrentHours(0);

        employee.getPaychecks().add(paycheck);

        employeeRepository.update(employee);
    }

    return paychecks;
}

The bahavior I'm getting:

When there are 0 paychecks, and a paycheck is added, the employee is not duplicated. I get the following logged:

Hibernate: call next value for hibernate_sequence

Hibernate: insert into Paycheck (date, employee_id, employeeId, employerFederalUnemploymentTax, employerMedicareTax, employerSocialSecurityTax, employerStateUnemploymentTax, federalWithholdingTax, grossAmount, medicareWithholdingTax, netAmount, socialSecurityWithholdingTax, stateWithholdingTax, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

Hibernate: update Employee set address=?, city=?, currentHours=?, dateOfBirth=?, email=?, federalExemptions=?, firstName=?, isMarried=?, lastName=?, payRate=?, phoneNumber=?, socialSecurityNumber=?, state=?, stateExemptions=?, zipcode=? where id=?

However, when I add a second paycheck to an employee, the employee entity is duplicated. I end up with two employees in the database with all the same properties, including 'id'. Also, both employees have the same two paychecks attached to them. The following is logged after the methods are run:

Hibernate: call next value for hibernate_sequence

Hibernate: insert into Paycheck (date, employee_id, employeeId, employerFederalUnemploymentTax, employerMedicareTax, employerSocialSecurityTax, employerStateUnemploymentTax, federalWithholdingTax, grossAmount, medicareWithholdingTax, netAmount, socialSecurityWithholdingTax, stateWithholdingTax, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

Hibernate: update Employee set address=?, city=?, currentHours=?, dateOfBirth=?, email=?, federalExemptions=?, firstName=?, isMarried=?, lastName=?, payRate=?, phoneNumber=?, socialSecurityNumber=?, state=?, stateExemptions=?, zipcode=? where id=?

Hibernate: update Paycheck set date=?, employee_id=?, employeeId=?, employerFederalUnemploymentTax=?, employerMedicareTax=?, employerSocialSecurityTax=?, employerStateUnemploymentTax=?, federalWithholdingTax=?, grossAmount=?, medicareWithholdingTax=?, netAmount=?, socialSecurityWithholdingTax=?, stateWithholdingTax=? where id=?
Safari137
  • 351
  • 3
  • 10
  • That's because hibernate keeps the other entity in it's memory and when you call `update`, hibernate will flush all entities in memory causing it to update the other one too. – Robin Jonsson Apr 10 '16 at 16:40

1 Answers1

2

This is a symptom of the N+1 problem. I've worked around this issue by using the @Fetch(FetchMode.SUBSELECT) annotation on my List entities. Alternatively you can use a Set instead, although that has other side-effects.

Chris Thompson
  • 35,167
  • 12
  • 80
  • 109