Hibernate one-to-one: getId() without fetching entire object
I want to fetch the id of a one-to-one relationship without loading the entire object. I thought I could do this using lazy loading as follows:
class Foo {
@OneToOne(fetch = FetchType.LAZY, optional = false)
private Bar bar;
}
Foo f = session.get(Foo.class, fooId); // Hibernate fetches Foo
f.getBar(); // Hibernate fetches full Bar object
f.getBar().getId(); // No further fetch, returns id
I want f.getBar() to not trigger another fetch. I want hibernate to give me a proxy object that allows me to call .getId() without actually fetching the Bar object. 开发者_开发知识库
What am I doing wrong?
Use property access strategy
Instead of
@OneToOne(fetch=FetchType.LAZY, optional=false)
private Bar bar;
Use
private Bar bar;
@OneToOne(fetch=FetchType.LAZY, optional=false)
public Bar getBar() {
return this.bar;
}
Now it works fine!
A proxy is initialized if you call any method that is not the identifier getter method. But it just works when using property access strategy. Keep it in mind.
See: Hibernate 5.2 user guide
Just to add to the Arthur Ronald F D Garcia'post: you may force property access by @Access(AccessType.PROPERTY)
(or deprecated @AccessType("property")
), see http://256stuff.com/gray/docs/misc/hibernate_lazy_field_access_annotations.shtml
Another solution may be:
public static Integer getIdDirect(Entity entity) {
if (entity instanceof HibernateProxy) {
LazyInitializer lazyInitializer = ((HibernateProxy) entity).getHibernateLazyInitializer();
if (lazyInitializer.isUninitialized()) {
return (Integer) lazyInitializer.getIdentifier();
}
}
return entity.getId();
}
Works for detached entities, too.
Unfortunately the accepted answer is wrong. Also other answers doesn't provide the simplest or a clear solution.
Use the Property Access Level for the ID
of the BAR
class.
@Entity
public class Bar {
@Id
@Access(AccessType.PROPERTY)
private Long id;
...
}
Just as simple as that :)
add @AccessType("property")
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@AccessType("property")
protected Long id;
The Java Persistence with Hibernate Book mentions this in "13.1.3 Understanding Proxies":
As long as you access only the database identifier property, no initialization of the proxy is necessary. (Note that this isn’t true if you map the identifier property with direct field access; Hibernate then doesn’t even know that the getId() method exists. If you call it, the proxy has to be initialized.)
However, based on @xmedeko answer in this page I developed a hack to avoid initializing the proxy even when using direct field access strategy. Just alter the getId()
method like shown below.
Instead of:
public long getId() { return id; }
Use:
public final long getId() {
if (this instanceof HibernateProxy) {
return (long)((HibernateProxy)this).getHibernateLazyInitializer().getIdentifier();
}
else { return id; }
}
The idea here is to mark the getId()
method as final
, so that proxies cannot override it. Then, calling the method cannot run any proxy code, and thus cannot initialize the proxy. The method itself checks if its instance is a proxy, and in this case returns the id from the proxy. If the instance is the real object, it returns the id.
In org.hibernate.Session you have a function who do the work without lazy loading the entity :
public Serializable getIdentifier(Object object) throws HibernateException;
Found in hibernate 3.3.2.GA :
public Serializable getIdentifier(Object object) throws HibernateException {
errorIfClosed();
checkTransactionSynchStatus();
if ( object instanceof HibernateProxy ) {
LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
if ( li.getSession() != this ) {
throw new TransientObjectException( "The proxy was not associated with this session" );
}
return li.getIdentifier();
}
else {
EntityEntry entry = persistenceContext.getEntry(object);
if ( entry == null ) {
throw new TransientObjectException( "The instance was not associated with this session" );
}
return entry.getId();
}
}
There is now a jackson hibernate datatype library here:
https://github.com/FasterXML/jackson-datatype-hibernate
And you can configure the features:
Hibernate4Module hibernate4Module = new Hibernate4Module();
hibernate4Module.configure(Hibernate4Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);
This will include the id of the lazy loaded relationship-
You could use a HQL query. The getBar() method will truly return a proxy, that won't be fetched until you invoke some data bound method. I'm not certain what exactly is your problem. Can you give us some more background?
change your getter method like this:
public Bar getBar() {
if (bar instanceof HibernateProxy) {
HibernateProxy hibernateProxy = (HibernateProxy) this.bar;
LazyInitializer lazyInitializer = hibernateProxy.getHibernateLazyInitializer();
if (lazyInitializer.getSession() == null)
bar = new Bar((long) lazyInitializer.getIdentifier());
}
return bar;
}
精彩评论