【发布时间】:2010-10-29 21:01:20
【问题描述】:
我有一个数据库视图,它产生一个没有真正主键的结果集。我想使用 Hibernate/Persistence 将此结果集映射到 Java 对象。当然,因为没有PK,我不能用@Id装饰任何字段。
部署时,Hibernate 抱怨缺少 @Id。我该如何解决这个问题?
【问题讨论】:
标签: java hibernate primary-key hibernate-annotations
我有一个数据库视图,它产生一个没有真正主键的结果集。我想使用 Hibernate/Persistence 将此结果集映射到 Java 对象。当然,因为没有PK,我不能用@Id装饰任何字段。
部署时,Hibernate 抱怨缺少 @Id。我该如何解决这个问题?
【问题讨论】:
标签: java hibernate primary-key hibernate-annotations
如果存在使行唯一的列组合,请围绕列组合建模主键类。如果没有,那么您基本上是不走运了——但是您应该重新检查视图的设计,因为它可能没有意义。
有几种不同的方法:
@Entity
public class RegionalArticle implements Serializable {
@Id
public RegionalArticlePk getPk() { ... }
}
@Embeddable
public class RegionalArticlePk implements Serializable { ... }
或者:
@Entity
public class RegionalArticle implements Serializable {
@EmbeddedId
public RegionalArticlePk getPk() { ... }
}
public class RegionalArticlePk implements Serializable { ... }
这是描述类似问题的帖子:http://www.theserverside.com/discussions/thread.tss?thread_id=22638
【讨论】:
【讨论】:
与其在 Hibernate 中搜索解决方法,不如在数据库视图中添加虚拟 id 可能更容易。假设我们有 PostgreSQL 视图,其中有两列并且它们都不是唯一的(并且没有主键,因为 Postgres 不允许对视图进行 PK 或任何其他约束)例如。
| employee_id | project_name |
|:------------|:-------------|
| 1 | Stack01 |
| 1 | Jira01 |
| 1 | Github01 |
| 2 | Stack01 |
| 2 | Jira01 |
| 3 | Jira01 |
------------------------------
由以下查询表示:
CREATE OR REPLACE VIEW someschema.vw_emp_proj_his AS
SELECT DISTINCT e.employee_id,
pinf.project_name
FROM someschema.project_info pinf
JOIN someschema.project_employee pe ON pe.proj_id = pinf.proj_id
JOIN someschema.employees e ON e.employee_id = pe.emloyee_id
我们可以使用 row_number() 添加虚拟 id:
SELECT row_number() OVER (ORDER BY subquery.employee_id) AS row_id
就像这个例子:
CREATE OR REPLACE VIEW someschema.vw_emp_proj_his AS
SELECT row_number() OVER (ORDER BY subquery.employee_id) AS row_id,
subquery.employee_id,
subquery.project_name
FROM
(SELECT DISTINCT e.employee_id,
pinf.project_name
FROM someschema.project_info pinf
JOIN someschema.project_employee pe ON pe.proj_id = pinf.proj_id
JOIN someschema.employees e ON e.employee_id = pe.emloyee_id ) subquery;
表格将如下所示:
| row_id | employee_id | project_name |
|:------------|:------------|:-------------|
| 1 | 1 | Stack01 |
| 2 | 1 | Jira01 |
| 3 | 1 | Github01 |
| 4 | 2 | Stack01 |
| 5 | 2 | Jira01 |
| 6 | 3 | Jira01 |
-------------------------------------------
现在我们可以在 JPA/Hibernate/Spring Data 中使用 row_id 作为 @Id:
@Id
@Column(name = "row_id")
private Integer id;
如示例:
@Entity
@Table(schema = "someschema", name = "vw_emp_proj_his")
public class EmployeeProjectHistory {
@Id
@Column(name = "row_id")
private Integer id;
@Column(name = "employee_id")
private Integer employeeId;
@Column(name = "project_name")
private String projectName;
//Getters, setters etc.
}
【讨论】:
rownum 和row number() over 在缓存中的表现非常糟糕(相同的查询,不同的参数,然后不同的结果将具有相同的 ID...)
这是一个有 2 个键作为“Id”键的示例:https://gist.github.com/3796379
【讨论】:
您可以检查逻辑上是否存在 id 并相应地提供映射信息。 Hibernate 不会检查数据库是否存在已定义的主键。
【讨论】:
修改用于创建视图的选择:
SELECT
ROWNUM ID, -- or use nextval of some sequence
-- other columns
FROM
TABLE
并将“ID”映射为主键。
【讨论】:
您可以按如下方式使用@EmbeddedId:
@EmbeddedId
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
【讨论】:
根据 Hibernate 用户指南,建议使用 pk,因此即使您正在使用数据库端不包含 pk 的现有项目,您也可以将 pk 列添加到相关的数据库表并映射它使用 Java 实体。
hibernate 的建议是这样的:
我们建议您在持久类上声明一致命名的标识符属性,并使用包装器(即非原始)类型(例如 Long 或 Integer)。
更多pk和Hibernate机制详情请访问here
【讨论】:
虽然不完全符合您的要求,但我使用了一个小技巧。让查询选择“rownum”并将“rownum”定义为模型中的 ID 列。这将有效地使每一行对 Hibernate 都是唯一的。
【讨论】: