When starting with Seam I had two problems: I knew Spring and Hibernate, but I knew nothing about Seam and EJB3's EntityManager. It took some time to learn Seam, but wrapping my head around the EntityManager was difficult.
Meanwhile I'm a little more familiar with that stuff and I'd like to share my observations with you guys that just started using Seam. Feel free to contribute - especially since I'm not sure that everything I write is correct ;).
So, what's to say about the EntityManager? It cares about your entities ;). Those are simple Java objects, with some properties plus their getters and setters. One of the properties must be an Id (mostly a Long) and the class itself must be annotated with @Entity. You can find a lot of examples in the Seam sample applications (like the booking example). In Seam it's important that every Entity even gets a @Name annotation, so that it can be injected into other Seam components.
Let's assume we have such a entity class which we will call "Entity". The lifecycle of such an entity can include
The EntityManager provides methods to perform those operations. But first: how to get the EntityManager into my code? It's simple:
@PersistenceContext private EntityManager em;
Ok, now let's begin by watching how an instance of Entity is born:
Entity entity = new Entity();
That's simple. Now the state of this entity is NEW/TRANSIENT. That means that an entity exists in your application, but it has no Id and it does not exist in the database.
Since we want to make it persistent (i.e. it should be written to the database) we will transform its state into MANAGED.
Now the entity is managed by the EntityManager. He takes care of the entity to be written to the database. This needn't happen immediately, he might keep your entity in the cache and write it to the database later. But you can be sure that it will be written.
Ok, what about reading an existing entity from the database? Therefore we use:
Entity entity = em.find(Entity.class, Id);
Every entity has an Id (as I said mostly of type Long) with which you can access it. This is the second parameter here. The first parameter shows that you want to access an instance of the Entity class. After performing the find operation the entity is in state MANAGED, too.
Ok, as long as an entity is managed every change on it will have an impact on the database. There's no guarantee when the EntityManager will write changes to the database. But it will be done, mostly even immediately, but not later than the em vanishes ;). You have the possibility to trigger database updates manually by
This will force the EntityManager to write updates to the DB right now. Mind that this affects all managed entities, not only a single one. Howeverm, usually you needn't call that method.
If you want it vice versa, namely to reload an entity from the database (maybe because it might have been changed by someone else), use
Now what about removing an entity? It's easy:
Now the entity's state becomes REMOVED which means that the entity is scheduled for deletion. You might call flush() if you want to make deletion performed immediately, but usually there's no need to.
Ok, now it gets a little more complicated. When injecting the EntityManager like I suggested above it has a Transaction Scoped Persistence Context. The persistence context is the "container" the entities live in while they are in state MANAGED. But was does "transaction scoped" mean? First, what is meant by "transaction" at all?
In EJB3 Stateful oder Stateless beans (recognizable by the homonymous annotations) every method invocation gets wrapped into a transaction. (Just by the way: Whenever a RuntimeException occurs during a transaction a rollback is performed and all changes to data get revoked). So the persistence context is created before invoking that method and removed after the method finished. Afterwards all entities that have been previously managed in this persitsence context become DETACHED.
Ok, so let's assume you have two methods in your bean. The first one is load(), that calls the find method in order to retrieve an entity in the database. The second method is finish() that might just return a JSF outcome. Between calling those two methods you edit the entity. Will those changes persisted to the database? The anwser is NO.
After the load() method ended, the EntityManager's persistence context ends, too. All the managed entities now become DETACHED. As a result, those entities - in opposition to transient/new - do have an Id, but they are not managed and changes on those detached entities don't affect the database. When you want to have an entity updated to the database you need to re-attach it to a persistence context. In this case add the following line to the finish() method:
Now the entity gets merged into the finish() method's persistence context (remember: every method is a transaction, and every transaction has its own persistence context) and is managed again.
This works, but it has two disadvantages:
- you need to call merge (->more code)
- if you access certain attributes of the entity that have not been initialized during calling find() (e.g. collections of related entities) you will get an exception
So there's an easy solution: We extend the lifetime of the persistence context so that the entities remain managed during calling multiple transactions/methods. Therefore we change the injection of the EntityManager:
@PersistenceContext(type=PersistenceContextType.EXTENDED private EntityManager em;
Now the managed entities "live" in an Extended Persistence Context
You even don't have to call the merge() method since the entities never get DETACHED. So you might ask what is the "normal" (transaction scoped) persistence context good for? Well, it always depends on what you do. The extended context might need more memory as he's always there, even if you don't need him. And whenever entities were changed by other beans (they have their own persistence context) you need to refresh() them explicitely (e.g. in overviews/list pages). The normal EntityManager is just there when you need him and due to his short lifetime he always serves fresh up-to-date data ;)
Last but not least, when talking about lists: To retrieve not a single entity but a collection of them, use
List<Entity> entities = em.createQuery("from Entity").getResultList();
This is not "real" SQL, it's a similar thing called EJBQL. You can even perform restrictions or ordering, e.g.:
..."from Entity where lastName=".nameToSearchFor." order by firstName"
Just use the names of the entity's properties. There's much more to say about EJBQL but this would be too much for this introduction ;).
Ok, so far for the basics. I hope I was able to give you a brief and comprehensible overview of how to work with the EntityManager.
As I said, I cannot guarantee everything is 100% correct. And there surely will be some ugly english parts in here (sorry, I'm German, don't beat me *g*). Feel free to add your comments and corrections.