I have two cases where entities created in a constructor of another entity are persisted in the next transaction, even after the entity is replaced upon loading from the database. For example:
@Entity
public class Person {
@Id @GeneratedValue(strategy= GenerationType.IDENTITY)
private long id;
@OneToOne(orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private ContactInfo contactInfo = new ContactInfo();
private String firstName;
...
}
In some conditions, when I load a Person object (entityManager.find), then set its firstName and commit the transaction, the firstName is changed, but an extra ContactInfo record is unexpectedly created. I expect the ContactInfo instance that was created in the constructor to be discarded when the Person entity is loaded with a ContactInfo record from the database. The database ends up with unreferenced records. This only happens for me when using bytecode enhancement. I have two test cases that reproduce this behavior. One involves a bi-directional OneToOne relationship. Here is the repo: git@github.com:robscala/extra_inserts_OneToOne_bi_test.git
@Entity public class Person {
@Id @GeneratedValue(strategy= GenerationType.IDENTITY)
private long id;
private String firstName;
@OneToOne(orphanRemoval=true, cascade={CascadeType.PERSIST, CascadeType.REMOVE}, fetch=FetchType.LAZY)
private LoginAccount loginAccount = new LoginAccount();
}
@Entity public class LoginAccount {
@Id @GeneratedValue(strategy= GenerationType.IDENTITY)
private long id;
private String name;
@OneToOne(orphanRemoval=true, cascade= CascadeType.ALL, fetch= FetchType.LAZY)
private AccountPreferences accountPreferences = new AccountPreferences();
@OneToOne(mappedBy = "loginAccount")
private Person owner;
}
@Entity public class AccountPreferences {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private boolean open = false;
}
In this case, after loading a Person and performing a transaction, an extra record in the AccountPreferences table is created. The other case involves a polymorphic class, Company. Here is the repo: git@github.com:robscala/extra_inserts_polymorphic_test.git
@Entity
public class Person {
@Id @GeneratedValue(strategy= GenerationType.IDENTITY)
private long id;
private String firstName;
@OneToOne(orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private ContactInfo contactInfo = new ContactInfo();
@ManyToOne(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
private Company company;
}
@Entity
@Inheritance(strategy= InheritanceType.JOINED)
@DiscriminatorColumn(name="CompanyType", discriminatorType= DiscriminatorType.INTEGER)
public abstract class Company {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
protected long id;
@OneToOne(orphanRemoval=true, cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
private ContactInfo contactInfo = new ContactInfo();
}
@Entity
@DiscriminatorValue("9")
public class CustomerCompany extends Company {
private String zipCode;
@ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE})
private CustomerCompany parent;
}
In this case, after loading a Person and performing a transaction, an extra record in the ContactInfo table is created. |