Код NetBeans для Postgresql и Eclipselink - вопрос определения отношения - PullRequest
1 голос
/ 30 августа 2010

У меня есть следующие таблицы базы данных:

  • сторона, с pk "pty_id", подключенным к последовательности для генерации значений pk.
  • персона с фпк "prs_pty_id" в идентифицирующем отношении к party.pty_id.
  • компания ... которая в данный момент не участвует, но, очевидно, это своего рода настройка подкласса, и, возможно, она могла бы быть реализована с помощью механизма подклассов в postgresql, но это на другой день.

Итак, я использую Netbeans 6.9.1 для генерации классов сущностей JPA и кода контроллера / дао, чтобы справиться с этим. Это работает просто замечательно, мне нужно только добавить одну аннотацию к компоненту Party Entity: @GeneratedValue (стратегии = GenerationType.IDENTITY). Это не требуется для бина сущности Person, потому что он всегда должен иметь значение 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());

Это работает просто отлично. Но почему я должен явно устанавливать pk bean-компонента Person? Он находится в идентификационных отношениях с партией. Если я пропущу это, я получу

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-компоненты, они генерируются сетевыми компонентами 6.9.1 из схемы, кроме @GeneratedValue (стратегии = GenerationType.IDENTITY) в Party, который я добавил для использования генерации последовательных / последовательных пакетов в PostgreSQL.

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.martinsolaas.webmarin.jpa;

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

/**
 *
 * @author jms
 */
@Entity
@Table(name = "person", catalog = "webmarin", schema = "webmarin")
@NamedQueries({
    @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")})
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 Person() {
    }

    public Person(Long prsPtyId) {
        this.prsPtyId = prsPtyId;
    }

    public Long getPrsPtyId() {
        return prsPtyId;
    }

    public void setPrsPtyId(Long prsPtyId) {
        this.prsPtyId = prsPtyId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCellphone() {
        return cellphone;
    }

    public void setCellphone(String cellphone) {
        this.cellphone = cellphone;
    }

    public String getOfficephone() {
        return officephone;
    }

    public void setOfficephone(String officephone) {
        this.officephone = officephone;
    }

    public Party getParty() {
        return party;
    }

    public void setParty(Party party) {
        this.party = party;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (prsPtyId != null ? prsPtyId.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Person)) {
            return false;
        }
        Person other = (Person) object;
        if ((this.prsPtyId == null && other.prsPtyId != null) || (this.prsPtyId != null && !this.prsPtyId.equals(other.prsPtyId))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.martinsolaas.webmarin.jpa.Person[prsPtyId=" + prsPtyId + "]";
    }

}

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.martinsolaas.webmarin.jpa;

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

/**
 *
 * @author jms
 */
@Entity
@Table(name = "party", catalog = "webmarin", schema = "webmarin")
@NamedQueries({
    @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")})
public class Party implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "pty_id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long ptyId;
    @Basic(optional = false)
    @Column(name = "comment", nullable = false, length = 2147483647)
    private String comment;
    @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
    private List partyList;
    @ManyToMany(mappedBy = "partyList")
    private List partyList1;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "party")
    private Person person;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "party")
    private Company company;

    public Party() {
    }

    public Party(Long ptyId) {
        this.ptyId = ptyId;
    }

    public Party(Long ptyId, String comment) {
        this.ptyId = ptyId;
        this.comment = comment;
    }

    public Long getPtyId() {
        return ptyId;
    }

    public void setPtyId(Long ptyId) {
        this.ptyId = ptyId;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public List getPartyList() {
        return partyList;
    }

    public void setPartyList(List partyList) {
        this.partyList = partyList;
    }

    public List getPartyList1() {
        return partyList1;
    }

    public void setPartyList1(List partyList1) {
        this.partyList1 = partyList1;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (ptyId != null ? ptyId.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Party)) {
            return false;
        }
        Party other = (Party) object;
        if ((this.ptyId == null && other.ptyId != null) || (this.ptyId != null && !this.ptyId.equals(other.ptyId))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.martinsolaas.webmarin.jpa.Party[ptyId=" + ptyId + "]";
    }

}

В конце концов, вот схема SQL


CREATE SEQUENCE webmarin.party_pty_id_seq;

CREATE TABLE webmarin.party (
                pty_id BIGINT NOT NULL DEFAULT nextval('webmarin.party_pty_id_seq'),
                comment TEXT NOT NULL,
                CONSTRAINT pty_pk PRIMARY KEY (pty_id)
);


ALTER SEQUENCE webmarin.party_pty_id_seq OWNED BY webmarin.party.pty_id;

CREATE TABLE webmarin.company (
                cmp_pty_id BIGINT NOT NULL,
                name VARCHAR(255) NOT NULL,
                CONSTRAINT cmp_pk PRIMARY KEY (cmp_pty_id)
);


CREATE TABLE webmarin.party_relationship (
                parent_pty_id BIGINT NOT NULL,
                child_pty_id BIGINT NOT NULL,
                CONSTRAINT ptr_pk PRIMARY KEY (parent_pty_id, child_pty_id)
);


CREATE TABLE webmarin.person (
                prs_pty_id BIGINT NOT NULL,
                name VARCHAR(255),
                cellphone VARCHAR(55),
                officephone VARCHAR(55),
                CONSTRAINT prs_pk PRIMARY KEY (prs_pty_id)
);


ALTER TABLE webmarin.party_relationship ADD CONSTRAINT parent_party_party_relationship_fk
FOREIGN KEY (parent_pty_id)
REFERENCES webmarin.party (pty_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;

ALTER TABLE webmarin.party_relationship ADD CONSTRAINT child_party_party_relationship_fk
FOREIGN KEY (child_pty_id)
REFERENCES webmarin.party (pty_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;

ALTER TABLE webmarin.person ADD CONSTRAINT party_person_fk
FOREIGN KEY (prs_pty_id)
REFERENCES webmarin.party (pty_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;

ALTER TABLE webmarin.company ADD CONSTRAINT party_company_fk
FOREIGN KEY (cmp_pty_id)
REFERENCES webmarin.party (pty_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;

1 Ответ

0 голосов
/ 30 августа 2010

JPA 2.0 значительно улучшил поддержку производных идентификаторов с аннотациями Id и MapsId (они должны быть предпочтительны сейчас), которые вы можете использовать для XxxToOne ассоциаций. Ниже один из примеров взят из спецификации:

2.4.1.3 Примеры производных идентификаторов

...

Пример 4:

Родительский объект имеет простой первичный ключ:

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

Случай (а): Зависимый объект имеет один атрибут первичного ключа, который сопоставляется отношения приписывать. Первичный ключ MedicalHistory имеет тип 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;
    <b><i>@Id
    @Basic(optional = false)
    @Column(name = "prs_pty_id", nullable = false)
    @MapsId
    private Long prsPtyId;</i></b>
    @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;
    <b><i>@MapsId
    @JoinColumn(name = "prs_pty_id", referencedColumnName = "pty_id")
    @OneToOne
    private Party party;</i></b>
    ...
}

Ссылки * * тысяча пятьдесят-одна спецификация JPA 2.0 Раздел 2.4.1 «Первичные ключи, соответствующие производным удостоверениям» Раздел 2.4.1.1 «Спецификация производных идентификаторов» Раздел 2.4.1.2 «Отображение производных идентификаторов» Раздел 2.4.1.3 «Примеры производных идентификаторов» JPA Wikibook 3.2 Первичные ключи через отношения OneToOne 3.2.2 JPA 2.0

...