Hibernate - 使用外键代替实体类

50

目前,Hibernate允许我直接使用*-to-one关系定义加载对象。

entity1.getEntity2()

是否可能获取外键而不是对象?

我目前看到的方法是在我的映射中添加addint:

@JoinColumn(name="message_key")
@ManyToOne(targetEntity=Message.class,fetch=FetchType.LAZY)
private Message message;  //these lines currently exist

@Column(name="message_key")
private Long message_fk; //the idea is to add those 2 lines

是否有更好的方法来获取外键,还是这是唯一的方法?

4个回答

47

是的,你可以这样做。你只需要让Hibernate清楚它应该维护哪个映射关系,就像这样:

@Column(name="message_key", updatable=false, insertable=false)
private Long message_fk;

3
如何指定此ID所引用的实体?数据库模式应包含真正的外键约束,这是无法从您的注释中识别的... - Jakub
2
建议是在原始问题中所示的实体关系的完整映射之外,进行此映射。 - Affe
当Hibernate混淆对象和键时,我会收到ClassCastException异常。 - Ryan
1
有没有一种方法可以完全替换消息实体本身来完成这个操作? - Daniel Patrick
看起来不太美观。考虑到大多数代码库使用驼峰命名法来命名成员。 - Peter Chaula
通常情况下相反的用例是不更新对象的:https://dev59.com/imUp5IYBdhLWcg3wvpcM#44539145 - Grigory Kislin

22

如果你仍然想要对实体的引用,但不想从数据库中加载它来获取外键,那么你的方法是正确的。将insertable和updatabale = false添加到Column属性中,以防止丢失对实体的正确引用。

@JoinColumn(name = "message_key")
@ManyToOne(targetEntity = Messages.class, fetch = FetchType.LAZY)
private Messages message;

@Column(name = "message_key", insertable = false, updatable = false)
private Long message_fk;

2
有人尝试过反过来吗?这样我只需要插入一个ID,Hibernate就可以正确地获取实体了吗? - kaiser
@kaiser 你能根据ID获取整个实体吗? - Neeraj Vernekar

5
实际上,如果FetchType为LAZY,则默认的Hibernate行为是仅加载外键而不是消息对象。这就是为什么在指定LAZY FetchType时需要使用对象代理来加载要加载的对象。
外键本身不可见,但它当然是OneToMany关系中"One"端对象的键。
然而,在基于字段的访问类型(例如,在您的情况下,注释放置在字段上)中,存在一个未解决的Hibernate问题:Hibernate从数据库中加载代理后面的整个对象。(http://blog.xebia.com/2009/06/13/jpa-implementation-patterns-field-access-vs-property-access/
我的具体建议是(例如,“正确”的答案在我的情况下不起作用):
  • 直接使用消息对象,因为只有在需要非外键数据时,Hibernate才会加载它。不要为外键指定额外的字段。
  • 将类切换为使用属性访问,即定义getter和setter,并将注释从字段移到getter上。

你怎么知道“只有在需要非外键数据时,Hibernate才会加载它”?你能提供一个参考吗? - John Henckel
你也可以混合使用字段和属性访问。我仅在id上使用了属性访问(由于错误https://hibernate.atlassian.net/browse/HHH-3718),而在其他情况下使用了字段访问。另请参见讨论https://dev59.com/BHRB5IYBdhLWcg3wiHv7 - Grigory Kislin

2
Long fk = entity1.getEntity2().getId();

这应该可以工作。只有在引用复合主键作为外键时,它才不起作用,但即使是在这种情况下,您的解决方案也不会起作用。考虑到我的解决方法,即使是复合主键也不会看起来那么丑陋。

Long fkField1 = entity1.getEntity2().getCol1();
String fkField2 = entity1.getEntity2().getCol2();

类似这样的方法可以起作用。

编辑: 仔细考虑了您提出的解决方案,它也行不通,因为Hibernate已经尝试自动为映射关系创建一个外键字段,因此定义另一个@Column将会尝试绑定到具有相同名称的第二个列。


2
我不喜欢这个解决方案,因为它需要加载被外键引用的实体。有没有避免这种情况的方法?另外:@Column应该被视为单独的参数,而不是FK。至少我是这么认为的。 - iliaden
关于@Column注释:有关我所说的内容,请参考Affe的答案,他在解释方面更清晰。 关于实体的加载,你是正确的,它会导致对你的延迟提取类进行加载。 你的问题没有说明它是尝试避免获取,只是“如何获得FK值?”; 你可能需要编辑它。 - Jesse Webb
2
实际上,如果你只获取关联实体的id值,Hibernate就不需要加载整个实体。在延迟加载中,它已经从实体映射表中的FK中加载了ID。 - Stevi Deter
2
如果entity1.entity2为null,此代码将抛出NPE异常。 - Pedro Borges

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接