更新可嵌入类中的值

时间:2018-07-31 19:57:01

标签: java hibernate jpa embeddable

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

@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.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,从而创建了一个新条目(我认为可以)。

Ph ...问题是:当将Embeddable类用作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

3 个答案:

答案 0 :(得分:0)

您尝试过吗:

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

答案 1 :(得分:0)

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:

  

从会话缓存中删除该实例。对实例的更改   将不会与数据库同步。此操作级联为   关联实例(如果关联映射到)   Cascade =“ evict”。

答案 2 :(得分:0)

主题How to update values associated with Primary Key in Spring-JPA与Dan上面发布的关于使用旧对象ID创建新对象的内容一致。但是,主题Hibernate - update the primary key 'id' column in the table确实声明了Hibernate不允许更新主键。

这里的目标是创建一个具有(S)SSN(可能带有护照)和公民身份的人。 SSN旨在作为主键,因此我将Person映射到Passport and Citizenship,并使用SSN作为JoinColumn。

人与护照是一对一的关系,所以这不是问题。

人与人之间的关系是一对多的关系。这种关系意味着我必须创建一个可嵌入的ID。为了使每个Citizenship具有唯一性,使用SSN和Country创建了可嵌入的类CitizenshipId。

使用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的想法。我希望这个主题及其参考对其他人有帮助。