更新 Embeddable class 中的值

Updating values in an Embeddable class

这就是我想要做的...我有一个人

@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.ssn 被赋予 Person.ssn 的值。这样做是因为 SSN 是唯一标识符,它避免了对 link 表的需要。

每个人也可以拥有公民身份

@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();

这对我来说看起来 weird/inefficient,但确实有效(如有改进建议,我们将不胜感激)。但根据需要,Person.ssn 被带入公民身份。问题是:

拥有双重国籍的人目前拥有美国和加拿大国籍。让我们假设这是错误的,并且此人拥有美国和墨西哥的公民身份,这意味着 CitizenshipId.country 需要从 "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" 时,这不会执行更新,即使我在调试时可以看到值发生变化。我确实尝试了一个 evict(),然后设置了国家,然后是 saveOrUpdate,它创建了一个新条目(我想它会)。

Phew...问题是:当 Embeddable class 被用作 EmbeddedId 时,如何更新 Embeddable class 中的值?我觉得我很接近但只是错过了一件事...

谢谢。

添加 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 中的描述,我删除了 .evict:

Remove this instance from the session cache. Changes to the instance will not be synchronized with the database. This operation cascades to associated instances if the association is mapped with cascade="evict".

主题 is inline with what Dan posted above about creating a new object with the old object Id. However, topic Hibernate - update the primary key 'id' column in the table 确实声明 Hibernate 不允许更新主键。

此处的 objective 是创建一个具有 (n) SSN、可能具有护照和公民身份的人。 SSN 旨在作为主键,因此我将 Person 映射到 Passport 和 Citizenship,并使用 SSN 作为 JoinColumn。

Person to Passport 是一对一的关系,所以这不是问题。

Person to Citizenship 是一对多的关系。这种关系意味着我必须创建一个可嵌入的 ID。为了使每个公民身份都独一无二,可嵌入的 class CitizenshipId 是使用 SSN 和国家/地区创建的。

使用 Hibernate - update the primary key 'id' column in the table 的公认答案,我更改了

的变体
    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。

感谢 Dan 的想法。我希望这个主题及其参考资料对其他人有所帮助。