1

我有以下数据库表:

  • 派对,将 pk“pty_id”连接到用于生成 pk 值的序列。
  • fpk“prs_pty_id”与party.pty_id 有识别关系的人。
  • company ...目前不涉及,但显然这是一种子超类设置,它可能已经使用postgresql中的子类化机制实现,但那是另一天的事了。

因此,我使用 Netbeans 6.9.1 生成 JPA 实体类和控制器/dao 代码来处理这个问题。它工作得很好,我只需向 Party Entity bean 添加一个注释:@GeneratedValue(strategy = GenerationType.IDENTITY)。这对于 Person 实体 bean 来说不是必需的,因为它应该始终具有它所连接的 Party 的 pk 值。

所以这是我为创建一个人所做的:

PartyJpaController parController = new PartyJpaController();
PersonJpaController perController = new PersonJpaController();
Party par = new Party();
Person per = new Person();
par.setComment("jalla");
per.setName("Per Vers");
parController.create(par);
per.setPrsPtyId(par.getPtyId()); // <== why do I need to set this ...
Long partyId = par.getPtyId();
par.setPerson(per); // <== ... when this explicitly expresses the relationship?
perController.create(per);
parController.edit(par);

Party foundParty = parController.findParty(partyId);

Person foundPerson = foundParty.getPerson();
System.err.println(foundPerson.getName());

这工作得很好。但是为什么我必须显式设置 Person bean 的 pk 呢?它与党是一种识别关系。如果我跳过它,我会得到

java.lang.IllegalArgumentException: An instance of a null PK has been incorrectly provided for this find operation.

在 perController.create(per) 中,这是 Netbeans 生成的代码:

EntityManager em = null;
try {
    em = getEntityManager();
    em.getTransaction().begin();
    Party party = person.getParty();
    if (party != null) {
        party = em.getReference(party.getClass(), party.getPtyId()); // <== Exception thrown here
        person.setParty(party);
    }
    em.persist(person);
    if (party != null) {
        party.setPerson(person);
        party = em.merge(party);
    }
    em.getTransaction().commit();

所以,我想 Netbeans 生成的代码并没有完全适应识别关系?编写此代码的最佳方法是什么?

使用的软件:Eclipselink 版本 2.1.1 Postgresql 8.4 Netbeans 6.9.1 Java/JDK 1.6.0_21

这是我的bean,它们是由netbeans 6.9.1从模式生成的,除了Party中的@GeneratedValue(strategy = GenerationType.IDENTITY),我添加它是为了在postgresql中使用序列/序列pk生成。

/*
 * 要更改此模板,请选择工具 | 模板
 * 并在编辑器中打开模板。
 */

包 com.martinsolaas.webmarin.jpa;

导入 java.io.Serializable;
导入 javax.persistence.Basic;
导入 javax.persistence.Column;
导入 javax.persistence.Entity;
导入 javax.persistence.Id;
导入 javax.persistence.JoinColumn;
导入 javax.persistence.MapsId;
导入 javax.persistence.NamedQueries;
导入 javax.persistence.NamedQuery;
导入 javax.persistence.OneToOne;
导入 javax.persistence.PrimaryKeyJoinColumn;
导入 javax.persistence.Table;

/**
 *
 * @author jms
 */
@实体
@Table(name = "person", catalog = "webmarin", schema = "webmarin")
@命名查询({
    @NamedQuery(name = "Person.findAll", query = "SELECT p FROM Person p"),
    @NamedQuery(name = "Person.findByPrsPtyId", query = "SELECT p FROM Person p WHERE p.prsPtyId = :prsPtyId"),
    @NamedQuery(name = "Person.findByName", query = "SELECT p FROM Person p WHERE p.name = :name"),
    @NamedQuery(name = "Person.findByCellphone", query = "SELECT p FROM Person p WHERE p.cellphone = :cellphone"),
    @NamedQuery(name = "Person.findByOfficephone", query = "SELECT p FROM Person p WHERE p.officephone = :officephone")})
公共类人实现可序列化{
    私有静态最终长序列版本UID = 1L;
    @ID
    @基本(可选=假)
    @Column(名称=“prs_pty_id”,可为空=假)
    @MapsId
    私人长prsPtyId;
    @Column(名称=“名称”,长度= 255)
    私有字符串名称;
    @Column(name = "手机", 长度 = 55)
    私人字符串手机;
    @Column(name = "officephone", length = 55)
    私人字符串办公电话;
    @JoinColumn(name = "prs_pty_id", referencedColumnName = "pty_id", nullable = false, insertable = false, updatable = false)
    @OneToOne(可选=假)
    私人派对;

    公共人(){
    }

    公共人员(长prsPtyId){
        this.prsPtyId = prsPtyId;
    }

    公共长 getPrsPtyId() {
        返回prsPtyId;
    }

    公共无效 setPrsPtyId(长 prsPtyId){
        this.prsPtyId = prsPtyId;
    }

    公共字符串 getName() {
        返回名称;
    }

    公共无效集合名称(字符串名称){
        this.name = 名称;
    }

    公共字符串 getCellphone() {
        归还手机;
    }

    公共无效setCellphone(字符串手机){
        this.cellphone = 手机;
    }

    公共字符串 getOfficephone() {
        归还办公电话;
    }

    公共无效setOfficephone(字符串办公电话){
        this.officephone = 办公电话;
    }

    公共聚会 getParty() {
        回归方;
    }

    公共无效setParty(派对){
        this.party = 派对;
    }

    @覆盖
    公共 int hashCode() {
        整数哈希 = 0;
        哈希 += (prsPtyId != null ? prsPtyId.hashCode() : 0);
        返回哈希;
    }

    @覆盖
    公共布尔等于(对象对象){
        // TODO: 警告 - 如果没有设置 id 字段,此方法将不起作用
        if (!(object instanceof Person)) {
            返回假;
        }
        其他人=(人)对象;
        if ((this.prsPtyId == null && other.prsPtyId != null) || (this.prsPtyId != null && !this.prsPtyId.equals(other.prsPtyId))) {
            返回假;
        }
        返回真;
    }

    @覆盖
    公共字符串 toString() {
        返回 "com.martinsolaas.webmarin.jpa.Person[prsPtyId=" + prsPtyId + "]";
    }

}

/*
 * 要更改此模板,请选择工具 | 模板
 * 并在编辑器中打开模板。
 */

包 com.martinsolaas.webmarin.jpa;

导入 java.io.Serializable;
导入 java.util.List;
导入 javax.persistence.Basic;
导入 javax.persistence.CascadeType;
导入 javax.persistence.Column;
导入 javax.persistence.Entity;
导入 javax.persistence.GeneratedValue;
导入 javax.persistence.GenerationType;
导入 javax.persistence.Id;
导入 javax.persistence.JoinColumn;
导入 javax.persistence.JoinTable;
导入 javax.persistence.ManyToMany;
导入 javax.persistence.MapsId;
导入 javax.persistence.NamedQueries;
导入 javax.persistence.NamedQuery;
导入 javax.persistence.OneToOne;
导入 javax.persistence.Table;

/**
 *
 * @author jms
 */
@实体
@Table(名称=“派对”,目录=“webmarin”,模式=“webmarin”)
@命名查询({
    @NamedQuery(name = "Party.findAll", query = "SELECT p FROM Party p"),
    @NamedQuery(name = "Party.findByPtyId", query = "SELECT p FROM Party p WHERE p.ptyId = :ptyId"),
    @NamedQuery(name = "Party.findByComment", query = "SELECT p FROM Party p WHERE p.comment = :comment")})
公共类Party实现Serializable {
    私有静态最终长序列版本UID = 1L;
    @ID
    @基本(可选=假)
    @Column(名称=“pty_id”,可为空=假)
    @GeneratedValue(策略 = GenerationType.IDENTITY)
    私人长 ptyId;
    @基本(可选=假)
    @Column(name = "comment", nullable = false, length = 2147483647)
    私有字符串注释;
    @JoinTable(name = "party_relationship", joinColumns = {
        @JoinColumn(name = "parent_pty_id", referencedColumnName = "pty_id", nullable = false)}, inverseJoinColumns = {
        @JoinColumn(name = "child_pty_id", referencedColumnName = "pty_id", nullable = false)})
    @ManyToMany
    私人名单partyList;
    @ManyToMany(mappedBy = "partyList")
    私人名单partyList1;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "party")
    私人的人;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "party")
    私人公司公司;

    公共方(){
    }

    公共方(长 ptyId){
        this.ptyId = ptyId;
    }

    公共方(长 ptyId,字符串注释){
        this.ptyId = ptyId;
        this.comment = 评论;
    }

    公共长 getPtyId() {
        返回 ptyId;
    }

    公共无效 setPtyId(长 ptyId){
        this.ptyId = ptyId;
    }

    公共字符串 getComment() {
        返回评论;
    }

    公共无效setComment(字符串评论){
        this.comment = 评论;
    }

    公共列表 getPartyList() {
        返回派对列表;
    }

    公共无效setPartyList(列表partyList){
        this.partyList = 派对列表;
    }

    公共列表 getPartyList1() {
        返回派对列表1;
    }

    公共无效setPartyList1(列出partyList1){
        this.partyList1 = 派对列表1;
    }

    公共人员 getPerson() {
        返回人;
    }

    公共无效 setPerson(个人){
        this.person = 人;
    }

    上市公司 getCompany() {
        返回公司;
    }

    公共无效setCompany(公司公司){
        this.company = 公司;
    }

    @覆盖
    公共 int hashCode() {
        整数哈希 = 0;
        hash += (ptyId != null ? ptyId.hashCode() : 0);
        返回哈希;
    }

    @覆盖
    公共布尔等于(对象对象){
        // TODO: 警告 - 如果没有设置 id 字段,此方法将不起作用
        if (!(object instanceof Party)) {
            返回假;
        }
        当事人其他=(当事人)对象;
        if ((this.ptyId == null && other.ptyId != null) || (this.ptyId != null && !this.ptyId.equals(other.ptyId))) {
            返回假;
        }
        返回真;
    }

    @覆盖
    公共字符串 toString() {
        返回 "com.martinsolaas.webmarin.jpa.Party[ptyId=" + ptyId + "]";
    }

}

最终,这里是模式 SQL

创建序列 webmarin.party_pty_id_seq;

创建表 webmarin.party (
                pty_id BIGINT NOT NULL DEFAULT nextval('webmarin.party_pty_id_seq'),
                评论文本不为空,
                约束 pty_pk 主键 (pty_id)
);


更改序列 webmarin.party_pty_id_seq 由 webmarin.party.pty_id 拥有;

创建表 webmarin.company (
                cmp_pty_id BIGINT 非空,
                名称 VARCHAR(255) 非空,
                约束 cmp_pk 主键 (cmp_pty_id)
);


创建表 webmarin.party_relationship (
                parent_pty_id BIGINT NOT NULL,
                child_pty_id BIGINT NOT NULL,
                约束 ptr_pk 主键(parent_pty_id,child_pty_id)
);


创建表 webmarin.person (
                prs_pty_id BIGINT 非空,
                名称 VARCHAR(255),
                手机 VARCHAR(55),
                办公电话 VARCHAR(55),
                约束 prs_pk 主键 (prs_pty_id)
);


ALTER TABLE webmarin.party_relationship 添加约束 parent_party_party_relationship_fk
外键(parent_pty_id)
参考 webmarin.party (pty_id)
删除无操作
更新无动作
不可延期;

ALTER TABLE webmarin.party_relationship 添加约束 child_party_party_relationship_fk
外键(child_pty_id)
参考 webmarin.party (pty_id)
删除无操作
更新无动作
不可延期;

ALTER TABLE webmarin.person 添加约束 party_person_fk
外键 (prs_pty_id)
参考 webmarin.party (pty_id)
删除无操作
更新无动作
不可延期;

ALTER TABLE webmarin.company 添加约束 party_company_fk
外键 (cmp_pty_id)
参考 webmarin.party (pty_id)
删除无操作
更新无动作
不可延期;
4

1 回答 1

0

JPA 2.0 极大地改进了对派生标识符Id的支持,以及您可以在关联上使用的MapsId注释(现在应该首选它们) 。XxxToOne下面是从规范中获取的示例之一:

2.4.1.3 派生身份示例

...

示例 4:

父实体有一个简单的主键:

@Entity
public class Person {
    @Id String ssn;
    ...
}

情况(a):依赖实体有一个由关系属性映射的主键属性。的主键 MedicalHistory是 type String

@Entity
public class MedicalHistory {
    // default join column name is overridden
    @Id
    @OneToOne
    @JoinColumn(name="FK")
    Person patient;
    ...
}

示例查询:

SELECT m
FROM MedicalHistory m
WHERE m.patient.ssn = '123-45-6789'

情况(b):依赖实体有一个与关系属性对应的主键属性。主键属性与父实体的主键具有相同的基本类型。应用于关系属性的 MapsId注解表示主键是由关系属性映射的。

@Entity
public class MedicalHistory {
    @Id String id; // overriding not allowed
    ...
    // default join column name is overridden
    @MapsId
    @JoinColumn(name="FK")
    @OneToOne Person patient;
    ...
}

示例查询:

SELECT m
FROM MedicalHistory m WHERE m.patient.ssn = '123-45-6789'

我认为这MapsId可能是您正在寻找的。

跟进:而不是这个:

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "prs_pty_id", nullable = false)
    @MapsId
    private Long prsPtyId;
    @Column(name = "name", length = 255)
    private String name;
    @Column(name = "cellphone", length = 55)
    private String cellphone;
    @Column(name = "officephone", length = 55)
    private String officephone;
    @JoinColumn(name = "prs_pty_id", referencedColumnName = "pty_id", nullable = false, insertable = false, updatable = false)
    @OneToOne(optional = false)
    private Party party;
    ...
}

试试这个:

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "prs_pty_id", nullable = false)
    private Long prsPtyId;
    @Column(name = "name", length = 255)
    private String name;
    @Column(name = "cellphone", length = 55)
    private String cellphone;
    @Column(name = "officephone", length = 55)
    private String officephone;
    @MapsId
    @JoinColumn(name = "prs_pty_id", referencedColumnName = "pty_id")
    @OneToOne
    private Party party;
    ...
}

参考

  • JPA 2.0 规范
    • 第 2.4.1 节“派生身份对应的主键”
    • 第 2.4.1.1 节“派生身份的规范”
    • 第 2.4.1.2 节“派生身份的映射”
    • 第 2.4.1.3 节“派生身份示例”
  • JPA 维基书
于 2010-08-30T00:05:26.367 回答