【问题标题】:What is the best solution to handle ID table in JPA?在 JPA 中处理 ID 表的最佳解决方案是什么?
【发布时间】:2016-01-05 21:55:37
【问题描述】:

您好,对于我的工作,我正在尝试正确处理一个用于保持其他表之间一致性的表。
架构师决定在 Oracle Database 12c 上提供此解决方案:

Relational architecture

由于数据量大,每个实体上都有一个由分区列控制的分区。实体 id 由每个实体类型生成。

我尝试使用 JPA 实体继承来简化数据库中的插入。这段代码是我找到的最佳解决方案:

@Entity
@Table(name="ID_TABLE")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="Entity_Type", discriminatorType=DiscriminatorType.STRING)
public abstract class IdTable implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    protected IdTablePK id;
}

@Embeddable
public class IdTablePK implements Serializable {
    @Column(name="Partition_col")
    protected short partitionCol;

    @Column(name="Entity_ID")
    protected long entityID;

    @Column(name="Version_ID")
    protected short versionID;

    @Column(name="Entity_Type")
    protected String entityType;
}

@Entity
@DiscriminatorValue("01")
@Table(name = "ENTITY1")
@PrimaryKeyJoinColumns({
    @PrimaryKeyJoinColumn(name="Partition_col1", referencedColumnName="Partition_col"),
    @PrimaryKeyJoinColumn(name="Entity_ID1", referencedColumnName="Entity_ID"),
    @PrimaryKeyJoinColumn(name="Version_ID1", referencedColumnName="Version_ID"),
    @PrimaryKeyJoinColumn(name="Entity_Type1", referencedColumnName="Entity_Type")
})
public class Entity1 extends IdTable{
...
}

问题在于,由于 JPA 自动进行内部连接,我预计 select 命令的性能会很差:

select
    entity1.Partition_col1
    entity1.Entity_ID1
    entity1.Version_ID1
    entity1.Entity_Type1
    entity1.Data1
from
    entity1
inner join
    table_ID 
    on entity1.Partition_col=table_ID.Partition_col
        entity1.Entity_ID=table_ID.Entity_ID
        entity1.Version_ID=table_ID.Version_ID
        entity1.Entity_Type=table_ID.Entity_Type
where
    entity1.Partition_col1=?
    and entity1.Entity_ID1=?
    and entity1.Version_ID1=?
    and entity1.Entity_Type1=?

我担心这种关系的表现是否正确?有没有更好的解决方案或任何方法来避免在 select 命令上加入?

**EDIT** : Chris 给了我一个想法。如果 IdTable 实体的所有相关实体都像这样加入会怎样?

@Entity
@Table(name="ID_TABLE")
public class IdTable implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    protected IdTablePK id;

    @OneToOne(fetch=FetchType.LAZY, cascade = CascadeType.PERSIST, optional = true)
    @JoinColumns({
        @JoinColumn(name="Partition_col1", referencedColumnName="Partition_col"),
        @JoinColumn(name="Entity_ID1", referencedColumnName="Entity_ID"),
        @JoinColumn(name="Version_ID1", referencedColumnName="Version_ID"),
        @JoinColumn(name="Entity_Type1", referencedColumnName="Entity_Type")
    })
    private Entity1 e1;
    ...
}

@Embeddable
public class IdTablePK implements Serializable {
    @Column(name="Partition_col")
    protected short partitionCol;

    @Column(name="Entity_ID")
    protected long entityID;

    @Column(name="Version_ID")
    protected short versionID;

    @Column(name="Entity_Type")
    protected String entityType;
}

@Entity
@Table(name = "ENTITY1")
public class Entity1 implements Serializable{
    @EmbeddedId
    protected Entity1PK id;
    ...
}

@Embeddable
public class Entity1PK implements Serializable {
    @Column(name="Partition_col1")
    protected short partitionCol1;

    @Column(name="Entity_ID1")
    protected long entityID1;

    @Column(name="Version_ID1")
    protected short versionID1;

    @Column(name="Entity_Type1")
    protected String entityType1;
}

IdTable 插入到Entity1 之前,在请求Entity1 时没有直接链接到IdTable。

现在,是否可以使用Entity1序列来填充IdTable中的Entity_ID?

【问题讨论】:

    标签: java oracle jpa


    【解决方案1】:

    使用您第二次尝试的 Entity1PK(您可以删除注释),尝试:

    @Entity
    @Table(name="ID_TABLE")
    @IdClass(Entity1PK.class)
    public class IdTable implements Serializable {
        private static final long serialVersionUID = 1L;
        @Id
        @OneToOne(fetch=FetchType.LAZY, cascade = CascadeType.PERSIST, optional = true)
        @JoinColumns({
            @JoinColumn(name="Partition_col1", referencedColumnName="Partition_col"),
            @JoinColumn(name="Entity_ID1", referencedColumnName="Entity_ID"),
            @JoinColumn(name="Version_ID1", referencedColumnName="Version_ID"),
            @JoinColumn(name="Entity_Type1", referencedColumnName="Entity_Type")
        })
        private Entity1 e1;
        ..
     }
    
    @Entity
    @Table(name = "ENTITY1")
    @IdClass(Entity1PK.class)
    public class Entity1 {
    
      @Id
      @Column(name="Partition_col1")
      protected short partitionCol1;
    
      @Id
      @GeneratedValue(strategy=GenerationType.TABLE)
      @Column(name="Entity_ID1")
      protected long entityID1;
    
      @Id
      @Column(name="Version_ID1")
      protected short versionID1;
    
      @Id
      @Column(name="Entity_Type1")
      protected String entityType1;
    })
    

    将任何形式的排序注释放在要使用排序的任何字段上。

    【讨论】:

    • 您的解决方案很优雅,但这不是我想要的。这里的结果是,JPA 在加载 Entity1 之前在 IdTable 中寻找,并且在 Entity1 之前没有持久化 IdTable 实体。问题是我们想使用 Entity1 序列,但之前我们必须坚持 IdTable。我正在编辑我的问题以添加我们所做的第二次尝试。
    • EclipseLink 将为您保留 IdTable,如果这是您正在寻找的。您只需在映射上设置 cascade.Persist 级联选项,这将导致找到引用的 IdTable 并将其插入 Entity1,并使用 IdTable 中的值填充 Entity1 字段。我不清楚你在问什么,但是如果你想让Entity1中设置的值填充IdTable,只需使用上面的模型作为示例切换参考方向,本质上只是重命名上面的两个类。
    • 我花了一段时间才了解这个项目。就像我说的,我们必须插入 Entity1,它的 id 由它的 sequencer 设置,但之前必须设置 IdTable。另外,在读取 Entity1 时,我们不想以任何方式读取 IdTable。而且,EclipseLink 在这个项目中是不允许的……看起来像死路一条!我认为我离上次编辑的最佳解决方案不远了,我正在寻找在 JPA(或 JPQL)中获取 Entity1 定序器值的最佳方法,我正在尝试这个:link
    • 我不确定你指的是什么 EclipseLink 是一个死胡同,但这只是 JPA 提供者的一个例子;我最喜欢的一个,因为我参与了开源项目。我应该说 JPA 作为任何提供者都可以工作。
    • 我永远不会对 EclipseLink 这么说。我说的是我正在寻找的解决方案,但我有限制。开源项目团队所做的工作给我留下了深刻的印象,如果我误会了自己,我很抱歉。关于解决方案,我们最终将所有 Entity 放入 TableID 以保持级联模式,然后我们只读取 Entity