Могу ли я иметь Eclipselink / JPA иметь иностранный составной ключ, который разделяет поле с первичным составным ключом? - PullRequest
5 голосов
/ 09 июля 2009

В моей базе данных есть две сущности; Компания и Персона. Компания может иметь много людей, но у человека должна быть только одна компания. Структура таблицы выглядит следующим образом.

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

Мне пришло в голову, что я могу удалить PERSON.OWNER и получить его через внешний ключ; однако я не могу внести это изменение, не затронув устаревший код.

Я смоделировал их как 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...
}

Мои классы PersonPK и CompanyPK не являются чем-то особенным, они просто служат держателем структуры и полем ID и переопределяют hashCode и равны (o).

Пока все хорошо. Однако я сталкиваюсь с проблемой при попытке разобраться с ассоциациями. Кажется, что если у меня есть существующая Компания, и я создаю Лицо, и связываю Лица с Компанией, и сохраняю компанию, ассоциация не сохраняется при вставке Лица. Например, мой основной код выглядит так:

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

Это завершается без ошибок; однако в базе данных я обнаружил, что запись Person была вставлена ​​с нулем в поле COMP_ID. Если для журнала отладки EclipseLink задано значение FINE, запрос SQL отображается в виде:

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

Я ожидал, что это будет сохранено, и запрос будет эквивалентен

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

Что дает? Неверно ли говорить, что Updatable / Inserttable = true для одной половины составного ключа и = false для другой половины? Если у меня есть Updatable / Inserttable = true для обеих частей внешнего ключа, то Eclipselink не запускается, говоря, что я не могу использовать столбец дважды, если у меня не установлен один параметр только для чтения, указав эти параметры.

Ответы [ 4 ]

4 голосов
/ 26 апреля 2012

Ваш код должен работать.Убедитесь, что вы перекомпилировали / развернули его правильно.Какую версию вы используете?

Также убедитесь, что для объекта вашей компании установлен правильный идентификатор.

Вы также можете попробовать ввести

insertable = false, updatable = false

в поле вашего владельца @Оставьте столбец и оставьте весь внешний ключ для вставки / обновления.

1 голос
/ 05 августа 2009

У меня была такая же проблема. И решение, которое я нашел, состоит в том, чтобы аннотировать поля первичного ключа как не вставляемые и не обновляемые. Ваше лицо должно выглядеть следующим образом:

@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...
}
0 голосов
/ 08 июня 2012

Помимо довольно странного дизайна баз данных, это может сработать:

@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;
}
0 голосов
/ 05 августа 2009

Я сделал так, чтобы аннотировать поля в классе PK, как если бы они были на объекте.

Тогда в сущности нет полей, которые содержит класс PK. Если вы хотите, вы можете реализовать геттеры, которые проходят

class Person {
  .....

private PersonPK pk;

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

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

PersonPK должен быть сериализуем и аннотирован @Embeddable

@Embeddable
class PersonPK implements Serializable {
...