Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Wed Sep 14, 2016 10:29 am 
Newbie

Joined: Tue Sep 13, 2016 11:24 am
Posts: 15
Hello,

I have an application in the following runtime env stack: Hibernate 5.2.2 (JPA 2.1), Spring 4.3.2, Java 8 (1.8.0_102), Weblogic 12.1.3 application server, Oracle 11 db.
I tried compile time instrumentation of my Hibernate persistence classes as indicated in (https://docs.jboss.org/hibernate/orm/5. ... nhancement) using hibernate-enhance-maven-plugin and setting :
Code:
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
I build the logic by specifying among the other goals: org.hibernate.orm.tooling:hibernate-enhance-maven-plugin:enhance. At Maven build time the goal is executed and it indicates in the console output that my Hibernate configured classes are successfully instrumented.

I have an entity MyEntity mapped on a single table MY_TABLE. The entity has a byte[] attribute mapped with a BLOB column from MY_TABLE:
@Lob
@Column(name="MY_FILE")
@Basic(fetch = FetchType.LAZY)
private byte[] myBinaryFile;

The rest are just simple properties, getters and setters.

I need to achieve lazy loading for the binary attribute above.
Therefore I enabled compile time instrumentation and annotated @Basic(fetch = FetchType.LAZY) to avoid the overhead of loading this large binary information each time. Problem is that while now the myBinaryFile property is not loaded anymore eagerly, it fails to be loaded lazily on demand(despite 2 hints below), during the same session loading MyEntity.

Also the Hibernate generated SQL query does not contain MY_FILE among selected columns.

I tried to hint the persistence provider(I use Hibernate Session api) that the attribute needs to be initialized without success as follows:
1. simply by calling inside the Session the getter - MyEntity.getMyBinaryFile() but no success, I get 'null' as result

2. by using an entity graph and "javax.persistence.loadgraph" mode - altering fetching only for specified attribute in the graph
Code:
             EntityGraph eg = session.createEntityGraph(MyClass.class);

            if (eg != null) {
               
               eg.addAttributeNodes("myBinaryFile");
               ((SharedSessionContractImplementor)session).getLoadQueryInfluencers().setLoadGraph(eg);
            }
            
            byte[] templatFile = session.get(MyClass.class, new Long(12345L)).getMyBinaryFile(); // here I get also 'null' as a result

The db contains the corresponding binary information

If I remove the @Basic(fetch = FetchType.LAZY) annotation, the myBinaryFile will be properly loaded, eagerly as usual.

Can anybody give me an idea what could go wrong here?
Are there other alternatives to hint the loading of the LOB when needed?

Thanks in advance


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Wed Sep 14, 2016 11:25 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1582
Location: Romania
You need to add the @LazyGroup as well:

Code:
@Lob
@Column(name="MY_FILE")
@Basic(fetch = FetchType.LAZY)
@LazyGroup( "binaryGroup" )
private byte[] myBinaryFile;

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Wed Sep 14, 2016 12:11 pm 
Newbie

Joined: Tue Sep 13, 2016 11:24 am
Posts: 15
Hi Vlad,
Thanks for the response.
I'm not sure I understand. From what I read in the documentation specifying the LazyGroup is optional. If is not specified is considered as 'default' lazy group.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 4:19 am 
Newbie

Joined: Tue Sep 13, 2016 11:24 am
Posts: 15
I tried also specifying @LazyGroup( "binaryGroup" ) but the myBinaryFile attribute is still 'null'. (https://docs.jboss.org/hibernate/orm/5. ... pabilities "By default, all singular attributes are part of a single group, meaning that when one lazy singular attribute is accessed all lazy singular attributes are loaded.")

Here are my Hibernate Session factory configuration properties:

Code:
hibernate.dialect = Oracle10gDialect
hibernate.bytecode.use_reflection_optimizer = true
hibernate.format_sql = true
hibernate.show_sql =   false
hibernate.max_fetch_depth = 3
hibernate.default_batch_fetch_size = 4
hibernate.order_updates = true
hibernate.jdbc.batch_size = 20
hibernate.hbm2ddl.auto = validate
hibernate.query.substitutions = true 1, false 0
hibernate.jdbc.use_streams_for_binary = true
hibernate.cache.use_second_level_cache = false
cache.provider_class = org.hibernate.cache.NoCacheProvider


Am I missing something in my application or is a bug in Hibernate?


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 4:53 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1582
Location: Romania
I tested it with @LazyGroup and it works. The test is on GitHub so you can try it yourself.

But there is an issue indeed since it requires @LazyGroup and this annotation should not be mandatory.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 5:36 am 
Newbie

Joined: Tue Sep 13, 2016 11:24 am
Posts: 15
mihalcea_vlad wrote:
I tested it with @LazyGroup and it works. The test is on GitHub so you can try it yourself.

But there is an issue indeed since it requires @LazyGroup and this annotation should not be mandatory.


Thanks for the help.
I can not find the Image class to run the test.
You used the entity manager. If you try to use Hibernate session instead in your LazyAttributeTest - doInHibernate(...). I'm curious if it still works.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 5:50 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1582
Location: Romania
You can fork the repository from GitHub and then you'll find the Image class in the src/main folder. As for the Hibernate native API, I'll have to give it a try to see if it works.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 6:39 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1582
Location: Romania
False alarm. I have two test cases that work just fine or both JPA and Hibernate native API. I'm going to add them into hibernate core so you can find them there.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 6:53 am 
Newbie

Joined: Tue Sep 13, 2016 11:24 am
Posts: 15
Ok, I managed to work around this.
Problem was that MyClass(annotated @Entity) was subclass of MyPartentClass annotated like:
Code:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISC_COLUMN", discriminatorType = DiscriminatorType.STRING)
@Table(name = "MY_TABLE")


By just moving "myBinaryFile" attribute/setter/getter to superclass. Things got solved in respect to LAZY fetching. Based on checking instrumented class files I see that only MyPartentClass gets instrumented(implementing the special interfaces and having setters/getters enhanced with calls to proxy). However subclass MyClass rests untouched by the maven compile instrumenting plugin that I use, despite the "class successfully enhanced" messages logged for each of these 2 classes. Is this a limitation of Maven instrumenting plugin (not dealing with entities subclasses) ?

Now as I said LAZY load works however I get stuck at persistence when calling Session.merge():

Quote:
java.lang.ClassCastException: org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer$1 cannot be cast to [B
at org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor.areEqual(PrimitiveByteArrayTypeDescriptor.java:26)
at org.hibernate.type.AbstractStandardBasicType.isEqual(AbstractStandardBasicType.java:195)
at org.hibernate.type.AbstractStandardBasicType.getReplacement(AbstractStandardBasicType.java:75)
at org.hibernate.type.AbstractStandardBasicType.replace(AbstractStandardBasicType.java:361)
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:211)
at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:430)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:232)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:172)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:886)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:853)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:391)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:316)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:155)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:104)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:445)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:238)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:172)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:68)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:862)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:843)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:848)
at org.springframework.orm.hibernate5.HibernateTemplate$22.doInHibernate(HibernateTemplate.java:782)
at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:270)
at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:323)
at org.springframework.orm.hibernate5.HibernateTemplate.merge(HibernateTemplate.java:777)


Is it any solution for this problem?


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 7:44 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1582
Location: Romania
Done. This Hibernate commit adds a test to prove that this functionality is working as expected.

As for that exception, I have no idea. If you can provide a replicating test case, then open a Jira issue for it.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 8:32 am 
Newbie

Joined: Tue Sep 13, 2016 11:24 am
Posts: 15
Thanks for the help!
mihalcea_vlad wrote:
Done. This Hibernate commit adds a test to prove that this functionality is working as expected.

Only question remains: why the "org.hibernate.orm.tooling:hibernate-enhance-maven-plugin:5.2.2.Final" instruments only the Entity superclass but not the entity subclass? Despite "class successfully instrumented" messages for both of them?

mihalcea_vlad wrote:
As for that exception, I have no idea. If you can provide a replicating test case, then open a Jira issue for it.

I will come back with a replicating test case for the Session.merge().

Here there is a ticket describing the same error during merge operation https://hibernate.atlassian.net/browse/HHH-5255
It's supposed to be fixed form 5.0.0.CR3. However I still get this problem in 5.2.2.Final


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 10:32 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1582
Location: Romania
If you have a replicating test case, then please add a new Jira issue.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 10:56 am 
Newbie

Joined: Tue Sep 13, 2016 11:24 am
Posts: 15
I did some debugging and it led me to this point:

Code:
MaterializedBlobType(AbstractStandardBasicType<T>).isEqual(Object, Object) line: 195   
MaterializedBlobType(AbstractStandardBasicType<T>).getReplacement(T, T, SharedSessionContractImplementor) line: 75   
MaterializedBlobType(AbstractStandardBasicType<T>).replace(Object, Object, SharedSessionContractImplementor, Object, Map, ForeignKeyDirection) line: 361   
TypeHelper.replace(Object[], Object[], Type[], SharedSessionContractImplementor, Object, Map, ForeignKeyDirection) line: 211   
DefaultMergeEventListener.copyValues(EntityPersister, Object, Object, SessionImplementor, Map, ForeignKeyDirection) line: 430   
DefaultMergeEventListener.entityIsTransient(MergeEvent, Map) line: 232   
DefaultMergeEventListener.onMerge(MergeEvent, Map) line: 172   
SessionImpl.fireMerge(Map, MergeEvent) line: 886   
SessionImpl.merge(String, Object, Map) line: 853   
CascadingActions$6.cascade(EventSource, Object, String, Object, boolean) line: 261   
Cascade.cascadeToOne(CascadingAction, EventSource, Object, Object, Type, CascadeStyle, Object, boolean) line: 391   
Cascade.cascadeAssociation(CascadingAction, CascadePoint, EventSource, int, Object, Object, Type, CascadeStyle, Object, boolean) line: 316   
Cascade.cascadeProperty(CascadingAction, CascadePoint, EventSource, int, Object, Object, Type, CascadeStyle, String, Object, boolean) line: 155   
Cascade.cascade(CascadingAction, CascadePoint, EventSource, EntityPersister, Object, Object) line: 104   
DefaultMergeEventListener(AbstractSaveEventListener).cascadeAfterSave(EventSource, EntityPersister, Object, Object) line: 445   
DefaultMergeEventListener.entityIsTransient(MergeEvent, Map) line: 238   
DefaultMergeEventListener.onMerge(MergeEvent, Map) line: 172   
DefaultMergeEventListener.onMerge(MergeEvent) line: 68   
SessionImpl.fireMerge(MergeEvent) line: 862   
SessionImpl.merge(String, Object) line: 843   
SessionImpl.merge(Object) line: 848   
HibernateTemplate$22.doInHibernate(Session) line: 782   


At the point (MaterializedBlobType(AbstractStandardBasicType<T>).getReplacement(T, T, SharedSessionContractImplementor) line: 75 )

Code:
   protected T getReplacement(T original, T target, SharedSessionContractImplementor session) {
      if ( !isMutable() ) {
         return original;
      }
      else if ( isEqual( original, target ) ) {
         return original;
      }
      else {
         return deepCopy( original );
      }
   }


original - is the actual byte[] array I'm trying to persist
target = org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY

After it goes on the branch isEqual( original, target ) and of course the 2 objects are not equal.

Maybe a solution would be to modify the following method ? org.hibernate.type.TypeHelper.replace(Object[], Object[], Type[], SharedSessionContractImplementor, Object, Map, ForeignKeyDirection))

by adding a condition like you did when fixing the other

Code:
else if ( target[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
            if ( types[i].isMutable() ) {
               copied[i] = types[i].deepCopy( original[i], session.getFactory() );
            }
            else {
               copied[i] = original[i];
            }
         }


just as you did with the other one: org.hibernate.type.TypeHelper.replace(Object[], Object[], Type[], SharedSessionContractImplementor, Object, Map) in pull 891 https://github.com/hibernate/hibernate-orm/pull/891/files

I think this method is called for MyEntity properties (org.hibernate.type.TypeHelper.replace(Object[], Object[], Type[], SharedSessionContractImplementor, Object, Map, ForeignKeyDirection))) because I am persisting MyEntity indirectly through a parent entity MyContainerEntity (one-to-one relation, MyEntity being the owning side).

So the particularity of this case is that that a child entity fails to get merged through a parent entity. The child entity is compile time instrumented and has a LAZY Basic attribute/property.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Thu Sep 15, 2016 11:00 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1582
Location: Romania
Let's open a JIRA issue so that we have multiple persons to check the replicating test case.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: @Basic(fetch = FetchType.LAZY) returns null despite hints
PostPosted: Tue Sep 20, 2016 6:20 am 
Newbie

Joined: Tue Sep 13, 2016 11:24 am
Posts: 15
A ticket was created reporting this situation/test case included also: https://hibernate.atlassian.net/browse/HHH-11117

Thanks in advance


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.