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<User> users = (List<User>) 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<Object> usersAttributes = (List<Object>) 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<User> users = (List<User>) 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