Here's what I'm trying to do...I have a Person
@Entity
@Table(name = "PERSON",
uniqueConstraints = {
@UniqueConstraint(columnNames = {"SSN"})
}
)
@DynamicInsert(true)
@DynamicUpdate(true)
@SelectBeforeUpdate(true)
public class Person implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
@Version
@Column(name = "OBJ_VERSION")
private Timestamp version;
@Id
@Column(name = "SSN", length = 12, nullable = false, insertable = true, updatable = true)
private String ssn;
@Column(name = "LAST_NAME", length = 50, nullable = false, insertable = true, updatable = true)
private String lastName;
@Column(name = "FIRST_NAME", length = 30, nullable = false, insertable = true, updatable = true)
private String firstName;
@Column(name = "MIDDLE_NAME", length = 30, nullable = true, insertable = true, updatable = true)
private String middleName;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "person", cascade = CascadeType.ALL)
private Passport passport;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Citizenship> citizenship = new HashSet<>();
// Getters and setters left out for brevity
and each person can have one Passport
@Entity
@Table(name = "PASSPORT",
uniqueConstraints = {
@UniqueConstraint(columnNames = {"SSN", "PASSPORT_NUMBER"})
}
)
@DynamicInsert(true)
@DynamicUpdate(true)
@SelectBeforeUpdate(true)
public class Passport implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
@Version
@Column(name = "OBJ_VERSION")
private Timestamp version;
@Id
@Column(name = "SSN", length = 12, nullable = false, insertable = true, updatable = true)
private String ssn;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SSN")
@MapsId
private Person person;
@Column(name = "EXPIRATION_DATE", nullable = true, insertable = true, updatable = false)
private GregorianCalendar expirationDate;
@Column(name = "ISSUING_COUNTRY", nullable = true, insertable = true, updatable = false)
private String issuingCountry;
@Column(name = "PASSPORT_NUMBER", nullable = false, insertable = true, updatable = false)
private String passportNumber;
// Getters and setters left out for brevity
This works, each person can have one Passport and the Passport.ssn is assigned the value of the Person.ssn. This is being done because SSN is a unique identifier and it avoids the need for link tables.
Each person can also have a Citizenship
@Entity
@Table(name = "CITIZENSHIP")
@DynamicInsert(true)
@DynamicUpdate(true)
@SelectBeforeUpdate(true)
public class Citizenship implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
@Version
@Column(name = "OBJ_VERSION")
private Timestamp version;
@EmbeddedId
private CitizenshipId citizenshipId;
@Column(name = "DATE_OF_CITIZENSHIP")
private GregorianCalendar dateOfCitizenship;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "SSN")
@MapsId("ssn")
private Person person;
// Getters and setters left out for brevity
I have successfully added a person with a passport and a person without a passport. I have added a third person with a passport and dual citizenship with
// This person has a passport and is a dual citizen.
person = new Person();
person.setSsn("654-89-7531");
person.setFirstName("Lois");
person.setLastName("Lane");
passport = new Passport();
passport.setExpirationDate(new GregorianCalendar());
passport.setIssuingCountry("USA");
passport.setPassportNumber("987654");
Set<Citizenship> citizenshipSet = new HashSet<>();
CitizenshipId citizenshipId = new CitizenshipId();
citizenshipId.setCountry("USA");
Citizenship c = new Citizenship();
c.setDateOfCitizenship(new GregorianCalendar());
c.setCitizenshipId(citizenshipId);
c.setPerson(person);
citizenshipSet.add(c);
citizenshipId = new CitizenshipId();
citizenshipId.setCountry("CAN");
c = new Citizenship();
c.setDateOfCitizenship(new GregorianCalendar());
c.setCitizenshipId(citizenshipId);
c.setPerson(person);
citizenshipSet.add(c);
person.setPassport(passport);
passport.setPerson(person);
session.saveOrUpdate(person);
for(Citizenship citizen : citizenshipSet) {
session.saveOrUpdate(citizen);
}
session.flush();
session.clear();
This looks weird/inefficient to me, but it does work (tips for improvement would be appreciated). But as desired, the Person.ssn is carried into the Citizenship. Here's the problem:
The Person with dual Citizenship currently has citizenship in USA and Canada. Let's assume this is wrong and the Person has citizenship in USA and Mexico, which means the CitizenshipId.country needs to change from "CAN" to "MEX". I have tried a bunch of variations of code like
Criteria citCriteria = session.createCriteria(Citizenship.class);
citCriteria.add(Restrictions.eq("citizenshipId.ssn", "654-89-7531"));
List<Citizenship> citizenship = citCriteria.list();
for(Citizenship c : citizenship) {
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
session.evict(c);
c.getCitizenshipId().setCountry("MEX");
session.saveOrUpdate(c);
session.flush();
session.clear();
}
}
With "show_sql" on, this doesn't perform an update, even though I can see the values change when debugging. I did try an evict(), then set the country, then saveOrUpdate, which made a new entry (I figured it would).
Phew...the question is: How can the values in an Embeddable class be updated when that class is being used as an EmbeddedId? I feel like I'm close but just missing one thing...
Thanks.
Adding CitizenshipID for reference
@Embeddable
public class CitizenshipId implements Serializable {
private static final long serialVersionUID = 6732775093033061190L;
String ssn;
String country;
// Omitted getters, setters, constructors, hashcode, and equals