如何在同一数据库表上映射两个 JPA 或休眠实体

2022-09-01 11:38:40

在我们的项目中,我们有一个实体“餐厅”,其中包含近30个字段(有些与其他实体有关系)。因此,每次我们需要一个“Restaurant”对象时,即使对于几个字段,所有其他字段也会被检索。这会影响性能。因此,在 HBM 文件中,我们编写了两个类,它们都指向相同的物理类和相同的数据库表,如下所示。

=== restaurant.hbm.xml ===
<!-- Light Weight Version -->
<class name="com.raj.model.Restaurant" table="RESTAURANTS" entity-name="RestaurantLite" 
                dynamic-update="false" dynamic-insert="false">
<cache usage="read-only"/>
     <!-- few basic properties and relationships -->
</class>

<!-- Restaurant -->
<class name="com.raj.model.Restaurant" table="RESTAURANTS" entity-name="Restaurant">
     <!-- all properties and relationships -->
</class>

在其中一个DAO实现中,我们使用的是标准,它采用“RestaurantLite”并返回餐厅列表,如下所示。

Criteria criteria = session.createCriteria("RestaurantLite");

   // criteria related stuff

return new LinkedHashSet<Restaurant>(criteria.list());

现在我们要删除所有 hbm 文件并使用注释。那么,如何使用entites的注释来完成相同的操作呢?我们是否需要创建一个额外的类“RestaurantLite”?如果这样,上述条件如何返回“餐厅”对象?


答案 1

总而言之,以下映射将演示如何将多个实体映射到同一个数据库表:

@Entity(name = "Post")
public class Post {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    private String name;

    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

@Entity(name = "PostSummary")
@Table(name = "Post")
@Immutable
public class PostSummary {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

@Entity(name = "UpdatablePostSummary")
@Table(name = "Post")
@DynamicUpdate
public class UpdatablePostSummary {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

而Hibernate将正常工作:

@Test
public void testOneTableMultipleEntities() {
    doInTransaction(session -> {
        Post post = (Post) session.get(Post.class, 1L);
        PostSummary postSummary = (PostSummary) session.get(PostSummary.class, 1L);
        UpdatablePostSummary updatablePostSummary = (UpdatablePostSummary) session.get(UpdatablePostSummary.class, 1L);
        assertEquals(post.getName(), postSummary.getName());
        assertEquals(post.getName(), updatablePostSummary.getName());
        updatablePostSummary.setName("Hibernate Master Class Tutorial.");
    });
}
  1. 它只是原始实体的只读视图,因此我用 注释了它。PostSummary@Immutable

  2. 标记为,因此您也可以从此 View 实体传播更改。UpdatablePostSummary@DynamicUpdate

此测试也可以在 GitHub 上找到。


答案 2

我有一个类似的问题。我有两个类,基类 DocumentContent 和一个派生类 DocumentContentWithData

DocumentContent {
    private Long documentContentId;
    private InputStream contentStream;
    private File importFile;
    ... and several other attributes
}

DocumentContentWithData extends DocumentContent {}

我为两个类定义了休眠映射,为类 DocumentContent 定义了一个没有 contentStream 的映射,为 DocumentContentWithData 类定义了一个与 contentStream 的映射。

使用类 DocumentContentWithData 保存数据,并且还可以使用 Session.get(Class, ID) 方法获取这两个类的实例。

但是,以下代码中的查询返回两个实体,一个作为 DocumentContent 的实例,另一个作为 DocumentContentWithData 的实例,尽管数据库中只有一个 enter 与 importFile

CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<DocumentContent> criteriaQuery = criteriaBuilder.createQuery(DocumentContent.class);
Root<DocumentContent> root = criteriaQuery.from(DocumentContent.class);
criteriaQuery.select(root).where(criteriaBuilder.equal(root.get("importFile"), importFile));
Query<DocumentContent> query = session.createQuery(criteriaQuery);
List<DocumentContent> contentList = query.getResultList();

有人可以解释这种行为吗?当我不从 DocumentContent 派生 DocumentContentWithData 类并复制所有属性时,它就可以工作了。然而,这不是一个很好的解决方案。


推荐