java 使用 Eclipselink/JPA,我可以拥有一个与主复合键共享一个字段的外复合键吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1100700/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 15:11:24  来源:igfitidea点击:

With Eclipselink/JPA, can I have a Foreign Composite Key that shares a field with a Primary Composite Key?

javaormjpaeclipselink

提问by user107924

My database has two entities; Company and Person. A Company can have many People, but a Person must have only one Company. The table structure looks as follows.

我的数据库有两个实体;公司和个人。一个公司可以有很多人,但一个人必须只有一个公司。表结构如下所示。

COMPANY
----------
owner   PK
comp_id PK
c_name
PERSON
----------------
owner    PK, FK1
personid PK
comp_id  FK1
p_fname
p_sname

It has occurred to me that I could remove PERSON.OWNER and derive it through the foreign key; however, I can't make this change without affecting legacy code.

我想到我可以删除 PERSON.OWNER 并通过外键派生它;但是,我无法在不影响遗留代码的情况下进行此更改。

I have modeled these as JPA-annotated classes;

我将这些建模为 JPA 注释的类;

@Entity
@Table(name = "PERSON")
@IdClass(PersonPK.class)
public class Person
    implements Serializable {

  @Id
  private String owner;

  @Id
  private String personid;

  @ManyToOne
  @JoinColumns(
    {@JoinColumn(name = "owner", referencedColumnName = "OWNER",
                 insertable = false, updatable = false),
     @JoinColumn(name = "comp_id", referencedColumnName = "COMP_ID",
                 insertable = true, updatable = true)})
  private Company company;

  private String p_fname;

  private String p_sname;

  ...and standard getters/setters...
}
@Entity
@Table(name = "COMPANY")
@IdClass(CompanyPK.class)
public class Company
    implements Serializable {

  @Id
  private String owner;

  @Id
  private String comp_id;

  private String c_name;

  @OneToMany(mappedBy = "company", cascade=CascadeType.ALL)
  private List people;

  ...and standard getters/setters...
}

My PersonPK and CompanyPK classes are nothing special, they just serve as a struct holding owner and the ID field, and override hashCode and equals(o).

我的 PersonPK 和 CompanyPK 类没有什么特别的,它们只是作为一个结构持有所有者和 ID 字段,并覆盖 hashCode 和 equals(o)。

So far so good. I come across a problem, however, when trying to deal with associations. It seems if I have an existing Company, and create a Person, and associate to the Person to the Company and persist the company, the association is not saved when the Person is inserted. For example, my main code looks like this:

到现在为止还挺好。但是,在尝试处理关联时,我遇到了一个问题。似乎如果我有一个现有的公司,并创建一个人,并将人关联到公司并保留公司,则插入人时不会保存关联。例如,我的主要代码如下所示:

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

CompanyPK companyPK = new CompanyPK();
companyPK.owner="USA";
companyPK.comp_id="1102F3";
Company company = em.find(Company.class, companyPK);

Person person = new Person();
person.setOwner("USA");
person.setPersonid("5116628123"); //some number that doesn't exist yet
person.setP_fname("Hannah");
person.setP_sname("Montana");
person.setCompany(company);
em.persist(person);

This completes without error; however in the database I find that the Person record was inserted with a null in the COMP_ID field. With EclipseLink debug logging set to FINE, the SQL query is shown as:

这完全没有错误;但是在数据库中,我发现在 COMP_ID 字段中插入了一个空值的 Person 记录。将 EclipseLink 调试日志设置为 FINE,SQL 查询显示为:

INSERT INTO PERSON (PERSONID,OWNER,P_SNAME,P_FNAME) VALUES (?,?,?,?)
  bind => [5116628123,USA,Montana,Hannah,]

I would have expected this to be saved, and the query to be equivalent to

我本来希望这会被保存,并且查询相当于

INSERT INTO PERSON (PERSONID,OWNER,COMP_ID,P_SNAME,P_FNAME) VALUES (?,?,?,?,?)
  bind => [5116628123,USA,1102F3,Montana,Hannah,]

What gives? Is it incorrect to say updatable/insertable=true for one half of a composite key and =false for the other half? If I have updatable/insertable=true for both parts of the foreign key, then Eclipselink fails to startup saying that I can not use the column twice without having one set to readonly by specifying these options.

是什么赋予了?对组合键的一半说可更新/可插入=真,对另一半说=假是否不正确?如果外键的两个部分都具有可更新/可插入=真,那么 Eclipselink 将无法启动,说如果没有通过指定这些选项将列设置为只读,我将无法使用该列两次。

回答by James

Your code should work. Ensure you recompiled/deployed it correctly. What version are you using?

您的代码应该可以工作。确保您正确地重新编译/部署它。你用的是什么版本?

Also ensure that your Company object has a valid id set.

还要确保您的 Company 对象具有有效的 id 集。

You could also try putting ,

你也可以尝试把,

insertable = false, updatable = false

in your owner field's @Column and leave the entire foreign key insertable/updatable.

在您的所有者字段的@Column 中,并保留整个外键可插入/可更新。

回答by James

I had the same problem. And the solution i've found is to anotate primary key fields as non-insertable and non-updatable. Your person entity should look like this:

我有同样的问题。我发现的解决方案是将主键字段注释为不可插入和不可更新。您的个人实体应如下所示:

@Entity
@Table(name = "PERSON")
@IdClass(PersonPK.class)
public class Person
    implements Serializable {

  @Id
  @Column(insertable = false, updatable=false)
  private String owner;

  @Id
  private String personid;

  @Id
  @Column(insertable = false, updatable=false)
  private String comp_id;


  @ManyToOne
  @JoinColumns({
     @JoinColumn(name = "owner", referencedColumnName = "OWNER"),
     @JoinColumn(name = "comp_id", referencedColumnName = "COMP_ID")
  })
  private Company company;

  private String p_fname;

  private String p_sname;

  ...and standard getters/setters...
}

回答by harmanjd

The way I did this was to annotate the fields in the PK class like I would have if they were on the entity.

我这样做的方法是注释 PK 类中的字段,就像我在实体上一样。

Then in the entity don't have the fields the PK class holds. If you want to you can implement getters that passthrough

然后在实体中没有 PK 类拥有的字段。如果你愿意,你可以实现传递的 getter

class Person {
  .....

private PersonPK pk;

@Id
public PersonPK getPk() { return pk; }

@Transient
public String getOwner() {
  return pk.getOwner();
}
....
}

The PersonPK needs to be serializable and annoted @Embeddable

PersonPK 需要可序列化和注释 @Embeddable

@Embeddable
class PersonPK implements Serializable {

回答by siebz0r

Apart from the databases design being rather weird, this might work:

除了数据库设计相当奇怪之外,这可能有效:

@Entity
public class Company
{
    @EmbeddedId
    private CompanyPK key;

    @MapsId(value = "ownerId")
    @OneToMany
    @JoinColumn(name = "owner", referencedColumnName = "personid")
    private Person owner;

    @ManyToOne(mappedBy = "company")
    private List<Person> persons;

    @Column(name = "c_name")
    private String name;
}

@Embeddable
public class CompanyPK
{
    @Column(name = "comp_id")
    private String id;

    @Column(name = "owner")
    private String ownerId;
}

@Entity
public class Person
{
    @EmbeddedId
    private PersonPK key;

    @MapdId(value = "ownerId")
    private Person owner;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "owner", referencedColumnName = "owner")
        @JoinColumn(name = "comp_id", referencedColumnName = "comp_id")
    })
    private Company company;

    @Column(name = "p_fname")
    private String firstName;

    @Column(name = "p_sname")
    private String surName;
}

@Embeddable
public class PersonPK
{
    @Column(name = "personid")
    private String id;

    @Column(name = "owner")
    private String ownerId;
}