how hibernate3 makes complex things easy - hibernate
TRANSCRIPT
The Professional Open Source™ Company
How Hibernate3 makes (some) complex things easy
Gavin KingJBoss, [email protected]@jboss.com
The Professional Open Source™ Company
Road Map
• The problems• The model• Using union-subclass mappings• A typed association• Using filters• Producing XML
The Professional Open Source™ Company
The problems
• Hibernate 2.1 assumes that the data model is “nice”Relationships have foreign key constraintsForeign keys point to primary keysA null association has a null foreign key (except for one-to-one special case)
• ORM solutions assume that the model is essentially staticThe data changes over time, but……at any particular time, all users see the same data
• If either of these assumptions fails, you can’t use an association mapping
You’ll need to use a query (HQL, or possibly SQL) to discover the relationships in your data
• This is a pain forMany legacy databases Databases with historical, regional, permissioned data
The Professional Open Source™ Company
The model
• A hypothetical legacy insurance application• Parties to a contract (policy holders, others)
are people or organizations• Addresses have a “role” (mailing, business,
home)
Identity
Person Organization
Address
mailing
business
home
1
1
1
The Professional Open Source™ Company
The model
• There are various standard policy definitions which change over time
• The terms of a contract (a policy variation) may also be varied over time
Identity Party PolicyVariation PolicyDefinition1 1 1** *
The Professional Open Source™ Company
Union-subclass mappings
• What if there is no Identity table?
• We have a one-to-one association between the Party and Identity classes
But it has no foreign key constraint
• In Hibernate3, use a <union-subclass>mapping
The Professional Open Source™ Company
Union-subclass mapping
<class name="Identity" abstract="true"><id name="id">
…</id><property name="phoneNumber"
length="20"/>
<union-subclass name="Person"><component name="name">
…</component>…
</union-subclass>
<union-subclass name="Organization"><property name="name"/>
</union-subclass>
</class>
The Professional Open Source™ Company
Union-subclass mapping
• Queries against this class result in a SQL union
session.createQuery(“from Party p left join fetch p.identity”).list();
select …
from Party p
left join (select … from Person union select … from Organization) i
on p.id = i.id
The Professional Open Source™ Company
Typed association
• There are three optional associations from the Identity hierarchy to the Address class
• But in the database, this is represented as the Address table having a foreign key value of the Organization or Person table, together with a “type”
• So, in the data model, we have a one-to-many association, while in the object model, we have three optional one-to-one associations
The Professional Open Source™ Company
Typed association
public class Address implements java.io.Serializable {
public enum AddressType { MAILING, HOME, BUSINESS }
private Identity addressee;private AddressType type;private String address;private String state;private String postCode;private String country;
…}
create table Address (identityId bigint not null,addressType varchar(10) not null
check addressType in (‘MAILING’, ‘BUSINESS’, ‘HOME),street varchar(100) not null,city varchar(30) not null,…primary key (identityId, addressType)
)
The Professional Open Source™ Company
Typed association
<class name="Address"><composite-id>
<key-many-to-one name="addressee"/><key-property name="type"
type="address_type"><column name="addressType"
check="addressType in ('HOME', 'BUSINESS', 'MAILING')"/></key-property>
</composite-id><property name=“street" length=“100"/><property name=“city" length="30"/>…
</class>
<typedef name="address_type" class="org.hibernate.demo.EnumUserType"><param name="enum">org.hibernate.demo.Address$AddressType</param>
</typedef>
The Professional Open Source™ Company
Typed association
<class name="Identity" abstract="true"><id name="id">…</id><property name="phoneNumber"
length="20"/><one-to-one name="mailingAddress"
cascade="all"><formula>id</formula><formula>'MAILING'</formula>
</one-to-one>
<union-subclass name="Organization"><property name="name"/><one-to-one name="businessAddress"
cascade="all"><formula>id</formula><formula>'BUSINESS'</formula>
</one-to-one></union-subclass>
The Professional Open Source™ Company
Typed association
• Queries against this class result in a SQL union
String hql =
“from Person p join fetch p.mailingAddress join fetch p.homeAddress”;
session.createQuery(hql).list();
select …
from Person p
join Address ma
on p.id = ma.identity_id and ‘MAILING’ = ma.addressType
join Address ha
on p.id = ha.identity_id and ‘HOME’ = ha.addressType
The Professional Open Source™ Company
Using filters
• A PolicyDefinition is identified by policy name and revision number
• For audit purposes, we keep the creation date of the revision
<class name="PolicyDefinition"><composite-id>
<key-property name="policyName"/><key-property name="revision"/>
</composite-id><property name="description"/><property name="creationDate"
type="calendar_date"/>…
</class>
The Professional Open Source™ Company
Using filters
• A PolicyVariation is identified by its PolicyDefinition, together with a “variation id”
• A PolicyVariation has an effectivity range
• We also track creation time, for audit purposes
The Professional Open Source™ Company
Using filters
<class name="PolicyVariation"><composite-id>
<key-many-to-one name="definition"><column name="policyName"/><column name="policyRevision"/>
</key-many-to-one><key-property name="variationId"/>
</composite-id><property name="creationDate"
type="calendar_date"/><property name="effectiveStartDate"
type="calendar_date"/><property name="effectiveEndDate"
type="calendar_date"/><property name="reason"/>…
</class>
The Professional Open Source™ Company
Using filters
• A Party inherits the pk of its Identity
• It also has a creation date and region
<class name="Party"><composite-id>
<key-many-to-one name="identity" column=“id"/>
</composite-id><property name="creationDate"
type="calendar_date"/><property name="region"/>…
</class>
The Professional Open Source™ Company
Using filters
• There is a many-to-many association between Party and PolicyVariation
<set name="parties" inverse="true" table="PartyPolicyVariation">
<key><column name="policyName"/><column name="policyRevision"/><column name="variationId"/>
</key><many-to-many column="party"
class="Party"/></set>
The Professional Open Source™ Company
Using filters
• And a one-to-many association between PolicyDefinition and PolicyVariation
<set name=“variations" inverse="true"><key>
<column name="policyName"/><column name="policyRevision"/>
</key><one-to-many class="PolicyVariation"/>
</set>
The Professional Open Source™ Company
Using filters
• For a particular user, we want to filter this data model fragment by
The user’s regionOptionally, the audit dateOptionally, by effectivity
<filter-def name="AuditDate"><filter-param name="date" type="date"/>creationDate < :date
</filter-def>
<filter-def name="EffectiveDate"><filter-param name="date" type="date"/>(:date between effectiveStartDate and effectiveEndDate)
</filter-def>
<filter-def name="Region"><filter-param name="region" type="string"/>region = :region
</filter-def>
The Professional Open Source™ Company
Using filters
• Now, apply these filters to our class mappings
<class name="Party">…<filter name="AuditDate"/><filter name="Region”/>
</class>
<class name="PolicyVariation">…<filter name="AuditDate"/><filter name="EffectiveDate"/>
</class>
<class name="PolicyVariation">…<filter name="AuditDate"/>
</class>
The Professional Open Source™ Company
Using filters
• Also, apply to the collection-valued associations
<set name="parties" inverse="true" table="PartyPolicyVariation">
<key>…</key><many-to-many column="party" class="Party">
<filter name="AuditDate"/>
<filter name=“Region"/></many-to-many>
</set>
<set name="variations" inverse="true"><key>…</key><one-to-many class="PolicyVariation"/><filter name="AuditDate"/><filter name="EffectiveDate"/>
</set>
The Professional Open Source™ Company
Using filters
• Let’s actually use a filter!
session.enableFilter(“Region”).setParameter( “region”, user.getRegion() );session.enableFilter(“EffectiveDate”).setParameter( “date”, date );session.enableFilter(“AuditDate”).setParameter( “date”, date );
session.createQuery(“from PolicyVariation pv join fetch pv.parties”).list();
from PolicyVariation pvjoin PartyPolicyVariation ppv
on pv.policyName = ppv.policyNameand pv.policyRevision = ppv.policyRevisionand pv.variationId = ppv.variationId
join Party pon p.id = ppv.partyand p.region = ?and p.creationDate < ?
where (? between pv.effectiveStartDate and pv.effectiveEndDate)and pv.creationDate < ?
The Professional Open Source™ Company
Mapping to XML
<class name="PolicyVariation“ entity-name="PolicyVariation“node=“policy-variation”>
<composite-id><key-many-to-one name="definition“ node=“definition”
embed-xml=“false”><column name="policyName"/><column name="policyRevision"/>
</key-many-to-one><key-property name="variationId“ node=“@variation-id”/>
</composite-id><property name="creationDate" node=“creation-date” type="calendar_date"/><property name="effectiveStartDate" node=“effective-start-date”
type="calendar_date"/><property name="effectiveEndDate" node=“effective-start-date”
type="calendar_date"/><property name="reason"/><set name="parties" inverse="true“ node=“.”
table="PartyPolicyVariation"><key>
<column name="policyName"/><column name="policyRevision"/><column name="variationId"/>
</key><many-to-many column="party" node=“party/@id”
class="Party“ embed-xml=“false”/></set>
</class>
The Professional Open Source™ Company
Retrieving XML
Session dom4j = session.getSession(EntityMode.DOM4J);Element pv = (Element) dom4j.createCriteria(“PolicyVariation”)
.add( Property.forName(“definition.name”).eq(policyName) )
.add( Property.forName(“definition.revision”).eq(policyRevision) )
.add( Property.forName(“variationId”).eq(variationId) )
.uniqueResult();print(pv);
<policy-variation variation-id=“1234”><definition>
<name>Standard Contents</name><revision>3</revision>
</definition><creation-date>2003-2-4</creation-date><effective-start-date>2003-2-4</effective-start-date><party id=“123455”/><party id=“788111”/>
</policy-variation>
The Professional Open Source™ Company
Further information
• http://hibernate.org