Object Persistence Using Hibernate: An Object-Relational Mapping Framework For Object Persistence
Object Persistence Using Hibernate: An Object-Relational Mapping Framework For Object Persistence
User Interface
UI event data xfer object
Application Logic
data request domain object
Domain Objects DAO
Hibernate API domain object
SessionFactory
Hibernate
hibernate.cfg.xml
JDBC API ResultSet, etc.
*.hbm.xml class mappings
Foundation Classes JDBC
Another View
to tell Hibernate to set the field values directly (don't use the "set"
method) in the class mapping file write:
<hibernate-mapping default-access="field">
...
Schema to create Locations table
This works for MySQL.
Hibernate can generate table schema at runtime.
LOCATIONS
PK id INTEGER
name VARCHAR(80)
address VARCHAR(160)
Mapping File Location.hbm.xml
An XML file describing how to map object to table.
Filename: Location.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC ... remainder omitted >
<hibernate-mapping package="eventmgr.domain">
<class name="Location" table="LOCATIONS">
<id name="id" column="id">
<!-- let hibernate choose id for new entities -->
<generator class="native"/>
</id>
<property name="name" column="name" not-null="true"/>
<property name="address"/>
</class>
</hibernate-mapping>
Mapping File Explained
root element of a hibernate mapping
<hibernate-mapping>
<class name="eventmgr.domain.Location"
table="LOCATIONS"
options... >
<id name="id"
column="id"
mapping for one class
unsaved-value="0">
<generator class="native"/>
</id>
object attribute mappings
</class>
</hibernate-mapping>
HibernateUtil 1
-sessionFactory: SessionFactory
+getSessionFactory( ): SessionFactory
+getCurrentSession( ): Session
+openSession( ): Session
HibernateUtil: a Session Factory (2)
public class HibernateUtil {
private static SessionFactory sessionFactory = null;
private static final Logger log = Logger.getLogger(..);
Session session =
HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
session.saveOrUpdate( loc1 );
session.saveOrUpdate( loc2 );
tx.commit();
System.out.println("Locations saved");
}
Persistence Test: LocationTest.java
public static void testQuery() {
System.out.println("Retrieving locations");
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
Event Location
id: long id: int
* 1
name: String name: String
startDate: Date address: String
location: Location
Event Location
id: long id: int
* 1
name: String name: String
startDate: Date address: String
location: Location
The Data Mapper converts a n-to-1
association to a foreign key relation (persist)
or foreign key to object (retrieve).
EVENTS LOCATIONS
PK id BIGINT PK id INTEGER
name VARCHAR name VARCHAR
start_date TIMESTAMP address VARCHAR
FK location_id INTEGER
Mapping for Event (Event.hbm.xml)
Use <many-to-one name="attribute" ... />
to map a reference to another object.
<!DOCTYPE hibernate-mapping PUBLIC ... remainder omitted >
<hibernate-mapping package="eventmgr.domain">
<class name="Event" table="EVENTS">
...
Error: LazyInstantiationException
Hibernate uses lazy instantiation and proxy objects
Hibernate instantiates the location object when it is first accessed
We closed the transaction before accessing the location
Two Solutions
1. Modify our code: getLocation( ) before closing the
session.
List<Event> list = (List<Event>)query.list( );
for(Event e : list ) out.printf("%d %s %s\n",
e.getId(), e.getName(), e.getLocation().getName()
);
tx.commit( );
Location Address
id: int * 1 street: String
name: String city: String
address: Address province: String
LOCATIONS
id name address
101 Kasetsart University street city province
102 Seacon Square
Organizing your Work
LocationDao
public LocationDao() { }
public Location findById( int id )
public List<Location> findByName( String name )
public List<Location> find( String query )
public boolean save( Location loc )
public boolean delete( Location loc )
}
The core of findById( ) - use "load"
public Location findById( int id ) {
Location result = null;
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getCurrentSession();
tx = session.beginTransaction();
result =
(Location)session.load(Location.class, id);
tx.commit();
session.close( );
} catch ...
}
return result;
}
The details of findById( )
public Location findById( int id ) {
Location result = null;
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getCurrentSession();
tx = session.beginTransaction();
result = (Location)session.load(Location.class,id);
tx.commit();
} catch (ObjectNotFoundException e) {
logger.info("Object not found. id = "+id, e);
if ( tx != null && ! tx.wasCommitted() ) tx.rollback();
} catch (HibernateException e) {
logger.error("Hibernate exception", e);
if ( tx != null && ! tx.wasCommitted() ) tx.rollback();
} finally {
if ( session != null && session.isOpen() ) session.close();
}
return result;
}
The core of findByName( ) - "query"
public List<Location> findByName( String name ) {
List<Location> result;
...
try {
session = HibernateUtil.getCurrentSession();
tx = session.beginTransaction();
Query query = session.createQuery(
"from Location where name=:name" );
query.setParameter( "name", name );
result = (List<Location>) query.list( );
tx.commit();
session.close( );
} catch ...
return result;
}
Details of findByName( )
Exercise
The core of save( ) - "saveOrUpdate"
public boolean save( Location location ) {
boolean result = false;
...
try {
session = HibernateUtil.getCurrentSession();
tx = session.beginTransaction();
session.saveOrUpdate( location );
tx.commit();
session.close( );
result = true;
} catch ...
return result;
}
Details of save
Exercise
The core of delete( ) - "delete"
public boolean delete( Location location ) {
boolean result = false;
...
try {
session = HibernateUtil.getCurrentSession();
tx = session.beginTransaction();
session.delete( location );
tx.commit();
session.close( );
result = true;
} catch ...
return result;
}
Redundant Code
Every DAO method has the same boilerplate code:
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getCurrentSession();
tx = session.beginTransaction();
do the work here
tx.commit();
} catch (ObjectNotFoundException e) {
logger.info("Object not found. "+id, e);
if ( tx != null && ! tx.wasCommitted() ) tx.rollback();
} catch (HibernateException e) {
logger.error("Hibernate exception", e);
if ( tx != null && ! tx.wasCommitted() ) tx.rollback();
} finally {
if ( session != null && session.isOpen() ) session.close();
}
Factor out Redundant Code
Class SimpleLocationDao {
private Session session; // declare as attributes
private Transaction tx;
public Event( ) { }
...
}
Event.hbm.xml Mapping File
<!DOCTYPE hibernate-mapping PUBLIC ... remainder omitted >
<hibernate-mapping package="eventmgr.domain">
<class name="Event" table="EVENTS">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name" not-null="true"/>
<property name="startDate" column="start_date"
column="timestamp"/>
<property name="duraction" />
<many-to-one name="location" column="location_id"
class="Location"/>
</class>
</hibernate-mapping>
Cascading Save & Update
When you save an Event, should Hibernate automatically
save the location, too?
no: then you must save the location yourself
Yes: specify cascade = "save-update"
<class name="Event" table="EVENTS">
<id name="id" column="id">
<generator class="native"/>
</id>
...
<many-to-one name="location" column="location_id"
class="Location"
cascade="save-update"/>
</class>