2

I'm facing LazyInitializationException when I'm trying to access ID of a lazy @ManyToOne reference of a detached entity. I do not want to fetch the refrence completely, but just need the ID (which should be exist in original object in order to fetch refrence in a lazy/deferred manner).

EntityA ea = dao.find(1) // find is @Transactional, but transaction is closed after method exits
ea.getLazyReference().getId() // here is get exception. lazyReference is a ManyToOne relation and so the foreight key is stored in EntityA side.

To paraphrase, how can I access ID of LazyReference (which actually exists in initial select for EntityA) without actually fetching the whole LazyReference?

v.ladynev
  • 19,275
  • 8
  • 46
  • 67
Mohsen
  • 3,512
  • 3
  • 38
  • 66
  • Possible duplicate of [Just getting id column value not using join in hibernate object one to many relation](http://stackoverflow.com/questions/32220951/just-getting-id-column-value-not-using-join-in-hibernate-object-one-to-many-rela) – Dragan Bozanovic Jan 26 '16 at 13:07
  • Thanks for your dup suggestion. BTW, the question you mentioned may not clearly answer anyone with my own question (although answered mine). The answer has nothing about LazyInitializationException, so people may hardly find it when searching for questions like this one. Please answer (@Access) this again here, suggesting the other question. – Mohsen Jan 27 '16 at 06:37
  • You're welcome. Done, please see my answer below. – Dragan Bozanovic Jan 27 '16 at 12:15

3 Answers3

5

When field access is used, Hibernate treats getId() method the same as any other method, meaning that calling it triggers proxy initialization, thus leading to LazyInitializationException if invoked on a detached instance.

To use property access only for id property (while keeping field access for all the other properties), specify AccessType.PROPERTY for the id field:

@Entity
public class A {
  @Id
  @Access(AccessType.PROPERTY)
  private int id;

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }
}
Dragan Bozanovic
  • 23,102
  • 5
  • 43
  • 110
1

That should be possible. I am able to get only the ID of the @ManyToOne LAZY entity.

But for that I have set annotations on the getters of the entity instead of setting them directly on the instance variables which results in null value.

I believe you are using annotations on the instance variables. You can try getter annotations and see if that helps you.

  • Yes, I actually put annotations on fields instead of getters. I'll try with getters and tell the result. Is this a bug with Hibernate? – Mohsen Jan 26 '16 at 12:57
  • I don't think it is a bug. I remember reading a link that explains this behavior. I will post the link as soon as I find it. – Madhusudana Reddy Sunnapu Jan 26 '16 at 13:14
  • @MadhusudanaReddySunnapu I just check your suggestion and it is correct — there is not any `LazyInitializationException` if we use annotations on getters. I can't remember in which situations, but, sometimes, our team have such kind of exceptions while getting identifier using a getters annotations approach. – v.ladynev Jan 26 '16 at 14:26
  • My ID field was field-annotated. I put a @Access(AccessType.FIELD) on my class (or possibly ID field) and everything worked. No need to put annotation on getters. – Mohsen Jan 27 '16 at 06:33
0

You get an LazyInitializationException exception, because of Hibernate wraps your persistent with a proxy object. A proxy generates an exception for any getter of a lazy object even for id that LazyReference already has of course.

To get id without LazyInitializationException you can use this method (you can refer the link for other interesting utilite methods)

@SuppressWarnings("unchecked")
public static <T> T getPid(Persistent<?> persistent) {
    if (persistent == null) {
        return null;
    }

    if (!(persistent instanceof HibernateProxy) || Hibernate.isInitialized(persistent)) {
        return (T) persistent.getPid();
    }

    LazyInitializer initializer = ((HibernateProxy) persistent).getHibernateLazyInitializer();
    return (T) initializer.getIdentifier();
}

Persistent is a base class for all persistents. For your LazyReference you can rewrite code like this

@SuppressWarnings("unchecked")
public static Long getId(LazyReference persistent) {
    if (persistent == null) {
        return null;
    }

    if (!(persistent instanceof HibernateProxy) || Hibernate.isInitialized(persistent)) {
        return persistent.getId();
    }

    LazyInitializer initializer = 
        ((HibernateProxy) persistent).getHibernateLazyInitializer();
    return initializer.getIdentifier();
}  
v.ladynev
  • 19,275
  • 8
  • 46
  • 67