以下是我要做的……我有一个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
每个人可以有一个护照
@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
这是可行的,每个人都可以有一个Passport,并且Passport.ssn被分配了person.ssn的值。这样做是因为ssn是一个唯一的标识符,它避免了对链接表的需要。
每个人还可以拥有公民身份
@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
我已经成功添加了一个有护照的人和一个没有护照的人。我添加了第三个人,他持有的护照和双重国籍
// 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();
这在我看来很奇怪/效率低下,但它确实有效(如有改进建议,将不胜感激(。但根据需要,Person.ssn被带入公民身份。问题是:
拥有双重国籍的人目前拥有美国和加拿大国籍。假设这是错误的,此人拥有美国和墨西哥公民身份,这意味着CitizenshipId.conational需要从"CAN"更改为"MEX"。我已经尝试了一堆类似的代码变体
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();
}
}
启用"show_sql"后,它不会执行更新,即使我可以在调试时看到值发生了变化。我确实尝试了驱逐((,然后设置了国家,然后保存OrUpdate,它创建了一个新条目(我想会的(。
Phew。。。问题是:当可嵌入类被用作EmbeddedId时,如何更新该类中的值?我觉得我很接近,但只是错过了一件事。。。
谢谢。
添加CitizenshipID以供参考
@Embeddable
public class CitizenshipId implements Serializable {
private static final long serialVersionUID = 6732775093033061190L;
String ssn;
String country;
// Omitted getters, setters, constructors, hashcode, and equals
您尝试过吗:
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
session.evict(c);
c.getCitizenshipId().setCountry("MEX");
c.getPerson().getCitizenship().add(c); // TRY ADDING THIS
session.saveOrUpdate(c);
session.flush();
session.clear();
}
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
// TRY ADDING THIS ------------------------
//session.evict(c);
CitizenshipId cid = new CitizenshipId();
cid.setSsn(c.getCitizenshipId().getSsn();
cid.setCountry("MEX");
c.setCitizenshipId(cid); // references new CID -- should issue update
// -----------------------------------------
session.saveOrUpdate(c);
session.flush();
session.clear();
}
由于API中的描述,我删除了.exet:
从会话缓存中删除此实例。对实例的更改将不会与数据库同步。此操作级联到关联实例(如果关联与cascade="驱逐"。
Topic如何在Spring JPA中更新与主键相关的值与Dan在上面发布的关于使用旧对象Id创建新对象的内容一致。然而,Topic Hibernate-更新主键';id';表中的列声明Hibernate不允许更新主键。
这里的目标是创建一个有(n(SSN的人,可能有护照和公民身份。SSN旨在作为主键,所以我将Person映射到Passport和Citizenship,并使用SSN作为JoinColumn。
人与护照是一对一的关系,所以这不是问题。
人与公民身份是一对多的关系。这种关系意味着我必须创建一个可嵌入的ID。为了使每个公民身份都是唯一的,可嵌入的类CitizenshipId是用SSN和Country创建的。
使用接受的Hibernate答案-更新主键';id';表中的列我更改了的变化
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();
}
}
至
Query query=session.createQuery("update Citizenship set country = :country1 where ssn = :ssn and country = :country2")
.setString("country1", "MEX").setString("ssn", "654-89-7531").setString("country2", "CAN");
query.executeUpdate();
并且确实发生了更新。无法通过典型的代码进行更新(使用条件获取数据,更新它,然后调用saveOrUpdate(,但能够通过查询进行更新对我来说意义不大。我知道密钥管理有时最好留给数据库,但当使用SSN等唯一值时,就不需要另一个密钥了。如果在没有生成策略的情况下在代码中识别ID,那么ID可以更新是理所当然的。。。JMHO。
感谢丹的想法。我希望这个主题及其参考资料能帮助其他人。