My database has two entities; Company and Person. A Company can have many People, but a Person must have only one Company. The table structure looks as follows.
COMPANY ---------- owner PK comp_id PK c_name
PERSON ---------------- owner PK, FK1 personid PK comp_id FK1 p_fname p_sname
It has occurred to me that I could remove PERSON.OWNER and derive it through the foreign key; however, I can't make this change without affecting legacy code.
I have modeled these as JPA-annotated classes;
@Entity @Table(name = "PERSON") @IdClass(PersonPK.class) public class Person implements Serializable { @Id private String owner; @Id private String personid; @ManyToOne @JoinColumns( {@JoinColumn(name = "owner", referencedColumnName = "OWNER", insertable = false, updatable = false), @JoinColumn(name = "comp_id", referencedColumnName = "COMP_ID", insertable = true, updatable = true)}) private Company company; private String p_fname; private String p_sname; ...and standard getters/setters... }
@Entity @Table(name = "COMPANY") @IdClass(CompanyPK.class) public class Company implements Serializable { @Id private String owner; @Id private String comp_id; private String c_name; @OneToMany(mappedBy = "company", cascade=CascadeType.ALL) private List people; ...and standard getters/setters... }
My PersonPK and CompanyPK classes are nothing special, they just serve as a struct holding owner and the ID field, and override hashCode and equals(o).
So far so good. I come across a problem, however, when trying to deal with associations. It seems if I have an existing Company, and create a Person, and associate to the Person to the Company and persist the company, the association is not saved when the Person is inserted. For example, my main code looks like this:
EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); CompanyPK companyPK = new CompanyPK(); companyPK.owner="USA"; companyPK.comp_id="1102F3"; Company company = em.find(Company.class, companyPK); Person person = new Person(); person.setOwner("USA"); person.setPersonid("5116628123"); //some number that doesn't exist yet person.setP_fname("Hannah"); person.setP_sname("Montana"); person.setCompany(company); em.persist(person);
This completes without error; however in the database I find that the Person record was inserted with a null in the COMP_ID field. With EclipseLink debug logging set to FINE, the SQL query is shown as:
INSERT INTO PERSON (PERSONID,OWNER,P_SNAME,P_FNAME) VALUES (?,?,?,?) bind => [5116628123,USA,Montana,Hannah,]
I would have expected this to be saved, and the query to be equivalent to
INSERT INTO PERSON (PERSONID,OWNER,COMP_ID,P_SNAME,P_FNAME) VALUES (?,?,?,?,?) bind => [5116628123,USA,1102F3,Montana,Hannah,]
What gives? Is it incorrect to say updatable/insertable=true for one half of a composite key and =false for the other half? If I have updatable/insertable=true for both parts of the foreign key, then Eclipselink fails to startup saying that I can not use the column twice without having one set to readonly by specifying these options.