【问题标题】:Hibernate/persistence without @Id没有@Id的休眠/持久性
【发布时间】:2010-10-29 21:01:20
【问题描述】:

我有一个数据库视图,它产生一个没有真正主键的结果集。我想使用 Hibernate/Persistence 将此结果集映射到 Java 对象。当然,因为没有PK,我不能用@Id装饰任何字段。

部署时,Hibernate 抱怨缺少 @Id。我该如何解决这个问题?

【问题讨论】:

    标签: java hibernate primary-key hibernate-annotations


    【解决方案1】:

    如果存在使行唯一的列组合,请围绕列组合建模主键类。如果没有,那么您基本上是不走运了——但是您应该重新检查视图的设计,因为它可能没有意义。

    有几种不同的方法:

    @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://docs.jboss.org/ejb3/app-server/HibernateAnnotations/reference/en/html_single/index.html#d0e1517

    这是描述类似问题的帖子:http://www.theserverside.com/discussions/thread.tss?thread_id=22638

    【讨论】:

    • 但是,如果这样的复合列集包含一个可为空的列,我没有 PK 但我定义了诸如 UNIQUE 约束或索引之类的东西,Hibernate 如何处理呢?听起来像一个悖论:具有可为空值的“@Id”不能存在,并且如果没有定义“@Id”就无法保存。
    • > 视图没有意义。我同意。存储过程返回的投影是完全有效的视图,没有标识符。
    【解决方案2】:

    对于每个实体,您必须至少指定以下一项:

    • 一个@Id
    • 多个@Id 和一个@IdClass(用于复合主键)
    • @EmbeddedId

    所以也许您可以创建一个包含多个字段的复合主键?

    【讨论】:

      【解决方案3】:

      与其在 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.
      
      }
      

      【讨论】:

      • 除非你真的需要一个唯一的 id,否则很难看出这比上面的 embededId 更清晰。是否还有性能方面的考虑?
      • 请注意rownumrow number() over 在缓存中的表现非常糟糕(相同的查询,不同的参数,然后不同的结果将具有相同的 ID...)
      • 8.0 以下的 MySQL 版本没有可用的 rownum 函数。但是可以使用以下方法 [stackoverflow.com/questions/1895110/row-number-in-mysql/… 作为替代方案。以防万一有人在寻找替代品。
      【解决方案4】:

      这是一个有 2 个键作为“Id”键的示例:https://gist.github.com/3796379

      【讨论】:

        【解决方案5】:

        您可以检查逻辑上是否存在 id 并相应地提供映射信息。 Hibernate 不会检查数据库是否存在已定义的主键。

        【讨论】:

          【解决方案6】:

          修改用于创建视图的选择:

          SELECT
             ROWNUM ID, -- or use nextval of some sequence
             -- other columns
          FROM
            TABLE
          

          并将“ID”映射为主键。

          【讨论】:

            【解决方案7】:

            您可以按如下方式使用@EmbeddedId:

            @EmbeddedId
            public Id getId() {
                return id;
            }
            
            public void setId(Id id) {
                this.id = id;
            }
            

            【讨论】:

              【解决方案8】:

              根据 Hibernate 用户指南,建议使用 pk,因此即使您正在使用数据库端不包含 pk 的现有项目,您也可以将 pk 列添加到相关的数据库表并映射它使用 Java 实体。

              hibernate 的建议是这样的:

              我们建议您在持久类上声明一致命名的标识符属性,并使用包装器(即非原始)类型(例如 Long 或 Integer)。

              更多pk和Hibernate机制详情请访问here

              【讨论】:

                【解决方案9】:

                虽然不完全符合您的要求,但我使用了一个小技巧。让查询选择“rownum”并将“rownum”定义为模型中的 ID 列。这将有效地使每一行对 Hibernate 都是唯一的。

                【讨论】:

                • 如果使用缓存会出问题
                猜你喜欢
                • 2012-01-09
                • 1970-01-01
                • 1970-01-01
                • 2023-03-22
                • 2012-01-07
                • 1970-01-01
                • 2012-07-03
                • 1970-01-01
                • 2011-03-30
                相关资源
                最近更新 更多