[hibernate-dev] Bytecode enhancement and collections
Steve Ebersole
steve at hibernate.org
Thu Sep 10 09:45:06 EDT 2015
Wanted to get some opinions. I am working on HHH-10055 which is basically
a report of problems with that "lazy loading outside of a
session/transaction" feature when used in combination with bytecode
enhancement. The initial problem was that bytecode interception was not
accounting for collection attributes properly; it was not building the
appropriate PersistentCollection to return. I changed that code to now
build the PersistentCollection.
But that led to another issue later on that made me question how I was
building the PersistentCollection during interception. Essentially I was
trying to still build an uninitialized PersistentCollection. The
interception code immediately tries to read the size of that collection as
and up-front part of its in-line dirty checking capabilities which
triggered another interception back in to an inconsistent state.
But what I started thinking about is the assumption that this interception
ought to prefer to return an uninitialized PersistentCollection. I now
think that is not a good assumption. Why? Consider code like:
Well the idea of an uninitialized PersistentCollection comes from the
scenario of proxy-based laziness. In proxy-based laziness, code like:
MyEntity myEntity = session.load( MyEntity.class, 1 );
System.out.println( myEntity.getName() );
In the case of proxy-based laziness, the second line immediately causes the
entire proxy to become initialized. Part of that is to set any of its
collection attributes. However, as the collections are not accessed here
we want to further delay initializing them. But since the proxy is
initialized completely that means the only way to achieve that here is
setting an uninitialized version of the PersistentCollection as state,
which will initialize itself later when accessed.
For bytecode enhancement, the situation is a little bit different. There
we'd not even build the PersistentCollection instance until that attribute
is accessed. So in the above code the collection attributes would never be
built. So when we are in the interception code I mentioned above, we know
that something is trying to access that collection attribute specifically.
This is the difference.
Back to the initial problem... I think the solution is not just to have the
bytecode interception code build the PersistentCollection, but to also have
it make sure that the PersistentCollection is initialized.
Going back to the sample code, and adding a line:
MyEntity myEntity = session.load( MyEntity.class, 1 );
print( myEntity.getName() );
myEntity.getChildren();
In the proxy-based solution the collection is still uninitialized after
this. For bytecode interception I am proposing that the collection would
be initialized by that 3rd line. Again we could return the uninitialized
collection here, and wait for PersistentCollection to initialize itself on
first "further access" (calling size(), iterating, etc). I am more think
through intent; because we know specifically that the collection attribute
itself was accessed it seems reasonable to go ahead and initialize it.
And if we do not go that route, then we need a different tact as well for
dealing with the in-line dirty checking aspect of this.
Really the only time this distinction becomes an issue is in code that
explicitly tries to check whether certain attributes are initialized. So
whereas this works for the proxy-based approach:
MyEntity myEntity = session.load( MyEntity.class, 1 );
if ( !Hibernate.isInitialized( myEntity.getChildren() ) ) {
// do something with the uninitialized collection
...
}
It will fail with the bytecode interception approach I propose, because the
call to `myEntity.getChildren()` itself causes the initialization. There
you'd have to use:
MyEntity myEntity = session.load( MyEntity.class, 1 );
if ( !Hibernate.isPropertyInitialized( myEntity, "children" ) ) {
// do something with the uninitialized collection
...
}
which has always been the suggested way to deal with questioning bytecode
initialization state and which matches the JPA call too.
So any thoughts?
More information about the hibernate-dev
mailing list