Persistence and Hibernate

treeexcellentSoftware and s/w Development

Dec 13, 2013 (3 years and 7 months ago)

91 views

Persistence-Layer
Axel B¨ottcher
13.Dezember 2011
Objectives

Understand the principles of ORM

Comprehend basic mappings of classes to tables

Know the difference between JPA and a concrete
implementation

Know how Hibernate does (lazy) loading

Be able to use Hibernate in a concrete application
Object-Relational Mapping (ORM)
Oh ja,man kann es sp¨uren,das Orm!Es kann Dir einen Tritt in
den Hintern verpassen,wie ein Blitz in Dich fahren oder Dir den
Magen umdrehen.Es kann Dir Dein Gehirn aus dem Kopf reißen
und es verkehrtherum wieder einsetzen!Es kann mitten in der
Nacht auf Deiner Brust sitzen und Dir einen furchtbaren Alptraum
verschaffen,aus dem Dein bester Roman wird.Ich habe es gesp
¨
urt,
das Orm.Oh ja!Und ich w¨unschte,ich w¨urde es nur noch ein
einziges Mal erleben.
Der Schattenk¨onig in Walter Moers,

Die Stadt der tr¨aumenden
B¨ucher“
ORM is a bridge (...) that allows applications to access relational
data in an object-oriented way.
Craig Russel,

Bridging the Object-Relational Divide“,ACM
queue Vol.6 No.3,2008
Introduction
Definition
Persistent data are
Data that outlive the program.
(Grady Booch)
Persistence in the Java universe:
Java Persistence APIs
EJB
JPA
Hibernate
EclipseLink (TopLink)
Java Data Objects
Domain Classes and POJOs
File access
Serialization
DB Access before JPA
Java Database Connectivity (JDBC) offers an SQL-oriented API in
java.sql.*
1 p u b l i c ResultSet DBSelectQuery (String sql) {
2 ResultSet resultSet = n u l l;
3 t r y {
4 Statement stmt = conn.createStatement ();
5 resultSet = stmt.executeQuery (sql);
6 } cat ch (Exception e) {
7//Pr ope r l y handl e e x c e p t i on s her e!
8 }
9 r e t u r n resultSet;
10 }
DB Access before JPA
1 List <Person > loadPersons () {
2 List <Person > list = new ArrayList <Person >();
3 ResultSet rsma = db.DBSelectQuery (
4"select id, name, prename, "+
5 +"address from person where id=’"+temp.getId()+"’")
6 rsma.beforeFirst ();
7 whi l e (rsma.next()) {
8 list.add(new Person(rsma.getInt(1),
9 rsma.getString ("name"),
10 rsma.getString ("prename"),
11 rsma.getString (3));
12 }
13 r e t u r n list;
14 }
Entity Classes

Not allowed to be final,should implement Serializable

Annotated with @javax.persistence.Entity

Public/protected no-argument constructor required

Primary key is annotated with @javax.persistence.Id

Non-persistent fields must be annoatated with @Transient

Entity mapping has to be configured in xml-configuration
(e.g.:hibernate.cfg.xml):
1 <mapping class="edu.hm.inheritance.Student"/>
2 <mapping class="edu.hm.mapping.Lecture"/>
Primary Keys
It is very common to use unique Integers as Ids (Primary Keys)
and let the persistence framework generate them automatically (so
called surrogate keys
1
) with
1 @Id
2 @javax.persistence.GeneratedValue
3 p r i v a t e Integer id;
However,other attributes may be used as a primary key:
1 @Id
2 p r i v a t e String userName;
1
surrogate:Stellvertreter,Ersatz(stoff)
Example Classes
ORM
Table
PERSON
ID
NAME
FIRSTNAME
1
Lockhart
Gilderoy
2
Longbottom
Neville
...
...
...
Class
Person
Id:int
lastName:String
firstName:String
Instance
Person@ABD0
1
"Lockhart"
"Gilderoy"
Deceptive Similarities
Caution:The mapping is not isomorphic!

Length of Strings is limited in databases

Floating points are not necessarily stored in IEEE-754-format

Behavior (methods) is not stored in database

Class names must not be SQL key words,e.g.User!In this
case use @Entity@Table(name="TUSER").
2
Hungarian Notation could be considered for persisted classe as
well,e.g.TUser
2
The @Table Annotation has some other noticeable attributes like unique,
updatable or nullable
Inheritance Strategies:Single Table
Mapping inheritance only works with classes,not with interfaces!
1 @Entity
2 @Inheritance (strategy=InheritanceType.SINGLE_TABLE )
3 p u b l i c c l a s s Person i mpl ement s Serializable {
PERSON
DTYPE
ID
FIRSTNAME
LASTNAME
MATRIKELNR
SALARY
Student
1
Neville
Longbottom
0785642
Teacher
2
Gilderoy
Lockhart
10000,01

Uses a discriminator column (customizable if desired).

Simple and performant

But many unused columns in wide inheritance hierarchies (bad
normalization)
Inheritance Strategies:Joined classes
1 @Entity
2 @Inheritance (strategy=InheritanceType.JOINED)
3 p u b l i c c l a s s Person i mpl ement s Serializable {
4
PERSON
ID
FIRSTNAME
LASTNAME
1
Neville
Longbottom
2
Gilderoy
Lockhart
STUDENT
ID
MATRIKELNR
1
0785642
TEACHER
ID
SALARY
2
10000,01

Good normalization

Requires joins for all data access operations
Inheritance Strategies:Table per concrete class
1 @Entity
2 @Inheritance (strategy=InheritanceType.TABLE_PER_CLASS )
3
TEACHER
ID
FIRSTNAME
LASTNAME
SALARY
2
Lockhart
Gilderoy
10000,01
STUDENT
ID
FIRSTNAME
LASTNAME
MATRIKELNR
1
Neville
Longbottom
0785642
Relationships
Example:more than one teacher per lecture
1 @Entity
2 p u b l i c c l a s s Lecture {
3 @ManyToMany
4 p r i v a t e List <Teacher > teachers = new ArrayList <Teacher >()
5...
6 }
Generates a join table:
LECTURE
TEACHER
LECTURE
ID
TEACHERS
ID
4
17
4
1
3
9
5
17
A closer look
But wait:that’s symmetric!
Map also from other side (class Teacher):
1 @OneToMany (mappedBy="teachers")
2 p r i v a t e List <Lecture > lectures = new ArrayList <Lecture >();

All four combinations of One and Many possible:

...ToOne:implemented with instance variable

...ToMany:Implemented with Collection (List,Set,Bag)

Behavior is dominated by the underlying database.E.g.
One
-to-One cannot be verified by the Java language,but by
database;a JPA-Exception can be thrown when violated.

A Set must not contain a null -Reference

Order can be maintained (thus maintaining null s):
1 @ManyToMany
2 @IndexColumn (name="INDEX_COL")
Maps
1 @Entity
2 p u b l i c c l a s s ShoppingBasket {
3 @Id
4 @GeneratedValue
5 p r i v a t e Integer id;
6
7 @ElementCollection
8 Map <Product,Integer > products =
9 new HashMap <Product,Integer >();
10
11 ShoppingBasket () { }
12
13 p ub l i c voi d addToBasket (Product product,i n t number) {
14 products.put(product,number);
15 }
16
17 }
Use @ElementCollection to map non-Entity-API-Classes.
Map is mapped (!) to a table on its own with (principally) three
references:
Sessions
The phrases Session,Entity Manager and Persistence Manager are
often used synonymously.
Persistence Frameworks use Factories to create Sessions (or Entity
Managers...).It must be assured that there is only one such
Factory configured per database in use!
1 p r i v a t e Configuration cfg;
2 cfg = new AnnotationConfiguration();
3 cfg.setProperty ("hibernate.connection.url","databaseName");
4 cfg.configure("/hibernate.cfg.xml");
5 sessionFactory = cfg.buildSessionFactory ();
Assure one factory per database!
Sessions,Transactions,and Objects
A session is required for any access to persisted data.
Session session = sessionFactory.getCurrentSession();
Transactions are obtained from sessions.There may be only one
active transaction per session:
1 t r y {
2 session.beginTransaction ();
3...
4//some op e r a t i ons her e
5...
6 session.getTransaction ().commit();
7 } cat ch (...Exception ex) {
8 session.getTransaction ().rollback ();
9 }
An object is always in one of four states:

Transient

Persistent

Detached

Removed
1 Person person = new Person();//Tr ans i e nt
2 session.save(person);//Pe r s i s t e n t
3 session.close();//Detached
4 session.update(person);//Pe r s i s t e n t
5 session.delete(person);//Removed
Cascading
1 @OneToMany (cascade=CascadeType.ALL)
2 PERSIST)
3 REMOVE)
4 REFRESH)
Lazy Loading
Depending objects are loaded as they are needed,
or they are loaded in advance (eagerly)
@OneToMany(fetch=FetchType.EAGER)
You might get:
org.hibernate.LazyInitializationException:failed to
lazily initialize a collection of role:
edu.hm.Lecture.teachers,no session or session was
closed
Queries
Remember the example at the beginning of this unit!
SQL queries as Strings have at least the following drawbacks:

Low level programming style

Syntactical problems (well not always;they might occur from
time to time)

Hard to debug at runtime

injection problems (see also below)

Late detection of problems
Query languages are somewhat limited but they are type safe!
Queries
Queries are executed on sessions.
Criteria API
cool since programmatic (but still buggy?)
1 Teacher teacher = (Teacher)session.
2 createCriteria (Teacher.c l a s s ).
3 add(Restrictions.eq("name",name )).
4 uniqueResult ();
Hibernate Query Language (HQL)
Direct SQL – no automatic mapping
SQL Injection
See:http://xkcd.com/327/
Example in HQL
1 String queryString =
2"from Lecture l inner join l.teacher t where "
3 +"l.module.name like "
4 +":searchString or t.name like :searchString";
5 Query query = session.createQuery (queryString );
6 query.setParameter ("searchString",searchString +"%");
7 lectures = query.list ();

Working with setParameter is save against SQL-injection
Same Example in Criteria
Search for teacher or module name starting with a given String
searchString
1 criteria = session.createCriteria (persistentClass ).
2 createAlias ("teacher","teacher").
3 createAlias ("module","module").
4 add(Restrictions.or(Restrictions.ilike("module.name",
5 searchString,MatchMode.START),
6 Restrictions.ilike("teacher.name",searchString,
7 MatchMode.START)));
8 lectures = criteria.list ();

Criteria is save against SQL-injection

createAlias creates the join operation (variants available with
parameters for specifying join types)
A more sophisticated example
3
Search in inheritance hierarchy built with
InheritanceType.SINGLE_TABLE.Problem:Dtype is not part of entity
classes!SUe annaotations to make sure you have the correct table
names and discriminator values at hand!
HQL:
1 String queryString =
2"from Exemplar e left join e.resource as r "+
3"where r.resourceName = :string and TYPE(r) = ’Dvd’";
4 Query query = session.createQuery (queryString );
5 query.setParameter ("string",searchFor );
6 List <Exemplar > list = query.list();
Criteria:
1 Criteria c = getSession ().createCriteria (Exemplar.c l a s s )
2.createAlias ("resource","r").add(Restrictions
3.eq("r.resourceName",string))
4.add(Restrictions.sqlRestriction ("DTYPE = ’Dvd’"));
3
Thanks to Rudolf Vocilka and Richard Voggenauer
Apache Derby
Derby is a relational database written fully in Java.Two variants
can be used:Embedded and Network-server based.
We will use the embedded variant only.
Easy to use:just derby.jar has to be in the classpath
esay integration with Hibernate (hibernate.cfg.xml):
1 <property name="connection.driver_class">
2 org.apache.derby.jdbc.EmbeddedDriver
3 </property >
4 <property name="dialect">
5 org.hibernate.dialect.DerbyDialect
6 </property >
Drawback:only one connection allowed!
What’s in my db?
1.
Eclipse JEE distribution contains a Data Source Explorer view
to inspect a database.
2.
Derby provides th command line tool ij to access a derby db
(see the ShareIt project for details).
3.
You can use schemaspy (schemaspy.sourceforge.net to
display and analyze database’s schema/metadata.
Testing database operations
Database testing is similar to testing singletons.
Explain why?