From 008087de38f21d6a2c160a93df1942215cf90df9 Mon Sep 17 00:00:00 2001
From: Martin Goik <goik@hdm-stuttgart.de>
Date: Fri, 22 Feb 2013 08:10:30 +0100
Subject: [PATCH] Criteria based queries

---
 Doc/course.xml                                | 200 +++++++++++++++---
 .../hibintro/v1/run/GetUsersAsAttributes.java |  48 +++++
 .../v1/run/RetrieveAllByCriteria.java         |  46 ++++
 3 files changed, 267 insertions(+), 27 deletions(-)
 create mode 100644 ws/eclipse/HibIntro/src/main/java/hibintro/v1/run/GetUsersAsAttributes.java
 create mode 100644 ws/eclipse/HibIntro/src/main/java/hibintro/v1/run/RetrieveAllByCriteria.java

diff --git a/Doc/course.xml b/Doc/course.xml
index d84d3147b..4490bd155 100644
--- a/Doc/course.xml
+++ b/Doc/course.xml
@@ -15959,36 +15959,39 @@ import javax.persistence.Entity;
           </figure>
         </section>
 
-        <section xml:id="loadingObjects">
-          <title>Loading Objects</title>
+        <section xml:id="loadingObjectsByPrimaryKey">
+          <title>Loading Objects by primary key</title>
 
           <para>Having persisted a single
           <classname>hibintro.v1.model.User</classname> instance by means of
           <classname>hibintro.v1.run.PersistSingleUser</classname> we may now
           load the database object. The easiest way is based on both the
-          requested object's type and its primary key value:</para>
+          requested object's type <coref linkend="specLoadType"/> and its
+          primary key value <coref linkend="specLoadPrimaryKey"/>:</para>
 
-          <programlisting>package hibintro.v1.run;
+          <figure xml:id="loadByClassAndPrimaryKey">
+            <title>Loading a single object by a primary key value.</title>
+
+            <programlisting>package hibintro.v1.run;
 ...
 public class RetrieveSingleUser {
-
-  public static void main(String[] args) {
-    final Session session = HibernateUtil.createSessionFactory("hibernate.cfg.xml").openSession();
-
+...
     final Transaction transaction = session.beginTransaction();
 
-    final User u = (User) session.load(User.class, "goik");
-    if (null != u ) {
-      System.out.println("Found user '" + u.getCname() + "'");
-    } else {
+    final User u = (User) session.load(<emphasis role="bold">User.class</emphasis> <co
+                xml:id="specLoadType"/>, "<emphasis role="bold">goik</emphasis>" <co
+                xml:id="specLoadPrimaryKey"/>);
+    if (null == u ) {
       System.out.println("No such user 'goik'");
+    } else {
+      System.out.println("Found user '" + u.getCname() + "'");
     }
-    transaction.commit();
-  }
-}</programlisting>
+    transaction.commit();...</programlisting>
+          </figure>
 
-          <para>This retrieves the expected result among logged SQL
-          <quote>background</quote> statements:</para>
+          <para>This retrieves the expected result. Buried in other log
+          messages we find the following SQL <quote>background</quote>
+          statement:</para>
 
           <programlisting>...
 INFO: HHH000232: Schema update complete
@@ -16003,8 +16006,73 @@ Hibernate:
 
 Found user 'Martin Goik'</programlisting>
 
-          <para>Often we are interested in a (sub)set of results. We first
-          populate our database with additional
+          <qandaset role="exercise">
+            <qandadiv>
+              <qandaentry>
+                <question>
+                  <para>Actually the code in <xref
+                  linkend="loadByClassAndPrimaryKey"/> is not quite correct.
+                  Execute it with a non-existing primary key value i.e.
+                  <quote>goik2</quote>. What do you observe? Can you explain
+                  that behaviour?</para>
+
+                  <para>Read the documentation of the
+                  <classname>session.org.hibernate.Session</classname>.<code>load()</code>
+                  method and correct the code snippet.</para>
+                </question>
+
+                <answer>
+                  <para>If there is no corresponding database object we
+                  receive a
+                  <classname>org.hibernate.ObjectNotFoundException</classname>
+                  :<coref linkend="loadUserObjectNotFoundException"/></para>
+
+                  <programlisting>Hibernate: 
+    select
+        user0_.uid as uid0_0_,
+        user0_.cname as cname0_0_ 
+    from
+        User user0_ 
+    where
+        user0_.uid=?
+Exception in thread "main" org.hibernate.ObjectNotFoundException: <co
+                      xml:id="loadUserObjectNotFoundException"/>No row with the given identifier exists: [hibintro.v1.model.User#goik2]
+...
+  at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
+  at hibintro.v1.model.User_$$_javassist_0.getCname(User_$$_javassist_0.java)
+  at hibintro.v1.run.RetrieveSingleUser.main(<emphasis role="bold">RetrieveSingleUser.java:35</emphasis>)<co
+                      xml:id="exceptionOnGetCname"/>
+</programlisting>
+
+                  <para>Due to <coref linkend="exceptionOnGetCname"/> the
+                  exception is being triggered by the <code>getCname()</code>
+                  call. The documentation of <code>load()</code> tells us that
+                  method calls may be delegated to proxy objects which is
+                  being implemented by byte code instrumentation. If however
+                  no matching database object exists calling the proxy
+                  instance yields a
+                  <classname>org.hibernate.ObjectNotFoundException</classname>.</para>
+
+                  <para>The documentation also tells us to use the
+                  corresponding <methodname>get()</methodname> method which
+                  actually returns <code>null</code> in case a primary key
+                  value does not exist:</para>
+
+                  <programlisting>... final User u = (User) session.get(User.class, "goik2");
+    if (null == u ) {
+      System.out.println("No such user having key value 'goik2'");
+...</programlisting>
+                </answer>
+              </qandaentry>
+            </qandadiv>
+          </qandaset>
+        </section>
+
+        <section xml:id="loadingObjectsByQuery">
+          <title>Loading objects by queries</title>
+
+          <para>Often we are interested in a (sub)set of results. We populate
+          our database with additional
           <classname>hibintro.v1.model.User</classname> instances:</para>
 
           <programlisting>package hibintro.v1.run;
@@ -16026,8 +16094,10 @@ public class PersistUsers {
           xlink:href="http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>)
           for object queries. As we will see <abbrev
           xlink:href="http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>
-          extents SQL with respect to polymorphic queries. The current example
-          does not use inheritance leaving us with a simple <abbrev
+          extends <acronym
+          xlink:href="http://en.wikipedia.org/wiki/Sql">SQL</acronym> with
+          respect to polymorphic queries. The current example does not use
+          inheritance leaving us with a simple <abbrev
           xlink:href="http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>
           query <coref linkend="hqlFromUser"/> in
           <classname>hibintro.v1.run.RetrieveAll</classname>:</para>
@@ -16049,19 +16119,83 @@ final List&lt;User&gt; users = (List&lt;User&gt;) searchUsers.list();
   }</programlisting>
           </figure>
 
-          <para>This yields the expected result:</para>
+          <para>Being used to <acronym
+          xlink:href="http://en.wikipedia.org/wiki/Sql">SQL</acronym>we notice
+          the absence of a SELECT clause in <coref linkend="hqlFromUser"/>:
+          The ratio behind is having a focus on objects rather than on
+          attribute sets. Thus our <abbrev
+          xlink:href="http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>
+          query returns a set of <classname>hibintro.v1.model.User</classname>
+          instances:</para>
 
           <programlisting>uid=eve, Eve Briggs
 uid=goik, Martin Goik
 uid=wings, Fred Wings</programlisting>
 
-          <para>The careful reader may already have expected <abbrev
+          <qandaset role="exercise">
+            <qandadiv>
+              <qandaentry>
+                <question>
+                  <para>We may actually retrieve attributes rather than
+                  objects. For this purpose our query actually resembles
+                  standard <acronym
+                  xlink:href="http://en.wikipedia.org/wiki/Sql">SQL</acronym><coref
+                  linkend="hqlWithSelect"/>:</para>
+
+                  <programlisting>final Query searchUsers = session.createQuery("<emphasis
+                      role="bold">select uid, cname from User</emphasis>" <co
+                      xml:id="hqlWithSelect"/>);
+final Object queryResult <co xml:id="queryResultFromSelect"/>= searchUsers.list();</programlisting>
+
+                  <para>Use the <methodname>getSimpleName()</methodname>
+                  reflection method to iteratively analyze the
+                  <code>queryResult</code> <coref
+                  linkend="queryResultFromSelect"/> instance's structure. This
+                  guides you in finding suitable casts to add code similar as
+                  in <xref linkend="retrieveAllUserByHql"/> in order to write
+                  user's attribute values to standard output.</para>
+                </question>
+
+                <answer>
+                  <para>A possible implementation reads:</para>
+
+                  <programlisting>package hibintro.v1.run;
+...
+public class GetUsersAsAttributes {
+...
+    final Query searchUsers = session.createQuery("<emphasis role="bold">select uid, cname from User</emphasis>");
+
+    @SuppressWarnings("unchecked")
+    final Object queryResult = searchUsers.list();
+    System.out.println("queryResult type:" + queryResult.getClass().getSimpleName()); <co
+                      xml:id="typeOfHqlResult"/>
+    final List&lt;Object&gt; usersAttributes = (List&lt;Object&gt;) queryResult;
+    for (final Object o: usersAttributes) {
+      System.out.println("result set element type:" + o.getClass().getSimpleName()); <co
+                      xml:id="typeOfEmbeddedObjects"/>
+      final Object attributes[] = (Object []) o;
+      for (Object attribute: attributes) {
+        System.out.println("attribute value:" + attribute);
+      }
+    }...</programlisting>
+
+                  <para>Actually the two lines <coref
+                  linkend="typeOfHqlResult"/> and <coref
+                  linkend="typeOfEmbeddedObjects"/> are only needed during the
+                  development process to discover the result set's object
+                  structure.</para>
+                </answer>
+              </qandaentry>
+            </qandadiv>
+          </qandaset>
+
+          <para>The careful reader may already expect <abbrev
           xlink:href="http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>
-          to offer additional features. Following
-          <classname>hibintro.v1.run.SelectUser</classname> we may restrict
-          our result set by an <acronym
+          to offer additional features namely predicate based queries.
+          Following <classname>hibintro.v1.run.SelectUser</classname> we may
+          restrict our result set by an <acronym
           xlink:href="http://en.wikipedia.org/wiki/Sql">SQL</acronym> style
-          <code>where</code> clause:</para>
+          <code>WHERE</code> clause:</para>
 
           <programlisting>    final List&lt;User&gt; users = (List&lt;User&gt;) session.createQuery(
             "<emphasis role="bold">from User u where u.cname like '%e%'</emphasis>").list();
@@ -16075,6 +16209,18 @@ uid=wings, Fred Wings</programlisting>
           <programlisting>Found user 'Eve Briggs'
 Found user 'Fred Wings'</programlisting>
         </section>
+
+        <section xml:id="criteriaBasedQueries">
+          <title>Criteria based queries</title>
+
+          <para>Selecting Objects by <abbrev
+          xlink:href="http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>c
+          queries technically means parsing <abbrev
+          xlink:href="http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>
+          and transforming it into some sort of abstract syntax tree. We may
+          instead create corresponding structures by using
+          <trademark>Hibernate</trademark>'s criteria API:</para>
+        </section>
       </section>
 
       <section xml:id="mappingSingleClasses">
diff --git a/ws/eclipse/HibIntro/src/main/java/hibintro/v1/run/GetUsersAsAttributes.java b/ws/eclipse/HibIntro/src/main/java/hibintro/v1/run/GetUsersAsAttributes.java
new file mode 100644
index 000000000..1b7bd8d99
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/hibintro/v1/run/GetUsersAsAttributes.java
@@ -0,0 +1,48 @@
+package hibintro.v1.run;
+
+
+
+import hibintro.util.HibernateUtil;
+import hibintro.v1.model.User;
+
+import java.util.List;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+
+/**
+ * @author goik
+ * 
+ * Retrieval all {@link User} attributes from a database.
+ *
+ */
+public class GetUsersAsAttributes {
+
+  /**
+   * Retrieving a single {@link User} instance by means
+   * of its primary key value "goik".
+   * @param args unused
+   */
+  public static void main(String[] args) {
+    final Session session = HibernateUtil.createSessionFactory("hibernate.cfg.xml").openSession();
+
+    final Transaction transaction = session.beginTransaction();
+
+    final Query searchUsers = session.createQuery("select uid, cname from User");
+
+    @SuppressWarnings("unchecked")
+    final Object queryResult = searchUsers.list();
+    System.out.println("queryResult type:" + queryResult.getClass().getSimpleName());
+    final List<Object> usersAttributes = (List<Object>) queryResult;
+    for (final Object o: usersAttributes) {
+      System.out.println("result set element type:" + o.getClass().getSimpleName());
+      final Object attributes[] = (Object []) o;
+      for (Object attribute: attributes) {
+        System.out.println("attribute value:" + attribute);
+      }
+    }
+
+    transaction.commit();
+  }
+}
\ No newline at end of file
diff --git a/ws/eclipse/HibIntro/src/main/java/hibintro/v1/run/RetrieveAllByCriteria.java b/ws/eclipse/HibIntro/src/main/java/hibintro/v1/run/RetrieveAllByCriteria.java
new file mode 100644
index 000000000..f5b07823a
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/hibintro/v1/run/RetrieveAllByCriteria.java
@@ -0,0 +1,46 @@
+package hibintro.v1.run;
+
+
+
+import hibintro.util.HibernateUtil;
+import hibintro.v1.model.User;
+
+import java.util.List;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+
+
+
+/**
+ * @author goik
+ * 
+ * Retrieval of all {@link User} objects from a database.
+ *
+ */
+public class RetrieveAllByCriteria {
+
+  /**
+   * Retrieving users containing the letter <code>r</code> in
+   * {@link User#getCname()}.
+   * 
+   * @param args
+   */
+  public static void main(String[] args) {
+    final Session session = HibernateUtil.createSessionFactory("hibernate.cfg.xml").openSession();
+    
+    //final Criteria crit = session.createCriteria(Cat.class);
+
+    final Transaction transaction = session.beginTransaction();
+    
+    final Query searchUsers = session.createQuery("from User");
+    @SuppressWarnings("unchecked")
+    final List<User> users = (List<User>) searchUsers.list();
+    for (final User u: users) {
+      System.out.println("uid=" + u.getUid() + ", " + u.getCname());
+    }
+
+    transaction.commit();
+  }
+}
\ No newline at end of file
-- 
GitLab