我正试图按照JPA tutorial并使用ElementCollection记录员工电话号码:

PHONE (table)
OWNER_ID    TYPE      NUMBER
  1         home      792-0001
  1         work      494-1234
  2         work      892-0005

短版
我需要的是这样一门课:
@Entity
@Table(name="Phones")
public class PhoneId {
  @Id
  @Column(name="owner_id")
  long owner_id;

  @Embedded
  List<Phone> phones;
}

将每个人的电话号码存储在一个集合中。
长版
我遵循教程代码:
@Entity
@Table(name="Phones")
public class PhoneId {
  @Id
  @Column(name="owner_id")
  long owner_id;

  @ElementCollection
  @CollectionTable(
    name="Phones",
    joinColumns=@JoinColumn(name="owner_id")
  )
  List<Phone> phones = new ArrayList<Phone>();
}

@Embeddable
class Phone {
  @Column(name="type")
  String type = "";
  @Column(name="number")
  String number = "";

  public Phone () {}
  public Phone (String type, String number)
    { this.type = type; this.number = number; }
}

有一点不同,我只保留一张桌子。我试图使用以下代码将记录添加到此表:
public static void main (String[] args) {
  EntityManagerFactory entityFactory =
     Persistence.createEntityManagerFactory("Tutorial");
  EntityManager entityManager = entityFactory.createEntityManager();

  // Create new entity
  entityManager.getTransaction().begin();
  Phone ph = new Phone("home", "001-010-0100");
  PhoneId phid = new PhoneId();
  phid.phones.add(ph);
  entityManager.persist(phid);
  entityManager.getTransaction().commit();

  entityManager.close();
}

但它总是抛出异常
内部异常:org.postgresql.util.PSQLException:错误:空
列“type”中的值违反了非空约束详细信息:失败
行包含(0,null,null)。错误代码:0呼叫:插入电话
(所有者id)值(?)bind=>[1参数绑定]查询:
InsertObjectQuery(tutorial.Phone1@162e295)
我做错了什么?

最佳答案

遗憾的是,我认为你只保留一张桌子的细微差别就是这里的问题。
看看PhoneId类的声明(我建议最好将其称为PhoneOwner或类似的声明):

@Entity
@Table(name="Phones")
public class PhoneId {

当您声明类是映射到某个表的实体时,您将生成一组断言,其中两个断言在这里特别重要。首先,对于实体的每个实例,表中都有一行,反之亦然。其次,对于实体的每个标量字段,表中都有一列,反之亦然。这两者都是对象关系映射思想的核心。
但是,在您的模式中,这两个断言都不成立。在你提供的数据中:
OWNER_ID    TYPE      NUMBER
  1         home      792-0001
  1         work      494-1234
  2         work      892-0005

有两行对应于owner_id1的实体,违反了第一个断言。有TYPENUMBER列未映射到实体中的字段,违反了第二个断言。
(要清楚的是,您声明的Phone类或phones字段没有任何错误-只有PhoneId实体)
因此,当JPA提供者试图将PhoneId的实例插入数据库时,它会遇到问题。因为TYPE中的NUMBERPhoneId列没有映射,所以当它为插入生成SQL时,它不包括它们的值。这就是为什么您看到错误-提供者写入INSERT INTO Phones (owner_id) VALUES (?),PostgreSQL将其视为INSERT INTO Phones (owner_id, type, number) VALUES (?, null, null),但被拒绝。
即使您成功地将一行插入到该表中,从中检索对象也会遇到问题。假设您请求使用PhoneId1的owner_id实例。提供程序将编写总计select * from Phones where owner_id = 1的SQL,并且它希望只找到一行,它可以映射到一个对象。但它会找到两排!
恐怕解决方法是使用两个表,一个用于PhoneId,另一个用于PhonePhoneId的表格将非常简单,但对于JPA机器的正确操作是必要的。
假设将PhoneId重命名为PhoneOwner,则表需要如下所示:
create table PhoneOwner (
    owner_id integer primary key
)

create table Phone (
    owner_id integer not null references PhoneOwner,
    type varchar(255) not null,
    number varchar(255) not null,
    primary key (owner_id, number)
)

(我将(owner_id, number)作为Phone的主键,假设一个所有者可能拥有给定类型的多个数字,但永远不会在两个类型下记录一个数字。如果能更好地反映您的领域,您可能更喜欢(owner_id, type)
这些实体是:
@Entity
@Table(name="PhoneOwner")
public class PhoneOwner {
    @Id
    @Column(name="owner_id")
    long id;

    @ElementCollection
    @CollectionTable(name = "Phone", joinColumns = @JoinColumn(name = "owner_id"))
    List<Phone> phones = new ArrayList<Phone>();
}

@Embeddable
class Phone {
    @Column(name="type", nullable = false)
    String type;
    @Column(name="number", nullable = false)
    String number;
}

现在,如果您真的不想为PhoneOwner引入一个表,那么您可以使用一个视图退出它。这样地:
create view PhoneOwner as select distinct owner_id from Phone;

据JPA提供者所知,这是一个表,它将支持读取数据所需的查询。
但是,它不支持插入。如果需要为当前不在数据库中的所有者添加电话,则需要绕到后面,直接在Phone中插入一行。不是很好。

关于java - JPA使用ElementCollection映射多行,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17244804/

10-10 11:13