java - JPA 使用 ElementCollection 映射多行

标签 java postgresql jpa

我正在尝试关注 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; }
}

略有不同,我只保留一张 table 。我尝试使用以下代码向该表中添加记录:

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

但它一直抛出异常

Internal Exception: org.postgresql.util.PSQLException: ERROR: null value in column "type" violates not-null constraint Detail: Failing row contains (0, null, null). Error Code: 0 Call: INSERT INTO Phones (owner_id) VALUES (?) bind => [1 parameter bound] Query: InsertObjectQuery(tutorial.Phone1@162e295)

我做错了什么?

最佳答案

遗憾的是,我认为您只保留一张 table 的细微差别是这里的问题。

查看 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_id 为 1 的实体,这违反了第一个断言。 TYPENUMBER 列未映射到实体中的字段,这违反了第二个断言。

(需要明确的是,您对 Phone 类或 phones 字段的声明没有任何问题 - 只是 PhoneId 实体)

因此,当您的 JPA 提供程序尝试将 PhoneId 的实例插入数据库时​​,它会遇到麻烦。因为 PhoneId 中的 TYPENUMBER 列没有映射,所以当它为插入生成 SQL 时,它不包括值他们。这就是您看到错误的原因 - 提供者写入 INSERT INTO Phones (owner_id) VALUES (?),PostgreSQL 将其视为 INSERT INTO Phones (owner_id, type, number) VALUES ( ?, null, null),被拒绝。

即使您设法向该表中插入一行,您在从中检索对象时也会遇到麻烦。假设您要求使用 owner_id 1 的 PhoneId 实例。提供者将编写相当于 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) 能更好地反射(reflect)您的域,您可能更喜欢。)

然后实体是:

@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 引入一个表,那么您可以使用 View 来摆脱它。像这样:

create view PhoneOwner as select distinct owner_id from Phone;

就 JPA 提供者而言,这是一个表,它将支持读取数据所需执行的查询。

但是,它不支持插入。如果您需要为当前不在数据库中的所有者添加电话,则需要绕过后面并直接在 Phone 中插入一行。不太好。

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

相关文章:

java - 为什么 Class.class.getResourceAsStream(String) 总是返回 null?

java - @NotNull 在 JPA/Hibernate 中默认

java - 如何引用 Hibernate @ManyToOne 关联而不从数据库中加载它

java - hibernate 继承在父级之后保存子级

java - 如何强制 Apache 的 CloseableHttpClient 使用 TLSv1.2?

Certificate Authority 不在 JRE 时的 Java SSL 连接解决方​​案

hibernate - 了解 Hibernate/JPA 对 findAll 和 findOne 的不同行为

postgresql - 如何在postgresql中将bigint(以毫秒为单位的时间戳)值写为时间戳

node.js - 启动应用程序时使用 pg-promise 验证数据库连接

sql - 使用 lower() 索引的 Postgres 搜索