Top Banner
Merge In session, Hibernate guarantees no two Persistent objects represent the same row. Again, this guarantee no longer holds with Detatched objects. In fact, this problem can create very unwanted consequences. Explore the code below. Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); BallPlayer p1 = (BallPlayer)session.get(BallPlayer.class, 1L); transaction.commit(); session.close(); //p1 is now detached. session = sessionFactory.openSession(); transaction = session.beginTransaction(); BallPlayer p2 = (BallPlayer)session.get(BallPlayer.class, 1L); //Oops! p2 represents the same persistent row as p1. //When an attempt to reattach p1 occurs, an exception is thrown session.update(p1); transaction.commit(); session.close(); This code throws an exception when an attempt to reattach the Detached object at p1 is made. Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.intertech.domain.BallPlayer#1] at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness (StatefulPersistenceContext.java:613) ... This is because Hibernate is trying to reinforce the guarantee that only a single instance of a Persistent object exist in memory. The merge operation, helps to deal with this situation. Session session = sessionFactory.openSession();
42

Merge

Jan 16, 2016

Download

Documents

me
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Merge

Merge

In session, Hibernate guarantees no two Persistent objects represent the same row. Again, this guarantee no longer holds with Detatched objects. In fact, this problem can create very unwanted consequences. Explore the code below.

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer p1 = (BallPlayer)session.get(BallPlayer.class, 1L);transaction.commit();session.close();//p1 is now detached.session = sessionFactory.openSession();transaction = session.beginTransaction();BallPlayer p2 = (BallPlayer)session.get(BallPlayer.class, 1L);//Oops!  p2 represents the same persistent row as p1.//When an attempt to reattach p1 occurs, an exception is thrownsession.update(p1);transaction.commit();session.close();

This code throws an exception when an attempt to reattach the Detached object at p1 is made.

Exception in thread "main" org.hibernate.NonUniqueObjectException: a   different object with the same identifier value was already associatedwith the session: [com.intertech.domain.BallPlayer#1]        atorg.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:613)...

This is because Hibernate is trying to reinforce the guarantee that only a single instance of a Persistent object exist in memory. The merge operation, helps to deal with this situation.

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer p1 = (BallPlayer)session.get(BallPlayer.class, 1L);transaction.commit();session.close();//p1 is now detached.session = sessionFactory.openSession();transaction = session.beginTransaction();BallPlayer p2 = (BallPlayer)session.get(BallPlayer.class, 1L);BallPlayer p3 = (BallPlayer) session.merge(p1);if (p2 == p3) {  System.out.println("They're equal");}

Page 2: Merge

transaction.commit();session.close();

The merge() method is a little complex and works differently depending on what is in/out of the persistence context. Hibernate will first check whether a Persistent instance of that type already exists in the persistent context. It uses the object identifiers to check on this existence. If another instance exists, it copies the state of the Detached object (p1 above) into the existing Persistence object (p2 above). If no other instance exists, Hibernate just reattaches the Detached object. In the example above, p2==p3 and one Persistent object exists. In the example below, two Persistent objects now exist and p2!=p3

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer p1 = (BallPlayer)session.get(BallPlayer.class, 3L);transaction.commit();session.close();//p1 is now detached.session = sessionFactory.openSession();transaction = session.beginTransaction();BallPlayer p2 = (BallPlayer)session.get(BallPlayer.class, 1L);BallPlayer p3 = (BallPlayer) session.merge(p1);if (p2 == p3) {  System.out.println("They're equal");}System.out.println(p2);System.out.println(p3);transaction.commit();session.close();

A complete diagram of the lifecycle now makes sense, given a description of the states and methods that transition the objects between states.

Page 3: Merge

Object Relational Mapping (ORM) An entire field of software engineering has grown from impedance mismatch.

ORM is the name given to automated technology developed to solve the mismatch problem. ORM provides transformations between object and relational models. Let the database do the things it does well – store and retrieve large amounts of data. Let objects do the things they do well – mimic the business domain. ORM technologies provide the tools to bridge the two in order to provide object state persistence in a relational database. This persistent automation should be transparent to developers.

ORM in Java means providing: • A Java API for performing data creates, read, update and delete operations on object state (without directly using SQL or JDBC). • A Java API to query and instantiate objects based on query results. • A means for mapping metadata in objects to metadata of the database. • A mechanism for handling transactions across the object-relational environments. • A mechanism for handling concurrency and cache issues.

Page 4: Merge

Persistent Framework What’s a persistent framework? - An ORM service that stores and retrieves objects into a relational database.

From the developer’s perspective, it should work transparently (or nearly transparently). Meaning the object developer should not have to know all the specifics about relational databases and SQL. A persistence framework is a tool for simplifying the development and maintenance of object systems. Hibernate is considered a persistent framework for Java!

So why do we need a persistence framework? - To avoid hard coding SQL into our Java applications.

JDBC and SQL require Java programmers to be familiar with relational database details. Java JDBC work is tedious and diverts programmer attention from programming business logic. Programmers have to constantly concern themselves with binding query parameters, writing SQL, executing queries, scrolling the result set and retrieving the required data. Java programs are object-oriented (or at least should be) in nature whereas relational databases are tabular in nature. As shown above, this complicates the object-relational mapping and forces developers to think about problems in two different ways. Persistent frameworks help alleviate the ORM “impedance mismatch.” Therefore, a persistent framework makes coding faster and improves maintenance. The persistent framework can also help to isolate the application from database specific intricacies. Finally, a persistent framework can, in some cases, improve performance with appropriate performance enhancements and caching.

Hibernate Created by Gavin King. • Started in 2001.

Developed to meet needs he thought EJB container managed persistent (CMP) entity beans did not achieve. • CMP did not scale. • Many, including King, believed CMP entity beans were too complex. • According to product documentation – “Hibernate’s goal is to relieve the developer from 95 percent of common data persistence related programming tasks.”

Hibernate is an open source persistent framework. Hibernate team joined JBoss.org in 2003. Hibernate also supports the Enterprise Java Bean 3.0 persistence standardization effort. In fact, Hibernate is considered an implementation of the EJB 3.0 persistence standard. Hibernate implements EJB 3.0 with Hibernate Annotations and EntityManager. Two members of the Hibernate development team are on the EJB Expert Group

Hibernate is available from hibernate.org. All Hibernate project documentation is also available on-line at same address. This class focuses on Hibernate 3.x (made available in 2005).

Java ORM/Persistent Frameworks There are several persistent frameworks and ORM options in Java. • Enterprise JavaBeans Entity Beans • Java Data Objects • Castor • TopLink

Page 5: Merge

• Spring DAO • And many more

Some alternatives are open source products and some are commercial offerings. Many have been around longer than Hibernate.

Of course, there is always JDBC and SQL. Typically using the Data Access Object design pattern. DAO offers a very simple method of mapping Java objects to databases. In general, Hibernate is considered highly portable, provides good mapping flexibility, and good performance.

Hibernate Example

The best way to learn most Java APIs and frameworks is to jump right in. In this chapter, you see your first, albeit simple, Hibernate application. You also see how to get Hibernate and install it into your application environment.

In this section, you are also introduced to the Hibernate Architecture, API, configuration and mapping files. The Hibernate API that you use in most applications is very small and simple to use. Most of the real work comes in configuring Hibernate and mapping persistent objects to the database.

A Quick Hibernate Example

So how does Hibernate work? What does Hibernate look like? Perhaps the best place to start is with a quick and simple example.

Imagine you had an application with a Java type called BallPlayer.

public class BallPlayer { private Long id; private String name; private String nickname; private Calendar dob; private String birthCity; private short uniformNumber; //getter and setter methods removed here for brevity.}

BallPlayer objects are to be saved in a database table called Player.

create table player ( id integer primary key, name varchar not null,

Page 6: Merge

 nickname varchar, date_of_birth date, city_of_birth varchar, uniform_number integer);

To create and persist a BallPlayer object into the Player table using the Hibernate API would look similar to the code below.

Calendar dob = Calendar.getInstance();dob.set(1934, Calendar.FEBRUARY, 5);BallPlayer player = new BallPlayer();player.setName("Henry Louis Aaron");player.setNickname("Hammerin' Hank");player.setDob(dob);player.setBirthCity("Mobile, AL");player.setUniformNumber((short) 44);

SessionFactory sessionFactory = newConfiguration().configure().  buildSessionFactory();Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();session.save(player);transaction.commit();session.close();

This code would launch the following SQL insert statement into the database.

INSERT INTO PLAYER VALUES(5,'Henry Louis Aaron','Hammerin''

Page 7: Merge

Hank','1934-02-05','Mobile, AL',44)

Note: the SQL shown throughout class is an example of the SQL generated by Hibernate. The actual SQL may look different depending on the database.

Code to read, or maybe more appropriately recreate, a BallPlayer object from its persistent state stored in the database is similar.

SessionFactory sessionFactory = newConfiguration().configure().buildSessionFactory();Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer aPlayer = (BallPlayer) session.get(BallPlayer.class, 1L);transaction.commit();session.close();

The SQL that Hibernate generates to fetch the object’s state looks a little complex, but upon closer scrutiny, it is just a simple SQL select.

select ballplayer0_.id as id0_0_, ballplayer0_.name as name0_0_,ballplayer0_.nickname as nickname0_0_, ballplayer0_.date_of_birth asdate4_0_0_, ballplayer0_.city_of_birth as city5_0_0_,ballplayer0_.uniform_number as uniform6_0_0_ from Player ballplayer0_where ballplayer0_.id=1

The “magic” which makes Hibernate work and be able to persist or recreate a BallPlayer to/from the database comes from a pair of XML files. First is a configuration file that tells Hibernate how to connect and talk to the database.

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><hibernate-configuration> <session-factory>   <property name="dialect"> org.hibernate.dialect.HSQLDialect</property>   <property name="connection.driver_class"> org.hsqldb.jdbcDriver</property>   <property name="connection.url"> jdbc:hsqldb:hsql:

Page 8: Merge

//localhost/baseballdb</property>   <property name="connection.username">sa</property>   <property name="connection.password"></property>   <property name="show_sql">true</property>   <mapping resource="com/intertech/domain/BallPlayer.hbm.xml" /> </session-factory></hibernate-configuration>

Hibernate uses JDBC under the covers. Therefore, just as in your JDBC code, Hibernate needs database driver and connection information.

Second is an XML mapping file shown below.

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping>       <class name="com.intertech.domain.BallPlayer" table="Player">               <id name="id">                       <generator class="sequence">                               <param name="sequence">common_seq</param>                       </generator>               </id>               <property name="name" />               <property name="nickname" />               <property name="dob" column="date_of_birth" />               <property name="birthCity" column="city_of_birth" />               <property name="uniformNumber" column="uniform_number" />       </class></hibernate-mapping>

The mapping file tells Hibernate how persist properties of a BallPlayer into the columns of the Player table (and vice versa).

Much more on the Hibernate API, configuration, and mapping is forthcoming. However, with this simple example hopefully several facts become apparent. First, while Hibernate uses JDBC/SQL under the covers, it removes the developer from most (if not all) thought of the JDBC API and SQL. Second, the number of lines of Java code to persist or read an object like BallPlayer is small specially compared to JDBC code. Third, Hibernate works to be non-intrusive. It works to persist and recreate simple Java objects like BallPlayers. It does not require those objects’ classes to implement any specific interface or extend any special Hibernate class. This allows developers to create domain models without ties to the persistence architecture. In turn, this makes the domain model more flexible, maintainable and typically easier to test.

Hibernate Architecture and API

Page 9: Merge

Hibernate is built on top of many Java APIs and other Java open source frameworks/APIs. The architecture is layered to keep you isolated from having to know the underlying APIs. In fact, while the available Hibernate API is large, the API used by most application developers is relatively small. One only needs to know fewer than a dozen Hibernate classes to use Hibernate.

The Configuration object, as seen in the example code, is the first Hibernate object you use. It represents a configuration or properties file for Hibernate. The Configuration object is usually created once during application initialization. The Configuration object reads and establishes the properties Hibernate uses to get connected to a database and configure itself for work. A Configuration object is used to create a SessionFactory and then typically is discarded.

The SessionFactory is created from a Configuration object, and as its name implies it is a factory for Session objects. The SessionFactory is an expensive object to create. It, like the Configuration object, is usually created during application start up. However, unlike the Configuration object, It should be created once and kept for later use.

The SessionFactory object is used by all the threads of an application. It is a thread safe object. One SessionFactory object is created per database. Multiple SessionFactory objects (each requiring a separate Configuration) are created when connecting to multiple databases. The SessionFactory can also provide caching of persistent objects.

Page 10: Merge

Session objects provide the main interface to accomplish work with the database. Persistent objects are saved and retrieved through a Session object. A Session object is lightweight and inexpensive to create. A Session object does the work of getting a physical connection to the database. Session objects maintain a cache for a single application thread (request). Session objects are not thread safe. Therefore, session objects should not be kept open for a long time.Applications create and destroy these as needed. Typically, they are created to complete a single unit of work, but may span many units.

When modifications are made to the database, Session objects are used to create a Transaction object. A Transaction represents a unit of work with the database (and potentially other systems). Transactions in Hibernate are handled by an underlying transaction manager and transaction (from JDBC, JTA or CORBA).Hibernate with use whatever transaction implementation is available (JDBC, JTA, CORBA, etc.). The Hibernate Transaction object abstracts the developer from having to deal with the underlying transaction manager/transaction. This allows developers to use a Transaction Manager of their choice without having to code to a specific Transaction Manager. Transaction objects should be kept open for as short of time as possible. There will be more information on Transactions and Transaction Management in a later chapter in class.

Query and Criteria objects are used to retrieve (and recreate) persistent objects. Query objects use SQL or Hibernate Query Language (HQL) string to retrieve data from the database and create objects. Criteria uses an object/method based means of constructing and executing a request to retrieve objects. Query and Criteria will also be covered in a later chapter in class.

Hibernate Installation/Setup

Hibernate can be downloaded in a single all-inclusive distribution file from www.hibernate.org. Importantly, from this distribution file, you will find the Hibernate JAR file (hibernate3.jar). This contains the core Hibernate API. This JAR file must be on your application’s classpath and made available to your IDE when creating the application.

However, Hibernate is built on top of many other open source packages. Therefore, several other JAR files are included in the distribution and many are required for Hibernate to work properly. The table below lists those that are required at the time of this release.

Page 11: Merge

Additionally, you will need the database driver JAR that Hibernate uses to connect to your database. Other JAR files are provided with the distribution and may be used for additional functionality. For example Hibernate ships with several caching engines and an open source connection pool facility. The Hibernate project has expanded to include several sub or ancillary projects and add-ons. These require additional Hibernate and open source JARs

The list above was created as of the release of Hibernate 3.3. The required JARs (and appropriate version) are changing with each release of Hibernate. Consult the latest Hibernate documentation/ReadMe files for the latest list of required JARs. Also, take care when upgrading any individual required JAR, even when you are aware of a more recent version.

The distribution file will also contain the Hibernate documentation.

Configuration

A Configuration object is created simply with a no-arguments constructor – new Configuration(). When created, classpath is searched for a file named hibernate.properties. The hibernate.properties file provides many configuration settings in key-value pair format. All hibernate.* properties are loaded into the Configuration object when it is created.

hibernate.connection.driver_class=org.hsqldb.jdbcDriverhibernate.connection.url=jdbc:hsqldb:hsql://localhost/baseballdbhibernate.connection.username=sahibernate.dialect=org.hibernate.dialect.HSQLDialecthibernate.show_sql=true Optionally, the configure() method can be called on the Configuration object.

Configuration c = new Configuration();c.configure();

When configure() is called, the classpath is searched for an XML configuration file called hibernate.cfg.xml.The hibernate.cfg.xml can also provide configuration parameters to Hibernate. Below the same configuration parameters are provided as in the hibernate.properties above, but in XML form in the hibernate.cfg.xml.

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

Page 12: Merge

<hibernate-configuration><session-factory> <property name="dialect"> org.hibernate.dialect.HSQLDialect</property> <property name="connection.driver_class"> org.hsqldb.jdbcDriver</property> <property name="connection.url"> jdbc:hsqldb:hsql://localhost/baseballdb</property> <property name="connection.username">sa</property> <property name="connection.password"></property> <property name="show_sql">true</property> <mapping resource="com/intertech/domain/BallPlayer.hbm.xml" /> </session-factory></hibernate-configuration>

When configuration parameters are duplicated, the parameters in the XML file will override those in the properties file. This actually provides some convenience. Many organizations have various levels of systems (dev, test, stage, production, etc.). Overriding allows level-independent information to be set in one file while level-dependent info is set in another file. You can use a different configuration file name and location for the XML configuration file. The configure method is overloaded. Provide a String to specify the name and location of the XML configuration file if hibernate.cfg.xml is not used.

c.configure(“/com/intertech/domain/myconfig.cfg.xml”);

If this does not provide you enough options to configure Hibernate, well then you have two other options.You can set configuration properties programmatically with setProperty().

c.setProperty(org.hibernate.cfg.Environment.SHOW_SQL, "true");

Using setProperty overrides what is in hibernate.properties and hibernate.cfg.xml. When starting the application, you can also set configuration properties from the command line.

java -Dhibernate.show_sql=true Myapplication

Given the number of configuration parameters that must usually be set, this last option is usually not appropriate for general configuration. Command line parameters will override what is in hibernate.properties, but be overridden by what is in hibernate.cfg.xml. Use a dynamic place holder in the hibernate.cfg.xml file if you wish to pass a parameter from the command line.

<property name="show_sql">${displaysql}</property>

The command line call must use the same placeholder name.

Page 13: Merge

java –displaysql=true Myapplication The goal of building a Configuration object is to eventually create a SessionFactory with a call to buildSessionFactory(). Recall, the Configuration object is used just to establish the SessionFactory object. Then it is forgotten and disposed of (lost to garbage collection). For this reason many Hibernate developers will use method chaining to create the Configuration object and SessionFactory. The Configuration object and many Hibernate objects support method chaining, so the typical call to create a SessionFactory is shown below.

SessionFactory sessionFactory = new Configuration().configure().setProperty(Environment.SHOW_SQL,"true").buildSessionFactory();

Configuration PropertiesSo what information can be specified in the hibernate.properties or hibernate.cfg.xml file? The Javadoc for org.hibernate.cfg.Environment list and document all the possible configuration properties. A complete review of all properties is not possible or warranted here, but a review of some of the more commonly used properties is provided.

Possibly the most important configuration property is the dialect parameter.

hibernate.dialect=org.hibernate.dialect.HSQLDialect

The dialect specifies to Hibernate the particular brand and possibly version of a database you are using. A list of the “out-of-the-box” dialects currently supported by Hibernate is listed in the table below.

Page 14: Merge

Specifying a dialect allows Hibernate to use defaults for many of the other configuration properties.

The JDBC connection properties have already been seen.

hibernate.connection.driver_class=org.hsqldb.jdbcDriverhibernate.connection.url=jdbc:hsqldb:hsql://localhost/baseballdbhibernate.connection.username=sahibernate.connection.password=sa

Most applications will want to use a connection pool when working with the database. Hibernate comes with its own connection pooling algorithm and can be configured.

hibernate.connection.pool_size=100

Page 15: Merge

This connection pooling system is very limited and the Hibernate documentation even warns about its use in production systems.

“It is intended to help you get started and is not intended for use ina production system or even for performance testing.”

More appropriately, Hibernate recommends you use one of the many third-party connection pools. Hibernate was built to work with many, and ships with one open source third-party connection pool called C3PO. In addition to making the C3PO library available on the class path, many configuration parameters provide the pool settings.

hibernate.c3p0.min_size=5hibernate.c3p0.max_size=25hibernate.c3p0.timeout=4000hibernate.c3p0.max_statements=100hibernate.c3pO.idle_test_period=3600 Most Java Enterprise applications run in an application server. The application server provides a datasource which in turn provides a connection pool. In Hibernate terminology, applications that run in an application server operate in a managed environment. In a managed environment, things like transactions, security and connection pools are used by Hibernate but provided outside of it. Applications that do not run in a managed environment operate it what Hibernate calls a non-managed environment. In non-managed environments, the application and/or Hibernate must manage their own transactions, security, connection pool, etc. More information on managed and non-managed environments will be covered later in class.

In a managed environment, a different set of configuration properties are used to provide Hibernate information about the datasource. The dialect property must still be provided, but in lieu of the connection information, a single JNDI lookup name for the datasource is provided.

hibernate.connection.datasource=java:/comp/env/jdbc/baseball

Additional properties can be provided to specify things such as the URL for the JNDI provider and class of the JNDI InitialContextFactory.

In a managed environment, after the SessionFactory is created, you may want to bind it into naming service with a JNDI name. Specify hibernate.session_factory_name in the properties to have Hibernate bind your SessionFactory object to the name after it is created.

hibernate.session_factory_name=”java:/hibernate/MySessionFactory” A few other important debugging properties allow you, as a developer, to see the SQL executed by Hibernate.The show_sql property has Hibernate write all the SQL executed in the JVM console.

Page 16: Merge

hibernate.show_sql=true

To display the same SQL to the console but in a more “pretty” format, turn on SQL formatting.

hibernate.format_sql=true

An example of the normal SQL displayed and the formatted SQL is shown below.

REGULAR SHOW_SQLHibernate: select ballplayer0_.id as id0_0_, ballplayer0_.name asname0_0_, ballplayer0_.nickname as nickname0_0_,ballplayer0_.date_of_birth as date4_0_0_, ballplayer0_.city_of_birthas city5_0_0_, ballplayer0_.uniform_number as uniform6_0_0_ fromPlayer ballplayer0_ where ballplayer0_.id=?

FORMATTEDHibernate:  select      ballplayer0_.id as id0_0_,      ballplayer0_.name as name0_0_,      ballplayer0_.nickname as nickname0_0_,      ballplayer0_.date_of_birth as date4_0_0_,      ballplayer0_.city_of_birth as city5_0_0_,      ballplayer0_.uniform_number as uniform6_0_0_  from      Player ballplayer0_  where      ballplayer0_.id=?

 Lastly, applications cause several SQL statements to get launched and it can be difficult to determine what SQL is used for which operation. Use the use_sql_comments property to have Hibernate generate and display comments with the SQL output to the console.

Hibernate:  /* load com.intertech.domain.BallPlayer */ select      ballplayer0_.id as id0_0_,      ballplayer0_.name as name0_0_,      ballplayer0_.nickname as nickname0_0_,      ballplayer0_.date_of_birth as date4_0_0_,      ballplayer0_.city_of_birth as city5_0_0_,      ballplayer0_.uniform_number as uniform6_0_0_  from      Player ballplayer0_  where      ballplayer0_.id=?

Page 17: Merge

Mapping Files

The hibernate.cfg.xml can also provide the location of mapping documents. In the example above, the BallPlayer.hbm.xml mapping document is specified as a mapping resource in addition to all the config properties.

<mapping resource="com/intertech/domain/BallPlayer.hbm.xml" />

The hibernate.properties file cannot specify mapping resources.

If not specified in the hibernate.cfg.xml, the mappings files can also be specified programmatically on the Configuration object.

Configuration c = new Configuration().configure();c.addResource(“com/intertech/domain/BallPlayer.hbm.xml”);

Mapping files specify how class properties and associations to other objects are mapped to the database table/columns. Details on the organization and content of the mapping files will be covered throughout class, but will start in the next chapter. Traditionally, there is one mapping file per class. The name of the mapping file conventionally takes the form Classname.hbm.xml. For example, the BallPlayer class has a BallPlayer.hbm.xml mapping file. The mapping file is also conventionally stored in the same package with the domain class it maps to the database. Following the conventions regarding name and location of the mapping file are not required, but when they are, a convenience follows. Namely, using these conventions allows the addClass() method to be used to load the mappings versus addResource().

c.addClass(com.intertech.domain.BallPlayer.class);

The mapping file must follow the Hibernate document type definition (DTD) (currently version 3.0).

Persistant Classes and POJO

Hibernate manages the persistence of Java objects to database tables/columns. But what are the rules around the types of Java objects and properties that is can persist? What must the Java objects look like? How does Hibernate know what to map to the database.

In this chapter, you explore Hibernate persistent classes and the mapping file that defines how they are stored in a database.

The mapping file can be quite complex. While no means an exhaustive look at class and property mapping, some of the more common class and property settings are covered in this chapter. More details about persistent objects and their association to other objects will be covered in subsequent chapters.

Persistent Classes

Page 18: Merge

Hibernate is designed to persist Java objects to a relational database. • Allowing you to develop, work with and focus on objects that represent domain or business entities. • Allowing you to ignore details of SQL and relational databases while programming your system.

Java classes whose objects or instances will be stored in database tables are called persistent classes in Hibernate. Another way to say this is that persistent classes are those that are mapped to database tables.

Hibernate works best at persisting plain ordinary Java objects (POJOs). Also known as plain old Java objects. JavaBeans, as a subset of POJOs, work equally well. Hibernate can persist other types of entities apart from POJOs and JavaBean objects (more on these later).

POJOs

POJOs are simple object state holders. A class with properties (member variables) accessible by public getter and setter methods (providing good data encapsulation). Actually, even the getters and setters are optional as far as Hibernate is concerned (more in a bit). Hibernate can actually access member variables directly, if needed.

public class BallPlayer {     private Long id;     private String name;     private String nickname;     private Calendar dob;     private String birthCity;     private short uniformNumber;          public BallPlayer(String name, String nickname, Calendar dob, String birthCity, short uniformNumber) {             this.name = name;             this.nickname = nickname;             this.dob = dob;             this.birthCity = birthCity;             this.uniformNumber = uniformNumber;     }     public BallPlayer(){             }     public Long getId() {             return id;     }     private void setId(Long id) {             this.id = id;     }     public String getName() {             return name;     }     public void setName(String name) {             this.name = name;     }     public String getNickname() {             return nickname;

Page 19: Merge

     }     public void setNickname(String nickname) {             this.nickname = nickname;     }     public Calendar getDob() {             return dob;

Persistent Object Updates and Automatic Dirty Checking

While a session remains open, if a Persistent object is modified, its data is kept synchronized with the database. The data will be synchronized (but not committed) when session.flush() is called. It will be synchronized and committed when the transaction is committed. It may be synchronized at other points. For example, before Hibernate performs some queries of the database.

For example, in the code below a change to a BallPlayer object during a session does not require any special method call to persist the change.

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer p = (BallPlayer)session.get(BallPlayer.class, 1L);p.setNickname("Bambino");transaction.commit();  //new nickname is synch’ed and committed heresession.close();

Hibernate knows and tracks the Persistent object and its state change. Merely committing the transaction will cause the new data to be synchronized to the database with an update statement.

Update Player set name=?, nickname=?, date_of_birth=?,city_of_birth=?, uniform_number=? Where id=?

Hibernate monitors all Persistent objects (i.e. the persistent context). At the end of a unit of work, it knows which objects have been modified. It then calls update statements on all updated objects. This process of monitoring and updating only objects that have changed is called automatic dirty checking. As opposed to updating all Persistent objects at the end of each work, automatic dirty checking can offer significant performance savings. Detached State

Once a session is closed, a Persistent object is still stored in the database. The object is still a valid Java object and can be manipulated (have its data changed). But without a session, the Hibernate persistence manager has no way of synchronizing the data in the Persistent object with the database. These objects are no longer part of the persistence context.

Persistent objects become Detached objects when the session is closed. Calling session.evict() on the object reference or session.clear() will also remove an object from session. These later session operations will be covered later in class when cache is discussed.

Page 20: Merge

Detached objects can be reassociated and synchronized to the database after a new session is opened. Calling session.update() or session.saveOrUpdate() on a Detached object reference transitions the Detached object back to the Persistent state. When a Detached object is “reattached,” the database is updated with object’s current state; to include changes made while detached.

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer p = (BallPlayer)session.get(BallPlayer.class, 1L);transaction.commit();session.close();//p is now Detachedp.setNickname("Bambino");  //change is unsynchronizedsession = sessionFactory.openSession();transaction = session.beginTransaction();//can also use session.saveOrUpdate() in method call belowsession.update(p); //now new nickname is appliedtransaction.commit();session.close();

The Detached state may seem odd at first glance, but it provides an incredibly powerful capability to applications. Applications could not (or should not) typically maintain long running transactions or connections to a database. The effect of a long running transaction could be to create application bottlenecks that affectively keep other users from accomplishing work. For example, think about the normal use and exchange of data in a Web application. A user may request data on a BallPlayer to be displayed on a Web page. The Web site fetches the BallPlayer data and displays it in HTML form. Later (who knows how much later – could be a second or an hour), the user submits the new Player data to be saved back to the database. A Web application that opens a session and transaction upon request of the data and waits for submit to close/commit the unit of work has issues.

Page 21: Merge

There is no telling when (if ever) the session/transaction will be closed in this circumstance. System timeouts will eventually cancel the transaction and close the connection to the database, but how long will that take?During that time, other users may not be able to access the BallPlayer’s data (or worse). The Detached state allows Java objects to exist and to be used, but without a long running session/transaction.In the example just sited, open a session and transaction to load the BallPlayer object for display to the user.Then close the session/transaction. The BallPlayer object is still available for manipulation but without ties to the database. Open a new session/transaction and reattach the object when the user has submitted the new data.

Detatched objects keep sessions and related transactions short while still allowing for longer duration work.

Deleted

To remove a Persistent object from the database call session.delete() on the object reference during a session.The delete() method has the affect of removing the row (or rows) associated with the Persistent object from the database.

Page 22: Merge

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer p = (BallPlayer)session.get(BallPlayer.class, 1L);session.delete(p);transaction.commit();session.close();

If fact, the delete() method call causes Hibernate to issue an SQL delete command.

delete from Player where id=?

After calling delete() on the object, the object is no longer considered in the Persistent state as it is no longer represented in the database. However, it is a valid Java object. So what is it? So what state is it in? The object is back in a Transient state! It is as if the object was just created a new.

There is one caveat about the transition from the Persistent state to the Transient state. The identifier in the Persistent (and now Transient) object should be removed when deleted.

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer p = (BallPlayer)session.get(BallPlayer.class, 1L);session.delete(p);transaction.commit();session.close();p.setId(null);

Why set the identifier to null? The answer is coming up.

Page 23: Merge

For completeness, it is possible to delete a detached object.

Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();BallPlayer p = (BallPlayer)session.get(BallPlayer.class, 1L);transaction.commit();session.close();//p is now Detachedsession = sessionFactory.openSession();transaction = session.beginTransaction();session.delete(p);transaction.commit();session.close();

In effect, this is taking a detached object, reattaching it and then deleting it, but without the manual call to update().

Entities and Values

Hibernate distinguishes persistent objects into two types: entity types and value types.

Entity objects are “first class” persistent business objects. Entity objects have their own identity (primary key) in the database. Entity objects have their own lifecycle (covered in the last chapter). Objects can have relationships with other objects. For example, a Team object may be associated to one or more BallPlayer objects. When another object is associated to an entity instance the reference is persisted in a database as a foreign key (more on this later). BallPlayer objects are Entity types in Hibernate.

Page 24: Merge

Value objects have no database identity. • Value objects belong to an owning entity object. • Value objects’ state is persisted in the table and row of the owning entity. • Value objects do not have an identifier (or identifier property).The lifespan of a value type object is bound to the lifespan of the owning entity. Common value type objects include instances of String, Date, Integer, Long, etc.

Components

What do entity and value types have to do with relationships among domain model types?

First off, it is important to understand how objects such as Strings, Dates, etc. are handled in Hibernate. While still Java objects, these are not considered “first-class” objects. That is, they are not as important from a management standpoint. Secondly, understanding entity and value types is critical to understanding the first type of relationship in Hibernate: components.

Components are user-defined classes whose instances are persisted to the same table as an owning entity.Components are therefore considered value types in Hibernate. More precisely, components are considered a subset of value types.

Components look like any other Java class. They can have properties, like entity types. Like entity types, they must have a no-argument constructor. However, the properties of a component instance are stored in the table with the owning entity and identified by the owning entity’s id. Take, for example, the relationship between a team and its manager (the head coach on a baseball team).

public class Team {  private long id;  private String nickname;

Page 25: Merge

  private Calendar founded;  private String mascot;  private Manager manager;  public Manager getManager() {    return manager;  }  public void setManager(Manager manager) {    this.manager = manager;  }  public Team(String nickname, Calendar founded, String mascot){    this.nickname=nickname;    this.founded=founded;    this.mascot=mascot;  }  public Team(){}  //getter/setter code removed for brevity  ...

Every team has a manager.

public class Manager {  private String name;  private int yearStarted;  public Manager(String name, int yearStarted){    this.name=name;    this.yearStarted=yearStarted;  }  public Manager() {}  //getter/setter code removed for brevity  ...

The Java Persistence specification calls components embedded classes, which may seem a more appropriate name.

When the manager object is a Component, the manager information is stored with the team data in the Team table.

Components, like other value types, do not have their own identifier. The component data is persisted and removed with its owning entity.

Page 26: Merge

To map a component, use the <component> element in the owning entity’s Hibernate mapping file.

<hibernate-mapping package="com.intertech.domain">  <class name="Team">    <id name="id">      <generator class="sequence">        <param name="sequence">common_seq</param>      </generator>    </id>    <property name="nickname" />    <property name="founded" />    <property name="mascot" />    <component name="manager" class="com.intertech.domain.Manager">      <property name="name" column="manager_name"/>      <property name="yearStarted" column="year_started" />    </component>  </class></hibernate-mapping>

No additional/separate mapping file is required for the Manager type. To create and store a team object and its associated manager object, create both, associate the manager to team and save the team.

  Calendar founded = Calendar.getInstance();  founded.set(1876, Calendar.APRIL, 25);  Team cubs = new Team("Cubs",founded,"Cubby Bear");  Manager mgr = new Manager("Lou Piniella", 2006);  Session session = sessionFactory.openSession();  Transaction transaction = session.beginTransaction();  cubs.setManager(mgr);  session.save(cubs);  transaction.commit();  session.close();

This first type of association mapping also provides an opportunity to reinforce some of the basic mapping and Hibernate code requirements. Notice both Manager and Team need a default constructor. This is required by Hibernate of any Entity or Component. In fact, without a no-arguments constructor on Manager, the code above would throw an exception.

Exception in thread "main" org.hibernate.InstantiationException: No   default constructor for entity: com.intertech.domain.Manager  atorg.hibernate.tuple.PojoInstantiator.instantiate(PojoInstantiator.java:107)

For the code that creates and persists a Team and associated Manager to work, add the Team mapping file as a mapping resource declaration.

Page 27: Merge

<hibernate-configuration>  <session-factory>    ...    <mapping resource="com/intertech/domain/BallPlayer.hbm.xml" />    <mapping resource="com/intertech/domain/Team.hbm.xml" />    </session-factory></hibernate-configuration>

Or, when building the Configuration object, add the Tam class to the configuration.

new Configuration().configure().addClass(Team.class)

The column and type information for properties in the Component may be set by default (just as with Entity mapping). By default, the column name of a property is the same as the Component’s property name.The type of a Component’s property is determined by reflection when not explicitly provided (just as with Entities). Attributes such as insert, update and access apply and can be used with Components just as with Entities. Components are allowed to have child components.

In the Team to Manager Component association above, the relationship is only one way or unidirectional. That is a Team has a Manager. The Component relationship can be made bidirectional. That is a Manager can have a Team. First, the Manger class would need to be modified to have/manage this relationship.

public class Manager {  private String name;  private int yearStarted;  private Team team;  public Team getTeam() {    return team;  }  public void setTeam(Team team) {    this.team = team;  }  ...

Secondly, add a <parent> element to the <component> element in the mapping file to signify that the Component object knows its parent.

<component name="manager" class="com.intertech.domain.Manager">   <parent name="Team"/>   <property name="name" column="manager_name"/>   <property name="yearStarted" column="year_started" /> </component>

The name attribute on the parent must match the class name of the Entity that owns the Component (in this

Page 28: Merge

case “Team”).

When building and assembling the Java Entities and related Component objects, you must manage the bidirectional relationship. That is, you must physically set the property on each object.

mgr.setTeam(cubs);cubs.setManager(mgr);

When reading the Entity, however, Hibernate will create and reassemble the appropriate Component object and set the properties for you. Hibernate will re-associate in both directions when mapped as a bidirectional relationship. However, if all the properties of a component are null values, Hibernate does not create and reassemble the component. Instead, it sets the Entity’s Component property to null. Therefore, it is always a good idea to check the Component property before using it after it has been read in/recreated from the database.

Team myTeam = (Team) session.get(Team.class, 9L);if ((myTeam.getManager() != null) && (myTeam.getManager().getYearStarted() > 2005)) { //As long as there is data, no need to explicitly get Manager} else { //If there is no Manager data for the team, then null is used for //the component}

It is possible in Java to assign the same Component object to two Entities.

Calendar founded = Calendar.getInstance();founded.set(1876, Calendar.APRIL, 25);Team cubs = new Team("Cubs",founded,"Cubby Bear");Calendar founded2 = Calendar.getInstance();founded2.set(1998, Calendar.MARCH, 31);Team rays = new Team("Rays",founded2, "Raymond");Manager mgr = new Manager("Lou Piniella", 2006);cubs.setManager(mgr);rays.setManager(mgr);

While physically possible, this creates a logical problem in the database.

The same Manager data is now associated to two rows in the database. In fact, when read back in from the database, Hibernate will create two Manager Components each with the same data.

Session session = sessionFactory.openSession();

Page 29: Merge

Transaction transaction = session.beginTransaction();Team tm1 = (Team) session.get(Team.class, 14L);Team tm2 = (Team) session.get(Team.class, 15L);if (tm1.getManager()==tm2.getManager()){  System.out.println("These are the same object");} else {  //This will always be the case unless tm1 and tm2  //are the same team  System.out.println("These are different objects");}transaction.commit();session.close();

Remember, Components are “owned” by their Entity as far as Hibernate is concerned. There is no identifier that identifies Components or makes a Component unique. Therefore, a new Component is always created from the associated data with the Entity. Components allow for a more fine-grained object model yet a more coarse-grained data model. The composition association that Components provide, allow for reuse of the type across many classes. The data model may be denormalized but the reason for this choice is for better performance. Getting all teams and their manager in this case does not require a join.

DispatchAction provides a mechanism for grouping a set of related functions into a single

action, thus eliminating the need to create seperate actions for each functions. In this

example we will see how to group a set of user related actions like add user, update user

and delete user into a single action called UserAction.

The class UserAction extends org.apache.struts.actions.DispatchAction. This class does

not provide an implementation of the execute() method as the normal Action class does.

The DispatchAction uses the execute method to manage delegating the request to the

individual methods based on the incoming request parameter. For example if the incoming

parameter is "method=add", then the add method will be invoked. These methods should

have similar signature as the execute method.

view sourceprint?

01.public class UserAction extends DispatchAction {02. 03.private final static String SUCCESS = "success";04. 05.public ActionForward add(ActionMapping mapping, ActionForm form,06.HttpServletRequest request, HttpServletResponse response)07.throws Exception {08.UserForm userForm = (UserForm) form;09.userForm.setMessage("Inside add user method.");10.return mapping.findForward(SUCCESS);11.}12. 

Page 30: Merge

13.public ActionForward update(ActionMapping mapping, ActionForm form,14.HttpServletRequest request, HttpServletResponse response)15.throws Exception {16.UserForm userForm = (UserForm) form;17.userForm.setMessage("Inside update user method.");18.return mapping.findForward(SUCCESS);19.}20. 21.public ActionForward delete(ActionMapping mapping, ActionForm form,22.HttpServletRequest request, HttpServletResponse response)23.throws Exception {24.UserForm userForm = (UserForm) form;25.userForm.setMessage("Inside delete user method.");26.return mapping.findForward(SUCCESS);27.}28.}

If you notice the signature of the add, update and delete methods are similar to the execute

method except the name. The next step is to create an action mapping for this action

handler. The request parameter name is specified using the parameter attribute. Here the

request parameter name is method.

view sourceprint?

1.<action-mappings>2.<action input="/index.jsp" parameter="method" name="UserForm"path="/UserAction" scope="session" type="com.vaannila.UserAction">3.<forward name="success" path="/index.jsp" />4.</action>5.</action-mappings>

Now lets see how to invoke a DispatchAction from jsp. We have a simple form with three

buttons to add, update and delete a user. When each button is clicked a different method in

UserAction class is invoked.

view sourceprint?

01.<html>02.<head>03.<script type="text/javascript">04.function submitForm()05.{06.document.forms[0].action = "UserAction.do?method=add"07.document.forms[0].submit();08.}09.</script>10.</head>

Page 31: Merge

11.<body>12.<html:form action="UserAction" >13.<table>14.<tr>15.<td>16.<bean:write name="UserForm" property="message" />17.</td>18.</tr>19.<tr>20.<td>21.<html:submit value="Add" onclick="submitForm()" />22.</td>23.</tr>24.<tr>25.<td>26.<html:submit property="method" value="update" />27.</td>28.</tr>29.<tr>30.<td>31.<html:submit  property="method" >delete</html:submit>32.</td>33.</tr>34.</table>35.</html:form>36.</body>37.</html>

Now consider the update and the delete button. The request parameter name specified in

the action handler is "method". So this should be specified as the property name for the

submit button. The name of the method to be invoked and the value of the button should be

the same. So when the button is clicked the corresponding method in the UserAction will be

called. The delete button shows an alternate way to specify the value of the button.

Here the main constraint is the method name and the button name should be same. So we

can't have an update button like this "Update". Inorder to avoid this you can call a javascript

function on click of the button. Specify the action and submit the form from javascript. In this

way we can have a different button name and method name. On click of the Add button the

action value is set to "UserAction.do?method=add" and the form is submitted from

javascript.

On executing the sample example the following page is displayed to the user.

Page 32: Merge

After clicking the add button the following page is displayed.

You can download the source code of the DispatchAction example by clicking on the

Download link below.

Page 33: Merge