JPA Embeddables - 每个实体多个,所有实体单个 Table
JPA Embeddables - Multiple per Entity, Single Table for all Entities
我正在探索能够为多个实体存储特定地址(家庭、工作等)的选项,并让一个 table 保存所有地址,可能每个记录都有某种鉴别器.所有 table 的主键都是 UUID。
我正在使用 Spring Boot 2.3.6 和 JPA/Hibernate。
理想情况下,我希望为每个实体使用命名属性,而不是持有实体集合,因为这将使 DTO 映射和更新更容易。
如果在共享地址 table 中有每个实体的所有 NULL 值的条目和 属性 对,如果没有输入数据,这对我来说不是问题。
在伪代码中,我希望能够将实体定义为:
@Entity
class Person {
private Address homeAddress;
private Address workAddress;
}
@Entity
class Incident {
private Address incidentLocation;
}
@Entity
class Address {
private String street;
private String zip;
}
我研究过使用 JPA 选项,例如 @Embeddable
,我看到的选项是 a) 每个实体有一个可嵌入的(我想要多个)b) 使用 @CollectionTable
(我想要特定的命名属性)或 c) 使用 @AttributeOverride
,这意味着每个 属性.
在 table 中重复和重命名列
我也查看了 @JoinTable
和 @OneToMany
,但这同样适用于使用集合。
我觉得 @Embeddable
是我需要的,但需要能够为每个使用此类型(homeAddress、workAddress、incidentLocation)的 属性 指定一个鉴别器,以便地址 table 中的数据遵循格式
id type street zip
=========================================
UUID-1 HOME 1 Main St 30002
UUID-1 WORK 10 North St 30005
UUID-2 INCIDENT 5 West Ave 30008
作为奖励,我还希望(如果可以的话)能够创建一个 JpaRepository<Address>
允许我 query/update 独立于父实体的地址。
有了所有可用的选项,我想知道是否有人知道是否有办法实现我想要的,或者我是否必须走收集路线才能实现这一目标?谢谢
只有一堆属性(homeAddress
、workAddress
...),每个属性都引用 Address
作为一对一关系,并且在 setter 设置判别器。
感谢您的帮助,我认为结合了 crizzis 和后来 Jens 的建议,我得到了这个 JPA 实现。
@Data
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "address_type", discriminatorType = DiscriminatorType.STRING)
@Table(name = "address")
@TypeDef(name = UUID_CUSTOM_TYPE_NAME, typeClass = com.example.onetoone.domain.entity.UUIDCustomType.class, defaultForType = UUID.class)
public class AddressEntity
{
@Id
private UUID uuid;
private String address1;
private String city;
private String zip;
}
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@DiscriminatorValue("HOME")
public class HomeAddressEntity extends AddressEntity
{
@OneToOne(mappedBy = "homeAddress", fetch = FetchType.LAZY)
private PersonEntity personHome;
}
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@DiscriminatorValue("WORK")
public class WorkAddressEntity extends AddressEntity
{
@OneToOne(mappedBy = "workAddress", fetch = FetchType.LAZY)
private PersonEntity personWork;
}
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@DiscriminatorValue("INCIDENT")
public class IncidentAddressEntity extends AddressEntity
{
@OneToOne(mappedBy = "incidentAddress", fetch = FetchType.LAZY)
private IncidentEntity incident;
}
@Data
@Entity
@Table(name = "person")
@TypeDef(name = UUIDCustomType.UUID_CUSTOM_TYPE_NAME, typeClass = com.example.onetoone.domain.entity.UUIDCustomType.class, defaultForType = UUID.class)
public class PersonEntity
{
@Id
private UUID uuid;
private String name;
@OneToOne(cascade = CascadeType.ALL)
private HomeAddressEntity homeAddress;
@OneToOne(cascade = CascadeType.ALL)
private WorkAddressEntity workAddress;
}
@Data
@Entity
@Table(name = "incident")
@TypeDef(name = UUIDCustomType.UUID_CUSTOM_TYPE_NAME, typeClass = UUIDCustomType.class, defaultForType = UUID.class)
public class IncidentEntity
{
@Id
private UUID uuid;
private String name;
@OneToOne(cascade = CascadeType.ALL)
private IncidentAddressEntity incidentAddress;
}
UUID 类型定义如下,以防有人也需要它
public class UUIDCustomType extends AbstractSingleColumnStandardBasicType<UUID> implements LiteralType<UUID>
{
private static final long serialVersionUID = -540308541695243812L;
public static final String UUID_CUSTOM_TYPE_NAME = "uuid-custom";
public UUIDCustomType()
{
//
super(VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE);
}
@Override
public String getName()
{
return UUID_CUSTOM_TYPE_NAME;
}
@Override
public String objectToSQLString(UUID value, Dialect dialect) throws Exception
{
return StringType.INSTANCE.objectToSQLString(value.toString(), dialect);
}
}
这会在 MySQL 数据库中生成以下 DDL
CREATE TABLE `address` (
`address_type` varchar(31) NOT NULL,
`uuid` varchar(255) NOT NULL,
`address1` varchar(255) DEFAULT NULL,
`city` varchar(255) DEFAULT NULL,
`zip` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uuid`)
)
CREATE TABLE `person` (
`uuid` varchar(255) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`home_address_uuid` varchar(255) DEFAULT NULL,
`work_address_uuid` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uuid`),
KEY `FKoqa1ado547ntt2lc6ppx1lvr4` (`home_address_uuid`),
KEY `FKjc3ayqtduyx0l342uu9ti32hl` (`work_address_uuid`)
)
CREATE TABLE `incident` (
`uuid` varchar(255) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`incident_address_uuid` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uuid`),
KEY `FKosj0m7i6beq7ijwh68tjpfaa7` (`incident_address_uuid`)
)
alter table incident add constraint FKosj0m7i6beq7ijwh68tjpfaa7 foreign key (incident_address_uuid) references address (uuid)
alter table person add constraint FKoqa1ado547ntt2lc6ppx1lvr4 foreign key (home_address_uuid) references address (uuid)
alter table person add constraint FKjc3ayqtduyx0l342uu9ti32hl foreign key (work_address_uuid) references address (uuid)
如果 Address 与许多实体相关,最好的解决方案是使用从任何实体到 Address
的直接关系,如下所示:
@Entity
class Person {
@OneToOne
private Address homeAddress;
@OneToOne
private Address workAddress;
}
@Entity
class Incident {
@OneToOne
private Address incidentLocation;
}
@Entity
class Address {
private String street;
private String zip;
}
如果多次使用同一个地址,您也可以使用@ManyToOne。例如 homeAddress
和 workAddress
是相同的,并且您不想要可为空的字段。
在这种情况下,实体 Address
不知道属于哪个实体,但关系的第二方(Person
或 Incident
)知道哪个地址是自己的。在表 Person
和 Incident
中将是 ID 为 address
的列
我正在探索能够为多个实体存储特定地址(家庭、工作等)的选项,并让一个 table 保存所有地址,可能每个记录都有某种鉴别器.所有 table 的主键都是 UUID。
我正在使用 Spring Boot 2.3.6 和 JPA/Hibernate。
理想情况下,我希望为每个实体使用命名属性,而不是持有实体集合,因为这将使 DTO 映射和更新更容易。
如果在共享地址 table 中有每个实体的所有 NULL 值的条目和 属性 对,如果没有输入数据,这对我来说不是问题。
在伪代码中,我希望能够将实体定义为:
@Entity
class Person {
private Address homeAddress;
private Address workAddress;
}
@Entity
class Incident {
private Address incidentLocation;
}
@Entity
class Address {
private String street;
private String zip;
}
我研究过使用 JPA 选项,例如 @Embeddable
,我看到的选项是 a) 每个实体有一个可嵌入的(我想要多个)b) 使用 @CollectionTable
(我想要特定的命名属性)或 c) 使用 @AttributeOverride
,这意味着每个 属性.
我也查看了 @JoinTable
和 @OneToMany
,但这同样适用于使用集合。
我觉得 @Embeddable
是我需要的,但需要能够为每个使用此类型(homeAddress、workAddress、incidentLocation)的 属性 指定一个鉴别器,以便地址 table 中的数据遵循格式
id type street zip
=========================================
UUID-1 HOME 1 Main St 30002
UUID-1 WORK 10 North St 30005
UUID-2 INCIDENT 5 West Ave 30008
作为奖励,我还希望(如果可以的话)能够创建一个 JpaRepository<Address>
允许我 query/update 独立于父实体的地址。
有了所有可用的选项,我想知道是否有人知道是否有办法实现我想要的,或者我是否必须走收集路线才能实现这一目标?谢谢
只有一堆属性(homeAddress
、workAddress
...),每个属性都引用 Address
作为一对一关系,并且在 setter 设置判别器。
感谢您的帮助,我认为结合了 crizzis 和后来 Jens 的建议,我得到了这个 JPA 实现。
@Data
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "address_type", discriminatorType = DiscriminatorType.STRING)
@Table(name = "address")
@TypeDef(name = UUID_CUSTOM_TYPE_NAME, typeClass = com.example.onetoone.domain.entity.UUIDCustomType.class, defaultForType = UUID.class)
public class AddressEntity
{
@Id
private UUID uuid;
private String address1;
private String city;
private String zip;
}
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@DiscriminatorValue("HOME")
public class HomeAddressEntity extends AddressEntity
{
@OneToOne(mappedBy = "homeAddress", fetch = FetchType.LAZY)
private PersonEntity personHome;
}
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@DiscriminatorValue("WORK")
public class WorkAddressEntity extends AddressEntity
{
@OneToOne(mappedBy = "workAddress", fetch = FetchType.LAZY)
private PersonEntity personWork;
}
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@DiscriminatorValue("INCIDENT")
public class IncidentAddressEntity extends AddressEntity
{
@OneToOne(mappedBy = "incidentAddress", fetch = FetchType.LAZY)
private IncidentEntity incident;
}
@Data
@Entity
@Table(name = "person")
@TypeDef(name = UUIDCustomType.UUID_CUSTOM_TYPE_NAME, typeClass = com.example.onetoone.domain.entity.UUIDCustomType.class, defaultForType = UUID.class)
public class PersonEntity
{
@Id
private UUID uuid;
private String name;
@OneToOne(cascade = CascadeType.ALL)
private HomeAddressEntity homeAddress;
@OneToOne(cascade = CascadeType.ALL)
private WorkAddressEntity workAddress;
}
@Data
@Entity
@Table(name = "incident")
@TypeDef(name = UUIDCustomType.UUID_CUSTOM_TYPE_NAME, typeClass = UUIDCustomType.class, defaultForType = UUID.class)
public class IncidentEntity
{
@Id
private UUID uuid;
private String name;
@OneToOne(cascade = CascadeType.ALL)
private IncidentAddressEntity incidentAddress;
}
UUID 类型定义如下,以防有人也需要它
public class UUIDCustomType extends AbstractSingleColumnStandardBasicType<UUID> implements LiteralType<UUID>
{
private static final long serialVersionUID = -540308541695243812L;
public static final String UUID_CUSTOM_TYPE_NAME = "uuid-custom";
public UUIDCustomType()
{
//
super(VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE);
}
@Override
public String getName()
{
return UUID_CUSTOM_TYPE_NAME;
}
@Override
public String objectToSQLString(UUID value, Dialect dialect) throws Exception
{
return StringType.INSTANCE.objectToSQLString(value.toString(), dialect);
}
}
这会在 MySQL 数据库中生成以下 DDL
CREATE TABLE `address` (
`address_type` varchar(31) NOT NULL,
`uuid` varchar(255) NOT NULL,
`address1` varchar(255) DEFAULT NULL,
`city` varchar(255) DEFAULT NULL,
`zip` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uuid`)
)
CREATE TABLE `person` (
`uuid` varchar(255) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`home_address_uuid` varchar(255) DEFAULT NULL,
`work_address_uuid` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uuid`),
KEY `FKoqa1ado547ntt2lc6ppx1lvr4` (`home_address_uuid`),
KEY `FKjc3ayqtduyx0l342uu9ti32hl` (`work_address_uuid`)
)
CREATE TABLE `incident` (
`uuid` varchar(255) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`incident_address_uuid` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uuid`),
KEY `FKosj0m7i6beq7ijwh68tjpfaa7` (`incident_address_uuid`)
)
alter table incident add constraint FKosj0m7i6beq7ijwh68tjpfaa7 foreign key (incident_address_uuid) references address (uuid)
alter table person add constraint FKoqa1ado547ntt2lc6ppx1lvr4 foreign key (home_address_uuid) references address (uuid)
alter table person add constraint FKjc3ayqtduyx0l342uu9ti32hl foreign key (work_address_uuid) references address (uuid)
如果 Address 与许多实体相关,最好的解决方案是使用从任何实体到 Address
的直接关系,如下所示:
@Entity
class Person {
@OneToOne
private Address homeAddress;
@OneToOne
private Address workAddress;
}
@Entity
class Incident {
@OneToOne
private Address incidentLocation;
}
@Entity
class Address {
private String street;
private String zip;
}
如果多次使用同一个地址,您也可以使用@ManyToOne。例如 homeAddress
和 workAddress
是相同的,并且您不想要可为空的字段。
在这种情况下,实体 Address
不知道属于哪个实体,但关系的第二方(Person
或 Incident
)知道哪个地址是自己的。在表 Person
和 Incident
中将是 ID 为 address