1.3.3. Working the association
Let's bring some people and events together in a new method in EventManager
:
private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
Event anEvent = (Event) session.load(Event.class, eventId);
aPerson.getEvents().add(anEvent);
session.getTransaction().commit();
}
After loading a Person
and an Event
, simply modify the collection using the normal collection methods. As you can see, there is no explicit call to update()
or save()
, Hibernate automatically detects that the collection has been modified and needs to be updated. This is called
automatic dirty checking
, and you can also try it by modifying the name or the date property of any of your objects. As long as they are in
persistent
state, that is, bound to a particular Hibernate Session
(i.e. they have been just loaded or saved in a unit of work), Hibernate monitors any changes and executes SQL in a write-behind fashion. The process of synchronizing the memory state with the database, usually only at the end of a unit of work, is called
flushing
. In our code, the unit of work ends with a commit (or rollback) of the database transaction - as defined by the thread
configuration option for the CurrentSessionContext
class.
You might of course load person and event in different units of work. Or you modify an object outside of a Session
, when it is not in persistent state (if it was persistent before, we call this state
detached
). You can even modify a collection when it is detached:
private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person aPerson = (Person) session
.createQuery("select p from Person p left join fetch p.events where p.id = :pid")
.setParameter("pid", personId)
.uniqueResult(); // Eager fetch the collection so we can use it detached
Event anEvent = (Event) session.load(Event.class, eventId);
session.getTransaction().commit();
// End of first unit of work
aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
// Begin second unit of work
Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
session2.beginTransaction();
session2.update(aPerson); // Reattachment of aPerson
session2.getTransaction().commit();
}
The call to update
makes a detached object persistent again, you could say it binds it to a new unit of work, so any modifications you made to it while detached can be saved to the database. This includes any modifications (additions/deletions) you made to a collection of that entity object.
Well, this is not much use in our current situation, but it's an important concept you can design into your own application. For now, complete this exercise by adding a new action to the EventManager
's main method and call it from the command line. If you need the identifiers of a person and an event - the save()
method returns it (you might have to modify some of the previous methods to return that identifier):
else if (args[0].equals("addpersontoevent")) {
Long eventId = mgr.createAndStoreEvent("My Event", new Date());
Long personId = mgr.createAndStorePerson("Foo", "Bar");
mgr.addPersonToEvent(personId, eventId);
System.out.println("Added person " + personId + " to event " + eventId);
}
This was an example of an association between two equally important classes, two entities. As mentioned earlier, there are other classes and types in a typical model, usually "less important". Some you have already seen, like an int
or a String
. We call these classes
value types
, and their instances
depend
on a particular entity. Instances of these types don't have their own identity, nor are they shared between entities (two persons don't reference the same firstname
object, even if they have the same first name). Of course, value types can not only be found in the JDK (in fact, in a Hibernate application all JDK classes are considered value types), but you can also write dependent classes yourself, Address
or MonetaryAmount
, for example.
You can also design a collection of value types. This is conceptually very different from a collection of references to other entities, but looks almost the same in Java.