简体   繁体   中英

JPA 2.0 / Hibernate: Why does LAZY fetching with “@OneToOne” work out of the box?

my question is regarding JPA 2.0 with Hibernate, @OneToOne relationships and lazy loading.

First my setup:

  • Spring 3.0.5.RELEASE
  • SprnigData JPA 1.0.1.RELEASE
  • Hibernate 3.5.2-Final
  • DBMS: PostgreSQL 9.0

I recently came across the fact, that a @OneToOne relationship can't be fetched the lazy way (FetchType.LAZY), at least not without byte code instrumentation, compile time weaving or the like. Many sites out there say this, for example:

The thing is, with my setup, a lazy loading of a @OneToOne entity seems to work "out of the box", and I really would like to understand why. Please, have a look at my unit test:

@Test
@Transactional
public void testAvatarImageLazyFetching()
{
    User user = new User();
    user.setAvatarImage( new AvatarImage() );

    User = userRepository.save( user );

    entityManager.flush();
    entityManager.clear();

    User loadedUser = userRepository.findOne( user.getId() );
    assertNotNull( loadedUser );

    PersistenceUtil persistenceUtil = Persistence.getPersistenceUtil();

    assertTrue( persistenceUtil.isLoaded( loadedUser ) );
    assertFalse( persistenceUtil.isLoaded( loadedUser, "avatarImage" ) );
}

This test case is successful, and in Hibernates SQL logging output, I can see clearly, that the "avatarImage" will not be fetched, just the "user" (just a single SELECT, no JOIN, no access to the "AvatarImage" table etc.)

The unidirectional @OneToOne relationshop in the User class looks like this:

@OneToOne( cascade = CascadeType.ALL, fetch = FetchType.LAZY )
private AvatarImage    avatarImage;

So, everything very simple - and it seems to work.

To repeat my question: why is it working, why can the "AvatarImage" be fetched lazily, although it is referenced with a @OneToOne association?

I really appreciate any help you can offer

Thanks a lot!

The problem with lazy loading of OneToOne relationship is only on the inverse part of it (the one which is marked with mappedBy attribute). It works fine on the owning side of the relationship. T he difference between those is clear on database level. In your case the question is if the User database table holds an id of AvatarImage as one of the columns or the other way round. If User table has a column with an id of AvatarImage then the lazy loading will work as you said "out-of-box" but it will not work the other way round.

Lazy fetching works out of the box for @OneToOne annotated relationships, with the Hibernate JPA provider, when some form of bytecode instrumentation is performed. In your case, we could rule out build-time instrumentation (inferring from your comment that it works out-of-the-box). This leaves you with the possibility of runtime weaving, which is quite possible in Hibernate & Spring. In recent releases of Hibernate, Javassist is used as the runtime bytecode instrumentation framework for Hibernate, as opposed to the other alternative of CGLIB ( which has been deprecated since Hibernate 3.5.5 ).

The question of whether Javassist is enabled in Spring is quite simple to answer. Hibernate EntityManager (which is the JPA 2.0 provider that delegates to Hibernate Core), requires Javassist, and therefore, it ought to be in the classpath of Hibernate, allowing for runtime weaving of the classes. You can confirm this by setting a breakpoint (in a remote debugger connected to your application server), and you'll notice that a Hibernate managed instance of the User class will not contain a reference to an AvatarImage instance; rather it would contain a reference to an enhanced class with a name like <package_name>.AvatarImage_$$_javassist_0 (which is the proxy that allows for lazy fetching).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM