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
- creation
- reading
- updating
- deletion
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.
em.persist(entity);
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
em.flush();
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
em.refresh(entity);
Now what about removing an entity? It's easy:
em.remove(entity);
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:
em.merge(entity);
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.
View the original post :
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3964316#...
Reply to the post :
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&a...