【发布时间】:2010-10-20 12:25:21
【问题描述】:
是否可以创建一个不包含主键/Id 的表(来自 JPA 注释的 Hibernate @Entity)?
我知道这不是一个好主意;一个表应该有一个主键。
【问题讨论】:
-
您可以创建一个从
Sequence自动插入的代理键(如果您不使用 Oracle,则为等效键)。对于旧数据,您可以使用运行数字填充新创建的键。
标签: java database hibernate jpa
是否可以创建一个不包含主键/Id 的表(来自 JPA 注释的 Hibernate @Entity)?
我知道这不是一个好主意;一个表应该有一个主键。
【问题讨论】:
Sequence 自动插入的代理键(如果您不使用 Oracle,则为等效键)。对于旧数据,您可以使用运行数字填充新创建的键。
标签: java database hibernate jpa
罗杰的自我回答是正确的。详细说明一下是什么意思(起初我并不清楚,认为这会有所帮助):
假设你有一个这样的表 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 的任何内容,它是我们用于 [插入重要业务功能] 的遗留数据库。我认为他们不会让我开始修改表以便我使用休眠。
相反,您可以将对象拆分为可用于休眠的结构,该结构允许将整行用作键。 (当然,这假设该行是唯一的。)
将 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");
希望这有助于初次使用此功能的人。
【讨论】:
使用以下代码; 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;
}
}
【讨论】:
@IdHibernate 使用所有列作为主键并且无法处理主键中的空值。这导致检索结果中出现空对象。将所有 @Id 字段复制到静态内部类中,在那里再次生成 getter/setter 以及 equals() 和 hashCode() 方法,并将这个内部类用作 @IdClass 为我完成了这项工作。
我发现这个技巧很有效:
<id column="ROWID" type="string" />
【讨论】:
我发现这是不可能的。对于那些使用遗留系统的人来说,运气太差了。
如果您进行逆向工程(从现有 JDBC 连接创建 JPA 注释实体),该表将创建两个 Java 类,一个实体和一个字段; id,以及一个包含关系中所有列的可嵌入 id。
【讨论】:
虽然ROWID是一个伪列,但是由于ROWID对应的是一个ROW的物理地址,它是检索任何行数据的最快方法。由于@Id 用于唯一标识对象,而 ROWID 在表中是唯一的,我们可以利用它来解决我们正在讨论的问题。实际上,如果我们不需要任何有意义的唯一标识符,ROWID 是使用@Id 注释进行注释的最佳列,因为它对应于行的物理地址。以下代码对我有用。
@Id
@Column(name = "ROWID")
private String Id;
【讨论】:
您不需要创建一个单独的类来作为您的@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;
}
}
【讨论】:
从表中创建 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());
}
}
}
【讨论】:
添加到 Awied 的评论中。 如果您想搜索柱线,请使用以下 HQL。
Query fooQuery = getSession().createQuery("from Foo.id.bar = '<barName>'");
【讨论】:
当涉及到视图而不是在 Hibernate 中搜索解决方法时,在数据库视图中添加虚拟 id 可能更容易。我在另一个问题中写过:https://stackoverflow.com/a/44050230/1673775
【讨论】:
我找到了没有主键和 null 作为值的表的解决方案。它将在 oracle DB 上运行。也许其他数据库也存在类似的情况。
您应该在 POJO 类中创建新的主键:
@Id @Column(name="id") 私有整数 id;
并像这样使用 createNativeQuery
getEntityManager().createNativeQuery("select rownum as id, .....
原生查询会生成主键,你会得到唯一的结果。
【讨论】:
@Entity POJO 永远不会进入缓存...