我正在尝试关注 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 的实体,这违反了第一个断言。 TYPE
和 NUMBER
列未映射到实体中的字段,这违反了第二个断言。
(需要明确的是,您对 Phone
类或 phones
字段的声明没有任何问题 - 只是 PhoneId
实体)
因此,当您的 JPA 提供程序尝试将 PhoneId
的实例插入数据库时,它会遇到麻烦。因为 PhoneId
中的 TYPE
和 NUMBER
列没有映射,所以当它为插入生成 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
,一张用于Phone
。 PhoneId
的表非常简单,但它对于 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/