Safari137
Safari137

Reputation: 360

Hibernate adding OneToMany Entity twice when updating

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

Upvotes: 1

Views: 1134

Answers (1)

Chris Thompson
Chris Thompson

Reputation: 35598

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.

Upvotes: 3

Related Questions