7

I'm starting a new project and I'm totally new to JPA/Hibernate use. I'm trying to understand how to use EntityManager properly. More precisely, when to instantiate them, how many do I need, should I close them, should I put everything into transactions?

Anyway, in my current code, I got an org.hibernate.LazyInitializationException while trying to read an entity that I previously saved. I would understand the opposite (reading an antity in a transaction then trying to save the read entity in another transaction but since transaction is over, the entity is unmanaged so save fails), but this I can't understand.

I put my code on GitHub (https://github.com/GaetanLeu/intl), it's just a couple of classes. My main is in src/sandbox/MessageSandbox.java and it fails at line 28 with the following stacktrace:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:164)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:285)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
    at entity.MessageKey_$$_jvstfcc_0.toString(MessageKey_$$_jvstfcc_0.java)
    at java.lang.String.valueOf(String.java:2854)
    at java.lang.StringBuilder.append(StringBuilder.java:128)
    at com.google.common.base.Present.toString(Present.java:88)
    at java.lang.String.valueOf(String.java:2854)
    at java.io.PrintStream.println(PrintStream.java:821)
    at sandbox.MessageSandbox.main(MessageSandbox.java:28)

Also I got a warning from Hibernate saying my EntityManager already exists, what happens then? Is the EntityManagerFactory.createEntityManager method returning the existing one?

WARN: HHH000436: Entity manager factory name (intl) is already registered.  If entity manager will be clustered or passivated, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'

Really I'm lost about when to create EntityManagers ^^ Any help would be appreciated, but please simple explanation I'm really new to this.

Oh BTW, I want to precise I'm not using Spring, I don't have EJBs, I want to manipulate EntityManagers manually for now until I understand it. Thanks :)

DeleteMePlease
  • 91
  • 1
  • 3
  • 8

1 Answers1

14

An entityManager manages a persistence context, in other word an in memory snapshot of the database state.

See What is a persistence object?

Every object loaded using the entityManager will be in managed state (see entity life cycle) until you close the EM. When an entity is managed, all change made to it will be tracked and then persisted when flushing the EM. If you access some lazy fetched attribute, a request will be automatically triggered to dynamically load the data, but if the entity is in detached state (if EM have been closed) accessing a lazy attribute will lead to the error you get.

The scope (/lifecycle) of your EM depends of your execution context. For a web application for example, an EM will typically be created for each http request.

For a standalone application, you have to mind if the database can be updated by another application/thread or not. If it can, your persistent context may not be consistent with the database state and you should create it for every unit of work (transaction) to avoid that. Otherwise you can create a single instance once for all the application lifecyle and flush it regulary.

For a CRUD app the lifecycle is generally the following :

  • create the EM
  • fetch some entities (they are so managed, any access to lazy attribute will load the data from DB)
  • close the EM (entity are now detached, any access to lazy attribute will lead to LazyInitializationException)
  • display the data to the user

On user updates validation :

  • create the em
  • open a transaction
  • merge (attach) your updated entities (this is what you call save) (if you have set up some optmistic locking, em will check the entity version against the database here)
  • eventually perform some business validation or additional updates
  • commit the transaction and close the em (changes will be flushed)

Keep is mind that EM is a lightweight object, cheap to create and destroy and NOT THREADSAFE.

BTW, JPA is a Java EE specification which is part of the EJB one (the persistence part). Its aim is to be used in a java EE container context (Java EE application server or CDI since JEE 6). You still can use hibernate in standalone mode through the JPA contract but even in this case, coupling with spring must be considered to take advantage of container managed features.

Community
  • 1
  • 1
Gab
  • 7,869
  • 4
  • 37
  • 68
  • Thanks for your answer. So, If understand what you're saying, since my 'save' function and my 'read' function both intanciate their own EntityManager, and since I don't manually flush after saving my entity, when my read function is reach and the second EM instanciated, my entity hasn't been persisted and cannot be found. But why do I get a 'LazyInitializationException' and not a NPE since it should be the whole entity that isn't found, no? – DeleteMePlease Mar 31 '14 at 23:13
  • And BTW, why aren't change persisted right away when calling .persist? Isn't it dangerous to have 'committed' changes that aren't actually persisted in the database? Especially of multiple apps/EM query this db? What's the best practice here? Do I have to flush every time I persist? / commit ? – DeleteMePlease Mar 31 '14 at 23:17
  • "when my read function reach the second EM instanciated, my entity hasn't been persisted and cannot be found." No no you misunderstand. you get the error cause you closed the EM before assessing a lazy fetched attribute. – Gab Apr 01 '14 at 07:21
  • you should have only one EM opened at a time per thread, there is no interest to maintain 2 persistence context at the same time. It's not only a waste of resource but it will lead to problem as they won't be aware each other of their modification – Gab Apr 01 '14 at 07:23
  • persist just change the state of the entity from 'new' to 'managed' as merge only change the state from 'detached' to managed. – Gab Apr 01 '14 at 07:27
  • in JEE context (Container managed EM) the EM will be flushed on each JTA transactions commit automatically. From a general point of view, when multiple EM do concurrent accesses to the DB they should be instantiated for each unit of work and released immediately – Gab Apr 01 '14 at 22:34