У меня есть следующие таблицы базы данных:
- сторона, с 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;