Object/Relational Mapping Solving the structural paradigm mismatch
Creating a Domain Model An object-oriented domain model captures the entities with the help of the business experts concrete things like User, Item, Category, abstract like PricingAlgorithm developers create an  object model  of the problem  domain can be only a mental model or elaborate as UML diagrams we may also have a relational data model, designed using E / R modelling a relational schema, or a model defined in some CASE tool Item 0..* User 0..* sells Category
Our example application CaveatEmptor  is an online auction system – we have the following domain model: Do we need any special programming model to implement this domain model in Java? Item 0..* User 0..* sells Bid 0..* 0..* Address 0..* BillingDetails CreditCard BankAccount home billing 0..* Category
Writing POJOs We implement our domain model persistent classes with POJOs loosely follow the JavaBean specification (actually, just about any Java class will do) we usually provide accessor methods for property management with (any visibility), but this is not required no-argument constructor (with at least  package  visibility) is required better to make the class and all public methods non-final declare the class  Serializable  if you need to transport data between tiers POJO Best Practices are actually requirements of Hibernate for persistent classes.
A simple POJO public class User implements Serializable { private String username; private Address address; public User() {} public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Address getAddress() { return address;} public void setAddress(Address address) { this.address = address; } public MonetaryAmount calcShippingCosts(Address from) { ... } }
Adding logic to accessor methods We can also add certain kinds of logic - especially validation - to our accessor methods: Let’s create the mapping for  User ... public class User { private String firstname; ... public void setFirstname(String firstname) throws InvalidNameException { if ( !StringUtil.isCapitalizedName(firstname) ) throw new InvalidNameException(firstname); this.firstname = firstname; ) ... }
Class mapping We use the  <class>  element for each persistent class requires class name, table and schema are optional by default, all columns are inserted and updated override with dynamic insert and update, no SQL generation on startup <hibernate-mapping package=&quot;org.hibernate.auction.model&quot; schema=&quot;AUCTION&quot;> <class name=&quot;User&quot; table=&quot;USER&quot; dynamic-insert=&quot;true&quot; dynamic-update=&quot;true&quot;> </class> ... </hibernate-mapping>
Property mapping We use  <property>  for each property of our class: If your property and class names are special (prefix, suffix, etc.), implement a Hibernate  NamingStrategy . <!- An immutable property --> <property name=&quot;orderNumber&quot; column=&quot;ORDER_NR&quot; type=&quot;string&quot; not-null=&quot;true&quot; access=&quot;field&quot; update=&quot;false&quot;/> <!-- A derived property (read only) --> <property name=&quot;totalIncludingTax&quot; formula=”PRICE + PRICE * TAX_RATE&quot; type=&quot;big_decimal&quot;/>
Writing persistent classes Entity associations are also represented as properties: 0..* public class Category { private String name; private Category  parentCategory ; private Set  childCategories  = new HashSet(); Set getChildCategories() { return childCategories; } void setChildCategories(Set categories) { childCategories = categories; } public Category getParentCategory() {  return parentCategory(); } void setParentCategory(Category category) { parentCategory = category; } } Category
Optimizing association handling To create an association, we have to set the  parentCategory  of a child the child must be added to the  childCategories  of the parent we don't have some container-managed relationship handling! We will come back to association mappings soon…. public void addChildCategory(Category child) { // Be defensive! if (child == null) throw new IllegalArgumentException(&quot;null Child&quot;); // Ensure multiplicity if ( child.getParent() != null ) child.getParent().getChildCategories().remove(child); child.setParent(this); getChildCategories().add(child); }
Primary keys The primary key for our tables should be unique:  no duplicate values ever constant:  value is never updated required:  never null Types of primary keys: natural key:  an attribute that is unique by virtue of its business meaning (any composite key is a natural key) surrogate key:  an attribute generated by the system, with no meaning to the user, and no business semantics For new systems, we recommend the use of  surrogate keys  everywhere ,  since this strategy makes schema evolution simpler – true natural keys are hard to find!
Database identity in Hibernate We add an  identifier property  to persistent classes the value of that property is the primary key value the name should always be the same for all classes, e.g.  id Should the identifier property be public? if we use the identifier outside of persistence, e.g. in web applications it is a convenient “handle” to a particular instance lookup by identifier is an especially efficient operation in Hibernate we usually have a public getter and a private setter public class Category { private Long id = null; public Long getId() { return this.id; } private void setId(Long id) { this.id = id; } }
Identity mapping Identifier property mappings the identifier property type may be any Hibernate type the identifier generation strategy may be customized identifier properties are actually optional, but  strongly  recommended (if you don’t have an identifier property, just omit  name  in the mapping) <class name=&quot;Category&quot; table=&quot;CATEGORY&quot;> <id name=&quot;id&quot; column=&quot;CATEGORY_ID&quot; type=&quot; long &quot;> <generator class=&quot; native &quot;/> </id> ... </class>
Identifier generation strategies increment incrementing value, unique to the JVM ( long, short, int ) native picks other strategies, depending on the database capabilities ( long, short, int ) identity identity column types in DB2, MS SQL Server, Sybase, MySQL, etc ( long, short, int ) sequence incrementing sequence in DB2, PostgreSQL, Oracle, etc ( long, short, int ) hilo high- and low-value algorithm, unique to database instance ( long, short, int ) uuid.hex Hibernate UUID algorithm, unique to a subnet ( String ) guid database GUID algorithm for MySQL and MS SQL Server, introduced in Hibernate3 ( String ) assigned the application assigns identifier values
Hibernate type system Entity types (e.g.  User ,  Category ,  Item ) own database identity (primary key) references are foreign keys own lifecycle independent of other entities shared references are possible usually user-defined classes Value types (e.g.  Address, String, Integer ) no database identity lifespan of value object is bound to containing entity built-in JDK types and user-defined types
Built-in value types integer, long, short, float, double, character, byte, boolean, yes_no, true_false map Java primitives or wrapper classes string maps  java.lang.String date, time, timestamp mappings for  java.util.Date  and subclasses calendar, calendar_date mappings for  java.util.Calendar big_decimal maps  java.math.BigDecimal locale, timezone, currency, class mappings for miscellaneous JDK classes binary, text mappings for binary (byte array) or text (string) data blob, clob  mappings for  java.sql.Blob, java.sql.Clob serializable maps a serializable Java object to a binary data type – do not use this!
Composition in our object model Classes can have a  composition  relationship: User Address home billing class User { private Long id; private Address homeAddress; private Address billingAddress; } class Address { private String street; private String zipcode; private String city; }
Mapping components We sometimes map the composed object to the same table USER  table with  STREET ,  ZIPCODE ,  CITY  columns <class name=&quot;User&quot; table=&quot; USER &quot;> <id name=&quot;id&quot; column=&quot;USER_ID&quot;> <generator class=&quot;native&quot;/> </id> <component name=&quot;homeAddress&quot; class=&quot;Address&quot;> <property name=&quot;street&quot; column=&quot; HOME_STREET &quot; not-null=&quot;true&quot;/> <property name=&quot;city&quot; column=&quot; HOME_CITY &quot;  not-null=&quot;true&quot;/> <property name=&quot;zipcode&quot; column=&quot; HOME_ZIPCODE &quot; not-null=&quot;true&quot;/> </component> ... </class>
Bidirectional component mapping We can even  make the relationship bidirectional: we can't have shared references to  Address  objects a null  homeAddress  will be represented as  null  values in all columns <component name=&quot;homeAddress&quot; class=&quot;Address&quot;> <parent name=&quot;user&quot;/> <property name=&quot;street&quot; column=&quot; HOME_STREET &quot; not-null=&quot;true&quot;/> <property name=&quot;city&quot; column=&quot; HOME_CITY &quot;  not-null=&quot;true&quot;/> <property name=&quot;zipcode&quot; column=&quot; HOME_ZIPCODE &quot; not-null=&quot;true&quot;/> </component>
Class inheritance hierarchies Three strategies to map an inheritance hierarchy: table per concrete class:  no polymorphism table per class hierarchy:  denormalization and discriminator table per subclass:  using &quot;has a&quot; foreign key relationships BillingDetails CreditCard BankAccount
Table per concrete class mapping polymorphic associations to  BillingDetails  are difficult a query against the superclass is several SELECTs changes to the superclass require multiple table changes no special Hibernate mapping, just  <class> << table >> CREDIT_CARD CREDIT_CARD_ID << PK >> OWNER NUMBER TYPE EXP_DATE << table >> BANK_ACCOUNT BANK_ACCOUNT_ID << PK >> OWNER NUMBER BANK_NAME BANK_SWIFT
Table per class hierarchy note the  type discriminator  column best performance, polymorphic queries and associations subclass columns must be nullable! we use the Hibernate  <subclass>  mapping << table >> BILLING_DETAILS BILLING_DETAILS_ID << PK >> BILLING_TYPE << discriminator >> OWNER NUMBER CREDIT_CARD_TYPE CREDIT_CARD_EXP_DATE BANK_ACCOUNT_NAME BANK_ACCOUNT_SWIFT
Mapping a class hierarchy to a single table <subclass>  elements can be nested mapping strategy has to be used for all classes in the sub-hierarchy <class name=&quot;BillingDetails&quot; table=&quot;BILLING_DETAILS&quot;> <id name=&quot;id&quot; column=&quot;BILLING_DETAILS_ID&quot;> <generator class=&quot;native&quot;/> </id> <discriminator column=&quot;BILLING_TYPE&quot;/> <property name=&quot;name&quot; column=&quot;OWNER&quot;/> ... <subclass name=&quot;CreditCard&quot;  discriminator-value=&quot;CC&quot; > <property name=&quot;type&quot; column=&quot;CREDIT_CARD_TYPE&quot;/> ... </subclass> ... </class>
Table per subclass normalized model polymorphic associations even to particular subclasses we use the Hibernate  <joined-subclass>  mapping << table >> CREDIT_CARD CREDIT_CARD_ID <<PK>> <<FK>> TYPE EXP_DATE << table >> BANK_ACCOUNT BANK_ACCOUNT_ID <<PK>> <<FK>> BANK_NAME BANK_SWIFT << table >> BILLING_DETAILS BILLING_DETAILS_ID <<PK>> OWNER NUMBER
Mapping a joined subclass to normalized tables <joined-subclass>  can be nested, but not  <subclass> difficult to implement by hand ad hoc reporting might be more complex <class name=&quot;BillingDetails&quot; table=&quot;BILLING_DETAILS&quot;> <id name=&quot;id&quot; column=&quot;BILLING_DETAILS_ID&quot;> <generator class=&quot;native&quot;/> </id> <property name=&quot;name&quot; column=&quot;OWNER&quot;/> ... <joined-subclass name=&quot;CreditCard&quot; > <key column=&quot;CREDIT_CARD_ID&quot;/> <property name=&quot;type&quot; column=&quot;CREDIT_CARD_TYPE&quot;/> ... </joined-subclass> ... </class>
Class associations A bidirectional  one-to-many  entity association: Managed associations: a change to one side is immediately reflect on the other side e.g.  bid.setItem(item) executes  item.getBids().add(bid) used with container-managed relationships (CMR) in EJB/CMP POJOs do  not  have automatically managed associations! the common Java semantics apply we have to manually set both &quot;ends&quot; of the &quot;link&quot; Hibernate doesn't interfere with POJO semantics! Item Bid 0..*
The tables of this association mapping << table >> ITEM ITEM_ID <<PK>> NAME PRICE ... << table >> BID BID_ID <<PK>> ITEM_ID <<FK>> AMOUNT ... PRICE NAME ITEM_ID 1 2 3 Bar Foo Baz 2.00 50.00 1.00 ITEM AMOUNT ITEM_ID BID_ID 1 2 3 1 1 2 10.00 20.00 55.00 BID
Mapping a unidirectional association The column  ITEM_ID  is a FK to the PK of the  ITEM  table. class Bid { private Item item; public void setItem(Item i) { this.item = i; } public Item getItem() { return item; } } <class name=&quot;Bid&quot; table=&quot;BID&quot;> ...  <many-to-one name=&quot;item&quot; class=&quot;Item&quot; column=&quot;ITEM_ID&quot; not-null=&quot;true&quot;/> </class>
Making the association bidirectional The key element refers to the  ITEM_ID  column in the  BID  table. class Item { private Set bids = new HashSet(); public void getBids() { return bids; } public void addBid(Bid b) { this.bids.add(b) b.setItem(this) // Manage association! } } <class name=&quot;Item&quot; table=&quot;ITEM&quot;> ...  <set name=&quot;bids&quot;> <key column=&quot;ITEM_ID&quot;/> <one-to-many class=&quot;Bid&quot;/> </set> </class>
The big problem We have referenced the ITEM_ID in two mappings At runtime, we have two in-memory representations of this associtaion, the  item  property of the  Bid  and the  bids  of the  Item . Suppose we modify this association: Hibernate does not transparently detect the fact that the two changes refer to the same database column, since at this point we have done nothing to indicate that this is a bidirectional association. item.getBids().add(newBid); newBid.setItem(item);
Making the association bidirectional We have to declare one end as  inverse: changes to the inverse end will be ignored! without  inverse=“true” , Hibernate would try to issue  two  UPDATE statements <class name=&quot;Item&quot; table=&quot;ITEM&quot;> ...  <set name=&quot;bids&quot;  inverse=&quot;true&quot; > <key column=&quot;ITEM_ID&quot;/> <one-to-many class=&quot;Bid&quot;/> </set> </class>
Mapping collections vs Java collections Persists an unordered,non-unique many-to-many collection using a surrogate key. java.util.List idbag Persists an indexed, non-unique collection of primitive values N/A primitive-array Persists an indexed, non-unique collection of values or objects N/A array Persists an unordered, non-unique collection of values or objects java.util.List bag Persists an ordered,non-unique collection of values or objects java.util.List list Persists a collection of key/value pairs java.util.Map map Persists an unordered,unique collection of values or objects java.util.Set set Description Java Collection Type Hibernate Collection Type
One-to-many association <hibernate-mapping package=“demo”> <class name=“Event”  table=“events”> … . <set name=“speakers”> <key column=“event_id”/> <one-to-many  class=“Speaker”/> </set> <set name=“attendees”> <key column=“event_id”/> <one-to-many  class=“Attendee”/> </set> … . </class> </hibernate-mapping> public class Event { private Set speakers; private set attendees; public void setSpeakers(Set speakers) { this.speakers=speakers;} public set getSpeakers() {return this.speakers;} public void setAttendees(Set attendees) { this.attendees=attendees;} public set getAttendees() {return this.attendees;}
One-to-many events events events attendees id bigint(pk) id bigint (pk) event_id bigint (fk) A one-to-many association from events to attendees
Many-to-many Suppose Attendees can attend more than one Event. This is a  Many-to-many mapping for the set of Attendees: <set name=“attendees”  table=“event_attendees”> <key column=“event_id”> <many-to-many column=“attendee_id”  class=“Attendee”/> </set>
many-to-many events events events event_attendees id bigint(pk) event_id bigint (fk) attendee_id bigint (fk) A many-to-many table schema from events to attendees events events attendees id bigint(pk)
Persisting collections of values The below mapping definition for a persistent set of Integers  <set name=“ratings”  table=“event_ratings”> <key column=“event_id”/> <element column=“rating” type=“integer” </set> The table attribute defines the event_ratings table, which is  Used to store the values.
Lists Unlike Set, Lists can contain duplicate elements. Since Lists are Indexed, meaning that contained elements are stored at a specific Location in the List, you need to define a <list-index> element: <list name=“speaker”> <list-index  column=“speaker_index”/> <key column = “event_id”/> <one-to-many  class=“Speaker”/> </list>
Maps Maps store entries in key/value pairs, meaning that we retrieve the Value by looking up the associated key. <map name=“speakers”> <map-key  column=“speakers_index” type=“string”  length=“20”/> <key column=“event_id”/> <one-to-many  class=“speaker”/> </map> Inserting elements into the map, use a String for the key Map speakers = new HashMap(); speakers.put(“speakers1”, new Speaker()); speakers.put(“speakers2”,new Speaker()); Event event = new Event(); event.setSpeakers(speakers);
Bags To store a collection of objects without worrying about ordering Or duplicate elements. This is where Bags come in : <bag  name=“speakers”> <key column=“event_id”/> <one-to-many  class=“Speaker”/> </bag> For the javabean’s perspective, the collection of Speakers is a List. Import java.util.List; Public class Event { Private List speakers; Public void setSpeakers(List speakers) { this.speakers = speakers;} Public List getSpeakers() { return this.speakers;} } However, using a Bag for Hibernate means you don’t need to explicitly create an index column. Set and Bag both doesn’t require an index column, but remember that a Set can’t contain duplicate elements, whereas Bags can
idbags Suppose we have a many-to-many association between Events and Speakers. Speakers can belong to the same Event multiple times, so we can’t use a Set. We also need to ensure fast and efficient access to the many-to-many table event_speakers. We really need to be able to assign a primary key to the event_speakers table. This is where idbags come in: <idbag  name=“speakers”  table=“event_speakers” lazy=“true”  cascade=“all”> <collection-id  type=“long”  column=“event_speakers_id”> <generator-class=“native”/> </collection-id> <key column=“event_id”/> <many-to-many  class=“Speaker”  column=“speaker_id”/> </idbag>
idbags events events events event_attendees id bigint(pk) event_id bigint  speaker_id bigint  events events speakers id bigint(pk) event_speaker_id bigint (PK)
Lazy collections A lazy collection is populated on demand. It means that the collection of entity objects or values is populated only when the application accesses the collection.  Populating the collection happens transparently when the collection is first accessed. Why lazy collections? A collection of objects can possibly contain hundreds or thousands of objects, and the application may not need to access the collection immediately, or at all. Loading hundreds of persistent objects for no reason will certainly impact application performance. It’s better to populate the persistent collection only when it is needed.
Lazy collections – Session closed problem To populate a lazy collection, the same Session instance used to retrieve the persistent object from the database must be open when the collection is populated. The following code is incorrectly attempts to load the collection of attendees for an Event after the Session has been closed: Session session =factory.openSession(); Event event = session.get(Event.class,eventId); Session.close(); Set attendees = event.getAttendees();
Lazy collections – correct method Session session =factory.openSession(); Event event = session.get(Event.class,eventId); Set attendees = event.getAttendees(); Session.close(); Persistent collections are lazy by default in Hibernate 3. For non-lazy collections, you must explicitly declare them as lazy=“false” in the mapping definition. For example: <set name=“attendees”  lazy=“false”> <key column=“event_id”/> <one-to-many class=“Attendee”/> </set>
Lazy collections – multitier problems Any collection, including collections of values and arrays, can be declared not to be “lazy’. The problem with lazy collections in populating them in a multitier application, such as web application,where keeping the Session open can be tricky. Solution :  The only requirement is that a Hibernate Session must Be available in the view tier, which is possible when you use a servlet filter or spring to manage your Session instances.
Sorted Collections A common requirement when dealing with collection is to sort them According to some criteria. The sorting criteria can be fixed or arbitrary, depending on application requirements. Hibernate supports sorting sets,maps and bags. <set name=“attendees”  order-by=“last_name”> <key column=“event_id”/> <one-to-many  class=“Attendee”/> </set> few more examples: ordery-by = “last_name, first_name” order_by = “last_name, first_name asc” Use Filter to a collection Set sortedAttendees =  session.filter(event.getAttendees(), “ order by this.phoneNumber”);
Filters Filters allow you to apply filtering criteria to returned objects at the Session level. You can pass parameters to filters, and they work with classes or collections.  To use filters, you first define them in the mapping files, and then enable a given filter by name when using the Session object. <hibernate-mapping> <filter-def  name=“nameFilter”> <filter-param name=“nameFilterParam”  type=“string”/> </fliter-def> </hibernate-mapping>
Filters - 2 With the filter defined, we can apply it to persistent classes and colections : <class name = “Event”> <filter name=“nameFilter” condition = “:nameFilterParam=name”/> <set name=“attendees”> <filter name=“nameFilter” condition = “:nameFilterParam = last_name”/> </set> </class>
Filters - 3 This code applies the filter to the name column in the events table, and to the last_name column in the attendees table. To apply the filters, we must explicitly enable them at the Session level ; Filter  f  = session.enablefilter(“nameFilter”); f.setParameter(“nameFilterParam”, “Plenary”); List results = session.createQuery(“from Event”).list(); This code enables the named filter ant then sets the filter parameter. This results will only contain instances of Event with the name Plenary. We can also enable multiple filters per session.
Metadata alternatives Attribute-oriented  metadata JSR-175 annotations support, coming soon! XDoclet Hibernate module Hibernate mapping metadata can be manipulated programmatically we can use dom4j or JDOM to generate mapping files in memory we can manipulate the Hibernate  Configuration  metamodel directly let's demonstrate this my adding a property to a mapped class… /** * @hibernate.class *  table=&quot;CATEGORY&quot; */ public class Category { ... /** * @hibernate.property */ public String getName() { return name; } ... }
Adding a property at runtime // Get the existing mapping for User from Configuration PersistentClass userMapping = cfg .getClassMapping(User.class); // Define a new column for the USER table Column column = new Column(); userMapping.getTable().addColumn(column); // Wrap the column in a Value SimpleValue value = new SimpleValue(); value.setTable( userMapping.getTable() ); value.addColumn(column); // Define a new property of the User class Property prop = new Property(); prop.setValue(value); prop.setName(&quot;motto&quot;); userMapping.addProperty(prop); // Build a new session factory, using the new mapping SessionFactory sf =  cfg.buildSessionFactory();
Reading metadata at runtime There is also the  runtime metamodel , exposed via the  SessionFactory : Category category = ...; ClassMetadata meta = sessionFactory.getClassMetadata(Category.class); String[] metaPropertyNames = meta.getPropertyNames(); Object[] propertyValues = meta.getPropertyValues(category);

03 Object Relational Mapping

  • 1.
    Object/Relational Mapping Solvingthe structural paradigm mismatch
  • 2.
    Creating a DomainModel An object-oriented domain model captures the entities with the help of the business experts concrete things like User, Item, Category, abstract like PricingAlgorithm developers create an object model of the problem domain can be only a mental model or elaborate as UML diagrams we may also have a relational data model, designed using E / R modelling a relational schema, or a model defined in some CASE tool Item 0..* User 0..* sells Category
  • 3.
    Our example applicationCaveatEmptor is an online auction system – we have the following domain model: Do we need any special programming model to implement this domain model in Java? Item 0..* User 0..* sells Bid 0..* 0..* Address 0..* BillingDetails CreditCard BankAccount home billing 0..* Category
  • 4.
    Writing POJOs Weimplement our domain model persistent classes with POJOs loosely follow the JavaBean specification (actually, just about any Java class will do) we usually provide accessor methods for property management with (any visibility), but this is not required no-argument constructor (with at least package visibility) is required better to make the class and all public methods non-final declare the class Serializable if you need to transport data between tiers POJO Best Practices are actually requirements of Hibernate for persistent classes.
  • 5.
    A simple POJOpublic class User implements Serializable { private String username; private Address address; public User() {} public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Address getAddress() { return address;} public void setAddress(Address address) { this.address = address; } public MonetaryAmount calcShippingCosts(Address from) { ... } }
  • 6.
    Adding logic toaccessor methods We can also add certain kinds of logic - especially validation - to our accessor methods: Let’s create the mapping for User ... public class User { private String firstname; ... public void setFirstname(String firstname) throws InvalidNameException { if ( !StringUtil.isCapitalizedName(firstname) ) throw new InvalidNameException(firstname); this.firstname = firstname; ) ... }
  • 7.
    Class mapping Weuse the <class> element for each persistent class requires class name, table and schema are optional by default, all columns are inserted and updated override with dynamic insert and update, no SQL generation on startup <hibernate-mapping package=&quot;org.hibernate.auction.model&quot; schema=&quot;AUCTION&quot;> <class name=&quot;User&quot; table=&quot;USER&quot; dynamic-insert=&quot;true&quot; dynamic-update=&quot;true&quot;> </class> ... </hibernate-mapping>
  • 8.
    Property mapping Weuse <property> for each property of our class: If your property and class names are special (prefix, suffix, etc.), implement a Hibernate NamingStrategy . <!- An immutable property --> <property name=&quot;orderNumber&quot; column=&quot;ORDER_NR&quot; type=&quot;string&quot; not-null=&quot;true&quot; access=&quot;field&quot; update=&quot;false&quot;/> <!-- A derived property (read only) --> <property name=&quot;totalIncludingTax&quot; formula=”PRICE + PRICE * TAX_RATE&quot; type=&quot;big_decimal&quot;/>
  • 9.
    Writing persistent classesEntity associations are also represented as properties: 0..* public class Category { private String name; private Category parentCategory ; private Set childCategories = new HashSet(); Set getChildCategories() { return childCategories; } void setChildCategories(Set categories) { childCategories = categories; } public Category getParentCategory() { return parentCategory(); } void setParentCategory(Category category) { parentCategory = category; } } Category
  • 10.
    Optimizing association handlingTo create an association, we have to set the parentCategory of a child the child must be added to the childCategories of the parent we don't have some container-managed relationship handling! We will come back to association mappings soon…. public void addChildCategory(Category child) { // Be defensive! if (child == null) throw new IllegalArgumentException(&quot;null Child&quot;); // Ensure multiplicity if ( child.getParent() != null ) child.getParent().getChildCategories().remove(child); child.setParent(this); getChildCategories().add(child); }
  • 11.
    Primary keys Theprimary key for our tables should be unique: no duplicate values ever constant: value is never updated required: never null Types of primary keys: natural key: an attribute that is unique by virtue of its business meaning (any composite key is a natural key) surrogate key: an attribute generated by the system, with no meaning to the user, and no business semantics For new systems, we recommend the use of surrogate keys everywhere , since this strategy makes schema evolution simpler – true natural keys are hard to find!
  • 12.
    Database identity inHibernate We add an identifier property to persistent classes the value of that property is the primary key value the name should always be the same for all classes, e.g. id Should the identifier property be public? if we use the identifier outside of persistence, e.g. in web applications it is a convenient “handle” to a particular instance lookup by identifier is an especially efficient operation in Hibernate we usually have a public getter and a private setter public class Category { private Long id = null; public Long getId() { return this.id; } private void setId(Long id) { this.id = id; } }
  • 13.
    Identity mapping Identifierproperty mappings the identifier property type may be any Hibernate type the identifier generation strategy may be customized identifier properties are actually optional, but strongly recommended (if you don’t have an identifier property, just omit name in the mapping) <class name=&quot;Category&quot; table=&quot;CATEGORY&quot;> <id name=&quot;id&quot; column=&quot;CATEGORY_ID&quot; type=&quot; long &quot;> <generator class=&quot; native &quot;/> </id> ... </class>
  • 14.
    Identifier generation strategiesincrement incrementing value, unique to the JVM ( long, short, int ) native picks other strategies, depending on the database capabilities ( long, short, int ) identity identity column types in DB2, MS SQL Server, Sybase, MySQL, etc ( long, short, int ) sequence incrementing sequence in DB2, PostgreSQL, Oracle, etc ( long, short, int ) hilo high- and low-value algorithm, unique to database instance ( long, short, int ) uuid.hex Hibernate UUID algorithm, unique to a subnet ( String ) guid database GUID algorithm for MySQL and MS SQL Server, introduced in Hibernate3 ( String ) assigned the application assigns identifier values
  • 15.
    Hibernate type systemEntity types (e.g. User , Category , Item ) own database identity (primary key) references are foreign keys own lifecycle independent of other entities shared references are possible usually user-defined classes Value types (e.g. Address, String, Integer ) no database identity lifespan of value object is bound to containing entity built-in JDK types and user-defined types
  • 16.
    Built-in value typesinteger, long, short, float, double, character, byte, boolean, yes_no, true_false map Java primitives or wrapper classes string maps java.lang.String date, time, timestamp mappings for java.util.Date and subclasses calendar, calendar_date mappings for java.util.Calendar big_decimal maps java.math.BigDecimal locale, timezone, currency, class mappings for miscellaneous JDK classes binary, text mappings for binary (byte array) or text (string) data blob, clob mappings for java.sql.Blob, java.sql.Clob serializable maps a serializable Java object to a binary data type – do not use this!
  • 17.
    Composition in ourobject model Classes can have a composition relationship: User Address home billing class User { private Long id; private Address homeAddress; private Address billingAddress; } class Address { private String street; private String zipcode; private String city; }
  • 18.
    Mapping components Wesometimes map the composed object to the same table USER table with STREET , ZIPCODE , CITY columns <class name=&quot;User&quot; table=&quot; USER &quot;> <id name=&quot;id&quot; column=&quot;USER_ID&quot;> <generator class=&quot;native&quot;/> </id> <component name=&quot;homeAddress&quot; class=&quot;Address&quot;> <property name=&quot;street&quot; column=&quot; HOME_STREET &quot; not-null=&quot;true&quot;/> <property name=&quot;city&quot; column=&quot; HOME_CITY &quot; not-null=&quot;true&quot;/> <property name=&quot;zipcode&quot; column=&quot; HOME_ZIPCODE &quot; not-null=&quot;true&quot;/> </component> ... </class>
  • 19.
    Bidirectional component mappingWe can even make the relationship bidirectional: we can't have shared references to Address objects a null homeAddress will be represented as null values in all columns <component name=&quot;homeAddress&quot; class=&quot;Address&quot;> <parent name=&quot;user&quot;/> <property name=&quot;street&quot; column=&quot; HOME_STREET &quot; not-null=&quot;true&quot;/> <property name=&quot;city&quot; column=&quot; HOME_CITY &quot; not-null=&quot;true&quot;/> <property name=&quot;zipcode&quot; column=&quot; HOME_ZIPCODE &quot; not-null=&quot;true&quot;/> </component>
  • 20.
    Class inheritance hierarchiesThree strategies to map an inheritance hierarchy: table per concrete class: no polymorphism table per class hierarchy: denormalization and discriminator table per subclass: using &quot;has a&quot; foreign key relationships BillingDetails CreditCard BankAccount
  • 21.
    Table per concreteclass mapping polymorphic associations to BillingDetails are difficult a query against the superclass is several SELECTs changes to the superclass require multiple table changes no special Hibernate mapping, just <class> << table >> CREDIT_CARD CREDIT_CARD_ID << PK >> OWNER NUMBER TYPE EXP_DATE << table >> BANK_ACCOUNT BANK_ACCOUNT_ID << PK >> OWNER NUMBER BANK_NAME BANK_SWIFT
  • 22.
    Table per classhierarchy note the type discriminator column best performance, polymorphic queries and associations subclass columns must be nullable! we use the Hibernate <subclass> mapping << table >> BILLING_DETAILS BILLING_DETAILS_ID << PK >> BILLING_TYPE << discriminator >> OWNER NUMBER CREDIT_CARD_TYPE CREDIT_CARD_EXP_DATE BANK_ACCOUNT_NAME BANK_ACCOUNT_SWIFT
  • 23.
    Mapping a classhierarchy to a single table <subclass> elements can be nested mapping strategy has to be used for all classes in the sub-hierarchy <class name=&quot;BillingDetails&quot; table=&quot;BILLING_DETAILS&quot;> <id name=&quot;id&quot; column=&quot;BILLING_DETAILS_ID&quot;> <generator class=&quot;native&quot;/> </id> <discriminator column=&quot;BILLING_TYPE&quot;/> <property name=&quot;name&quot; column=&quot;OWNER&quot;/> ... <subclass name=&quot;CreditCard&quot; discriminator-value=&quot;CC&quot; > <property name=&quot;type&quot; column=&quot;CREDIT_CARD_TYPE&quot;/> ... </subclass> ... </class>
  • 24.
    Table per subclassnormalized model polymorphic associations even to particular subclasses we use the Hibernate <joined-subclass> mapping << table >> CREDIT_CARD CREDIT_CARD_ID <<PK>> <<FK>> TYPE EXP_DATE << table >> BANK_ACCOUNT BANK_ACCOUNT_ID <<PK>> <<FK>> BANK_NAME BANK_SWIFT << table >> BILLING_DETAILS BILLING_DETAILS_ID <<PK>> OWNER NUMBER
  • 25.
    Mapping a joinedsubclass to normalized tables <joined-subclass> can be nested, but not <subclass> difficult to implement by hand ad hoc reporting might be more complex <class name=&quot;BillingDetails&quot; table=&quot;BILLING_DETAILS&quot;> <id name=&quot;id&quot; column=&quot;BILLING_DETAILS_ID&quot;> <generator class=&quot;native&quot;/> </id> <property name=&quot;name&quot; column=&quot;OWNER&quot;/> ... <joined-subclass name=&quot;CreditCard&quot; > <key column=&quot;CREDIT_CARD_ID&quot;/> <property name=&quot;type&quot; column=&quot;CREDIT_CARD_TYPE&quot;/> ... </joined-subclass> ... </class>
  • 26.
    Class associations Abidirectional one-to-many entity association: Managed associations: a change to one side is immediately reflect on the other side e.g. bid.setItem(item) executes item.getBids().add(bid) used with container-managed relationships (CMR) in EJB/CMP POJOs do not have automatically managed associations! the common Java semantics apply we have to manually set both &quot;ends&quot; of the &quot;link&quot; Hibernate doesn't interfere with POJO semantics! Item Bid 0..*
  • 27.
    The tables ofthis association mapping << table >> ITEM ITEM_ID <<PK>> NAME PRICE ... << table >> BID BID_ID <<PK>> ITEM_ID <<FK>> AMOUNT ... PRICE NAME ITEM_ID 1 2 3 Bar Foo Baz 2.00 50.00 1.00 ITEM AMOUNT ITEM_ID BID_ID 1 2 3 1 1 2 10.00 20.00 55.00 BID
  • 28.
    Mapping a unidirectionalassociation The column ITEM_ID is a FK to the PK of the ITEM table. class Bid { private Item item; public void setItem(Item i) { this.item = i; } public Item getItem() { return item; } } <class name=&quot;Bid&quot; table=&quot;BID&quot;> ... <many-to-one name=&quot;item&quot; class=&quot;Item&quot; column=&quot;ITEM_ID&quot; not-null=&quot;true&quot;/> </class>
  • 29.
    Making the associationbidirectional The key element refers to the ITEM_ID column in the BID table. class Item { private Set bids = new HashSet(); public void getBids() { return bids; } public void addBid(Bid b) { this.bids.add(b) b.setItem(this) // Manage association! } } <class name=&quot;Item&quot; table=&quot;ITEM&quot;> ... <set name=&quot;bids&quot;> <key column=&quot;ITEM_ID&quot;/> <one-to-many class=&quot;Bid&quot;/> </set> </class>
  • 30.
    The big problemWe have referenced the ITEM_ID in two mappings At runtime, we have two in-memory representations of this associtaion, the item property of the Bid and the bids of the Item . Suppose we modify this association: Hibernate does not transparently detect the fact that the two changes refer to the same database column, since at this point we have done nothing to indicate that this is a bidirectional association. item.getBids().add(newBid); newBid.setItem(item);
  • 31.
    Making the associationbidirectional We have to declare one end as inverse: changes to the inverse end will be ignored! without inverse=“true” , Hibernate would try to issue two UPDATE statements <class name=&quot;Item&quot; table=&quot;ITEM&quot;> ... <set name=&quot;bids&quot; inverse=&quot;true&quot; > <key column=&quot;ITEM_ID&quot;/> <one-to-many class=&quot;Bid&quot;/> </set> </class>
  • 32.
    Mapping collections vsJava collections Persists an unordered,non-unique many-to-many collection using a surrogate key. java.util.List idbag Persists an indexed, non-unique collection of primitive values N/A primitive-array Persists an indexed, non-unique collection of values or objects N/A array Persists an unordered, non-unique collection of values or objects java.util.List bag Persists an ordered,non-unique collection of values or objects java.util.List list Persists a collection of key/value pairs java.util.Map map Persists an unordered,unique collection of values or objects java.util.Set set Description Java Collection Type Hibernate Collection Type
  • 33.
    One-to-many association <hibernate-mappingpackage=“demo”> <class name=“Event” table=“events”> … . <set name=“speakers”> <key column=“event_id”/> <one-to-many class=“Speaker”/> </set> <set name=“attendees”> <key column=“event_id”/> <one-to-many class=“Attendee”/> </set> … . </class> </hibernate-mapping> public class Event { private Set speakers; private set attendees; public void setSpeakers(Set speakers) { this.speakers=speakers;} public set getSpeakers() {return this.speakers;} public void setAttendees(Set attendees) { this.attendees=attendees;} public set getAttendees() {return this.attendees;}
  • 34.
    One-to-many events eventsevents attendees id bigint(pk) id bigint (pk) event_id bigint (fk) A one-to-many association from events to attendees
  • 35.
    Many-to-many Suppose Attendeescan attend more than one Event. This is a Many-to-many mapping for the set of Attendees: <set name=“attendees” table=“event_attendees”> <key column=“event_id”> <many-to-many column=“attendee_id” class=“Attendee”/> </set>
  • 36.
    many-to-many events eventsevents event_attendees id bigint(pk) event_id bigint (fk) attendee_id bigint (fk) A many-to-many table schema from events to attendees events events attendees id bigint(pk)
  • 37.
    Persisting collections ofvalues The below mapping definition for a persistent set of Integers <set name=“ratings” table=“event_ratings”> <key column=“event_id”/> <element column=“rating” type=“integer” </set> The table attribute defines the event_ratings table, which is Used to store the values.
  • 38.
    Lists Unlike Set,Lists can contain duplicate elements. Since Lists are Indexed, meaning that contained elements are stored at a specific Location in the List, you need to define a <list-index> element: <list name=“speaker”> <list-index column=“speaker_index”/> <key column = “event_id”/> <one-to-many class=“Speaker”/> </list>
  • 39.
    Maps Maps storeentries in key/value pairs, meaning that we retrieve the Value by looking up the associated key. <map name=“speakers”> <map-key column=“speakers_index” type=“string” length=“20”/> <key column=“event_id”/> <one-to-many class=“speaker”/> </map> Inserting elements into the map, use a String for the key Map speakers = new HashMap(); speakers.put(“speakers1”, new Speaker()); speakers.put(“speakers2”,new Speaker()); Event event = new Event(); event.setSpeakers(speakers);
  • 40.
    Bags To storea collection of objects without worrying about ordering Or duplicate elements. This is where Bags come in : <bag name=“speakers”> <key column=“event_id”/> <one-to-many class=“Speaker”/> </bag> For the javabean’s perspective, the collection of Speakers is a List. Import java.util.List; Public class Event { Private List speakers; Public void setSpeakers(List speakers) { this.speakers = speakers;} Public List getSpeakers() { return this.speakers;} } However, using a Bag for Hibernate means you don’t need to explicitly create an index column. Set and Bag both doesn’t require an index column, but remember that a Set can’t contain duplicate elements, whereas Bags can
  • 41.
    idbags Suppose wehave a many-to-many association between Events and Speakers. Speakers can belong to the same Event multiple times, so we can’t use a Set. We also need to ensure fast and efficient access to the many-to-many table event_speakers. We really need to be able to assign a primary key to the event_speakers table. This is where idbags come in: <idbag name=“speakers” table=“event_speakers” lazy=“true” cascade=“all”> <collection-id type=“long” column=“event_speakers_id”> <generator-class=“native”/> </collection-id> <key column=“event_id”/> <many-to-many class=“Speaker” column=“speaker_id”/> </idbag>
  • 42.
    idbags events eventsevents event_attendees id bigint(pk) event_id bigint speaker_id bigint events events speakers id bigint(pk) event_speaker_id bigint (PK)
  • 43.
    Lazy collections Alazy collection is populated on demand. It means that the collection of entity objects or values is populated only when the application accesses the collection. Populating the collection happens transparently when the collection is first accessed. Why lazy collections? A collection of objects can possibly contain hundreds or thousands of objects, and the application may not need to access the collection immediately, or at all. Loading hundreds of persistent objects for no reason will certainly impact application performance. It’s better to populate the persistent collection only when it is needed.
  • 44.
    Lazy collections –Session closed problem To populate a lazy collection, the same Session instance used to retrieve the persistent object from the database must be open when the collection is populated. The following code is incorrectly attempts to load the collection of attendees for an Event after the Session has been closed: Session session =factory.openSession(); Event event = session.get(Event.class,eventId); Session.close(); Set attendees = event.getAttendees();
  • 45.
    Lazy collections –correct method Session session =factory.openSession(); Event event = session.get(Event.class,eventId); Set attendees = event.getAttendees(); Session.close(); Persistent collections are lazy by default in Hibernate 3. For non-lazy collections, you must explicitly declare them as lazy=“false” in the mapping definition. For example: <set name=“attendees” lazy=“false”> <key column=“event_id”/> <one-to-many class=“Attendee”/> </set>
  • 46.
    Lazy collections –multitier problems Any collection, including collections of values and arrays, can be declared not to be “lazy’. The problem with lazy collections in populating them in a multitier application, such as web application,where keeping the Session open can be tricky. Solution : The only requirement is that a Hibernate Session must Be available in the view tier, which is possible when you use a servlet filter or spring to manage your Session instances.
  • 47.
    Sorted Collections Acommon requirement when dealing with collection is to sort them According to some criteria. The sorting criteria can be fixed or arbitrary, depending on application requirements. Hibernate supports sorting sets,maps and bags. <set name=“attendees” order-by=“last_name”> <key column=“event_id”/> <one-to-many class=“Attendee”/> </set> few more examples: ordery-by = “last_name, first_name” order_by = “last_name, first_name asc” Use Filter to a collection Set sortedAttendees = session.filter(event.getAttendees(), “ order by this.phoneNumber”);
  • 48.
    Filters Filters allowyou to apply filtering criteria to returned objects at the Session level. You can pass parameters to filters, and they work with classes or collections. To use filters, you first define them in the mapping files, and then enable a given filter by name when using the Session object. <hibernate-mapping> <filter-def name=“nameFilter”> <filter-param name=“nameFilterParam” type=“string”/> </fliter-def> </hibernate-mapping>
  • 49.
    Filters - 2With the filter defined, we can apply it to persistent classes and colections : <class name = “Event”> <filter name=“nameFilter” condition = “:nameFilterParam=name”/> <set name=“attendees”> <filter name=“nameFilter” condition = “:nameFilterParam = last_name”/> </set> </class>
  • 50.
    Filters - 3This code applies the filter to the name column in the events table, and to the last_name column in the attendees table. To apply the filters, we must explicitly enable them at the Session level ; Filter f = session.enablefilter(“nameFilter”); f.setParameter(“nameFilterParam”, “Plenary”); List results = session.createQuery(“from Event”).list(); This code enables the named filter ant then sets the filter parameter. This results will only contain instances of Event with the name Plenary. We can also enable multiple filters per session.
  • 51.
    Metadata alternatives Attribute-oriented metadata JSR-175 annotations support, coming soon! XDoclet Hibernate module Hibernate mapping metadata can be manipulated programmatically we can use dom4j or JDOM to generate mapping files in memory we can manipulate the Hibernate Configuration metamodel directly let's demonstrate this my adding a property to a mapped class… /** * @hibernate.class * table=&quot;CATEGORY&quot; */ public class Category { ... /** * @hibernate.property */ public String getName() { return name; } ... }
  • 52.
    Adding a propertyat runtime // Get the existing mapping for User from Configuration PersistentClass userMapping = cfg .getClassMapping(User.class); // Define a new column for the USER table Column column = new Column(); userMapping.getTable().addColumn(column); // Wrap the column in a Value SimpleValue value = new SimpleValue(); value.setTable( userMapping.getTable() ); value.addColumn(column); // Define a new property of the User class Property prop = new Property(); prop.setValue(value); prop.setName(&quot;motto&quot;); userMapping.addProperty(prop); // Build a new session factory, using the new mapping SessionFactory sf = cfg.buildSessionFactory();
  • 53.
    Reading metadata atruntime There is also the runtime metamodel , exposed via the SessionFactory : Category category = ...; ClassMetadata meta = sessionFactory.getClassMetadata(Category.class); String[] metaPropertyNames = meta.getPropertyNames(); Object[] propertyValues = meta.getPropertyValues(category);