Beruflich Dokumente
Kultur Dokumente
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
Functional Scope
Admissions (Prospects/Applicants/Institutions ...) Student Records (Courses/Enrollment/Degrees ...) Financial Aid Billing Advising
Schema
Hibernate/JPA
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
Advantages
Disadvantages
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
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
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
Fetch Plan
EAGER: when the owning entity object is loaded LAZY: when your code accesses the associated object or collection
Student
Address
Proxy
Configuring Hibernate/JPA
Annotations Overview
Core
Inheritance
@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
...
}
@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
@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
@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
Joined Subclass
Prospect p = new Prospect(); EntityManager em = ... em.persist(p); // Hibernate will execute an SQL insert statement
HQL
HQL
Supports
Object retrieval as well as projection Joins on mapped relationships aggregate functions (avg, min, max, count(*), ...) polymorphic queries subqueries ordering grouping
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
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
assert p1.getClass().equals(p2.getClass());
// assert will fail one reference is a proxy // and the other is not
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(); } }
SecondarySchool
PostSecondarySchool
http://blog.xebia.com/2008/03/08/advanced-hibernate-proxy-pitfalls/ http://cwmaier.blogspot.com/2007/07/liskov-substitution-principleequals.html
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
JPA Callbacks
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 = ?
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