Java JPA Embeddedables-每个实体有多个,所有实体有一个表

Java JPA Embeddedables-每个实体有多个,所有实体有一个表,java,hibernate,jpa,hibernate-mapping,Java,Hibernate,Jpa,Hibernate Mapping,我正在探索能够为多个实体存储特定地址(家庭、工作等)的选项,并拥有一个包含所有地址的表,可能每个记录都有某种鉴别器。所有表的主键都是UUID 我正在使用SpringBoot2.3.6和JPA/Hibernate 理想情况下,我希望使用每个实体的命名属性,而不是持有一个实体集合,因为这将使DTO映射和更新更容易 如果共享地址表中的条目中没有输入数据,则每个实体和属性对的值都为空,这对我来说不是问题 在伪代码中,我希望能够将实体定义为: @Entity class Person { priva

我正在探索能够为多个实体存储特定地址(家庭、工作等)的选项,并拥有一个包含所有地址的表,可能每个记录都有某种鉴别器。所有表的主键都是UUID

我正在使用SpringBoot2.3.6和JPA/Hibernate

理想情况下,我希望使用每个实体的命名属性,而不是持有一个实体集合,因为这将使DTO映射和更新更容易

如果共享地址表中的条目中没有输入数据,则每个实体和属性对的值都为空,这对我来说不是问题

在伪代码中,我希望能够将实体定义为:

@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选项,例如
@embeddedable
,我看到的选项是a)每个实体有一个可嵌入的(我想要多个)b)使用
@CollectionTable
(我想要特定的命名属性)或c)使用
@AttributeOverride
,这意味着表中每个属性的重复列和重命名列

我还研究了
@JoinTable
@OneToMany
,但这也是为了使用集合

我觉得
@embeddeble
是我所需要的,但需要能够为使用此类型(homeAddress、workAddress、incidentLocation)的每个属性指定一个鉴别器,以便地址表中的数据遵循以下格式

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
,允许我独立于父实体查询/更新地址


有了所有可用的选项,我想知道是否有人知道是否有一种方法可以实现我想要的,或者我是否必须沿着收集路线来实现这一点?感谢您,

只要有一组属性(
家庭地址
工作地址
…),每个属性以一对一的关系引用
地址
,并在setter中设置鉴别器。

只要有一组属性(
家庭地址
工作地址
…)每一个引用一个
地址作为一对一的关系,并在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()
    {

        // https://stackoverflow.com/questions/42559938/hibernate-uuid-with-postgresql-and-sql-server
        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);

    }
}

多亏了他的帮助,我认为克里兹和最近詹斯的建议结合起来,使我实现了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()
    {

        // https://stackoverflow.com/questions/42559938/hibernate-uuid-with-postgresql-and-sql-server
        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);

    }
}

如果地址与多个实体相关,最好的解决方案是使用一个从任何实体到
地址的直接关系,如下所示:

    @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。例如,
homemaddress
workAddress
是相同的,并且您希望不存在可为空的字段


在这种情况下,实体
地址
不知道属于哪个实体,但关系的第二方(
事件
)知道哪个地址是自己的。在表
Person
Incident
中将列为id为address的列

如果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。例如,
homemaddress
workAddress
是相同的,并且您希望不存在可为空的字段


在这种情况下,实体
地址
不知道属于哪个实体,但关系的第二方(
事件
)知道哪个地址是自己的。在表
Person
Incident
中将列为id为address的列

能否请您提供架构的ER图。如果您将外键放在父表中,并让
address
拥有自己的id,则更容易实现。然后可以使用简单的单向
@OneToOne
。对于
类型
列,您可以将其作为常规属性映射到
地址
中,也可以为
地址
es引入一个
单表
继承层次结构,其中
类型
作为鉴别器。如果这对您不起作用,那么我恐怕您需要使用集合(可能带有
@MapKeyColumn
的映射更有意义)。另外,如果您需要一个存储库,那么您需要的是
@OneToMany
,而不是
@ElementCollection
@crizzis-这看起来可能是一个很有前途的解决方案。我试试看,然后再报告。谢谢请提供架构的ER图。如果您将外键放在父表中,并让
Address
拥有自己的ID,那么实现起来会更容易。然后可以使用简单的单向
@OneToOne
。对于
类型
列,您可以将其作为常规属性映射到
地址
中,也可以为
地址
es引入一个
单表
继承层次结构,其中
类型
作为鉴别器。如果这对您不起作用,那么我恐怕您需要使用集合(可能带有
@MapKeyColumn
的映射更有意义)。另外,如果您需要一个存储库,那么您需要的是
@OneToMany
,而不是
@ElementCollection
@crizzis-这看起来可能是一个很有前途的解决方案。我试试看,然后再报告。谢谢