如何在同一个数据库表上映射两个 JPA 或 Hibernate 实体
Posted
技术标签:
【中文标题】如何在同一个数据库表上映射两个 JPA 或 Hibernate 实体【英文标题】:How to map two JPA or Hibernate entities on the same database table 【发布时间】:2015-05-14 11:39:15 【问题描述】:在我们的项目中,我们有一个实体“餐厅”,其中包含近 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 实现中,我们使用的 Criteria 采用“RestaurantLite”并返回如下所示的餐厅列表。
Criteria criteria = session.createCriteria("RestaurantLite");
// criteria related stuff
return new LinkedHashSet<Restaurant>(criteria.list());
现在我们要删除所有 hbm 文件并使用注释。那么如何使用实体的注释来完成相同的操作呢?我们是否需要创建一个额外的类“RestaurantLite”?如果是这样,上述条件如何返回 'Restaurant' 对象??
【问题讨论】:
这会影响性能。到什么程度?你量过吗?正如 Hibernate 文档所指出的:优化行读取比优化列读取重要得多。但是,仅加载类的某些属性在极端 情况下可能有用。例如,当旧表具有 数百 列并且无法改进数据模型时。 docs.jboss.org/hibernate/orm/3.3/reference/en/html/… @Alan Hay 我们有一个数据库,每个表都有数千条记录。大多数操作都需要上面提到的类。因此,每次检索每家餐厅的所有属性肯定会影响性能。因此,我们在 hbm 文件中提出了该解决方案。 所有数据库都有数千、几万、几百万行。正如 Hibernate 文档指出的那样,列优化很少值得。 en.wikipedia.org/wiki/Program_optimization#When_to_optimize @AlanHay 是的。但是我们不能更改遗留代码。我们必须使用注释并删除 hbm 文件.. 【参考方案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.");
);
PostSummary
只是原始实体的只读视图,因此我用@Immutable
对其进行了注释。
UpdatablePostSummary
标有@DynamicUpdate
,因此您也可以从该视图实体传播更改。
GitHub 也提供此测试。
【讨论】:
如果更新UpdatablePostSummary
会发生什么? Post
实体是否会更新为? (我的意思是在 Hibernate 内部,显然在数据库中这是同一张表)。
在 Hibernate 中,Post
实体必须手动刷新,否则 Hibernate 不会自动刷新。【参考方案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 的实例,尽管只有一个带有 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 并复制它的所有属性时。然而,这不是一个很好的解决方案。
【讨论】:
这是一个问题,但您已将其作为答案发布。而是将其作为新问题发布。【参考方案3】:您必须在您的班级上添加注释@Entity,@Table(name="RESTAURANT"),添加注释并将hbm文件中的详细映射替换为 .
这里有一个完整的例子:http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/
【讨论】:
这适用于“餐厅”类。 'RestaurantLite'怎么样??以上是关于如何在同一个数据库表上映射两个 JPA 或 Hibernate 实体的主要内容,如果未能解决你的问题,请参考以下文章