49

Currently, Hibernate allows me to load objects defined by *-to-one relationships directly with

entity1.getEntity2()

Is it possible to get the foreign key instead of the object?

The current approach which I see is having addint to my mapping:

@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

Is there a better approach to get the foreign key, or is this the only one?

davnicwil
  • 28,487
  • 16
  • 107
  • 123
iliaden
  • 3,791
  • 8
  • 38
  • 50

4 Answers4

47

Yes, you can do that. You just need to make it clear for hibernate which one is the mapping that it's supposed to maintain, like so:

@Column(name="message_key", updatable=false, insertable=false)
private Long message_fk;
Affe
  • 47,174
  • 11
  • 83
  • 83
  • 3
    and how could one specify to which entity this ID refers? The database schema should contain real foreign key constraint, which is not possible to recognize from your annotations... – Jakub Oct 04 '12 at 12:29
  • 2
    The suggestion is that this mapping is made in addition to the normal full mapping of the entity relationship, as shown in the original question. – Affe Oct 04 '12 at 16:38
  • I get a classcastexception when hibernate seems to get confused which one is the object and which one is the key – Ryan Apr 18 '13 at 21:20
  • 1
    Any way of doing this "fully in substitution of" the Message entity itself? – Daniel Patrick May 15 '18 at 10:55
  • That doesn't look very pretty. Considering the fact that most code bases use camel case for members – Peter Chaula Jan 10 '19 at 19:14
  • It more often opposite usecase, do not update object: https://stackoverflow.com/a/44539145/548473 – Grigory Kislin Sep 29 '21 at 14:56
22

If you still want a reference to your entity but don't want to load it from the database just to get the foreign key, your approach is the correct one. Add insertable and updatabale = false to the Column attribute to prevent losing the correct reference to an entity.

@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;
Marcelo
  • 11,218
  • 1
  • 37
  • 51
5

Actually, it is default Hibernate behavior to only load the foreign key instead of the message object if the FetchType is LAZY. This is why there are proxies to the objects to be loaded when you specify LAZY FetchType.

The foreign key is not visible directly, but it is of course the key of the object at the "one" end of the OneToMany relationship.

However, with a field-based access type (e.g., in your case, where annotations are placed on fields), there is an unresolved hibernate issue: Hibernate loads the whole object behind the proxy from the database. ( http://blog.xebia.com/2009/06/13/jpa-implementation-patterns-field-access-vs-property-access/ )

My concrete suggestion would be (as, for example, the "right" answer did not work in my case):

  • Use the message object directly, as Hibernate will only load it if non-foreign-key data is needed. Do not specify an additional field for the foreign key.
  • Switch the class to use property access, i.e. define getters and setters, and put your annotations from the fields to the getters.
RobertG
  • 1,550
  • 1
  • 23
  • 42
  • How do you know that "Hibernate will only load it if non-foreign-key data is needed"? Can you provide a reference please? – John Henckel May 26 '17 at 20:14
  • You also can mix field and property access. I've used property access only for id (due to bug https://hibernate.atlassian.net/browse/HHH-3718) and field access for others. See also discussion http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access – Grigory Kislin Jun 29 '17 at 12:39
2
Long fk = entity1.getEntity2().getId();

This should work. It would only not work if you have composite, primary keys being referenced as foreign keys but your solution wouldn't work either in that case. Considering my solution, even a composite key wouldn't look that ugly.

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

Something like that will work.

EDIT: Thinking about your proposed solution more, it wouldn't work anyway because Hibernate already tries to automatically create a FK field for a Mapped relationship, so defining another @Column would simply try to bind to a second column with the same name.

Jesse Webb
  • 43,135
  • 27
  • 106
  • 143
  • 2
    I don't like this solution, because it requires the entity being referenced by the foreign key to be loaded. Is there a way to avoid it? Also: @Column would be considered as a separate parameter, not as a FK. At least I thinks so. – iliaden Jun 10 '11 at 20:48
  • About the @Column comment: Refer to Affe's answer for what I meant, he was clearer in his explanation. About the entity getting loaded, you are right, it would cause a load on your lazy fetch class. Your question did not state that it was attempting to avoid a fetch, just "How do I get the FK value?"; you might want to edit it. – Jesse Webb Jun 10 '11 at 20:57
  • 2
    Actually, if you're just getting the id value of the associated entity, Hibernate does not need to load the entire entity. In lazy loading, it will have already loaded the ID from the FK in the entity's mapped table. – Stevi Deter Jun 10 '11 at 21:25
  • 2
    This code would throw NPE if entity1.entity2 = null – Pedro Borges Apr 07 '16 at 11:12