Hibernate one-to-one: getId() without fetching entire object

JavaHibernateJpa

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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

Solution 2 - Java

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.

Solution 3 - Java

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 :)

Solution 4 - Java

add @AccessType("property")

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@AccessType("property")
protected Long id;

Solution 5 - Java

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.

Solution 6 - Java

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();
  		}
  }

Solution 7 - Java

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-

Solution 8 - Java

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?

Solution 9 - Java

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;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRobView Question on Stackoverflow
Solution 1 - JavaArthur RonaldView Answer on Stackoverflow
Solution 2 - JavaxmedekoView Answer on Stackoverflow
Solution 3 - JavaPaul WasilewskiView Answer on Stackoverflow
Solution 4 - JavaDimaView Answer on Stackoverflow
Solution 5 - JavaMarcGView Answer on Stackoverflow
Solution 6 - JavaGrégoryView Answer on Stackoverflow
Solution 7 - JavachrismarxView Answer on Stackoverflow
Solution 8 - JavaBozhidar BatsovView Answer on Stackoverflow
Solution 9 - JavaMohsen KashiView Answer on Stackoverflow