Sunteți pe pagina 1din 54

The Nuts and Bolts of Hibernate/JPA

Jay Paulsen ITS Administrative Information Systems

Agenda

Introduction Java Persistence Choices Terminology Configuring Hibernate/JPA Working with Persistent Objects Gotchas Final Comments

Introduction

What is Hibernate?

Hibernate is an object-relational mapping (ORM) library for the Java language, providing a framework for mapping an object-oriented domain model to a traditional relational database.

http://en.wikipedia.org/wiki/Hibernate_%28Java%29

What is JPA?

Java Persistence API a standardized interface for Java Persistence that persistence providers implement Hibernate is one such JPA provider Others include TopLink from Oracle and OpenJPA from Apache

MAUI

Student Information System project

Java web application(s) backed with Oracle database

Functional Scope

Admissions (Prospects/Applicants/Institutions ...) Student Records (Courses/Enrollment/Degrees ...) Financial Aid Billing Advising

Schema

354 tables 3979 columns 728 relationships still growing

Java Persistence Frameworks

JDBC with DAO Pattern


iBATIS SQL Mapper

Hibernate/JPA

JDBC with DAO Pattern

Advantages

Developers familiar with pattern Helper classes and framework already existed Several successful projects already using it

Disadvantages

All SQL hand-coded Objects too much like result sets lacked relationships between domain objects

iBATIS SQL Mapper

Advantages
Disadvantages

No experience with it All SQL hand-coded XML heavy

Hibernate/JPA

Advantages

Some developers familiar with pattern Could truly map the domain including relationships A few successful projects already using it All insert/update/delete SQL generated by Hibernate Widely gaining adoption JPA persistence provider no XML (okay just a little)

Disadvantages
New language for querying HQL Generated SQL hard to read Familiarity breeds contempt

Terminology

Entity Entity Relationships Persistence Unit Persistence Context EntityManager Fetch Plan Proxy

Entity

Lightweight persistent domain object the thing you persist

Restrictions

must have a public or protected no-arg constructor cannot be final cannot have final methods or final instance variables that are to be persisted can be abstract or concrete class must have a primary key

Entity Relationships

Defined by relationships in the database schema

ManyToOne OneToOne OneToMany ManyToMany

Facilitates object graph navigation

Persistence Unit

The set of all classes mapped to a single database for the application Defined in META-INF/persistence.xml An application can have multiple persistence units MAUI has one persistence unit
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="maui" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> ... </properties> </persistence-unit> </persistence>

Persistence Context

Set of unique entity instances that an application works with at any one time local working copy of persistent objects

Entity Manager

The JPA interface for working with a persistence context More/less equivalent to the Hibernate Session interface Core methods a JPA application will use

persist remove find createQuery close

Fetch Plan

Determines when an associated entity should be loaded

EAGER: when the owning entity object is loaded LAZY: when your code accesses the associated object or collection
Student

Address

Proxy

A placeholder object generated by Hibernate at runtime


Used by hibernate to implement LAZY loading of ManyToOne/OneToOne related Entities Proxy is a subclass of the mapped class a placeholder for the actual object that contains the data

Configuring Hibernate/JPA

Need to indicate which classes should have their objects persisted


Well thought out defaults convention over configuration

Java 5 annotations or separate XML files

Annotations Overview

Core

@Entity @Table @Id @Basic @Column @Transient @Enumerated @Temporal @Type

Relationships @ManyToOne @OneToOne @OneToMany @JoinColumn

Inheritance

@MappedSuperclass @Inheritance @DiscriminatorColumn @DiscriminatorValue

@Entity / @Table
@Entity @Table(name=PROSPECT) public class Prospect extends Persistent { ... public Prospect() { ... } }
MAUI.PROSPECT PROSPECT_ID NUMBER(18)

LAST_UPD_BY
LAST_UP_TS STUDENT_ID ...

NUMBER(18)
TIMESTAMP(9) NUMBER(18) ...

@Id

Every entity needs a primary key

... public abstract class Persistent { @Id Long id;

...
}

@Basic / @Column
... public class Prospect extends Persistent { @Basic @Column(name = "HS_SELF_REPORTED_CLASS_SIZE") private Integer selfReportedClassSize; @Basic @Column(name = "HS_ANTICIPATED_YR_GRAD") private String anticipatedHighSchoolGraduationYear; ... } PROSPECT_ID ... HS_SELF_REPORTED_CLASS_SIZE HS_ANTICIPATED_YR_GRAD MAUI.PROSPECT NUMBER(18) ... NUMBER(5) CHAR(4)

...

...

@Transient

Indicates fields that should not be persisted


But it could be used for computed/derived data based on the persistent fields of the object

@Enumerated
public enum ProspectStatusEnum { PROSPECT("Prospect"), PROSPECT_INACTIVE( "Prospect Inactive"), PROSPECT_APPLIED( "Prospect Applied");
private String label; ProspectStatusEnum(String label) { this.label = label; } public String toString() { return this.label; } }

@Enumerated
... public class Prospect extends Persistent { ... @Enumerated(EnumType.STRING) @Column(name = "PRSP_STATUS_EN") ProspectStatusEnum prospectStatusEnum; ... }
MAUI.PROSPECT ... PRSP_STATUS_EN ... ... VARCHAR(25) ...

@Temporal
... public class Prospect extends Persistent { ... @Temporal(TemporalType.DATE) @Column(name = "PRSP_STATUS_DT") private Date prospectStatusDate; ... }
MAUI.PROSPECT ... PRSP_STATUS_DT ... ... DATE ...

@Type
... public class Prospect extends Persistent { ... @Basic @Column(name = "IS_CONTACT_DESIRED") @org.hibernate.annotations.Type( type = "org.hibernate.type.YesNoType" ) private Boolean contactDesired; ... } MAUI.PROSPECT
... IS_CONTACT_DESIRED ... ... CHAR(1) ...

@ManyToOne
... public class Prospect extends Persistent { @ManyToOne() @JoinColumn(name = "PURGE_SESSION_INFO_ID") private Session purgeSession; ... } MAUI.PROSPECT

PROSPECT_ID
... PURGE_SESSION_INFO_ID @Entity @Table(name = "MAUI.SESSION_INFO") public class Session extends Persistent { ... }

NUMBER(18) PK
... NUMBER(18) FK

MAUI.SESSION_INFO SESSION_INFO_ID ... NUMBER(18) ...

@OneToOne
public class Prospect extends Persistent { @OneToOne() @JoinColumn(name = "STUDENT_ID") private Student student; ... } PROSPECT_ID

MAUI.PROSPECT NUMBER(18) PK

...
STUDENT_ID

...
NUMBER(18) FK UNIQUE NOT NULL

@Entity @Table(name = "MAUI.STUDENT") public class Student extends Persistent { ... } MAUI.STUDENT STUDENT_ID ... NUMBER(18) PK ...

@OneToMany
public class Prospect extends Persistent { ... @OneToMany(mappedBy = "prospect) private Set<ProspectComment> prospectComments; } MAUI.PROSPECT PROSPECT_ID ... NUMBER(18) PK ...

@Entity public class ProspectComment extends Persistent { ... @ManyToOne() @JoinColumn(name = "PROSPECT_ID") private Prospect prospect; }
MAUI.PRSP_COMMENT PRSP_COMMENT_ID PROSPECT_ID ... NUMBER(18) PK NUMBER(18) FK NOT NULL ...

@MappedSuperclass
@MappedSuperclass public abstract class Persistent { ... @Id private Long id;
@Temporal @Column(name = LAST_UPD_TS) private Date lastUpdatedTimestamp; }

@Inheritance

Three strategies

Single Table Per Class Hierarchy

SINGLE_TABLE JOINED TABLE_PER_CLASS

Joined Subclass

Table Per Concrete Class

Single Table Per Class Hierarchy


@Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorFormula(...) public abstract class Institution {} @DiscriminatorValue(SECONDARY) public class SecondarySchool extends Institution {}
@DiscriminatorValue(POST_SECONDARY) public class PostSecondarySchool extends Institution {}

Working with Persistent Objects


// create a new prospect // and make it persistent persistent

Prospect p = new Prospect(); EntityManager em = ... em.persist(p); // Hibernate will execute an SQL insert statement

Working with Persistent Objects


// find an existing Prospect by Id // and delete it
EntityManager em = ... Long id = ... Prospect p = em.find(Prospect.class, id); em.remove(p);

// Hibernate will execute an SQL delete statement

Working with Persistent Objects


// find an existing Prospect by Id // and modify it
EntityManager em = ... Long id = ... Prospect p = em.find(Prospect.class, id); p.setSelfReportedClassSize(400); // // // // no explicit persist() call is needed this object is already persistent Hibernate will determine that it is dirty and will execute an SQL update statement

HQL

HQL is an object-oriented, SQL Dialect agnostic query language


JPQL is the query language standardized in the JPA specification

JPQL is a subset of HQL

HQL

Supports

Object retrieval as well as projection Joins on mapped relationships aggregate functions (avg, min, max, count(*), ...) polymorphic queries subqueries ordering grouping

Nearly everything* you can do in SQL, you can do in HQL

*Your DBA may not agree

HQL
// retrieve future sessions of a specific // term type (Fall, Spring ...) // order by the session start date select s from Session s where s.defaultStartDate > :now and s.term = :term order by s.defaultStartDate

HQL
select a from CoursePrerequisite a where a.course = :course and a.effectiveSession.defaultStartDate = ( select max(a2.effectiveSession.defaultStartDate) from CoursePrerequisite a2 where (1=1) and a2.course = a.course and a2.effectiveSession.defaultStartDate <= :sessionDate ) and ( (a.endSession in (select s from Session s where s.defaultStartDate >= :sessionDate ) ) or (a.endSession is null) ) order by a.effectiveSession.defaultStartDate DESC

HQL
select new uiowa.maui.biz.master.PersonSearchResult( p.id, prospect.id, p.ssn, p.prospectFlag, bio.birthDate, stName.lastName, stName.firstName, stName.middleName, rsAddr.city, rsAddrState.naturalKey, rsAddr.postalCode, pvEmail.emailAddress, uiEmail.emailAddress, purgeSession.shortDescription, prospect.studentTypeEnum ) from Person p inner join p.bio bio inner join p.currentNames stName with stName.nameLookup.id = :st left outer join p.currentAddresses rsAddr with rsAddr.addressLookup.id = :rs left outer join rsAddr.stateLookup rsAddrState left outer join p.currentEmails pvEmail with pvEmail.emailLookup.id = :pv left outer join p.currentEmails uiEmail with uiEmail.emailLookup.id = :ui left outer join p.student student left outer join student.prospect prospect left outer join prospect.purgeSession purgeSession where p.statusEnum = 'ACTIVE' and currentStandardName.cleansedLastName = maui.cleanse(:lastName) order by currentStandardName.cleansedLastName, currentStandardName.cleansedFirstName, currentStandardName.cleansedMiddleName

Gotchas

Object identity
Proxies are not transparent Fetch plan defaults JPA Callbacks Paging Query Results

Object identity

MAUI uses JVM object identity (a == b) Within a single Persistence Context, Hibernate guarantees that for any particular database row, there will be only one object instance MAUI does not use detached instances
Overriding equals() and hashcode() is necessary if you put persistent objects in a Set AND you want to work with detached instances

Proxies are not transparent

Recall, a proxy is: used for implementing lazy loading of a *ToOne relationship a subclass of the mapped class a placeholder for the actual object that contains the data

Proxies are not transparent

Runtime class comparison

Prospect p1 = ... Prospect p2 = ...

assert p1.getClass().equals(p2.getClass());
// assert will fail one reference is a proxy // and the other is not

Proxies are not transparent

Field access returns null

Prospect p1 = ... public class Prospect { ... public void someMethod(Prospect p) { String s = p.someStringField; // s will always be null // access via reflection returns null too // use getter method instead String s2 = p.getSomeStringField(); } }

Proxies are not transparent

Mapped inheritance hierarchies ClassCastExceptions instanceof object identity issues


Institution

SecondarySchool

PostSecondarySchool

Proxy for Institution

Proxies are not transparent

http://blog.xebia.com/2008/03/08/advanced-hibernate-proxy-pitfalls/ http://cwmaier.blogspot.com/2007/07/liskov-substitution-principleequals.html

Proxies are not transparent

Solutions

Turn off Proxy for inheritance hierarchies (or all classes) Specify an interface for Proxies to implement; code to the interface Deal with it Use bytecode instrumentation instead of proxies

Fetch plan defaults

ManyToOne defaults to EAGER


OneToOne defaults to EAGER

OneToMany defaults to LAZY

JPA Callbacks

Allows for injection of code into the object lifecycle

@PreLoad @PrePersist/@PostPersist @PreUpdate/@PostUpdate @PreRemove

Cant invoke the EntityManager, however

Paging Query Results

Queries with fetch joins of collections have to be paged by Hibernate


ScrollableResults not supported by Oracle server emulated by jdbc driver setFirstResult/setMaxResults will generate SQL that will return just the rows needed

will still need a separate query to determine total number of rows

Final Comments

Hibernate is a full fledged ORM with the added benefit of the standardized JPA behind it
For project the size of MAUI, not having to hand code insert/update/delete SQL statements is huge Minimum project size threshold = ?

What Ive left out

Criteria queries/Query by example Named queries Compound Primary Keys Filters Components Batch process Bulk operations Hibernate Tools

Resources

Java Persistence with Hibernate by Bauer, King Pro EJB 3 by Keith, Schincariol Patterns of Enterprise Application Architecture by Fowler www.hibernate.org http://java.sun.com/javaee/technologies/persistence.jsp

S-ar putea să vă placă și