Presentation is loading. Please wait.

Presentation is loading. Please wait.

Object Persistence using Hibernate An Object-Relational mapping framework for object persistence.

Similar presentations


Presentation on theme: "Object Persistence using Hibernate An Object-Relational mapping framework for object persistence."— Presentation transcript:

1 Object Persistence using Hibernate An Object-Relational mapping framework for object persistence

2 What Hibernate Does  Object - Relational mapping  Transparent persistence & retrieval of objects  Persistence of associations and collections  Guaranteed uniqueness of an object (within a session)

3 Hibernate Features  O-R mapping using ordinary JavaBeans  Can set attributes using private fields or private setter methods  Lazy instantiation of collections (configurable)  Polymorphic queries, object-oriented query language  Cascading persist & retrieve for associations, including collections and many-to-many  Transaction management with rollback  Can integrate with other container-provided services

4 Application Architecture User Interface Application Logic Domain Objects DAO Hibernate JDBC Foundation Classes UI event data request Hibernate API domain object data xfer object JDBC APIResultSet, etc. hibernate.cfg.xml *.hbm.xml class mappings SessionFactory

5 Another View Source: Hibernate Reference Manual (online)

6 Example of Persisting an Object sessionFactory = new Configuration().configure().buildSessionFactory(); // an Event object to save Location ku = new Location( "Kasetsart University" ); ku.setAddress( "90 Pahonyotin Road; Bangkok" ); Event event = new Event("Java Days"); event.setLocation( ku ); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.save( event ); tx.commit(); session.close();

7 Example of Retrieving an Object // use the existing session factory Session session = sessionFactory.openSession(); // a query for Event objects Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Event where name='Java Days'"); List events = query.list( ); out.println("Upcoming Java Days events: "); for( Object obj : events ) { Event event = (Event) obj; String name = event.getName( ); Location loc = event.getLocation( );... }

8 Using Named Parameters in Query // use the existing session factory Session session = sessionFactory.openSession(); // Hibernate Query Language (HQL) can use named params Query query = session.createQuery( "from Event where name=:name"); query.setParameter( "name", "Java Days"); List events = query.list( ); out.println("Upcoming Java Days events: "); for( Object obj : events ) { Event event = (Event) obj; String name = event.getName( ); Location loc = event.getLocation( );...

9 Exercise 1  Configure a project with Hibernate create an EventManager project add Hibernate libraries to the project add JAR for database driver create a hibernate.cfg.xml file create log4j.properties

10 Project Configuration for Hibernate EventManager/ the project base directory src/ hibernate.cfg.xml Hibernate configuration file log4j.properties Log4J configuration file eventmgr/ base package is "eventmgr" domain/ Location.java location.hbm.xml O-R mapping file for a class bin/ hibernate.cfg.xml copied here by IDE during build log4j.properties copied here by IDE during build eventmgr/ domain/ Location.class location.hbm.xml copied here by IDE during build lib/ hibernate3.jar Hibernate requires several jars asm.jar...

11 Where to Get Hibernate http://www.hibernate.org Local copy: http://se.cpe.ku.ac.th/download/hibernate

12 Required Hibernate Libraries  Libraries are in lib/ directory of Hibernate distribution.  Which ones do you need? See _README.txt or my Using Hibernate notes.  In your IDE: create a Hibernate "library" add JARs to the library better than copying JAR files to your project Required Libraries hibernate3.jar antlr-2.7.6.jar asm.jar cglib.jar commons-collections.jar commons-logging.jar dom4j.jar ehcache.jar jta.jar log4j.jar // and maybe xml-apis.jar

13 Create a Hibernate Library in Eclipse 1. Project -> Properties -> Java Build Path 2. select Libraries tab. 3. Click "Add Library..." and select "User Library", Next> 4. Create a new user library named "Hibernate" 5. Add Jar files

14 Add Library or jar for Database Driver  For Embedded Derby Database /path-to-derby/lib/derby.jar  For HSQLDB /path-to-hsqldb/lib/hsqldb.jar

15 hibernate.cfg.xml for Embedded Derby DB <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate- configuration-3.0.dtd"> org.hibernate.dialect.DerbyDialect org.apache.derby.jdbc.EmbeddedDriver jdbc:derby:/temp/eventmgr;create=true create Remove after database created

16 hibernate.cfg.xml for MySQL org.hibernate.dialect.MySQLDialect com.mysql.jdbc.Driver student secret jdbc:mysql://se.cpe.ku.ac.th/eventmgr... other mappings omitted

17 XML Syntax, 1  XML Declaration: "this file contains XML"  Root Element: "this whole file is a hibernate-configuration" content of document goes here  Child Elements: define content, have a tree-like nested structure...  Tree structure & Scope: all elements must have a closing tag a start element element start & end an end tag

18 XML Syntax, 2  Attributes: values must always be quotes, no duplicates  Special characters: & ' " must use reference except in CDATA "Hello <b>world</b>&quot  Child Elements can be repeated (depends on DTD) object oriented programming software spec & design  Elements can sometimes be written as attributes 219341

19 Logging  Hibernate apps will log errors and/or activity.  Two choices: Java SDK logging (since JDK 1.4) Log4j if you use Log4j, create a log4j.properties in your application source root directory. Copy this file from the Hibernate etc/ directory.

20 Sample log4j.properties  Too long to print here  See an actual file, for example: [hibernate.dir]/doc/tutorial/src/log4j.properties  Configuration logging of Hibernate: log4j.logger.org.hibernate=warn  Log the SQL generated by Hibernate: #log4j.logger.org.hibernate.SQL=debug  Log JDBC bind parameters (can be security leak): log4j.logger.org.hibernate.type=info

21 Exercise 2  Define a Location class and LOCATIONS table  Write the Location class  Create a mapping file for Location: Location.hbm.xml

22 Create the Location class Location class Location id: Integer name: String address: String LOCATIONS PKidINTEGER nameVARCHAR(80) addressVARCHAR(160) LOCATIONS table (Hibernate can auto-generate this)

23 Write the Location class package eventmgr.domain; public class Location { private Integer id; private String name; private String address; public Location() { } // a no argument constructor public Integer getId( ) { return id; } public void setId( Integer id ) { this.id = id; } public String getName( ) { return name; } public void setName( String n ) { this.name = n; } public String getAddress( ) { return address; } public void setAddress( String a ) { address = a; } public String toString( ) { return [id] name address} } Use JavaBean conventions in Persistent object classes.

24 Write a toString() for Location public String toString( ) { return String.format("[%d] %s, %s", id, name, address); } This will simplify printing the results of our tests.

25 Hibernate can access private methods package eventmgr.domain; public class Location { private int id; private String name; private String address; public Location() { } public int getId( ) { return id; } private void setId( int id ) { this.id = id; } public String getName( ) { return name; } private void setName( String n ) { this.name = n; } public String getAddress( ) { return address; } private void setAddress( String a ) { address = a; } } OK to use "private" or "protected" for mutators.

26 Hibernate can access private data, too public class Location { private int id; private String name; private void setName( String name ) { if ( name.length() < 3 ) new RuntimeException("name too short");... } Some mutator methods contain data validity checks or other complicated logic. to tell Hibernate to set the field values directly (don't use the "set" method) in the class mapping file write:...

27 Schema to create Locations table CREATE TABLE locations ( id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(80) NOT NULL, address VARCHAR(160) ) DEFAULT CHARSET=utf8 ; Hibernate includes utilities to create the table schema at runtime. This is provided for reference This works for MySQL. mysql> use eventmgr ; mysql> source locationschema.sql ;

28 O-R Mapping for the Location class Map between object attributes and table columns. LOCATIONS PKidINTEGER nameVARCHAR(80) addressVARCHAR(160) Location id: Integer name: String address: String O-R Mapping requires a mapping file

29 Mapping File Location.hbm.xml An XML file describing how to map object to table. Filename: Location.hbm.xml

30 Mapping File Explained <class name="eventmgr.domain.Location" table="LOCATIONS" options... > object attribute mappings root element of a hibernate mapping Every persistent class needs an "identifier" attribute and column. The identifier is used to establish object identity (obj1 == obj2) and locate the table row for a persisted object. The id is usually the Primary Key of the table. id is used to distinguish objects; it should be unique and usually the primary key of table id value can be set by application or by Hibernate. "native" lets Hibernate decide how to generate the id.

31 Attribute Mapping: <property name="name" column="name “ type= “ name “ not-null="true “ /> You omit elements if Hibernate can guess the value itself: Name of the attribute in Java class Column name in the database table Hibernate or Java data type (usually you can omit it) Constraints

32 What you have so far  Project configured for Hibernate  Hibernate configuration file  Location class and mapping file Location.hbm.xml  Configure a database schema (for learning, we will auto-generate the schema)

33 Exercise 3a  Design a utility class to manage Hibernate sessions create a singleton SessionFactory create session objects, to eliminate redundant code

34 HibernateUtil: a Session Factory (1) HibernateUtil -sessionFactory: SessionFactory +getSessionFactory( ): SessionFactory +getCurrentSession( ): Session +openSession( ): Session // create only one sessionFactory (Singleton) static SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); // use sessionFactory to get a Session Session session = sessionFactory.openSession(); // or: get current session (creates if no current sess) Session session = sessionFactory.getCurrentSession(); 1

35 HibernateUtil: a Session Factory (2) public class HibernateUtil { private static SessionFactory sessionFactory = null; private static final Logger log = Logger.getLogger(..); /** get a single instance of the SessionFactory */ public static SessionFactory getSessionFactory() { if ( sessionFactory == null ) { try { // Create the SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Exception ex) { System.err.println("sessionFactory error "+ ex); log.error("SessionFactory creation failed", ex); //throw new ExceptionInInitializerError(ex); } return sessionFactory; }

36 HibernateUtil: a Session Factory (3) /** * Get the current Hibernate session. * This creates a new session if no current session. * @return the current Hibernate Session */ public static Session getCurrentSession() { return getSessionFactory().getCurrentSession(); } public static Session openSession() { return getSessionFactory().openSession(); }

37 Exercise 3b: Test of Save/Retrieve Let's do 3 tests:  testSave( ) : save some locations to database  testRetrieve( ) : get the locations from database  testUpdate( ) : update a location public class LocationTest { public static void main( ) { saveLocations( );// save some locations testRetrieve( ); // can we get same data? testUpdate( name, newAddress ); // edit a Location testRetrieve( );// do we get the updated values? }

38 Persistence Test: testSave( ) public static void saveLocations() { Location loc1 = new Location( ); loc1.setName( "Kasetsart University" ); loc1.setAddress( "90 Pahonyotin Rd, Bangkok" ); Location loc2 = new Location(); loc2.setName( "Mahidol University" ); loc2.setAddress( "Salaya, Nathorn Pathom" ); System.out.println( "Saving locations..." ); //TODO print description of loc1 and loc2 Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); session.save( loc1 ); session.save( loc2 ); tx.commit(); System.out.println( "Locations saved" ); }

39 Persistence Test: testRetrieve( ) public static void testRetrieve() { System.out.println( "Retrieving locations" ); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Location" ); // query.list() returns Objects, cast to List List list = (List )query.list( ); tx.commit(); for(Location loc : list ) out.println( loc.toString() ); if ( session.isOpen() ) session.close(); }

40 Persistence Test: testUpdate( ) public static void testUpdate( String name, String newAddress ) { out.println( "Updating location "+name ); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Location where name = :name" ); query.setParameter("name", name); List list = (List )query.list( ); if ( list.size()==0 ) out.println("No matches found"); else { // update the first match Location loc = (Location) list.get(0); loc.setAddress( newAddress ); out.println( name+" updated" ); } tx.commit(); }

41 Exercise 3c Test object uniqueness: 1.In one session if we query the same thing 2 times, do we get 2 different objects or the same object? ku1 = (Location) query1.list().get(0); ku2 = (Location) query2.list().get(0) (ku1 == ku2)? 2.Query same thing 2 times, but in different sessions. Do we get the same object or a new object? 3.Start a new session, attach the old location [merge(loc) or update(loc)], then query location again. Do we get the same object or a new object?

42 Test for Object identity: testIdentity() Session session = HibernateUtil.getCurrentSession(); Query query1 = session.createQuery( "from Location where name=‘Kasetsart University’"); // query.uniqueResult() returns only first match // uniqueResult() is same as: query1.list().get(0); Location ku1 = (Location) query1.uniqueResult( ); Query query2 = session.createQuery( "from Location l where l.name like 'Kasetsart%'"); // query.uniqueResult() returns only first match Location ku2 = (Location) query2.uniqueResult( ); Query using Pattern match: " like " - use "%" as wildcard if ( ku1 == ku2 ) out.println("Got same object 2 times"); else out.println( "Got different objects" );

43 Mapping a Class with Associations Simplified version of Event class. Event id: Integer name: String startDate: Date location: Location Location id: Integer name: String address: String 1 *

44 Many-to-one Associations public class Event { private Integer id; private String name; private Location location; // 1-to-many assoc. private Date startDate; public Event( ) { }... public void setLocation( Location loc ) { this.location = loc; } public Location getLocation() { return location; }

45 O-R Mapping of n-to-1 Associations Event id: Integer name: String startDate: Date location: Location LOCATIONS PKidINTEGER nameVARCHAR addressVARCHAR Location id: Integer name: String address: String EVENTS PKidINTEGER nameVARCHAR start_date TIMESTAMP FKlocation_id INTEGER The Data Mapper converts a n-to-1 association to a foreign key relation (persist) or foreign key to object (retrieve). 1 *

46 Mapping for Event ( Event.hbm.xml ) Use to map a reference to another object.... <many-to-one name="location" column="location_id" class="Location" /> you can omit class (Hibernate can determine it from the object reference) the foreign key column used to establish the association the attribute name in Event class

47 Event.hbm.xml Mapping File <property name="startDate" column="start_date" column="timestamp"/> <many-to-one name="location" column="location_id" class="Location"/>

48 Exercise 4a: Test many-to-one 1. Create mapping file Event.hbm.xml 2. Write the Event.java class default constructor save time: let Hibernate generate "get/set" methods a toString() method to display object, including id Event id: Integer name: String startDate: Date location: Location + Event( ) + get_____( ) + set_____( param ) + toString():String public String toString() { return String.format("[%d] %s, starts %tD", id, name, startDate); }

49 Exercise 4b: Test many-to-one  saveEvents( ) : save some events to database  testRetrieve( ) : get the event from database  testUpdate( name, location ) : update an event public class EventTest { public static void main( ) { LocationTest.saveLocations(); // create locations saveEvents( );// save some events testRetrieve( ); // do we get the saved data? testUpdate( name, Location ); // update an event testRetrieve( );// do we see the update? }

50 Test: Save an Event public static void saveEvents() { Location loc1 = new Location( ); loc1.setName("Queen Sirikit Convention Center"); loc1.setAddress("Ratchapisek Rd, Bangkok"); Event event = new Event( ); event.setName("Java Days"); event.setStartDate( new Date(108,Calendar.JULY, 1) ); event.setLocation( loc1 ); out.println("\nSaving event..."); out.printf(" %s\n at: %s\n",event,event.getLocation()); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); session.save( event ); // OR: saveOrUpdate( event ) tx.commit(); out.println("Event saved"); }

51 Did you get an Error? The Location doesn't exist in database (transient object). Exception in thread "main“ org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: eventmgr.domain.Location System.out.println("Saving event "+event+ "..."); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); session.save( event ); tx.commit();

52 Solution: persist the Event location 1. save location during the transaction (manual save) 2. tell Hibernate to cascade the save operation (automatically save Location) <many-to-one name="location" column="location_id" class="Location" cascade="save-update" /> cascade="none"don't cascade operations "all"cascade all operations (be careful) "save-update"cascade save and updates "delete-orphan"cascade all, delete unreferenced orphan children session.save( event ); session.save( event.getLocation() ); tx.commit( );

53 Test: Retrieve an Event public static void testRetrieve() { System.out.println("\nRetrieving event..."); Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Event e where e.name= :name" ); query.setParameter("name", "Java Days"); // query.list() returns objects, cast to List List list = (List )query.list( ); tx.commit( ); session.close( ); for( Event e : list ) out.printf("%s\n at %s\n", e.toString(), e.getLocation() ); }

54 Lazy Instances and Proxies Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Event e where e.name=:name"); query.setParameter("name", "Java Days"); List list = (List )query.list( ); tx.commit(); session.close( ); for( Event e : list ) out.printf("%s\n at: %s\n", e.toString(), e.getLocation() ); Error: LazyInstantiationException Hibernate uses lazy instantiation and proxy objects for associations Hibernate instantiates the location object when it is first accessed We closed the transaction before accessing the location

55 Two Solutions 1.Modify our code: getLocation( ) before closing the session. 2.Tell Hibernate not to use lazy instantiation of Location objects (in Location.hbm.xml ) List list = (List )query.list( ); for( Event e : list ) out.printf("%s\n at: %s\n", e.toString(), e.getLocation() ); session.close( );...

56 TestUpdate: update an Event public static void testUpdate(String name, Location loc) { System.out.println("\nUpdating event "+name); Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Event e where e.name= :name"); query.setParameter("name", name); List list = (List )query.list( ); Event event = list.get(0); // move event event.setLocation( loc ); tx.commit( ); session.close( ); }

57 Mapping of 1-to-many Associations 1-to-many is modeled as a List or Set List: ordered collection Set: unordered collection, no duplicates Event id: Integer name: String startDate: Date location: Location Speaker id: Integer name: String telephone: String speakers 1 *

58 Mapping of 1-to-n Associations Event id: Integer name: String startDate: Date SPEAKERS PKidINTEGER nameVARCHAR telephoneVARCHAR FKevent_idINTEGER Speaker id: Integer name: String telephone: String EVENTS PKidINTEGER nameVARCHAR start_date TIMESTAMP FKlocation_id INTEGER Event has a Set of Speakers. The SPEAKERS table needs a FK reference to EVENTS to save the association. speakers *

59 Java for Event with Speakers public class Event { private Set speakers; public Set getSpeakers( ) { return speakers; } private void setSpeakers( Set speakers ) { this.speakers = (Set ) speakers; } public void addSpeaker( Speaker spkr ) { this.speakers.add( spkr ); } IMPORTANT: The declared type of a collection must be Set, List, or Map interface type, not a specific implementation. addSpeaker is not required by Hibernate

60 Event Mapping File Add a for the speakers attribute. It is 1-to-many.... <many-to-one name="location" column="location_id" class="Location" /> Foreign key field in SPEAKERS table Declare the data type of speakers

61 Exercise: write the Speaker class  Write the Speaker class  Write the Speaker.hbm.xml file  Add Speaker.hbm.xml as a resource in the Hibernate configuration file. Speaker id: Integer name: String telephone: String Speaker( ) Speaker( name, telephone ) - setId( id : Integer ): void... Convenience constructors

62 Add speakers to EventTest.java public static void saveEvents() {... Event event = new Event( );... Speaker gosling = new Speaker("James Gosling"); event.addSpeaker( gosling ); Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); session.save( event ); tx.commit(); session.close( ); out.println("Event saved"); } public static void testRetrieve() { //TODO print the Event with Speakers }

63 Add a Speaker in testUpdate, too public static void testUpdate(String name, Location loc) { System.out.println("\nUpdating event "+name); Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery( "from Event e where e.name= :name"); query.setParameter("name", name); List list = (List ) query.list( ); Event event = list.get(0);... Speaker yuen = new Speaker("Prof. Yuen"); event.addSpeaker( yuen ); tx.commit( ); }

64 Lazy Instantiation

65 More Mappings 1. Components an object that is mapped into fields of the "owning" object's table. 2. Collection of values collections of Strings, Dates, etc., where "identity" of the object is not important 3. Bidirectional associations any association can be made bidirectional Components and bidirectional associations are listed as Hibernate "Best Practices".

66 Creating a Component for Address Address street: String city: String province: String Location id: int name: String address: Address 1 * LOCATIONS idname street city province 101Kasetsart University Pahon.. Bangk Bangkok 102Queen Sirikit Center Ratch.. Bangk Bangkok A component lets you use fine-grained objects in Java without creating a separate database table. Component fields are columns in owning class's table.

67 Mapping for a Component Filename: Location.hbm.xml

68 Notes about Component  Default fetching is lazy="false". No problem with proxies or detached objects  Components don't need an id field  Components don't need a separate mapping file  You could do the same thing without a component: Location id: Integer name: String street: String city: String province: String Alternative to Using an Address component: Address properties as attributes of Location

69 Collection of Values We used a collection for 1-to-many association of Event to Speakers.  each speaker object has a unique identity. Event id: Integer name: String startDate: Date Speaker id: Integer name: String telephone: String speakers * Foreign key field in SPEAKERS table The class for mapping speakers

70 Collection of Values What about a collection of Strings?  we don't care about the String identity, only its value. Speaker id: Integer name: String telephone: String email: String[*] public class Speaker { Integer id; String telephone; Set email; Each speaker can have several email addresses.

71 Mapping a Collection of Values... SPEAKERS PKidINTEGER nameVARCHAR telephoneVARCHAR FKevent_idINTEGER SPEAKER_EMAIL PKidINTEGER emailVARCHAR FKspeaker_id INTEGER

72 Hibernate "value" types  See Hibernate manual, Section 5.2 for full list. integer, long, float, double, character, byte, boolean, yes_no string date, time, timestamp calendar, calendar_date big_decimal, big_integer locale, currency, timezone, text clob, blob serializable Java primitive and wrapper types, yes_no is for boolean maps Java String to varchar maps Java Date to SQL DATE, TIME, TIMESTAMP Java String to CLOB or TEXT map Java Serializable to an SQL binary type, e.g. BLOB

73 Bidirectional Associations

74 Bidirectional 1-to-many or many-to-1 public class Speaker { private Event event; public void setEvent( Event evt ) { if ( event != null ) event.removeSpeaker( this ); this.event = evt; event.addSpeaker( this ); } public Event getEvent( ) { return event; } Event id: Integer name: String startDate: Date speakers: Set Speaker id: Integer name: String telephone: String event: Event * Make both ends consistent

75 Revise the Event class  Add code to make the association consistent. public class Event { private Set speakers; /** only Hibernate should call setSpeakers */ protected void setSpeakers( Set speakers ) { this.speakers = speakers; } public void addSpeaker( Speaker speaker ) { if ( ! speakers.contains( speaker ) ) { speakers.add( speaker ); if ( speaker.getEvent( ) != this ) speaker.setEvent( this ); } Avoid infinite loop

76 Update the class mapping files Designate one end as "inverse".... <many-to-one name="event" column="event_id" class="Event" />...

77 Transactions and Sessions

78 Sessions & Transactions Explained  Good explanation at Hibernate web site: http://www.hibernate.org/42.html  Also read the Hibernate manual: Chapter 11. Transactions and Concurrency

79 Session  A session is a "conversation" between your code and Hibernate.  During a session, persistent objects are tracked by Hibernate.  In a session Hibernate manages: object identity transparent updates lazy fetching of associations  Use a Session for a Unit of Work

80 Transaction  Transaction is a group of data access operations.  All communication with database has to execute inside a transaction.  Every SQL statement runs inside a transaction.  Database commands aren't executed immediately; they may be delayed until a commit or flush command.

81 Session and Transaction lifetime factory.openSession( )session.close( ) session lifetime sess.beginTransaction() event = sess.load( id ) tx1.commit( ) transaction1 event.getLocation(). getName( ) implicit trans event.addSpeaker( spkr ) updated in Hibernate cache, but not committed yet flush cache lazy fetch

82 Two Usage Patterns 1. Session per request with detached objects good for web applications long think-time between user requests Session Transaction

83 Two Usage Patterns 2. Session per conversation efficient for multiple requests grouped close together Session Transaction

84 Caveat about getCurrentSession( ) sessionFactory.getCurrentSession( ); creates a Session that is bound to a transaction! When transaction is committed, the session is also closed. Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); Event event = session.load( id ); // load an event tx.commit(); // now the session is closed, too! Location loc = event.getLocation(); // ERROR unless // lazy="false" // you cannot begin another transaction tx = session.beginTransaction( ); // ERROR

85 openSession for longer conversation sessionFactory.openSession( ); creates a Session that is not bound to a transaction. Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Event event = session.load( id ); // load an event tx.commit(); // the session is still open Location loc = event.getLocation(); // OK

86 Organizing your Work Use DAO to separate OR behavior from the rest of your project.

87 A DAO for the Location class The "useful" methods depend on the domain class and the application. LocationDao findById(id: int) : Location findByName(name : String): Location[*] find(query: String) : Location[*] save(loc: Location) : boolean delete(loc: Location) : boolean

88 Java code for Simple LocationDao package eventmgr.dao; import...; public class SimpleLocationDao { private static final Logger logger = Logger.getLogger(LocationDao.class); public LocationDao() { } public Location findById( int id ) public List findByName( String name ) public List query( String query ) public boolean save( Location loc ) public boolean delete( Location loc ) }

89 The core of findById( ) - use "load" public Location findById( int id ) { Location result = null; Session session = null; Transaction tx = null; try { session = HibernateUtil.getCurrentSession(); tx = session.beginTransaction(); result = (Location)session.load(Location.class, id); tx.commit(); session.close( ); } catch... } return result; }

90 The details of findById( ) public Location findById( int id ) { Location result = null; Session session = null; Transaction tx = null; try { session = HibernateUtil.getCurrentSession(); tx = session.beginTransaction(); result = (Location)session.load(Location.class,id); tx.commit(); } catch (ObjectNotFoundException e) { logger.info("Object not found. id = "+id, e); if ( tx != null && ! tx.wasCommitted() ) tx.rollback(); } catch (HibernateException e) { logger.error("Hibernate exception", e); if ( tx != null && ! tx.wasCommitted() ) tx.rollback(); } finally { if ( session != null && session.isOpen() ) session.close(); } return result; }

91 The core of findByName( ) - "query" public List findByName( String name ) { List result;... try { session = HibernateUtil.openSession(); tx = session.beginTransaction(); Query query = session.createQuery( "from Location where name=:name" ); query.setParameter( "name", name ); result = (List ) query.list( ); tx.commit(); session.close( ); } catch... return result; }

92 Details of findByName( )  Exercise

93 The core of save( ) - "saveOrUpdate" public boolean save( Location location ) { boolean result = false;... try { session = HibernateUtil.openSession(); tx = session.beginTransaction(); session.saveOrUpdate( location ); tx.commit(); session.close( ); result = true; } catch... return result; }

94 Details of save  Exercise

95 The core of delete( ) - "delete" public boolean delete( Location location ) { boolean result = false;... try { session = HibernateUtil.openSession(); tx = session.beginTransaction(); session.delete( location ); tx.commit(); session.close( ); result = true; } catch... return result; }

96 Redundant Code  Every DAO method has the same boilerplate code: Session session = null; Transaction tx = null; try { session = HibernateUtil.openSession(); tx = session.beginTransaction(); do the work here tx.commit(); } catch (ObjectNotFoundException e) { logger.info("Object not found. "+id, e); if ( tx != null && ! tx.wasCommitted() ) tx.rollback(); } catch (HibernateException e) { logger.error("Hibernate exception", e); if ( tx != null && ! tx.wasCommitted() ) tx.rollback(); } finally { if ( session != null && session.isOpen() ) session.close(); }

97 Factor out Redundant Code Class SimpleLocationDao { private Session session; // declare as attributes private Transaction tx; public Location findById( int id ) { try { beginTransaction( ); do the work here commitTransaction( ); } catch (ObjectNotFoundException e) { handleError( e ); } catch (HibernateException e) { handleError( e ); } finally { closeSession( ); }

98 Duplicate Code Between DAO  In every DAO, the CRUD methods are almost the same  Consider save( )... public boolean save( Location location ) { boolean result = false;... try { beginTransaction( ); session.saveOrUpdate( location ); commitTransaction( ); } catch (... ) { handleException( e ); } finally {... } return result; }

99 Apply the Layer Superclass Pattern AbstractDao #load( class, id ) : Object #find( class, query ) : List #saveOrUpdate( object: Object ) #delete( object: Object ) LocationDaoEventDaoSpeakerDao

100 AbstractDao.saveOrUpdate protected Object saveOrUpdate( Object obj ) { Object result = null; try { beginTransaction(); result = session.saveOrUpdate( obj ); commitTransaction(); } catch (ObjectNotFoundException e) { handleError( e ); } catch (HibernateException e) { handleError( e ); } // closing session probably not desired here return result; }

101 LocationDao using Layer Superclass public class LocationDao extends AbstractDao { public boolean save( Location location ) { return super.saveOrUpdate( location ); }

102 AbstractDao.load protected Object load( Class clazz, Serializable id ) { Object result = null; try { beginTransaction(); result = session.load( clazz, id); commitTransaction() } catch (ObjectNotFoundException e) { handleError( e ); } catch (HibernateException e) { handleError( e ); } return result; }

103 LocationDao using Layer Superclass public class LocationDao extends AbstractDao { public LocationDao( ) { super( ); } public Location findById( int id ) { return (Location) super.load( Location.class, id ); }

104 Advice from the Hibernate authors... Implementing data access objects (DAOs) Writing DAOs that call Hibernate is incredibly easy. You don't need a framework. You don't need to extend some "DAOSupport" superclass from a proprietary library. All you need to do is keep your transaction demarcation (begin and commit) as well as any Session handling code outside of the DAO implementation. For example, a ProductDAO class has a setCurrentSession() method or constructor, or it looks up the "current" Hibernate Session internally. Where this current Session comes from is not the responsibility of the DAO. How a transaction begins and ends is not the responsibility of the DAO! All the data access object does is use the current Session to execute some persistence and query operations. For a pattern that follows these rules, see Generic Data Access Objects (http://www.hibernate.org/328.html).Generic Data Access Objects

105  http://www.hibernate.org/328.html

106 Exercise  use your SimpleLocationDao to write a layer superclass named AbstractDao.  write a LocationDao that extends AbstractDao

107 Hibernate Query Language (HQL)

108 Hibernate Query Language Query query = session.createQuery( "from Event where name='Java Days'"); Query query = session.createQuery( "select e from Event e where e.name='Java Days'");  Hibernate queries you Hibernate Query Language (HQL).  HQL is object-centric - use class and property names, not SQL table names. is same as writing:

109 HQL example String query = "select e from Event e where e.location.name = 'Kasetsart University'"; Query q = session.createQuery( query ); String sqlquery = "select * from EVENTS e join LOCATIONS l ON e.location_id = l.id where l.name = 'Kasetsart University'"; Statement stmt = connection.createStatement( sqlquery ); Problem: Find all events which are held at Kasetsart HQL: SQL and JDBC:

110 Using Named Parameters in Query // use the existing session factory Session session = sessionFactory.openSession(); // Hibernate Query Language (HQL) can use named params Query query = session.createQuery( "from Event where name=:name"); query.setParameter( "name", "Java Days"); List events = query.list( ); out.println("Upcoming Java Days events: "); for( Object obj : events ) { Event event = (Event) obj; String name = event.getName( ); Location loc = event.getLocation( );...

111 Cascading Save & Update... <many-to-one name="location" column="location_id" class="Location" cascade="save-update"/> When you save an Event, should Hibernate automatically save the location, too? no: then you must save the location yourself Yes: specify cascade = "save-update" Other choices for cascade: all, none, save-update, delete

112 Learning Hibernate  Tutorial at www.hibernate.org  Another good tutorial: http://www.allapplabs.com/hibernate/hibernate_tutorials.htm  Peak & Heudecker, Hibernate Quickly, 2006.  Baur & King, Java Persistence with Hibernate, 2007, update of Hibernate in Action, and much longer.  Baur & King, Hibernate in Action, 2005, considered the best Hibernate book, by one of the Hibernate creators.

113 Hibernate Tools Hibernate Tools Eclipse plugin - HibernateConsole and more Test HQL queries, browse database Middlegen - generates class mapping files (hbm.xml) from an existing database schema. Has Eclipse plugin. Hibernate Synchronizer - generates class mapping files (hbm.xml) and Java classes from a database schema. hbm2ddl - creates a database schema (in SQL) from a hbm.xml file. hbm2java - creates a Java class from a hbm.xml file.

114 Hibernate Wisdom  "Best Practices" - last chapter of Hibernate Reference Manual. "must reading" -- only 2 pages


Download ppt "Object Persistence using Hibernate An Object-Relational mapping framework for object persistence."

Similar presentations


Ads by Google