Hibernate并没有PK

时间:2009-04-20 07:44:32

标签: java database hibernate jpa

是否可以创建一个不包含主键/ Id的表(来自JPA注释的Hibernate @Entity)?

我知道这不是一个好主意;表应该有一个主键。

10 个答案:

答案 0 :(得分:50)

罗杰的自我回答是正确的。详细说明一下是什么意思(我一开始并不清楚,并认为这会有所帮助):

假设你有一张桌子Foo:

TABLE Foo (
bar varchar(20),
bat varchar(20)
)

通常,您可以编写带有注释的类来使用此表:

// Technically, for this example, the @Table and @Column annotations 
// are not needed, but don't hurt. Use them if your column names 
// are different than the variable names.

@Entity
@Table(name = "FOO")
class Foo {

  private String bar;
  private String bat;


  @Column(name = "bar")
  public String getBar() {
   return bar;    
  }

  public void setBar(String bar) {
   this.bar = bar;    
  }

  @Column(name = "bat")
  public String getBat() {
   return bat;    
  }

  public void setBat(String bat) {
   this.bat = bat;    
  }

}

..但是,该死的。这个表没有我们可以用作id的东西,它是我们用于[插入重要业务功能]的遗留数据库。我不认为他们会让我开始修改表格以便我使用hibernate。

相反,您可以将对象拆分为休眠可操作的结构,该结构允许将整行用作键。 (当然,这假设行是唯一的。)

将Foo对象分成两部分:

@Entity
@Table(name = "FOO")
class Foo {

  @Id
  private FooKey id;

  public void setId(FooKey id) {
    this.id = id;
  }

  public void getId() {
    return id;
  }
}

@Embeddable
class FooKey implements Serializable {
  private String bar;
  private String bat;

  @Column(name = "bar")
  public String getBar() {
   return bar;    
  }

  public void setBar(String bar) {
   this.bar = bar;    
  }

  @Column(name = "bat")
  public String getBat() {
   return bat;    
  }

  public void setBat(String bat) {
   this.bat = bat;    
  }

}

..那应该是它。 Hibernate将使用Embeddable键作为其所需的身份,您可以正常拨打电话:

Query fooQuery = getSession().createQuery("from Foo");

希望这有助于第一次使用这项工作。

答案 1 :(得分:45)

使用以下代码; Hibernate没有自己的逻辑来区分重复记录

如果此方法存在任何问题,请告诉我

@Entity @IdClass(Foo.class)
class Foo implements Serializable {
  @Id private String bar;
  @Id private String bat;

  public String getBar() {
   return bar;    
  }

  public void setBar(String bar) {
   this.bar = bar;
  }


  public String getBat() {
   return bat;    
  }

  public void setBat(String bat) {
   this.bat = bat;    
  }
}

答案 2 :(得分:9)

我发现它不可能这样做。对那些使用遗留系统的人来说,运气不好。

如果您进行逆向工程(从现有JDBC连接创建JPA带注释的实体),该表将创建两个Java类,一个实体和一个字段; id,以及一个包含您关系中所有列的可嵌入ID。

答案 3 :(得分:9)

我发现这个技巧有效:

<id column="ROWID" type="string" />

答案 4 :(得分:2)

您无需创建单独的类作为@Id或主键。只需使用整数(或其他)。此外,不要发布假密钥,因为使用它的开发人员可能会认为它是真实的,否则会尝试使用它。最后,这最适用于VIEW。我同意早先的帖子,在大多数情况下,如果不是所有情况,表格都应该有一个主键。例如:

@Entity
@Table(name = "FOO")
class Foo {

  @SuppressWarnings("unused")
  @Id
  private Integer id;

  @Column(name = "REAL_COLUMN")
  private String realColumn;

  public String getRealColumn() {
    return realColumn;    
  }

  public void setRealColumn(String realColumn) {
    this.realColumn= realColumn;    
  }


}

答案 5 :(得分:1)

从表中创建Pojo - 使用eclipse中存在的逆向工程方法。 对于非主键表,eclipse将生成两个Pojo类。

eclipse generated class and hbm.xml - 
---
Foo.java 
//

public class Foo implements java.io.Serializable {

    private FooId id;

    public Foo() {
    }

    public Foo(FooId id) {
        this.id = id;
    }

    public FooId getId() {
        return this.id;
    }

    public void setId(FooId id) {
        this.id = id;
    }

}
---
FooId.java
//

public class FooId implements java.io.Serializable {

    private String bar;
    private String bat;

    public FooId() {
    }

    public FooId(String bar, String bat) {
        this.bar = bar;
        this.bat = bat;
    }

    public String getBar() {
        return this.bar;
    }

    public void setBar(String bar) {
        this.bar = bar;
    }

    public String getBat() {
        return this.bat;
    }

    public void setBat(String bat) {
        this.bat = bat;
    }

    public boolean equals(Object other) {
        if ((this == other))
            return true;
        if ((other == null))
            return false;
        if (!(other instanceof FooId))
            return false;
        FooId castOther = (FooId) other;

        return ((this.getBar() == castOther.getBar()) || (this.getBar() != null
                && castOther.getBar() != null && this.getBar().equals(
                castOther.getBar())))
                && ((this.getBat() == castOther.getBat()) || (this.getBat() != null
                        && castOther.getBat() != null && this.getBat().equals(
                        castOther.getBat())));
    }

    public int hashCode() {
        int result = 17;

        result = 37 * result
                + (getBar() == null ? 0 : this.getBar().hashCode());
        result = 37 * result
                + (getBat() == null ? 0 : this.getBat().hashCode());
        return result;
    }

}
---
Foo.hbm.xml

<hibernate-mapping>
    <class name="com.Foo" table="foo" schema="public" catalog="hibernate_poc">
        <composite-id name="id" class="com.FooId">
            <key-property name="bar" type="string">
                <column name="bar" length="20" />
            </key-property>
            <key-property name="bat" type="string">
                <column name="bat" length="20" />
            </key-property>
        </composite-id>
    </class>
</hibernate-mapping>

---
entry in the Hibernate.cfg.xml - 

<mapping class="com.poc.Foo" resource="Foo.hbm.xml"/>

---
Fetch the Data from table -
FooDataFetch.java
//

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class FooDataFetch {
        private static Session session = null;

   public static void main(String[] args) {
          try{            
            Configuration cfg = new Configuration();
            cfg.configure("/hibernate.cfg.xml");
            SessionFactory sf = cfg.buildSessionFactory();
            session = sf.openSession();

            session.beginTransaction();
              queryPerson(session);
            session.close(); 

          }catch (Throwable ex) { 
             System.err.println("Failed to create sessionFactory object." + ex);
            ex.printStackTrace();
             throw new ExceptionInInitializerError(ex); 
          }       
       }    

       private static void queryPerson(Session session) {
            Query query = session.createQuery("from Foo");                 
            List <Foo>list = query.list();
            java.util.Iterator<Foo> iter = list.iterator();
            while (iter.hasNext()) {
                Foo foo = iter.next();
                System.out.println("Foo Details: \"" + foo.getId().getBar() +"\", " + foo.getId().getBat());
            }
        }      
}

答案 6 :(得分:1)

添加到Awied的评论。 如果您想要搜索一个栏,请使用以下HQL。

Query fooQuery = getSession().createQuery("from Foo.id.bar = '<barName>'");

答案 7 :(得分:1)

当涉及到视图而不是在Hibernate中搜索变通方法时,在数据库视图中添加虚拟id可能更容易。我在另一个问题中写到了这一点:https://stackoverflow.com/a/44050230/1673775

答案 8 :(得分:0)

我找到了没有主键和null作为值的表的解决方案。它适用于oracle DB。也许其他数据库存在类似的东西。

  1. 您应该在POJO类中创建新的主键:

    @Id @Column(名称=&#34; ID&#34) private Integer id;

  2. 并像这样使用createNativeQuery

    getEntityManager().createNativeQuery("select rownum as id, .....
    

    本机查询将生成主键,您将获得独特的结果。

答案 9 :(得分:0)

尽管ROWID是一个伪列,但是由于ROWID对应于ROW的物理地址,它是检索任何行数据的最快方法。由于@Id用于唯一标识对象,而ROWID在表中是唯一的,因此我们可以利用它来克服我们正在讨论的问题。实际上,如果我们不需要任何有意义的唯一标识符,则ROWID是使用@Id注释进行注释的最佳列,因为它对应于行的物理地址。以下代码对我有用。       @Id @Column(name = "ROWID") private String Id;