11

I'm having a problem with a Hibernate entity that does not get initialised.
It seems that it's still returning a not initialised proxy...

If I take a look at my debug info I would expect my entity to be initialised.
But it looks like the following:

entity = {SomeEntity_$$_jvst47c_1e@9192}"SomeEntityImpl@1f3d4adb[id=1,version=0]"
    handler = {org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer@9196}
        interfaces = {java.lang.Class[2]@9197}
        constructed = true
        persistentClass = {java.lang.Class@3605}"class SomeEntityImpl"
        getIdentifierMethod = null
        setIdentifierMethod = null
        overridesEquals = true
        componentIdType = null
        replacement = null
        entityName = {java.lang.String@9198}"SomeEntityImpl"
        id = {java.lang.Long@9199}"1"
        target = {SomeEntityImpl@9200}"SomeEntityImpl@1f3d4adb[guid=<null>,id=1,version=0]"
        initialized = true
        readOnly = true
        unwrap = false
        session = {org.hibernate.internal.SessionImpl@6878}"SessionImpl(PersistenceContext[entityKeys=[EntityKey[EntityReferenceImpl#2], EntityKey[SomeEntityImpl#1], EntityKey[...
        readOnlyBeforeAttachedToSession = null
        sessionFactoryUuid = null
        allowLoadOutsideTransaction = false

Notice that my Hibernate POJO still only contains a handlereven after doing an explicit initialisation...
In my debug view, I can see the 'real' property values (not displayed above) when I expand the target node.

What I'm doing:

EntityReferenceImpl entityReference = findEntityReference(session);
SomeEntity entity = null;
if (entityReference != null) {
    // initialize association using a left outer join
    HibernateUtil.initialize(entityReference.getSomeEntity());
    entity = entityReference.getSomeEntity();
}
return entity;

Notice the HibernateUtil.initialize call!

SomeEntity mapping:

public class SomeEntityImpl extends AbstractEntity implements SomeEntity {
    @OneToMany(mappedBy = "someEntity", fetch = FetchType.EAGER, targetEntity = EntityReferenceImpl.class, orphanRemoval = true)
    @Cascade(CascadeType.ALL)
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private Set<EntityReference> entityReferences = new HashSet<>();

    @Target(EntityName.class)
    @Embedded
    private Name name;

    @Target(EntityAddress.class)
    @Embedded
    private Address address;

    ...

}

EntityReferenceImpl mapping:

public class EntityReferenceImpl extends AbstractEntity implements EntityReference {

@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = SomeEntityImpl.class)
@JoinColumn(name = "entity_id")
private SomeEntity someEntity;

...

}

So what is the side effect: When the POJO later comes with updated properties I'm still having the same structure (as mentioned above) and I can see the updated properties under the target node.
But when I'm trying to update the entity using session.merge() or session.update()or session.saveOrUpdate(), Hibernate does not detect the 'dirty' properties and does not invoke an update query to the database.


Does anyone have some clues about this weird behavior? I have tried everything what I can but without any results.
All help is very welcome!!

2
  • What package does the HibernateUtil class comes from? Don't you use Hibernate class? Commented Oct 10, 2014 at 10:59
  • DId u solve this issue ? Iam facing a similar issue Commented Nov 13, 2014 at 9:46

3 Answers 3

11

Entity in your debug window looks like properly initialized.

When you have some entity that may be proxied by hibernate, this entity is stored inside proxy object even after being properly initialized. After initialisation proxy object itself doesn't disappear...

public class EntityReferenceImpl extends AbstractEntity implements EntityReference {

@ManyToOne(fetch = FetchType.LAZY, ...)
private SomeEntity someEntity;
...

In your example you have EntityReferenceImpl entity which has @ManyToOne(LAZY) to SomeEntity entity.

When hibernate loads EntityReferenceImpl it fills all fields from resultSet values but someEntity field is set to proxy object.

This proxy objects looks like this:

class SomeEntity_$$_javassist_3 extends SomeEntity implements HibernateProxy {
  + firstname = NULL;
  + lastname = NULL;
  + age = 0;
  + handler; //of type: JavassistLazyInitializer

  getFirstname() {
    handler.invoke(..., Method thisMethod, Method proceed, args);
  }
  getLastName() {...}
}

Your SomeEntity class has (for example) methods getFirstName() etc, but javassist generated class simply extends your SomeEntity and has few new bytecode-generated methods like c7getFirstName() etc.

And most important - proxy class has new field: handler of type JavassistLazyInitializer.

Lets see how JavassistLazyInitializer looks like:

JavassistLazyInitializer {
  + target; //holds SomeEntity object
  invoke(..., Method thisMethod, Method proceed, args) {
    if (target == null) {
      target = initialize(); // calls sessionImpl.immediateLoad
    }
    return thisMethod.invoke( target, args );
  }
}

So when you look into your proxy object - it has your fields like firstname, lastname etc. When you initialize this proxy, SomeEntity is loaded into target field. Your firstname, lastname fields on proxy objects are null as before - proxy doesn't use them, but real data is in SomeEntity object held by target field.

This is how proxy is implemented in hibernate.

You may ask - why such solution? Such design comes from polymorphism issues. If SomeEntity would be abstract parent class with 2 subclasses EntityA and EntityB hibernate has no problem - someEntity field holds proxy (generated) class extending SomeEntity but having concrete EntityA or EntityB inside target field.

However there are some pitfalls with this solution and polymorphism. Your someEntity field will be instance of SomeEntity but never instance of EntityA nor instance of EntityB.

Sign up to request clarification or add additional context in comments.

1 Comment

I've got exactly the same issue. My Object contains a "handler". And in this handler on a property called "target" there is my real object. How can I force him to first invoke himself?
3

Hibernate uses Proxies to intercept calls to LAZY entities. That structure you see in debug is how a Proxy looks like.

You don't need to call HibernateUtil.initialize, but simply use "fetch joins" to load all entities you are interested in a single query.

If the entity is attached to the current Session, the dirty checking mechanism will automatically translate all entity state transitions to database DML statements.

Session.update is meant to re-attach detached entities (entities that were loaded in a Session that's been closed).

Session.merge is for copying the entity state onto an already loaded entity (which is loaded on the fly, if not loaded previously).

Check if you have enabled transactions, as otherwise you can only select entities. For persist/merge and dirty checking updates you must use transactions (use Java EE or Spring @Transactional support).

2 Comments

I agree with you comments. I've already used 'fetch join' in a HQL query but I had the same effect. That's why I tried the Hibernate.initialize() just to be sure. I'm using Spring Transactional annotation with readOnly=false. It just seems that Hibernate doesn't detect the dirty properties. The problem is that the proxy does not get initialised properly (like you can see in the debug view). Hibernate doesn't check not initialised associations (would be a tremendous overhead). The question still is why it isn't initialised properly.Would it have something to do with the implementing interfaces?
In my experience, it's better to avoid interface associations on entities. You can have interfaces but make all associations on actual entities because this models the real relationships anyway.
0

The post https://forum.hibernate.org/viewtopic.php?f=1&t=997278 was useful for me. As the getter methods in Entity Model Objects were marked as final, Javaassist wasn't able to override the method and thus change it's value. My Entity Objects were like this -

@Entity
@Table(name = "COUNTRIES")
public class Country {
  @Id
  private Long id;
  @Column(nullable = false)
  private String name;

  private final getId() {
    return id;
  }

  private final getName() {
    return name;
  }
}

1 Comment

While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.