diff --git a/Doc/Sda2/Ref/Fig/billing.fig b/Doc/DbDevel/Ref/Fig/billing.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/billing.fig rename to Doc/DbDevel/Ref/Fig/billing.fig diff --git a/Doc/Sda2/Ref/Fig/billingData.fig b/Doc/DbDevel/Ref/Fig/billingData.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/billingData.fig rename to Doc/DbDevel/Ref/Fig/billingData.fig diff --git a/Doc/Sda2/Ref/Fig/billingMapJoined.fig b/Doc/DbDevel/Ref/Fig/billingMapJoined.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/billingMapJoined.fig rename to Doc/DbDevel/Ref/Fig/billingMapJoined.fig diff --git a/Doc/Sda2/Ref/Fig/billingSql.fig b/Doc/DbDevel/Ref/Fig/billingSql.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/billingSql.fig rename to Doc/DbDevel/Ref/Fig/billingSql.fig diff --git a/Doc/Sda2/Ref/Fig/classUser.fig b/Doc/DbDevel/Ref/Fig/classUser.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/classUser.fig rename to Doc/DbDevel/Ref/Fig/classUser.fig diff --git a/Doc/Sda2/Ref/Fig/concurrentOptimistic.svg b/Doc/DbDevel/Ref/Fig/concurrentOptimistic.svg similarity index 100% rename from Doc/Sda2/Ref/Fig/concurrentOptimistic.svg rename to Doc/DbDevel/Ref/Fig/concurrentOptimistic.svg diff --git a/Doc/Sda2/Ref/Fig/concurrentOptimisticFail.svg b/Doc/DbDevel/Ref/Fig/concurrentOptimisticFail.svg similarity index 100% rename from Doc/Sda2/Ref/Fig/concurrentOptimisticFail.svg rename to Doc/DbDevel/Ref/Fig/concurrentOptimisticFail.svg diff --git a/Doc/Sda2/Ref/Fig/figureInherit.fig b/Doc/DbDevel/Ref/Fig/figureInherit.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/figureInherit.fig rename to Doc/DbDevel/Ref/Fig/figureInherit.fig diff --git a/Doc/Sda2/Ref/Fig/jpacache.svg b/Doc/DbDevel/Ref/Fig/jpacache.svg similarity index 100% rename from Doc/Sda2/Ref/Fig/jpacache.svg rename to Doc/DbDevel/Ref/Fig/jpacache.svg diff --git a/Doc/Sda2/Ref/Fig/mapInherit.svg b/Doc/DbDevel/Ref/Fig/mapInherit.svg similarity index 100% rename from Doc/Sda2/Ref/Fig/mapInherit.svg rename to Doc/DbDevel/Ref/Fig/mapInherit.svg diff --git a/Doc/Sda2/Ref/Fig/mapUser.fig b/Doc/DbDevel/Ref/Fig/mapUser.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/mapUser.fig rename to Doc/DbDevel/Ref/Fig/mapUser.fig diff --git a/Doc/Sda2/Ref/Fig/mapUserIntegrity.fig b/Doc/DbDevel/Ref/Fig/mapUserIntegrity.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/mapUserIntegrity.fig rename to Doc/DbDevel/Ref/Fig/mapUserIntegrity.fig diff --git a/Doc/Sda2/Ref/Fig/mavenIntro.fig b/Doc/DbDevel/Ref/Fig/mavenIntro.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/mavenIntro.fig rename to Doc/DbDevel/Ref/Fig/mavenIntro.fig diff --git a/Doc/Sda2/Ref/Fig/persistProvider.fig b/Doc/DbDevel/Ref/Fig/persistProvider.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/persistProvider.fig rename to Doc/DbDevel/Ref/Fig/persistProvider.fig diff --git a/Doc/Sda2/Ref/Fig/persistentStates.fig b/Doc/DbDevel/Ref/Fig/persistentStates.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/persistentStates.fig rename to Doc/DbDevel/Ref/Fig/persistentStates.fig diff --git a/Doc/Sda2/Ref/Fig/transitiveDep.fig b/Doc/DbDevel/Ref/Fig/transitiveDep.fig similarity index 100% rename from Doc/Sda2/Ref/Fig/transitiveDep.fig rename to Doc/DbDevel/Ref/Fig/transitiveDep.fig diff --git a/Doc/Sda2/Ref/Screen/accountTransferSum.png b/Doc/DbDevel/Ref/Screen/accountTransferSum.png similarity index 100% rename from Doc/Sda2/Ref/Screen/accountTransferSum.png rename to Doc/DbDevel/Ref/Screen/accountTransferSum.png diff --git a/Doc/Sda2/Ref/Screen/ldapSampleUsers.png b/Doc/DbDevel/Ref/Screen/ldapSampleUsers.png similarity index 100% rename from Doc/Sda2/Ref/Screen/ldapSampleUsers.png rename to Doc/DbDevel/Ref/Screen/ldapSampleUsers.png diff --git a/Doc/DbDevel/dbDevel.xml b/Doc/DbDevel/dbDevel.xml new file mode 100644 index 0000000000000000000000000000000000000000..c9383d7e9ce929dbe0b0d29e45629472f841d1e8 --- /dev/null +++ b/Doc/DbDevel/dbDevel.xml @@ -0,0 +1,3806 @@ +<?xml version="1.0" encoding="UTF-8"?> +<part version="5.1" xml:id="dbDevel" xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:ns="http://docbook.org/ns/transclusion" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:db="http://docbook.org/ns/docbook"> + <title>113473 Database and application development</title> + + <chapter xml:id="chap_dbDevel_dbFeatures"> + <title>Database features</title> + + <section xml:id="chap_dbDevel_dbFeatures_queryLanguage"> + <title>Query language</title> + + <para/> + </section> + + <section xml:id="chap_dbDevel_dbFeatures_schema"> + <title>Schema</title> + + <para/> + </section> + + <section xml:id="chap_dbDevel_dbFeatures_singleMulti"> + <title>Single / Multiuser</title> + + <para/> + </section> + + <section xml:id="chap_dbDevel_dbFeatures_network"> + <title>Network</title> + + <para/> + </section> + + <section xml:id="chap_dbDevel_dbFeatures_transact"> + <title>Transactions / recovery</title> + + <para/> + </section> + + <section xml:id="chap_dbDevel_dbFeatures_"> + <title>API</title> + + <para/> + </section> + </chapter> + + <chapter xml:id="chap_dbDevel_dbOverview"> + <title>Selected database (types) overview</title> + + <section xml:id="chap_dbDevel_dbOverview_Sql"> + <title>SQL</title> + + <para/> + </section> + </chapter> + + <chapter xml:id="ldap"> + <title><link linkend="glo_LDAP">Ldap</link> and <link + linkend="glo_JDBC">JDBC</link></title> + + <section xml:id="ldapIntro"> + <title>Getting started with <xref linkend="glo_LDAP"/></title> + + <para>Resources:</para> + + <itemizedlist> + <listitem> + <para><link + xlink:href="http://www.zytrax.com/books/ldap/ch3">Schema, + objectclasses and attributes</link></para> + </listitem> + + <listitem> + <para><link xlink:href="http://www.zytrax.com/books/ldap/apd">LDAP + Glossary</link></para> + </listitem> + </itemizedlist> + </section> + + <section xml:id="ldapSetup"> + <title>Setting up an <productname + xlink:href="http://www.openldap.org">Openldap</productname> + server</title> + + <para>The MI department provides an <productname + xlink:href="http://www.openldap.org">Openldap</productname> server at + each workstation (<productname + xlink:href="http://www.ubuntu.com/desktop">Ubuntu</productname> / Linux + only). The <xref linkend="glo_LDAP"/> manager credentials are:</para> + + <glosslist> + <glossentry> + <glossterm>Bind <xref linkend="glo_DN"/>:</glossterm> + + <glossdef> + <para><code>cn=admin,dc=hdm-stuttgart,dc=de</code></para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Bind password:</glossterm> + + <glossdef> + <para><code>XYZ</code></para> + </glossdef> + </glossentry> + </glosslist> + + <para>In addition <xref linkend="glo_Soft_ApacheDirectoryStudio"/> is in + place and may serve as a means to conveniently establish <xref + linkend="glo_LDAP"/> communications. Console geeks may use <command + xlink:href="http://tldp.org/HOWTO/LDAP-HOWTO/utilities.html">ldapmodify</command> + and friends.</para> + + <task> + <title>Set up a connection to your local <xref linkend="glo_LDAP"/> + server</title> + + <procedure> + <step> + <para>Open <xref + linkend="glo_Soft_ApacheDirectoryStudio"/>.</para> + </step> + + <step> + <para>Activate the <xref linkend="glo_LDAP"/> perspective.</para> + </step> + + <step> + <para>In the <quote>Connections</quote> window right click + <quote>New Connection ...</quote> like in <uri + xlink:href="https://directory.apache.org/studio/users-guide/ldap_browser/gettingstarted_create_connection.html">http://directory.apache.org/studio/users-guide/ldap_browser/gettingstarted_create_connection.html</uri>.</para> + </step> + + <step> + <para>Configure a connection to <parameter + xlink:href="http://www.techterms.com/definition/localhost">localhost</parameter> + using the above bind parameters.</para> + </step> + </procedure> + </task> + </section> + + <section xml:id="ldapSimpleInserts"> + <title>Adding some sample data using <xref + linkend="glo_Soft_ApacheDirectoryStudio"/></title> + + <qandaset defaultlabel="qanda" xml:id="qandaPopulateLdap"> + <title>Populating the <xref linkend="glo_DIT"/></title> + + <qandadiv> + <qandaentry> + <question> + <para>Add two departments <code>billing</code> and + <code>research</code>. Then supply corresponding user entries to + both departments by using the <link + xlink:href="https://directory.apache.org/studio/users-guide/ldap_browser/gettingstarted_browse.html">LDAP + browser view</link>. (As being mentioned previously hardcore + hackers take <link + xlink:href="http://tldp.org/HOWTO/LDAP-HOWTO/utilities.html">this + track</link> neglecting time consuming <xref linkend="glo_GUI"/> + stuff).</para> + + <para>Hint: If you do have limited understanding of <xref + linkend="glo_LDAP"/> classes an schemata you may want to create + entries containing the following <parameter + xlink:href="http://www.zytrax.com/books/ldap/apd/index.html#objectclass">objectClass</parameter> + values:</para> + + <glosslist> + <glossentry> + <glossterm>Departments <code>billing</code> and + <code>research</code>:</glossterm> + + <glossdef> + <itemizedlist> + <listitem> + <para><code + xlink:href="http://www.zytrax.com/books/ldap/ape/#organizationalunit">organizationalUnit</code> + (structural)</para> + </listitem> + </itemizedlist> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Child user entries below <code>billing</code> and + <code>research</code>:</glossterm> + + <glossdef> + <itemizedlist> + <listitem> + <para><code + xlink:href="http://www.zytrax.com/books/ldap/ape/#organizationalperson">organizationalPerson</code> + (structural) and <code + xlink:href="http://www.zytrax.com/books/ldap/ape/#posixaccount">posixAccount</code> + (auxiliary)</para> + </listitem> + </itemizedlist> + </glossdef> + </glossentry> + </glosslist> + + <para>Note that required parent <parameter + xlink:href="http://www.zytrax.com/books/ldap/apd/index.html#objectclass">objectClass</parameter> + definitions like <code>top</code> and <code + xlink:href="http://www.zytrax.com/books/ldap/ape/#person">person</code> + are being omitted here. <xref + linkend="glo_Soft_ApacheDirectoryStudio"/> will gracefully add + missing objectClasses on behalf of you automatically. The + subsequent <xref linkend="glo_LDIF"/> dump may serve as a + hint:</para> + + <programlisting language="ldif">... + <emphasis role="bold">dn: ou=billing,dc=hdm-stuttgart,dc=de</emphasis> + objectClass: top + objectClass: organizationalUnit + ou: billing + + <emphasis role="bold">dn: ou=research,dc=hdm-stuttgart,dc=de</emphasis> + objectClass: top + objectClass: organizationalUnit + ou: research + + <emphasis role="bold">dn: uid=lapinski,ou=billing,dc=hdm-stuttgart,dc=de</emphasis> + objectClass: posixAccount + objectClass: top + objectClass: person + objectClass: organizationalPerson + cn: Mee Lapinski + gidNumber: 100 + homeDirectory: /home/lapinski + sn: Lapinski + uid: lapinski + uidNumber: 1023 + ...</programlisting> + + <para>Question: What is the ratio behind adding the + <code>objectClass</code> value <code>posixAccount</code>? Hint: + Try to create a corresponding dataset having two persons with + identical names within the same department.</para> + </question> + + <answer> + <para>Your result may look like:</para> + + <figure xml:id="figureLdapTwoDepartments"> + <title>Two departments billing and research populated with + sample user entries</title> + + <screenshot> + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Screen/ldapSampleUsers.png"/> + </imageobject> + </mediaobject> + </screenshot> + </figure> + + <para>Without having the <code>objectClass</code> value + <code>posixAccount</code> the attribute <code>uid</code> would + be disallowed and could thus not be part of our <xref + linkend="glo_DN"/> values. This would leave us with solutions + like:</para> + + <programlisting language="ldif"><emphasis role="bold">dn: cn=Mee Lapinski,ou=billing,dc=hdm-stuttgart,dc=de</emphasis></programlisting> + + <para>This in turn disallows identical common names (e.g. a + second <personname>Mee Lapinski</personname>) within the same + department. Thus the auxiliary objectClass posixAccount enables + us to introduce additional mandatory <code>uid</code> attribute + being the unique identifier within a given parent scope.</para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + + <section xml:id="ldifImportExport"> + <title><xref linkend="glo_LDIF"/> export and import</title> + + <para>This section is intended to get acquainted with <xref + linkend="glo_LDIF"/> representation of <xref linkend="glo_LDAP"/> data + and requires successful completion of <xref + linkend="qandaPopulateLdap"/> as a prerequisite. You may want to read + <uri + xlink:href="http://www.zytrax.com/books/ldap/ch8">http://www.zytrax.com/books/ldap/ch8</uri>.</para> + + <qandaset defaultlabel="qanda" xml:id="qanda_ldifExportImport"> + <title>Exporting, modifying and importing <xref linkend="glo_LDAP"/> + data using the <xref linkend="glo_LDIF"/> interchange + representation.</title> + + <qandadiv> + <qandaentry> + <question> + <para>Export your current database state being left from <xref + linkend="qandaPopulateLdap"/> to an <xref linkend="glo_LDIF"/> + text file.</para> + + <para>Subsequently use this database dump file as a starting + point to create a <xref linkend="glo_LDIF"/> import file adding + a department <quote>pr</quote> (public relations) containing a + user <quote>Paul Simon</quote> with suitable attribute values to + the dataset.</para> + </question> + + <answer> + <para>Adding the new entries in question requires:</para> + + <screen>version: 1 + + dn: ou=pr,dc=hdm-stuttgart,dc=de + objectClass: top + objectClass: organizationalUnit + ou: pr + + dn: uid=simon,ou=pr,dc=hdm-stuttgart,dc=de + objectClass: posixAccount + objectClass: top + objectClass: person + objectClass: organizationalPerson + cn: Paul Simon + gidNumber: 130 + homeDirectory: /home/tauras + sn: Svetlana + uid: tauras + uidNumber: 1028</screen> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + + <section xml:id="ditSearch"> + <title>Searching the <xref linkend="glo_DIT"/></title> + + <para>Like relational and other database types <xref + linkend="glo_LDAP"/> provides a <link + xlink:href="http://www.zytrax.com/books/ldap/apa/search.html">query + language</link> to filter result entries.</para> + + <qandaset defaultlabel="qanda" xml:id="qanda_firstLdapQuery"> + <title>Filtering child elements</title> + + <qandadiv> + <qandaentry> + <question> + <para>Create <xref linkend="glo_LDAP"/> queries corresponding to + the following descriptions:</para> + + <orderedlist> + <listitem> + <para>All users entries within the whole <xref + linkend="glo_DIT"/> having a gidNumber value of 100.</para> + </listitem> + + <listitem> + <para>All user entries belonging to the billing department + having a <code>uid</code> value greater than 1023.</para> + </listitem> + + <listitem> + <para>All user entries within the whole <xref + linkend="glo_DIT"/> having a common name containing the + substring <quote>ei</quote>.</para> + </listitem> + + <listitem> + <para>All user entries within the whole <xref + linkend="glo_DIT"/> belonging to gidNumber == 100 or having + a <code>uid</code> value starting with letter + <quote>t</quote>.</para> + </listitem> + </orderedlist> + + <para>Hint: <xref linkend="glo_Soft_ApacheDirectoryStudio"/> + allows both for <link + xlink:href="https://directory.apache.org/studio/users-guide/ldap_browser/tools_filter_editor_dialog.html">filtering</link> + and <link + xlink:href="https://directory.apache.org/studio/users-guide/ldap_browser/gettingstarted_search.html">searching</link> + providing nifty features like attribute name completion and + syntax highlighting. For regular searches you may define:</para> + + <itemizedlist> + <listitem> + <para>The <xref linkend="glo_DIT"/> entry to start from + being identified by its <xref linkend="glo_DN"/>.</para> + </listitem> + + <listitem> + <para>The search scope being either of object, one level or + subtree.</para> + </listitem> + + <listitem> + <para>Boolean expressions based on attribute values.</para> + </listitem> + </itemizedlist> + + <para>But yes, I forgot to mention <link + xlink:href="http://tldp.org/HOWTO/LDAP-HOWTO/utilities.html">something</link>.</para> + </question> + + <answer> + <orderedlist> + <listitem> + <para><emphasis role="bold">All users entries within the + whole </emphasis><xref linkend="glo_DIT"/><emphasis + role="bold"> having a gidNumber value of + 100.</emphasis></para> + + <para>Solution: <code>(gidNumber=100)</code>, starting from + top of <xref linkend="glo_DIT"/> having subtree + scope.</para> + </listitem> + + <listitem> + <para><emphasis role="bold">All user entries belonging to + the billing department having a <code>uid</code> value + greater than 1023.</emphasis></para> + + <para>Solution: <code>(uidNumber>=1024)</code> starting + from <xref linkend="glo_DN"/> + <code>ou=billing,dc=hdm-stuttgart,dc=de</code> and scope + <code>one level</code>.</para> + + <para>Notice the expression + <code>(uidNumber>=1024)</code> in favour of the seemingly + equivalent but syntactically illegal counterpart + <code>(uidNumber>1023)</code>.</para> + </listitem> + + <listitem> + <para><emphasis role="bold">All user entries within the + whole </emphasis><xref linkend="glo_DIT"/><emphasis + role="bold"> having a common name containing the substring + <quote>ei</quote>.</emphasis></para> + + <para>Solution: <code>(cn=*ei*)</code>, starting from top of + <xref linkend="glo_DIT"/> having subtree scope.</para> + </listitem> + + <listitem> + <para><emphasis role="bold">All user entries within the + whole </emphasis><xref linkend="glo_DIT"/><emphasis + role="bold"> belonging to gidNumber == 100 or having a + <code>uid</code> value starting with letter + <quote>t</quote>.</emphasis></para> + + <para>Solution: <code>(|(gidNumber=100)(uid=t*))</code>, + starting from top of <xref linkend="glo_DIT"/> having + subtree scope.</para> + </listitem> + </orderedlist> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + + <section xml:id="unix2sql2ldap"> + <title><xref linkend="glo_UNIX"/> to <xref linkend="glo_SQL"/> to <xref + linkend="glo_LDAP"/></title> + + <para><xref linkend="glo_UNIX"/> type operating systems manage users, + groups and their respective relationships in three different text + files:</para> + + <glosslist> + <glossentry> + <glossterm><filename>/etc/passwd</filename></glossterm> + + <glossdef> + <para>Users being defined on the system:</para> + + <literallayout>root:x:0:0:root:/root:/bin/bash +daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin +bin:x:2:2:bin:/bin:/usr/sbin/nologin +sys:x:3:3:sys:/dev:/usr/sbin/nologin +sync:x:4:65534:sync:/bin:/bin/sync +...</literallayout> + + <para>We illustrate the meaning of this <xref linkend="glo_CSV"/> + (actually **character** separated) by examining the first + row:</para> + + <glosslist> + <glossentry> + <glossterm>Column 1, <code>root</code>:</glossterm> + + <glossdef> + <para>The user's unique system name as being entered at + login.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 2, x<code>:</code></glossterm> + + <glossdef> + <para>This field is not being used on current <xref + linkend="glo_UNIX"/> implementations. Historically either + the user's clear text password or its hash value was present + here. For security reasons this attribute has been moved to + a third file <filename>/etc/shadow</filename> being read + access protected to non-administrator users.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 3, <code>0</code>:</glossterm> + + <glossdef> + <para>The user's unique integer numerical + <parameter>uid</parameter> number value.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 4, <code>0</code>:</glossterm> + + <glossdef> + <para>The user's unique primary group integer numerical + <parameter>gid</parameter> number value. The value + <quote>0</quote> here refers to a group root of identical + name being defined in <filename>/etc/group</filename>, see + next section.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 5, <code>root</code>:</glossterm> + + <glossdef> + <para>The user's common name. For a regular user account + this might be <quote>Jim Beam</quote> for example.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 6, <filename>/root</filename>:</glossterm> + + <glossdef> + <para>The user's home directory. Might be /home/beam for a + user <quote>Jim Beam</quote>.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 7, <command>/bin/bash</command>:</glossterm> + + <glossdef> + <para>The user's login shell (command interpreter. This + attribute contains a reference to a command interpreter like + <command>/bin/(t)csh</command>, <command>/bin/ksh</command> + and so on.</para> + </glossdef> + </glossentry> + </glosslist> + </glossdef> + </glossentry> + + <glossentry> + <glossterm><filename>/etc/group</filename></glossterm> + + <glossdef> + <para>This file contains all groups being defined on the + system:</para> + + <literallayout>root:x:0: +daemon:x:1: +bin:x:2: +sys:x:3: +adm:x:4:syslog,mi <co xml:id="secondaryGroupmembership"/> +tty:x:5: +...</literallayout> + + <glosslist> + <glossentry> + <glossterm>Column1,root:</glossterm> + + <glossdef> + <para>The group's name</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 2, x:</glossterm> + + <glossdef> + <para>Not used</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 3, 4:</glossterm> + + <glossdef> + <para>The group's unique <parameter>gid</parameter> + number</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Column 4, <code>syslog,mi</code>:</glossterm> + + <glossdef> + <para>The set <code>{syslog,mi}</code> <coref + linkend="secondaryGroupmembership"/> defines secondary group + memberships: These two users will belong to the group + <code>adm</code> in addition to their respective primary + group definition.</para> + </glossdef> + </glossentry> + </glosslist> + </glossdef> + </glossentry> + </glosslist> + + <qandaset defaultlabel="qanda" xml:id="qandUnixToSqlToLdap"> + <title>Exporting and importing data</title> + + <qandadiv> + <qandaentry> + <question> + <para>Write two applications being able to perform the following + tasks:</para> + + <orderedlist> + <listitem> + <para>Import the previously described UNIX user and group + data ton an RDBMS using <xref linkend="glo_JDBC"/>. You will + have to define a suitable SQL schema first.</para> + </listitem> + + <listitem> + <para>Transfer RDBMS data to your local <xref + linkend="glo_LDAP"/> server using <link + xlink:href="https://docs.oracle.com/javase/jndi/tutorial/ldap/misc/url.html">JNDI</link>.</para> + </listitem> + </orderedlist> + </question> + + <answer> + <annotation role="make"> + <para + role="eclipse">DbDevel/UnixSqlLdap/Jndi/Unix2Rdbms</para> + </annotation> + + <annotation role="make"> + <para + role="eclipse">DbDevel/UnixSqlLdap/Jndi/Rdbms2Ldap</para> + </annotation> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + + <section xml:id="transactionsInJdbc"> + <title>Transactions in <xref linkend="glo_JDBC"/></title> + + <para>You may review some remarks on SQL standard isolation level + definitions:</para> + + <itemizedlist> + <listitem> + <para><xref linkend="glo_Javadoc"/>:</para> + + <itemizedlist> + <listitem> + <para><link + xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Connection.html#TRANSACTION_READ_UNCOMMITTED">TRANSACTION_READ_UNCOMMITTED</link></para> + </listitem> + + <listitem> + <para><link + xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Connection.html#TRANSACTION_READ_COMMITTED">TRANSACTION_READ_COMMITTED</link></para> + </listitem> + + <listitem> + <para><link + xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Connection.html#TRANSACTION_REPEATABLE_READ">TRANSACTION_READ_REPEATABLE_READ</link></para> + </listitem> + + <listitem> + <para><link + xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Connection.html#TRANSACTION_SERIALIZABLE">TRANSACTION_READ_SERIALIZABLE</link></para> + </listitem> + </itemizedlist> + </listitem> + + <listitem> + <para><link + xlink:href="http://www.oracle.com/technetwork/issue-archive/2005/05-nov/o65asktom-082389.html">On + Transaction Isolation Levels (Oracle)</link></para> + </listitem> + + <listitem> + <para><link + xlink:href="https://technet.microsoft.com/en-us/library/ms378149(v=sql.110).aspx">Understanding + Isolation Levels (Microsoft)</link></para> + </listitem> + </itemizedlist> + + <section xml:id="accountTransferPessimistic"> + <title>Account Transfer using pessimistic concurrency control</title> + + <qandaset defaultlabel="qanda" xml:id="qandaJdbcIsolation"> + <title>Accounts and balances</title> + + <qandadiv> + <qandaentry> + <question> + <para>Consider the following simple schema of accounts keeping + customer balances:</para> + + <programlisting language="sql">CREATE TABLE Account ( + number INT NOT NULL PRIMARY KEY + ,balance INT NOT NULL + )</programlisting> + + <para>Write two GUI applications to:</para> + + <itemizedlist> + <listitem> + <para>Transfer amounts from one account to another</para> + </listitem> + + <listitem> + <para>Get the sum of all balances</para> + </listitem> + </itemizedlist> + + <informalfigure> + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Screen/accountTransferSum.png"/> + </imageobject> + </mediaobject> + </informalfigure> + </question> + + <answer> + <annotation role="make"> + <para role="eclipse">DbDevel/account</para> + </annotation> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + + <section xml:id="accountTransferOptimistic"> + <title>Account Transfer using optimistic concurrency control</title> + + <figure xml:id="fig_optimisticConcurrencyControl"> + <title>Optimistic concurrency control</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/concurrentOptimistic.svg"/> + </imageobject> + </mediaobject> + </figure> + + <para>An interfering transaction obeying the protocol causes a + transaction failure:</para> + + <figure xml:id="concurrentObtimisticFail"> + <title>Failure with optimistic transactions</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/concurrentOptimisticFail.svg"/> + </imageobject> + </mediaobject> + </figure> + + <para>Considerations:</para> + + <itemizedlist> + <listitem> + <para>Race conditions, time of check to time of use</para> + </listitem> + </itemizedlist> + + <qandaset defaultlabel="qanda" xml:id="qandaTransferOptimistic"> + <title>Optimistic account transfer</title> + + <qandadiv> + <qandaentry> + <question> + <para>Implement your (pessimistic) account transfer + application <xref linkend="qandaJdbcIsolation"/> in an + optimistic manner:</para> + + <itemizedlist> + <listitem> + <para>Make sure both source and destination accounts get + protected against interfering transactions.</para> + </listitem> + + <listitem> + <para>Provide a means to definitely avoid deadlocks during + the second transaction section of a balance transfer + operation.</para> + </listitem> + + <listitem> + <para>Supply a suitable message in case of an interfering + second balance transfer</para> + </listitem> + </itemizedlist> + </question> + </qandaentry> + </qandadiv> + </qandaset> + </section> + </section> + </chapter> + + <chapter version="5.1" xml:id="jpa"> + <title><xref linkend="glo_JPA"/></title> + + <remark>Mapping tools should be used only by someone familiar with + relational technology. O-R mapping is not meant to save developers from + understanding mapping problems or to hide them altogether. It is meant for + those who have an understanding of the issues and know what they need, but + who don't want to have to write thousands of lines of code to deal with a + problem that has already been solved.<xref + linkend="bib_Keith09"/>.</remark> + + <section xml:id="configureEclipseMaven"> + <title>Configuring a Maven based Eclipse <xref linkend="glo_Java"/> + project with Hibernate</title> + + <para>We will use Maven for several purposes:</para> + + <figure xml:id="fig_reasonsUsingMaven"> + <title>Reasons for using Maven</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/mavenIntro.fig" scale="65"/> + </imageobject> + </mediaobject> + </figure> + + <para>We do explain the problem of managing transitive dependencies in + projects:</para> + + <figure xml:id="fig_transitiveDependencies"> + <title>Transitive dependencies</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/transitiveDep.fig" scale="65"/> + </imageobject> + </mediaobject> + </figure> + + <section xml:id="sect_createHibernateConfiguration"> + <title>Creating a <xref linkend="glo_JPA"/> configuration</title> + + <para><xref linkend="glo_JPA"/> is intended to provide persistence + services saving transient <xref linkend="glo_Java"/> instances to a + database. For this purpose we have to provide:</para> + + <itemizedlist> + <listitem> + <para>The type of database (Oracle, DB2, <xref + linkend="glo_Soft_Mysql"/>,...)</para> + </listitem> + + <listitem> + <para><xref linkend="glo_JDBC"/> driver class name.</para> + </listitem> + + <listitem> + <para><xref linkend="glo_JDBC"/> connection parameters</para> + + <itemizedlist> + <listitem> + <para>Server name</para> + </listitem> + + <listitem> + <para>port</para> + </listitem> + + <listitem> + <para>user name</para> + </listitem> + + <listitem> + <para>password</para> + </listitem> + </itemizedlist> + </listitem> + + <listitem> + <para>A list of classes to be mapped</para> + </listitem> + + <listitem> + <para>Parameters defining the log level, whether generated SQL + code shall be logged etc.</para> + </listitem> + </itemizedlist> + + <para><xref linkend="glo_JPA"/> offers an XML based configuration + syntax typically residing in + <filename>src/main/resources/META-INF/persistence.xml</filename>. We + show a toy example of a <filename>hibernate.cfg.xml</filename> + configuration file mapping just one class + <classname>hibintro.v1.model.User</classname> to a <xref + linkend="glo_Soft_Mysql"/> database server:</para> + + <figure xml:id="hibernateConfigurationFile"> + <title>A basic <filename>persistence.xml</filename> <xref + linkend="glo_JPA"/> configuration file.</title> + + <programlisting language="xml"><persistence + version="2.1" + xmlns="http://xmlns.jcp.org/xml/ns/persistence" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence + http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> + + <!-- + The <code>persistence.xml</code> file configures at least one persistence unit; + each unit must have a unique name. + --> + <persistence-unit name="entitytemplatePU"> + + <!-- + Hibernate will scan your classpath for mapped classes and add them automatically + to your persistence unit. + <exclude-unlisted-classes>false</exclude-unlisted-classes> + --> + <exclude-unlisted-classes>false</exclude-unlisted-classes> + + <!-- + Disabling the previous option "exclude-unlisted-classes" requires entity classes to + be listed explicitely. You may want to uncomment the following definition. + + <class>my.base.url.Airline</class> + --> + + <!-- + Standard or vendor-specific options can be set as properties on a persistence unit. + Any standard properties have the <code>javax.persistence</code> name prefix, Hibernate's + settings use <code>hibernate</code> + --> + <properties> + <!-- + JDBC database connection parameter + --> + <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> + <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/hdm?useSSL=false"/> + <property name="javax.persistence.jdbc.user" value="hdmuser"/> + <property name="javax.persistence.jdbc.password" value="XYZ"/> + + <!-- + The JPA engine should drop and re-create the SQL schema in the database + automatically when it boots. This is ideal for automated testing, when + you want to work with a clean database for every test run. + --> + <property + name="javax.persistence.schema-generation.database.action" + value="drop-and-create"/> + + <!-- + When printing SQL in logs, let Hibernate format the SQL nicely and generate + comments into the SQL string so we know why Hibernate executed the SQL statement. + --> + <property name="hibernate.format_sql" value="true"/> + <property name="hibernate.use_sql_comments" value="true"/> + + <!-- Enable Hibernate scanning for entity classes and adding them automatically + but not for hbm.xml files. --> + <property name="hibernate.archive.autodetection" value="class"/> + + </properties> + </persistence-unit> + +</persistence></programlisting> + </figure> + + <para>This file may be edited with a simple text editor. Note the + <filename>persistence_2_1.xsd</filename> schema which allows for + validation.</para> + + <para>The Eclipse <productname + xlink:href="https://www.jboss.org/tools">Jboss Tools</productname> + Eclipse plugin provides a configuration editor simplifying this task. + They may be installed on top of Eclipse <link + xlink:href="https://www.jboss.org/tools/download">in several + ways</link>. The following video shows some of its features.</para> + + <mediaobject> + <videoobject> + <videodata fileref="Ref/Video/hibernateConfig.mp4"/> + </videoobject> + </mediaobject> + </section> + </section> + + <section xml:id="sect_hibernateBasics"> + <title>A round trip working with objects</title> + + <para>Hibernate may be regarded as a persistence provider to <xref + linkend="glo_JPA"/>:</para> + + <figure xml:id="jpaPersistProvider"> + <title><xref linkend="glo_JPA"/> persistence provider</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/persistProvider.fig"/> + </imageobject> + </mediaobject> + </figure> + + <para>The following class <classname>hibintro.v1.model.User</classname> + will be used as a starting example to be mapped to a database. Notice + the <classname>javax.persistence.Entity</classname> <link + xlink:href="https://docs.oracle.com/javase/tutorial/java/javaOO/annotations.html">annotation</link> + <coref linkend="entityAnnotation"/>:</para> + + <figure xml:id="mappingUserInstances"> + <title>Mapping <classname>hibintro.v1.model.User</classname> instances + to a database.</title> + + <programlisting language="java">package hibintro.v1; + +import javax.persistence.Entity; +import javax.persistence.Id; + +/** + * A simple class intended to be mapped to a database server + * + */ +@Entity <co xml:id="entityAnnotation"/> +public class User { + + String uid; + /** + * @return The user's unique login name e.g. "goik" + */ + @Id + public String getUid() { + return uid; + } + /** + * @param uid See {@link #getUid()}. + */ + public void setUid(String uid) { + this.uid = uid; + } + String cname; + /** + * @return The user's common name e.g. "Martin Goik" + */ + public String getCname() { + return cname; + } + /** + * @param cname See {@link #getCname()}. + */ + public void setCname(String cname) { + this.cname = cname; + } + + /** + * Hibernate/JPA require a default constructor. It has has to be + * implemented if any non-default constructor has been defined + */ + public User() {} + + /** + * @param uid See {@link #getUid()}. + * @param cname See {@link #getCname()}. + */ + public User(String uid, String cname) { + this.uid = uid; + this.cname = cname; + } +}</programlisting> + </figure> + + <para>With respect to <xref linkend="hibernateConfigurationFile"/> we + notice our class <classname>hibintro.v1.model.User</classname> being + referenced:</para> + + <programlisting language="xml"><persistence ... > + + <persistence-unit name="hibintroV1PU"> + <emphasis role="bold"><class><classname>hibintro.v1.User</classname></class></emphasis> + ... + </persistence-unit> + ...</programlisting> + + <para>This line tells Hibernate to actually map + <classname>hibintro.v1.model.User</classname> to the database.</para> + + <section xml:id="persistingObjects"> + <title>Persisting objects</title> + + <para>Persisting transient objects may be achieved in various ways. + <xref linkend="glo_JDBC"/> allows to store and retrieved object + values.</para> + + <para>Having larger projects these tasks become increasingly tedious. + It is thus desired to automate these tasks while still using + <trademark + xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> + for the low level transport layer.</para> + + <para>The following sections start with a single class + <classname>hibintro.v1.model.User</classname>:</para> + + <figure xml:id="fig_BasicUser"> + <title>A basic <code>User</code> class.</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/classUser.fig"/> + </imageobject> + </mediaobject> + </figure> + + <para>Object relational mapping (ORM) denotes the process of mapping + instances of classes to relational table data. In our current example + we may draw a simple implementation sketch:</para> + + <figure xml:id="mappingProperties2attributes"> + <title>Mapping properties to attributes.</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/mapUser.fig"/> + </imageobject> + </mediaobject> + </figure> + + <para>This is far too simplistic. What about integrity + constraints?</para> + + <figure xml:id="mappingIntegrityConstraints"> + <title>Annotating integrity constraints</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/mapUserIntegrity.fig"/> + </imageobject> + </mediaobject> + </figure> + + <para>We start with the following + <classname>hibintro.v1.model.User</classname> class lacking integrity + constraints completely:</para> + + <programlisting language="java">package hibintro.v1.model; + @Entity + public class User { + String uid; + public String getUid() {return uid;} + public void setUid(String uid) {this.uid = uid;} + + String cname; + public String getCname() {return cname;} + public void setCname(String cname) {this.cname = cname;} + + /** + * Hibernate/JPA require a default constructor. It has has to be implemented + * if any non-default constructor has been defined + */ + public User() {} + + /** + * @param uid See {@link #getUid()}. + * @param cname See {@link #getCname()}. + */ + public User(String uid, String cname) { + this.uid = uid; + this.cname = cname; + } + }</programlisting> + + <para>Persisting objects with Hibernate requires a + <classname>org.hibernate.Session</classname> instance <coref + linkend="sessionInstance"/>. It happens between the start <coref + linkend="startTransaction"/> and commit <coref + linkend="commitTransaction"/> of a transaction being derived from that + session:</para> + + <programlisting language="java">package hibintro.v1.run; + + ... + public class PersistSingleUser { + + public static void main(String[] args) { + final <classname>org.hibernate.Session</classname> session <co + xml:id="sessionInstance"/>= HibernateUtil.createSessionFactory("hibernate.cfg.xml").openSession(); + + final <classname>org.hibernate.Transaction</classname> transaction = session.beginTransaction();<co + xml:id="startTransaction"/> + + final <classname>hibintro.v1.model.User</classname> u = new User("goik", "Martin Goik"); + session.save(u); + + transaction.commit(); <co xml:id="commitTransaction"/> + } + }</programlisting> + + <para>Executing the above code yields a runtime exception:</para> + + <screen>Exception in thread "main" java.lang.ExceptionInInitializerError + at myhibernate.intro.run.PersistUser.main(PersistUser.java:14) + Caused by: org.hibernate.AnnotationException: <emphasis role="bold">No identifier specified for entity: myhibernate.intro.model.User</emphasis> + ... + at myhibernate.intro.util.HibernateUtil.buildConfiguration(HibernateUtil.java:17) + at myhibernate.intro.util.HibernateUtil.<clinit>(HibernateUtil.java:9)</screen> + + <para>This runtime error is a little bit cryptic. The missing + <quote>identifier</quote> refers to the absence of a primary key + definition already mentioned in <xref + linkend="mappingIntegrityConstraints"/>. We define a key by annotating + the <code>uid</code> property with a + <classname>javax.persistence.Id</classname> annotation <coref + linkend="primaryKeyDefinition"/>:</para> + + <programlisting language="java">package hibintro.v1.model; + + import javax.persistence.Entity; + <emphasis role="bold">import javax.persistence.Id;</emphasis> + + ... + @Entity public class User {... + <emphasis role="bold">@Id</emphasis> <co xml:id="primaryKeyDefinition"/> + public String getUid() { + return uid; + } ...</programlisting> + + <para>The careful reader will have noticed that we've annotated the + getter method rather than the property <code>uid</code> itself. + Hibernate / <xref linkend="glo_JPA"/> can work both ways. Annotating a + getter however offers additional support e.g. when logging for + debugging purposes is required.</para> + + <para>This time we are successful. Since we enabled the logging of SQL + statements in <xref linkend="hibernateConfigurationFile"/> Hibernate + shows us the corresponding <code>INSERT</code> statement:</para> + + <programlisting language="sql">Hibernate: + insert + into + User + (cname, uid) sky + values + (?, ?)</programlisting> + + <para>Notice the (?,?) part of our log: This indicates the internal + usage of <trademark + xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> + <classname>java.sql.PreparedStatement</classname> instances. Hibernate + generates the following create table statement:</para> + + <figure xml:id="fig_createTableV1User"> + <title>Database schema mapping instances of + <classname>hibintro.v1.model.User</classname>.</title> + + <programlisting language="sql">CREATE TABLE User ( + uid VARCHAR(255) NOT NULL PRIMARY KEY, + cname VARCHAR(255) + ) </programlisting> + </figure> + </section> + + <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 <coref linkend="specLoadType"/> and its + primary key value <coref linkend="specLoadPrimaryKey"/>:</para> + + <figure xml:id="loadByClassAndPrimaryKey"> + <title>Loading a single object by a primary key value.</title> + + <programlisting language="java">package hibintro.v1.run; + ... + public class RetrieveSingleUser { + ... + final Transaction transaction = session.beginTransaction(); + + 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> + </figure> + + <para>This retrieves the expected result. Buried in other log messages + we find the following SQL <quote>background</quote> statement:</para> + + <screen>... + INFO: HHH000232: Schema update complete + Hibernate: + <emphasis role="bold">select + user0_.uid as uid0_0_, + user0_.cname as cname0_0_ + from + User user0_ + where + user0_.uid=?</emphasis> + + Found user 'Martin Goik'</screen> + + <qandaset defaultlabel="qanda" xml:id="quandaentry_MetodNullIfNoexist"> + <title>Choosing the correct method</title> + + <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>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> + + <screen>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"/> + </screen> + + <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>org.hibernate.Session.get(Class,Serializable)</methodname> + method which actually returns <code>null</code> in case a + primary key value does not exist:</para> + + <programlisting language="java">... 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 language="java">package hibintro.v1.run; + ... + public class PersistUsers { + ... + final Transaction transaction = session.beginTransaction(); + + final User users[] = {new User("wings", "Fred Wings"), + new User("eve", "Eve Briggs")} ; + for (final User u : users ) {session.save(u);} + + transaction.commit(); ...</programlisting> + + <para>Now we'd like to retrieve these objects. Hibernate offers the + <emphasis role="bold">H</emphasis>ibernate <emphasis + role="bold">Q</emphasis>uery <emphasis role="bold">L</emphasis>anguage + (<xref linkend="glo_HQL"/>) for object queries. As we will see <xref + linkend="glo_HQL"/> extends <acronym linkend="glo_SQL">SQL</acronym> + with respect to polymorphic queries. The current example does not use + inheritance leaving us with a simple <xref linkend="glo_HQL"/> query + <coref linkend="hqlFromUser"/> in + <classname>hibintro.v1.run.RetrieveAll</classname>:</para> + + <figure xml:id="retrieveAllUserByHql"> + <title>Retrieving <classname>hibintro.v1.model.User</classname> + instances by <abbrev + xlink:href="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>.</title> + + <programlisting language="java">package hibintro.v1.run; + ... + public class RetrieveAll { + ... + final Query searchUsers = session.createQuery("<emphasis role="bold">from User</emphasis>");<co + xml:id="hqlFromUser"/> + final List<User> users = (List<User>) searchUsers.list(); + for (final User u: users) { + System.out.println("uid=" + u.getUid() + ", " + u.getCname()); + }</programlisting> + </figure> + + <para>Being used to <xref linkend="glo_SQL"/>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 + <xref linkend="glo_HQL"/> query returns a set of + <classname>hibintro.v1.model.User</classname> instances:</para> + + <screen>uid=eve, Eve Briggs +uid=goik, Martin Goik +uid=wings, Fred Wings</screen> + + <qandaset defaultlabel="qanda" xml:id="quandaentry_AnalyzeHqlResult"> + <title><xref linkend="glo_HQL"/> and <xref linkend="glo_SQL"/>, + analyzing results.</title> + + <qandadiv> + <qandaentry> + <question> + <para>We may actually retrieve attributes rather than objects. + For this purpose our query actually resembles standard <xref + linkend="glo_SQL"/> <coref linkend="hqlWithSelect"/>:</para> + + <programlisting language="java">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>Class.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 language="java">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="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev> + to offer additional features namely predicate based queries. Following + <classname>hibintro.v1.run.SelectUser</classname> we may restrict our + result set by an <xref linkend="glo_SQL"/> style <code>WHERE</code> + clause:</para> + + <programlisting language="java"> final List<User> users = (List<User>) session.createQuery( + "<emphasis role="bold">from User u where u.cname like '%e%'</emphasis>").list(); + for (final User u: users) { + System.out.println("Found user '" + u.getCname() + "'"); + }</programlisting> + + <para>This time we receive a true subset of + <classname>hibintro.v1.model.User</classname> instances:</para> + + <screen>Found user 'Eve Briggs' + Found user 'Fred Wings'</screen> + </section> + + <section xml:id="criteriaBasedQueries"> + <title>Criteria based queries</title> + + <para>Selecting Objects by <abbrev + xlink:href="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>c + queries technically means parsing <abbrev + xlink:href="https://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"> + <title>Mapping single entities and database tables</title> + + <section xml:id="transientProperties"> + <title>Transient properties</title> + + <para>We take a closer look at <xref linkend="mappingUserInstances"/> + assuming that Instances of + <classname>hibintro.v1.model.User</classname> need an additional + <emphasis role="bold">GUI related</emphasis> property + <code>selected</code> <coref linkend="propertyIsSelected"/>:</para> + + <programlisting language="java">package hibintro.v2; + +import javax.persistence.Entity; +import javax.persistence.Id; + +/** + * A simple class intended to be mapped to a database server + */ +@Entity +public class User { + + String uid; + /** + * @return The user's unique login name e.g. "goik" + */ + @Id + public String getUid() { + return uid; + } + /** + * @param uid See {@link #getUid()}. + */ + public void setUid(String uid) { + this.uid = uid; + } + String cname; + /** + * @return The user's common name e.g. "Martin Goik" + */ + public String getCname() { + return cname; + } + /** + * @param cname See {@link #getCname()}. + */ + public void setCname(String cname) { + this.cname = cname; + } + + boolean selected = false; <co xml:id="propertyIsSelected"/> + + public boolean isSelected() { + return selected; + } + public void setSelected(boolean selected) { + this.selected = selected; + } + protected User() {} + + /** + * @param uid See {@link #getUid()}. + * @param cname See {@link #getCname()}. + */ + public User(String uid, String cname) { + this.uid = uid; + this.cname = cname; + } +}</programlisting> + + <para>Hibernates produces the following <abbrev + xlink:href="https://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev> + statements containing an attribute <code>selected</code> <coref + linkend="attributeSelected"/>:</para> + + <programlisting language="sql">CREATE TABLE User ( + uid VARCHAR(255) NOT NULL PRIMARY KEY, + cname VARCHAR(255), + <emphasis role="bold">selected</emphasis> <co xml:id="attributeSelected"/> BIT NOT NULL, +) </programlisting> + + <para>If we just annotate a Java class with an + <classname>javax.persistence.Entity</classname> Annotation all + properties of the class in question will be mapped. A <xref + linkend="glo_JPA"/> framework of course cannot distinguish between + transient and persistent properties. If we want a property to be + transient we have to add a <classname + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Transient.html">javax.persistence.Transient</classname> + annotation:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package hibintro.v3; + +@Entity +public class User { + ... + boolean selected = false; + @Transient <co xml:id="transientAnnotation"/> + public boolean isSelected() { return selected; } + public void setSelected(boolean selected) { this.selected = selected; } + ... +}</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">SQL</emphasis></td> + + <td><programlisting language="sql">CREATE TABLE User ( + uid VARCHAR(255) NOT NULL PRIMARY KEY, + cname VARCHAR(255) +) </programlisting></td> + </tr> + </informaltable> + + <para>The <classname + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Transient.html">javax.persistence.Transient</classname> + annotation inhibits the mapping of our property + <code>selected</code>.</para> + + <caution> + <para>When loading a <classname>hibintro.v3.User</classname> + instance from a database all transient property values are of course + entirely determined by the default constructor.</para> + </caution> + </section> + + <section xml:id="sect_mappingNullValues"> + <title>Properties and NULL values</title> + + <para>In <xref linkend="fig_createTableV1User"/> the primary key + <code>uid</code> property's value must not be <code>NULL</code>. This + is an immediate consequence of the + <classname>javax.persistence.Id</classname> annotation and the fact + that databases don't allow NULL values for key attributes.</para> + + <para>The <code>cname</code> property however may be null. Sometimes + we want to ensure the corresponding database attributes to be set, at + least carrying an empty string value. This can be achieved by adding a + <classname + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html">javax.persistence.Column</classname> + <option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html#nullable--">(nullable + = false)</option> annotation:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package hibintro.v4; + ... +@Entity public class User { + ... + String cname; + <emphasis role="bold"><classname + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html">@Column</classname>(<option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html#nullable--">nullable</option> = false)</emphasis> public String getCname() { + return cname; + } + ...</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">SQL</emphasis></td> + + <td><programlisting language="sql">CREATE TABLE User ( + uid VARCHAR(255) NOT NULL PRIMARY KEY, + cname VARCHAR(255) <emphasis role="bold">NOT NULL</emphasis> <co + xml:id="cnameDatabaseNotNull"/> +)</programlisting></td> + </tr> + </informaltable> + + <para>This results in a corresponding database constraint <coref + linkend="cnameDatabaseNotNull"/>. Attempts to store instances with + null values now fail:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package hibintro.v4; + ... + public class PersistSingleUser { + + em.getTransaction().begin(); + { + em.persist(new User("goik", "Martin Goik")); + } + em.getTransaction().commit();</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Log</emphasis></td> + + <td><screen>Exception in thread "main" javax.persistence.PersistenceException: + org.hibernate.PropertyValueException: <emphasis role="bold">not-null property references a null or transient value</emphasis> : +hibintro.v4.User.cname + at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692) + ... + at hibintro.v4.PersistSingleUser.main(PersistSingleUser.java:22)</screen></td> + </tr> + </informaltable> + + <para>The database constraint violation causes the <trademark + xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> + driver to throw this exception.</para> + </section> + + <section xml:id="mappingKeys"> + <title>Defining keys</title> + + <para>Frequently we need more than just a primary key. Starting from + <classname>hibintro.v4.User</classname> we may want to add a property + <code>uidNumber</code> adding a common requirement: On UNIX type + operation systems each user does have both a unique login name (like + <quote>goik</quote>) and a unique numerical value (like + <quote>123</quote>). We choose our primary key to be numeric <coref + linkend="uidNumberIsPrimaryKey"/>and the login name to become a second + candidate key <coref linkend="uidIsUnique"/>:</para> + + <programlisting language="java">package hibintro.v5; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +@Entity +@Table(uniqueConstraints={<classname + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/UniqueConstraint.html">@UniqueConstraint</classname>(<option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/UniqueConstraint.html#columnNames--">columnNames</option>={"uid"})}) <co + xml:id="uidIsUnique"/> +public class User { + + int uidNumber; + @Id <co xml:id="uidNumberIsPrimaryKey"/> + public int getUidNumber() { return uidNumber; } + public void setUidNumber(int uidNumber) { this.uidNumber = uidNumber; } + + String uid; + + /** + * @return The user's unique login name e.g. "goik" + */ + @Column(nullable=false) + public String getUid() { return uid; } + + /** + * @param uid, See {@link #getUid()}. + */ + public void setUid(String uid) { this.uid = uid; } + + ... +}</programlisting> + + <para>Notice the slight difference: The property <code>uid</code> may + need a + <code>@</code><code><classname>javax.persistence.Column</classname>(nullable=false)</code> + annotation to become a candidate key. This is <emphasis>not</emphasis> + automatically inferred by the + <classname>javax.persistence.UniqueConstraint</classname> definition + <coref linkend="uidIsUnique"/>. In contrast the property + <code>uidNumber</code> is not being referenced by the preceding + <classname>javax.persistence.Table</classname> annotation but + annotated by <classname>javax.persistence.Id</classname>. Hence a + <code>nullable=false</code> is not needed.</para> + + <para>This is in accordance with <abbrev + xlink:href="https://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev>: + Attributes composing a primary key must not allow <code>NULL</code> + values but attributes only appearing in UNIQUE declarations may become + <code>NULL</code>.</para> + + <para>The resulting <abbrev + xlink:href="https://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev> + reads:</para> + + <programlisting language="sql">CREATE TABLE User ( + uidNumber INT NOT NULL PRIMARY KEY, + cname VARCHAR(255) NOT NULL, + uid VARCHAR(255) NOT NULL UNIQUE +)</programlisting> + </section> + + <section xml:id="sect_ComposedKeys"> + <title>Composed keys</title> + + <para>Composed candidate keys are often business keys. The underlying + logic defines which objects are considered to be identical based on + their values.</para> + + <para>As an example, we consider a company having several departments. + Regarding projects he following business rules shall apply:</para> + + <figure xml:id="projectBusinessRules"> + <title>Business rules for projects</title> + + <orderedlist> + <listitem> + <para>Each department must have a unique name.</para> + </listitem> + + <listitem> + <para>A project's name must be unique within the set of all + projects belonging to the same department.</para> + </listitem> + + <listitem> + <para>A project must be assigned to exactly one + department.</para> + </listitem> + </orderedlist> + </figure> + + <para>Right now we defer considerations of the n:1 relationship + between departments and projects to a later chapter. Instead we focus + just on project instances and represent departments just by their + integer id values which will later become foreign keys.</para> + + <para>In addition each project receives a unique integer id value as + well. This is in accordance with the <quote>best practice</quote> rule + of defining a <link + xlink:href="https://en.wikipedia.org/wiki/Surrogate_key">surrogate + key</link> <coref linkend="projectPrimaryKeyDefinition"/> to be used + as (primary) object identifier. This immutable key will then become + the target in foreign key definitions:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package hibintro.v6; + ... +@Entity +@Table(uniqueConstraints={<classname + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/UniqueConstraint.html">@UniqueConstraint</classname>(columnNames={"name", "department"})}) <co + xml:id="projectBusinessKey"/> +public class Project { + + int id; + @Id <co xml:id="projectPrimaryKeyDefinition"/> + public int getId() { return id; } + protected void setId(int id) { this.id = id; } + + String name; + @Column(nullable=false) + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + int department; + @Column(nullable=false) + public int getDepartment() { return department; } + public void setDepartment(int department) { this.department = department; } + ... +}</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting language="sql">CREATE TABLE Project ( + id int(11) NOT NULL PRIMARY KEY <coref linkend="projectPrimaryKeyDefinition"/>, + department int(11) NOT NULL, + name varchar(255) NOT NULL, + UNIQUE KEY name (name,department) <coref linkend="projectBusinessKey"/> +)</programlisting></td> + </tr> + </informaltable> + + <calloutlist> + <callout arearefs="projectPrimaryKeyDefinition"> + <para>Defining the surrogate primary key.</para> + </callout> + + <callout arearefs="projectBusinessKey"> + <para>Defining a business key composed of a project's + <code>name</code> and <code>department</code> number. This + implements our second business rule in <xref + linkend="projectBusinessRules"/>.</para> + </callout> + </calloutlist> + + <qandaset defaultlabel="qanda" xml:id="quandaentry_IdGetterProtected"> + <title><xref linkend="glo_JPA"/> <code language="java">id</code> + getter visibility.</title> + + <qandadiv> + <qandaentry> + <question> + <para>The setter void <methodname + annotations="nojavadoc">setId(int)</methodname>in + <classname>hibintro.v6.Project</classname> has protected write + access. Explain this choice.</para> + </question> + + <answer> + <para>From an application developer's point of view the setter + should be absent: The <code>id</code> property is immutable + and should not be accessed at all.</para> + + <para>When loading an instance from a database a persistence + provider however has to set its value. Hibernate uses the + reflection-API to override the restriction being imposed by + the <code>protected</code> modifier. So why not declare it + private? Doing so may cause our IDE to flag a warning about an + unused private method.</para> + + <para>So choosing <code>protected</code> is a compromise: An + application developer cannot modify the property (unless + deriving a class) and our persistence provider can still set + its value to the database's primary key attribute + value.</para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + + <section xml:id="nonUniqueIndexes"> + <title>Indexes (non-unique)</title> + + <para>From the viewpoint of software modelling non-unique indexes are + not part of the business logic but refer to database optimization. + Consequently <xref linkend="glo_JPA"/> has no support for non-unique + indexes.</para> + + <para>On the other hand performance matters. Hibernate and other + persistence providers offer vendor specific <xref linkend="glo_JPA"/> + extensions. We may find it useful to access + <classname>hibintro.v5.User</classname> instances having a specific + <code>cname</code> quickly. This can be achieved by adding a Hibernate + specific + <code>org.hibernate.annotations.</code><classname>org.hibernate.annotations.Table</classname> + index generating annotation <coref linkend="hibernateExtensionIndex"/> + which works on top of <xref linkend="glo_JPA"/>'s + <code>javax.persistence.</code><classname>javax.persistence.Table</classname>:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package hibintro.v7; +... +@Entity +@Table(uniqueConstraints={@UniqueConstraint(name="uidKey", columnNames={"uid"})}, + <emphasis role="bold">indexes = {<classname + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html">@Index</classname>(<option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html#name--">name</option> = "byCname", <option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html#columnList--">columnList</option>="cname", <option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html#unique--">unique</option> = false)}</emphasis> <co + xml:id="hibernateExtensionIndex"/> +) +public class User { + + int uidNumber; + @Id + public int getUidNumber() { return uidNumber; } + public void setUidNumber(int uidNumber) { this.uidNumber = uidNumber; } + + String uid; + /** + * @return The user's unique login name e.g. "goik" + */ + @Column(nullable=false) + public String getUid() { return uid; } + + /** + * @param uid See {@link #getUid()}. + */ + public void setUid(String uid) { this.uid = uid; } + ... +}</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting language="sql">CREATE TABLE User ( + uidNumber INT NOT NULL PRIMARY KEY, + cname VARCHAR(255) NOT NULL, + uid VARCHAR(255) NOT NULL UNIQUE +); + +CREATE INDEX findCname ON User (cname ASC);</programlisting></td> + </tr> + </informaltable> + </section> + + <section xml:id="sect_RenameTablesAndAttributes"> + <title>Renaming tables and attributes</title> + + <para>So far we assumed that we map classes to database tables having + identical names: A <xref linkend="glo_Java"/> class <code>User</code> + is being mapped to a relational table with identical name + <code>User</code>. Sometimes a renaming is desired. We may for example + want to access a legacy database by a newly implemented <xref + linkend="glo_Java"/> application. Choosing meaningful names may + conflict with decisions being taken when the original database design + took place.</para> + + <para>In the following example we change the database tables name from + its default User to Person <coref linkend="renameUserToPerson"/>. The + properties <code>uidNummbe</code>r and <code>cname</code> are changed + to attribute names <code>numericUid</code> <coref + linkend="renameUidNumberToNumericUid"/>and <code>fullName</code> + <coref linkend="renameCnameToFullName"/> respectively:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package hibintro.v8; + ... +@Entity +<classname xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Table.html">@Table</classname>(<option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Table.html#name--">name</option>="Person") <co + xml:id="renameUserToPerson"/> +public class User { + + int uidNumber; + @Id + <classname xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html">@Column</classname>(<option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html#name--">name</option>="numericUid") <co + xml:id="renameUidNumberToNumericUid"/> + public int getUidNumber() {return uidNumber;} + public void setUidNumber(int uidNumber) {this.uidNumber = uidNumber;} + + String uid; + @Column(nullable=false) + public String getUid() {return uid;} + public void setUid(String uid) {this.uid = uid;} + + String cname; + <classname xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html">@Column</classname>(<option + xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html#name--">name</option>="fullName", nullable = false) <co + xml:id="renameCnameToFullName"/> + public String getCname() {return cname;} + public void setCname(String cname) {this.cname = cname;} + ...</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting language="sql">CREATE TABLE Person <coref + linkend="renameUserToPerson"/> ( + numericUid <coref linkend="renameUidNumberToNumericUid"/> int(11) NOT NULL PRIMARY KEY, + fullName <coref linkend="renameCnameToFullName"/> varchar(255) NOT NULL, + uid varchar(255) NOT NULL +)</programlisting></td> + </tr> + </informaltable> + </section> + + <section xml:id="sectChangeDefaultTypeMapping"> + <title>Changing the default type mapping</title> + + <para>Sometimes we are interested in changing <xref + linkend="glo_JPA"/>'s default type mapping strategy. For example + <trademark + xlink:href="https://www.mysql.com/about/legal/trademark.html">Mysql</trademark> + versions prior to 5.0 lack an appropriate type representing boolean + values. It was therefore quite common mapping boolean properties to + <code>CHAR(1)</code> with possible values being <code>'Y'</code> and + <code>'N'</code>. Hibernate will map boolean values to + <code>tinyint(1)</code>. Supporting older software may require to + tweak the standard mapping.</para> + + <para>Unfortunately <xref linkend="glo_JPA"/> itself does not offer + any interface for this purpose. The persistence provider may offer a + solution though. Hibernate for example allows to remap <coref + linkend="remapBooleanChar"/> types . We assume our + <classname>hibintro.v9.User</classname> class to have a + <code>boolean</code> property <code>active</code>:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package hibintro.v9; + +import org.hibernate.type.Type; + ... +public class User { + ... + public void setCname(String cname) {this.cname = cname;} + + boolean active = false; + @Type(type="yes_no") <co xml:id="remapBooleanChar"/> + public boolean isActive() {return active;} + public void setActive(boolean active) {this.active = active;} +}</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting language="sql">CREATE TABLE User ( + uidNumber int(11) NOT NULL PRIMARY KEY, + active char(1) NOT NULL, + cname varchar(255) DEFAULT NULL, + uid varchar(255) NOT NULL +)</programlisting></td> + </tr> + </informaltable> + + <para>Readers being interested in more sophisticated strategies like + mapping user defined data types to database types are advised to read + the <link + xlink:href="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch05.html#mapping-types">manual + section on Hibernate types</link>.</para> + </section> + + <section xml:id="sda2SectObjectStates"> + <title>Object states and transitions</title> + + <figure xml:id="sda2FigObjectStates"> + <title>Persistence related object states</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/persistentStates.fig"/> + </imageobject> + </mediaobject> + </figure> + </section> + + <section xml:id="sectionXmlJpaJaxb"> + <title><xref linkend="glo_XML"/> to <xref linkend="glo_JPA"/> using + <xref linkend="glo_JAXB"/></title> + + <para>Consider the following <xref linkend="glo_XML"/> dataset + example:</para> + + <programlisting language="xml"><catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="cd.xsd"> +<cd id="12"> + <title>Empire Burlesque</title> + <artist>Bob Dylan</artist> + <country>USA</country> + <company>Columbia</company> + <price>10.90</price> + <year>1985</year> +</cd> +<cd id="34"> + <title>Hide your heart</title> + <artist>Bonnie Tylor</artist> + <country>UK</country> + <company>CBS Records</company> + <price>9.90</price> + <year>1988</year> +</cd> + ...</programlisting> + + <section xml:id="sectionXmlJpaJaxbSchema"> + <title>Supplying a schema</title> + + <qandaset defaultlabel="qanda" xml:id="qandaXmlJpaJaxbSchema"> + <title>Integrity constraints</title> + + <qandadiv> + <qandaentry> + <question> + <para>Create a suitable XML schema enforcing the following + integrity constraints:</para> + + <itemizedlist> + <listitem> + <para>All elements are required.</para> + </listitem> + + <listitem> + <para>The element <tag class="starttag">price</tag> is + of decimal type having two fractional digits.</para> + </listitem> + + <listitem> + <para>The elements <tag class="starttag">year</tag> and + <code>@id</code> are of integer type.</para> + </listitem> + + <listitem> + <para>The attribute <code>@id</code> defines a + key.</para> + </listitem> + </itemizedlist> + </question> + + <answer> + <programlisting language="xml"><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> + + <xs:simpleType name="money"> + <xs:restriction base="xs:decimal"> + <xs:fractionDigits value="2"/> + </xs:restriction> + </xs:simpleType> + + <xs:element name="catalog"> + <xs:complexType> + <xs:sequence> + <xs:element ref="cd" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + <xs:key name="uniqueId"> + <xs:selector xpath="cd"/> + <xs:field xpath="@id"/> + </xs:key> + </xs:element> + + <xs:element name="cd"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="xs:string"/> + <xs:element name="artist" type="xs:string"/> + <xs:element name="country" type="xs:string"/> + <xs:element name="company" type="xs:string"/> + <xs:element name="price" type="money"/> + <xs:element name="year" type="xs:int"/> + </xs:sequence> + <xs:attribute name="id" type="xs:int" use="required"/> + </xs:complexType> + </xs:element> + + </xs:schema></programlisting> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + + <section xml:id="sectionXmlJpaJaxbImport"> + <title>Importing <xref linkend="glo_XML"/> data to a RDBMS</title> + + <qandaset defaultlabel="qanda" xml:id="qandaXmlJpaJaxbImport"> + <title>Reading XML, write using <xref linkend="glo_JPA"/></title> + + <qandadiv> + <qandaentry> + <question> + <para>Read the given XML data example using <xref + linkend="glo_JAXB"/> and persist the corresponding objects + to a RDBMS. This requires the following steps:</para> + + <itemizedlist> + <listitem> + <para>Use the <xref linkend="glo_JAXB"/> schema compiler + <command + xlink:href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/xjc.html"><xref + linkend="glo_xjc"/></command> accordingly to create + domain classes corresponding to <tag + class="starttag">catalog</tag> and <tag + class="starttag">cd</tag> .</para> + </listitem> + + <listitem> + <para>Create a <classname + xlink:href="https://docs.oracle.com/javase/9/docs/api/javax/xml/bind/JAXBContext.html">JAXBContext</classname> + and an <classname + xlink:href="https://docs.oracle.com/javase/9/docs/api/javax/xml/bind/Unmarshaller.html">Unmarshaller</classname> + instance to parse valid <tag + class="starttag">catalog</tag> instances + accordingly.</para> + </listitem> + + <listitem> + <para>Supply <xref linkend="glo_JPA"/> annotations to + the <command + xlink:href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/xjc.html"><xref + linkend="glo_xjc"/></command> generated + <classname>de.hdm_stuttgart.mi.sda2.jpa.cd.domain.Catalog</classname> + class.</para> + </listitem> + </itemizedlist> + + <para>Hint: The Maven archetype <link + xlink:href="https://maven-repository.com/artifact/com.github.lalyos/standalone-jpa-eclipselink-archetype">standalone-jpa-eclipselink-archetype</link> + may help getting started with <xref linkend="glo_JPA"/> + configuration issues. You may want to add <link + xlink:href="https://repo1.maven.org/maven2/archetype-catalog.xml">http://repo1.maven.org/maven2/archetype-catalog.xml</link> + to your list of archetype repositories.</para> + </question> + + <answer> + <annotation role="make"> + <para role="eclipse">Sda2/Jpa/Cd</para> + </annotation> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + </section> + </section> + + <section xml:id="jpaInheritance"> + <title>Inheritance</title> + + <figure xml:id="sda2FigMapInheritIntro"> + <title>Introducing inheritance mapping</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/mapInherit.svg"/> + </imageobject> + </mediaobject> + </figure> + + <para>Mapping inheritance hierarchies to relational databases means + bridging the gap between object <link + xlink:href="https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch">oriented + and relational models</link>. We start with a slightly modified example + from <xref linkend="bib_Bauer15"/>:</para> + + <figure xml:id="fig_BillingDetails"> + <title>Modelling payment.</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/billing.fig"/> + </imageobject> + + <caption> + <para>Simplified Billing details example derived from <xref + linkend="bib_Bauer15"/>. Notice + <classname>inherit.v1.BillingDetails</classname> being an abstract + parent class of two concrete classes + <classname>inherit.v1.CreditCard</classname> and + <classname>inherit.v1.BankAccount</classname>. The attribute + <code>number</code> applies both to bank account and credit card + payments.</para> + </caption> + </mediaobject> + </figure> + + <para>Since the relational model lacks inheritance completely we have to + implement a database schema ourselves. We subsequently explore three + main approaches each of which having its own advantages and + disadvantages.</para> + + <section xml:id="sect_InheritTablePerClassHierarchie"> + <title>Single table per class hierarchy</title> + + <para>This approach may be considered the most simple: We just create + one database table for storing instances of arbitrary classes + belonging to the inheritance hierarchy in question:</para> + + <figure xml:id="fig_TablePerClassHierarchyData"> + <title>A single relation mapping.</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/billingData.fig"/> + </imageobject> + + <caption> + <para>Fitting both <classname>inherit.v1.CreditCard</classname> + and <classname>inherit.v1.BankAccount</classname> instances into + a single relation.</para> + </caption> + </mediaobject> + </figure> + + <para>The relation may be created by the following <abbrev + xlink:href="https://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev>:</para> + + <figure xml:id="fig_TablePerClassHierarchyMapping"> + <title>Mapping the inheritance hierarchy.</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/billingSql.fig"/> + </imageobject> + </mediaobject> + </figure> + + <para>We take a closer look at the generated relation. Since</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package inherit.v1; + ... + @Entity + @Inheritance(strategy=InheritanceType.<emphasis role="bold">SINGLE_TABLE</emphasis>) <co + linkends="billingMapSingleTableCallout" + xml:id="billingMapSingleTable"/> + @DiscriminatorColumn(name="dataType", discriminatorType=DiscriminatorType.STRING) <co + linkends="billingMapSingleTableDiscriminatorCallout" + xml:id="billingMapSingleTableDiscriminator"/> + abstract class BillingDetails { + @Id @GeneratedValue <co + linkends="billingMapSingleTableIdGeneratedCallout" + xml:id="billingMapSingleTableIdGenerated"/> public Long getId() ... + @Column(nullable = false, length = 32)public final String getNumber() ... + @Temporal(TemporalType.TIMESTAMP) + @Column(nullable = false) public Date getCreated() ...</programlisting> + <programlisting language="java">package inherit.v1; + ... + @Entity + @DiscriminatorValue(value = "Credit card" <co + xml:id="billingMapSingleTableDiscriminatorCredit"/>) + public class CreditCard extends BillingDetails { + ... //Nothing JPA related happens here</programlisting> <programlisting + language="java">package inherit.v1; + ... + @Entity + @DiscriminatorValue(value = "Bank account" <co + xml:id="billingMapSingleTableDiscriminatorBank"/>) + public class BankAccount extends BillingDetails { + ... //Nothing JPA related happens here</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting continuation="continues" language="sql">CREATE TABLE BillingDetails <co + linkends="billingMapSingleTableCallout" + xml:id="BillingDetailsGeneratedRelationName"/> ( + dataType varchar(31) NOT NULL, + id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, + number varchar(255) NOT NULL, <co + linkends="billingMapSingleTableBaseNotNull" + xml:id="billingMapSingleTableCalloutNumberNotNull"/> + created datetime NOT NULL, <co + linkends="billingMapSingleTableBaseNotNull" + xml:id="billingMapSingleTableCalloutCreatedNotNull"/> + cardType int(11) DEFAULT NULL, <co + linkends="billingMapSingleTableDerivedNull" + xml:id="billingMapSingleTableCardTypeNull"/> + expiration datetime DEFAULT NULL, <co + linkends="billingMapSingleTableDerivedNull" + xml:id="billingMapSingleTableExpirationNull"/> + bankName varchar(255) DEFAULT NULL, <co + linkends="billingMapSingleTableDerivedNull" + xml:id="billingMapSingleTableBankNameNull"/> + swiftcode varchar(255) DEFAULT NULL <co + linkends="billingMapSingleTableDerivedNull" + xml:id="billingMapSingleTableSwiftCodeNull"/> + )</programlisting></td> + </tr> + </informaltable> + + <calloutlist> + <callout arearefs="billingMapSingleTable" + xml:id="billingMapSingleTableCallout"> + <para>All classes of the inheritance hierarchy will be mapped to a + single table. Unless stated otherwise the <xref + linkend="glo_JPA"/> provider will choose the root class' name + (<code>BillingDetails</code>) as default value for the generated + relation's name <coref + linkend="BillingDetailsGeneratedRelationName"/>.</para> + </callout> + + <callout arearefs="billingMapSingleTableDiscriminator" + xml:id="billingMapSingleTableDiscriminatorCallout"> + <para>The <xref linkend="glo_JPA"/> provider needs a column to + distinguish the different types of database objects. We've chosen + the discriminator attribute <code>dataType</code> values to be + simple strings. Due to the definitions in <coref + linkend="billingMapSingleTableDiscriminatorCredit"/> and <coref + linkend="billingMapSingleTableDiscriminatorBank"/> database object + types are being identified by either of the two values:</para> + + <itemizedlist> + <listitem> + <para><code>Credit card</code>: object will be mapped to + <classname>inherit.v1.CreditCard</classname>.</para> + </listitem> + + <listitem> + <para><code>Bank account</code>: object will be mapped to + <classname>inherit.v1.BankAccount</classname>.</para> + </listitem> + </itemizedlist> + + <para>In a productive system the + <classname>javax.persistence.DiscriminatorType</classname> setting + will typically favour + <classname>javax.persistence.DiscriminatorType</classname><code>.INTEGER</code> + over + <classname>javax.persistence.DiscriminatorType</classname><code>.STRING</code> + unless the application in question has to deal with a legacy + database schema.</para> + </callout> + + <callout arearefs="billingMapSingleTableIdGenerated" + xml:id="billingMapSingleTableIdGeneratedCallout"> + <para>This one is unrelated to inheritance: Our primary key values + will be auto generated by the database server e.g. by + <code>SEQUENCE</code> or <code>IDENTITY</code> mechanisms if + available.</para> + </callout> + + <callout arearefs="billingMapSingleTableCalloutNumberNotNull billingMapSingleTableCalloutCreatedNotNull" + xml:id="billingMapSingleTableBaseNotNull"> + <para>Only the base class' attributes may exclude + <code>NULL</code> values.</para> + </callout> + + <callout arearefs="billingMapSingleTableCardTypeNull billingMapSingleTableExpirationNull billingMapSingleTableBankNameNull billingMapSingleTableSwiftCodeNull" + xml:id="billingMapSingleTableDerivedNull"> + <para>All derived classes' attributes must allow <code>NULL</code> + values.</para> + </callout> + </calloutlist> + + <para>We may now insert instances of + <classname>inherit.v1.BankAccount</classname> or + <classname>inherit.v1.CreditCard</classname>:</para> + + <figure xml:id="insertCreditBank"> + <title>Inserting payment information</title> + + <programlisting language="java">package inherit.v1; + ... + public class Persist { + public static void main(String[] args) throws ParseException { + ... final Transaction transaction = session.beginTransaction(); + { + final CreditCard creditCard = new CreditCard("4412 8334 4512 9416", 1, "05/18/15"); + session.save(creditCard); + + final BankAccount bankAccount = new BankAccount("1107 2 31", "Lehman Brothers", "BARCGB22"); + session.save(bankAccount); + } + transaction.commit(); ...</programlisting> + </figure> + + <section xml:id="sect_InheritTablePerClassHierarchieLoad"> + <title>Database object retrieval</title> + + <para>As in <xref linkend="retrieveAllUserByHql"/> objects being + stored by <xref linkend="insertCreditBank"/> may be queried using + <abbrev + xlink:href="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>.</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package inherit.v1; + ... + public class RetrieveCredit { + public static void main(String[] args) { + ... + final Transaction transaction = session.beginTransaction(); + + final Query searchCreditPayments = session.createQuery("<emphasis + role="bold">from inherit.v1.CreditCard</emphasis>"); <co + xml:id="hqlQueryCreditCard"/> + final List<CreditCard> creditCardList = (List<CreditCard>) searchCreditPayments.list(); + for (final CreditCard c: creditCardList) { + System.out.println(c); + } ...</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting continuation="continues" language="sql">INFO: HHH000232: Schema update complete + Hibernate: + select + creditcard0_.id as id0_, + creditcard0_.created as created0_, + creditcard0_.number as number0_, + creditcard0_.cardType as cardType0_, + creditcard0_.expiration as expiration0_ + from + BillingDetails creditcard0_ + where + creditcard0_.<emphasis role="bold">dataType</emphasis> <co + xml:id="hqlQueryCreditCard_dataType"/>='<emphasis + role="bold">Credit card</emphasis>' + + <emphasis role="bold">CreditCard: number=4412 8334 4512 9416, created 2013-02-19 13:09:22.0, + cardType=1, expiration=2015-05-18 00:00:00.</emphasis> <co + xml:id="hqlQueryCreditCardResultSet"/></programlisting></td> + </tr> + </informaltable> + + <para>Some Remarks: Our query asks for instances of + <classname>inherit.v2.CreditCard</classname> <coref + linkend="hqlQueryCreditCard"/>. This gets implemented as an <xref + linkend="glo_SQL"/> <code>SELECT</code> choosing datasets whose + discriminator attribute <code>value of dataType</code> <coref + linkend="hqlQueryCreditCard_dataType"/> equals <quote><code>Credit + card</code></quote>. The current result set contains just one + element <coref linkend="hqlQueryCreditCardResultSet"/> in accordance + with <xref linkend="insertCreditBank"/>.</para> + + <para>Retrieving both <classname>inherit.v1.CreditCard</classname> + and <classname>inherit.v1.BankAccount</classname> instances is + accomplished by querying for the common base class + <classname>inherit.v1.BillingDetails</classname>:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package inherit.v1; + ... + public class RetrieveAll { + ... + final Query searchBilling = session.createQuery("from <emphasis + role="bold">inherit.v1.BillingDetails</emphasis>"); + @SuppressWarnings("unchecked") + final List<BillingDetails> billingDetailsList = (List<BillingDetails>) searchBilling.list(); + for (final BillingDetails c: billingDetailsList) { + System.out.println(c); + } ...</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting continuation="continues" language="sql">INFO: HHH000232: Schema update complete + Hibernate: + select + billingdet0_.id as id0_, + ... + billingdet0_.dataType as dataType0_ + from + BillingDetails billingdet0_ + + CreditCard: number=4412 8334 4512 9416, created 2013-02-19 13:09:22.0, <co + xml:id="resultSetHeterogeneous"/> + cardType=1, expiration=2015-05-18 00:00:00.0 + BankAccount: number=1107 2 31, created 2013-02-19 13:09:22.0, + bankName=Lehman Brothers, swiftcode=BARCGB22</programlisting></td> + </tr> + </informaltable> + + <para>This is the first example of a polymorphic query yielding a + heterogeneous result set<coref + linkend="resultSetHeterogeneous"/>.</para> + </section> + + <section xml:id="sect_InheritTablePerClassHierarchieNullProblem"> + <title>Null values</title> + + <para>Our current mapping strategy limits our means to specify data + integrity constraints. It is no longer possible to disallow + <code>null</code> values for properties belonging to derived + classes. We might want to disallow <code>null</code> values in the + <code>bankName</code> property. Hibernate will generate a + corresponding database attribute <coref + linkend="require_bankNameNotNullDb"/>:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package inherit.v2; + ... + @Entity @DiscriminatorValue(value = "Bank account") + public class BankAccount extends BillingDetails { + String bankName; + @Column(<emphasis role="bold">nullable=false</emphasis>) <co + xml:id="require_bankNameNotNull"/> + public String getBankName() {return bankName;} ...</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting continuation="continues" language="sql">CREATE TABLE BillingDetails ( + id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, + bankName varchar(255) <emphasis role="bold">NOT NULL</emphasis>, <co + xml:id="require_bankNameNotNullDb"/> + ...</programlisting></td> + </tr> + </informaltable> + + <para>Looks good? Unfortunately the attempt to save a bank account + <coref linkend="saveBankAccount"/> yields a runtime exception <coref + linkend="saveBankAccountException"/>:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package inherit.v2; + ... + public class Persist { + ... + final CreditCard creditCard = new CreditCard("4412 8334 4512 9416", 1, "05/18/15"); + session.save(creditCard); + + final BankAccount bankAccount = new BankAccount("1107 2 31", "Lehman Brothers", "BARCGB22"); + session.save(bankAccount) <co xml:id="saveBankAccount"/>; ...</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting continuation="continues" language="sql">... + Feb 19, 2013 10:28:00 AM org.hibernate.tool.hbm2ddl.SchemaUpdate execute + INFO: HHH000232: Schema update complete + Hibernate: + insert + into + BillingDetails + (created, number, cardType, expiration, dataType) + values + (?, ?, ?, ?, 'Credit card') + Feb 19, 2013 10:28:00 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions + WARN: SQL Error: 1364, SQLState: HY000 + Feb 19, 2013 10:28:00 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions + <emphasis role="bold">ERROR: Field 'bankName' doesn't have a default value + Exception in thread "main" org.hibernate.exception.GenericJDBCException: + Field 'bankName' doesn't have a default value</emphasis> <co + xml:id="saveBankAccountException"/> + ... + at inherit.v2.Persist.main(Persist.java:28) + Caused by: java.sql.SQLException: Field 'bankName' doesn't have a default value</programlisting></td> + </tr> + </informaltable> + + <para>Conclusion: A table per class hierarchy mapping does not allow + to specify not null constraints for properties of derived + classes.</para> + + <qandaset defaultlabel="qanda" + xml:id="quandaentry_MappingFigureInherit"> + <title>Mapping figures</title> + + <qandadiv> + <qandaentry> + <question> + <para>Map the following model to a database:</para> + + <figure xml:id="modelFigureInheritance"> + <title>Figure subclasses</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/figureInherit.fig"/> + </imageobject> + </mediaobject> + </figure> + + <para>The two properties <code>xCenter</code> and + <code>yCenter</code> in the abstract base class + <code>Figure</code> represent the coordinates of the + concrete figure's center of gravity. In a drawing + application this would be considered the placement of the + respective object.</para> + + <para>The abstract method <code>getArea()</code> is meant to + be implemented without interfering with your database + mapping. Choose an integer discriminator. Test your + application by storing and loading objects.</para> + </question> + + <answer> + <para>The main difference to the current + <classname>inherit.v1.BillingDetails</classname> example is + the <classname>javax.persistence.Transient</classname> + annotation of the <code>area</code> property in + <classname>inherit.v3.Figure</classname>, + <classname>inherit.v3.Circle</classname> and + <classname>inherit.v3.Rectangle</classname>. The storage ant + retrieval applications are + <classname>inherit.v3.Persist</classname>, + <classname>inherit.v3.RetrieveRectangles</classname> and + <classname>inherit.v3.RetrieveAll</classname> are + straightforward.</para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + </section> + + <section xml:id="joinedSubclass"> + <title>Joined subclasses</title> + + <para>The basic idea is to generate a normalized schema implementing + inheritance relationships by foreign keys:</para> + + <figure xml:id="joindSubclassMapping"> + <title>Joined subclass mapping.</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/billingMapJoined.fig"/> + </imageobject> + </mediaobject> + </figure> + + <para>The inheritance strategy of joined subclasses <coref + linkend="strategyJoinedSubclass"/> is being defined in the abstract + base class + <classname>inherit.joined.v1.BillingDetails</classname>:</para> + + <programlisting language="java">package inherit.joined.v1; + ... + @Entity + @Inheritance(strategy=InheritanceType.JOINED) <co + xml:id="strategyJoinedSubclass"/> + public abstract class BillingDetails { ... }</programlisting> + + <para>The derived classes need to provide an implementation hint in + order to identify the required foreign key <coref + linkend="referenceParentClass"/> to the parent class + <classname>inherit.joined.v1.BillingDetails</classname>:</para> + + <programlisting language="java">package inherit.joined.v1; + ... + @Entity + @PrimaryKeyJoinColumn(name="parent" <co xml:id="referenceParentClass"/>, referencedColumnName="id") + public class CreditCard extends BillingDetails { + + int cardType; + @Column(nullable=false) <co xml:id="tpcNotNullCardType"/> + public int getCardType() {return cardType;} + public void setCardType(int cardType) {this.cardType = cardType;} + + Date expiration; + @Column(nullable=false) <co xml:id="tpcNotNullexpiration"/> + public Date getExpiration() {return expiration;} + public void setExpiration(Date expiration) {this.expiration = expiration;} + ... + }</programlisting> + + <para>Notice the ability to exclude null values in <coref + linkend="tpcNotNullCardType"/> and <coref + linkend="tpcNotNullexpiration"/>.</para> + + <section xml:id="joinedSubclassRetrieve"> + <title>Retrieving Objects</title> + + <para>On the database server side object retrieval results in a more + expensive operation: A query for root class instances + of<classname>inherit.joined.v1.BillingDetails</classname> <coref + linkend="joinedQueryBillingDetails"/> of our inheritance hierarchy + results in joining all three tables <code>BillingDetails</code> + <coref linkend="joinFromBillingDetails"/>, <code>BankAccount</code> + <coref linkend="joinFromBankAccount"/> and <code>CreditCard</code> + <coref linkend="joinFromCreditCard"/>:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting language="java">package inherit.joined.v1; + ... + public class RetrieveAll { + ... + final Query searchBilling = session.createQuery("<emphasis role="bold">from inherit.tpc.v1.BillingDetails</emphasis>" <co + xml:id="joinedQueryBillingDetails"/>); + ...</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting continuation="continues" language="sql">Hibernate: + select + billingdet0_.id as id0_, + billingdet0_.created as created0_, + billingdet0_.number as number0_, + billingdet0_1_.bankName as bankName1_, + billingdet0_1_.swiftcode as swiftcode1_, + billingdet0_2_.cardType as cardType2_, + billingdet0_2_.expiration as expiration2_, + case + when billingdet0_1_.id is not null then 1 + when billingdet0_2_.id is not null then 2 + when billingdet0_.id is not null then 0 + end as clazz_ + from + <emphasis role="bold">BillingDetails</emphasis> billingdet0_ <co + xml:id="joinFromBillingDetails"/> + left outer join + <emphasis role="bold">BankAccount</emphasis> billingdet0_1_ <co + xml:id="joinFromBankAccount"/> + on billingdet0_.id=billingdet0_1_.id + left outer join + <emphasis role="bold">CreditCard</emphasis> billingdet0_2_ <co + xml:id="joinFromCreditCard"/> + on billingdet0_.id=billingdet0_2_.id </programlisting></td> + </tr> + </informaltable> + + <qandaset defaultlabel="qanda" + xml:id="quandaentry_IntegrityGenerate"> + <title><xref linkend="glo_JPA"/> constraints and database + integrity.</title> + + <qandadiv> + <qandaentry> + <question> + <para>Explain all integrity constraints of the Hibernate + generated schema. Will it implement the constraints corectly + on database level corresponding to the inheritance related + <xref linkend="glo_Java"/> objects? On contrary: Are there + possible database states which do not correspond to the + domain model's object constraints?</para> + </question> + + <answer> + <para>We take a look to the database schema:</para> + + <programlisting language="java">CREATE TABLE BillingDetails ( + id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY <co + linkends="inheritJoinSqlJava-1" + xml:id="inheritJoinSqlJava-1-co"/>, + created datetime NOT NULL, + number varchar(32) NOT NULL + ); + CREATE TABLE CreditCard ( + id bigint(20) NOT NULL PRIMARY KEY <co linkends="inheritJoinSqlJava-2" + xml:id="inheritJoinSqlJava-2-co"/> REFERENCES <co + linkends="inheritJoinSqlJava-3" + xml:id="inheritJoinSqlJava-3-co"/> BillingDetails, + cardType int(11) NOT NULL, + expiration datetime NOT NULL + ); + CREATE TABLE BankAccount ( + id bigint(20) NOT NULL PRIMARY KEY <co linkends="inheritJoinSqlJava-4" + xml:id="inheritJoinSqlJava-4-co"/> REFERENCES <co + linkends="inheritJoinSqlJava-4" + xml:id="inheritJoinSqlJava-5-co"/> BillingDetails, + bankName varchar(255) NOT NULL, + swiftcode varchar(255) NOT NULL + );</programlisting> + + <calloutlist> + <callout arearefs="inheritJoinSqlJava-1-co" + xml:id="inheritJoinSqlJava-1"> + <para>The table implementing the root class + <classname>inherit.joined.v1.BillingDetails</classname> + of the inheritance hierarchy will be referenced both by + <code>CreditCard</code> and <code>BankAccount</code> + datasets and thus requires a key to become addressable. + Moreover the corresponding + <classname>inherit.joined.v1.BillingDetails</classname> + class requires this attribute to be the primary key + anyway.</para> + </callout> + + <callout arearefs="inheritJoinSqlJava-2-co" + xml:id="inheritJoinSqlJava-2"> + <para>Each <code>CreditCard</code> specific set of + attributes belongs to exactly one + <code>BillingDetails</code> instance and hence the id + within our table <code>CreditCard</code> must be + unique.</para> + </callout> + + <callout arearefs="inheritJoinSqlJava-3-co" + xml:id="inheritJoinSqlJava-3"> + <para>As stated in <coref + linkend="inheritJoinSqlJava-2-co"/> each + <code>CreditCard</code> dataset must refer to its parent + <code>BillingDetails</code> instance.</para> + </callout> + + <callout arearefs="inheritJoinSqlJava-4-co inheritJoinSqlJava-5-co" + xml:id="inheritJoinSqlJava-4"> + <para>These constraints likewise describe <coref + linkend="inheritJoinSqlJava-2-co"/> and <coref + linkend="inheritJoinSqlJava-3-co"/> for + <code>BankAccount</code> datasets.</para> + </callout> + </calloutlist> + + <para>The NOT NULL constraints implement their counterpart + properties in the corresponding <xref linkend="glo_Java"/> + objects.</para> + + <para>The mapping does not cover one important integrity + constraint of our domain model: The base class + <classname>inherit.joined.v1.BillingDetails</classname> is + abstract. Thus each entry in the database must refer either + to a <classname>inherit.joined.v1.CreditCard</classname> or + a <classname>inherit.joined.v1.BankAccount</classname> + instance. But the above database schema allows for datasets + to appear in the <code>BillingDetails</code> table not being + referenced by either <code>BankAccount</code> or + <code>CreditCard</code> datasets.</para> + + <para>So the current database schema actually refers to a + domain model having a <emphasis + role="bold">concrete</emphasis> base class + <code>BillingDetails</code>.</para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + + <qandaset defaultlabel="qanda" + xml:id="quandaentry_FigureJoinedSubclass"> + <title>Implementing figures by joined subclasses</title> + + <qandadiv> + <qandaentry> + <question> + <para>Implement the model being given in <xref + linkend="modelFigureInheritance"/> by joined + subclasses.</para> + </question> + + <answer> + <para>See + <classname>inherit.joined.v2.Figure</classname>.</para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + </section> + + <section xml:id="inheritTablePerConcrete"> + <title>Table per concrete class</title> + + <para/> + </section> + </section> + + <section xml:id="jpaAssociations"> + <title>Mapping related Components and entities</title> + + <section xml:id="primaryKeyRevisit"> + <title>Primary keys revisited</title> + + <para>Following <xref linkend="bib_Bauer15"/> (p.88) we list important + properties of primary keys with respect to <quote>best + practices</quote> on top of their relational counterparts:</para> + + <itemizedlist> + <listitem> + <para>A primary key's values never change</para> + </listitem> + + <listitem> + <para>Primary key values should not have a business meaning</para> + </listitem> + + <listitem> + <para>Primary keys should be chosen to have proper indexing + support with respect to the database product in question.</para> + </listitem> + </itemizedlist> + + <para>Regarding persistence we have three different concepts related + to object identity:</para> + + <glosslist> + <glossentry> + <glossterm>Java Object identity</glossterm> + + <glossdef> + <para>The operator == checks whether two identifiers point to + the same memory address.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Java Object equality</glossterm> + + <glossdef> + <para>The <methodname>Object.equals(Object)</methodname>.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Database identity</glossterm> + + <glossdef> + <para>Two distinct datasets (tuples) are identical if all + primary key attributes have the same value.</para> + + <para>In other words: Two distinct database objects differ at + least in one of their primary key attribute values.</para> + </glossdef> + </glossentry> + </glosslist> + + <para>We recommend <xref linkend="bib_Bloch08"/> regarding Java + identity and equality of objects.</para> + + <section xml:id="objectEqualityByPrimaryKey"> + <title>Defining object equality by primary key</title> + + <para>Consider the following code:</para> + + <programlisting language="java">package session1; + ... + public class LoadUser { + ... + final User user; + { + final Session session = HibernateUtil.createSessionFactory("session1/hibernate.cfg.xml").openSession(); + final Transaction transaction = session.beginTransaction(); + user = (User) session.load(User.class, 1L); + final User user1 = (User) session.load(User.class, 1L); + + System.out.println("first transaction: user.equals(user1):" + user.equals(user1)); <co + xml:id="rereadInstance_1"/> + + transaction.commit(); + session.close(); + } + { + final Session session = HibernateUtil.createSessionFactory("session1/hibernate.cfg.xml").openSession(); + final Transaction transaction = session.beginTransaction(); + final User user2 = (User) session.load(User.class, 1L); + + System.out.println("second transaction: user.equals(user2):" + user.equals(user2)); <co + xml:id="rereadInstance_2"/> + + transaction.commit(); + session.close(); + } ...</programlisting> + + <para>At first we compare two <classname>session1.User</classname> + instances being read by the same session manager <coref + linkend="rereadInstance_1"/>. Subsequently we compare a detached + <classname>session1.User</classname> instance with a second instance + being read by a different session <coref + linkend="rereadInstance_2"/>. This yields the following + result:</para> + + <screen>first transaction: user.equals(user1):true <coref + linkend="rereadInstance_1"/> +second transaction: user.equals(user2):false <coref linkend="rereadInstance_2"/></screen> + + <para>The two instances in question definitely represent the same + database entity. The two entity managers referring to different + sessions create two distinct instances within a given <xref + linkend="glo_Java"/> runtime.</para> + + <para>Since <xref linkend="glo_JPA"/> entities require an + <code>@javax.persistence.Id</code> attribute we may generally define + object equality solely based on this at tribute's value:</para> + + <programlisting language="java">package session2; + ... + public class User { + + @Id @GeneratedValue + private Long id; + ... + @Override public boolean equals(Object other) { + if (this == other) <co linkends="equalByPrimaryKey-1" + xml:id="equalByPrimaryKey-1-co"/>{ + return true; + } else if (other instanceof User) { + return getId().equals(((User) other).getId()) <co + linkends="equalByPrimaryKey-2" xml:id="equalByPrimaryKey-2-co"/>; + } else { + return false; + } + } + @Override public int hashCode() { <co linkends="equalByPrimaryKey-3" + xml:id="equalByPrimaryKey-3-co"/> + if (null == id) { + return System.identityHashCode(this); + } else { + return getId().hashCode(); + } + } + }</programlisting> + + <para>This way of defining + <methodname>session2.User.equals(java.lang.Object)</methodname> + implies that either or both of the following two conditions must be + met:</para> + + <calloutlist> + <callout arearefs="equalByPrimaryKey-1-co" + xml:id="equalByPrimaryKey-1"> + <para>Both instances must be identical.</para> + </callout> + + <callout arearefs="equalByPrimaryKey-2-co" + xml:id="equalByPrimaryKey-2"> + <para>Both instances must have the same + <methodname>session2.User.getId()</methodname> value. It is not + necessary to prepend <emphasis role="bold"><code>null != other + </code></emphasis><code>&& other instanceof User</code> + since the operator <code>instanceof</code> evaluates to + <code>false</code> if its left side is <code>null</code>.</para> + </callout> + </calloutlist> + + <caution> + <para>Do not forget to implement + <methodname>Object.hashCode()</methodname> <coref + linkend="equalByPrimaryKey-3-co" xml:id="equalByPrimaryKey-3"/> + accordingly: Two instances <code>a</code> and <code>b</code> + returning <code>a.equals(b) == true</code> equal must return an + identical hash code value <code>a.hashCode() == + b.hashCode()</code> in order to satisfy a collection's + contract.</para> + + <para><xref linkend="bib_Bloch08"/> covers the contract of + <methodname>Object.hashCode()</methodname> and its close relation + to <methodname>Object.equals(Object)</methodname> in + detail.</para> + </caution> + </section> + + <section xml:id="objectEqualityByNaturalKey"> + <title>Object equality by natural key</title> + + <para>Defining entity equality based on database identity suffers + from a severe deficiency: Newly created instances invariably differ + from any foreign non-identical instance regardless whether it does + have a database identity or not. We consider an example:</para> + + <programlisting language="java">package session2; + ... + public class CompareNewlyCreated { ... + + // Create two transient instances + final User a = new User(123, "goik", "Martin Goik"), <co + linkends="compareTransientById-1_2" + xml:id="compareTransientById-1-co"/> + b = new User(123, "goik", "Martin Goik"); <co + linkends="compareTransientById-1_2" + xml:id="compareTransientById-2-co"/> + + System.out.println("a.equals(b):" + a.equals(b)); <co + linkends="compareTransientById-3" + xml:id="compareTransientById-3-co"/> + + { + final Session session = HibernateUtil.createSessionFactory("session2/hibernate.cfg.xml").openSession(); + final Transaction transaction = session.beginTransaction(); + + // previously saved as new User(123, "goik", "Martin Goik"); + final User user = (User) session.load(User.class, 1L); <co + linkends="compareTransientById-4" + xml:id="compareTransientById-4-co"/> + + System.out.println("a.equals(user)):" + a.equals(user)); <co + linkends="compareTransientById-5" + xml:id="compareTransientById-5-co"/> + + transaction.commit(); + session.close(); + } ...</programlisting> + + <calloutlist> + <callout arearefs="compareTransientById-1-co compareTransientById-2-co" + xml:id="compareTransientById-1_2"> + <para><!--1 + 2-->Create two transient instances being identical + by value.</para> + </callout> + + <callout arearefs="compareTransientById-3-co" + xml:id="compareTransientById-3"> + <para><!--3-->Both instances are defined to differ by + value.</para> + </callout> + + <callout arearefs="compareTransientById-4-co" + xml:id="compareTransientById-4"> + <para><!--4-->Load a persistent entity from the database.</para> + </callout> + + <callout arearefs="compareTransientById-5-co" + xml:id="compareTransientById-5"> + <para><!--5-->Transient and persistent instances are defined to + differ by value.</para> + </callout> + </calloutlist> + + <para>Apparently this is definitely wrong: We do have unique + database index definitions. All objects in question do have common + values 123 and <code>"goik"</code> on these respective keys.</para> + </section> + + <section xml:id="equalityByNaturalKey"> + <title>Implementing <methodname>Object.equals(Object)</methodname> + by natural keys</title> + + <para>The last section's result actually provides a hint to + implement <methodname>Object.equals(Object)</methodname> in a more + meaningful way. The problem comparing transient instances occurs + since a surrogate key's value is being provided by the database + server only at the time an entity is being persisted. If at least + one natural key (sometimes referred to as <quote>business + key</quote>) is being defined this one may be used instead:</para> + + <figure xml:id="implementEqualsByNaturalKey"> + <title>Implementing <methodname>Object.equals(Object)</methodname> + by natural keys</title> + + <programlisting language="java">package session3; + + @Entity + @Table(uniqueConstraints={@<emphasis role="bold">UniqueConstraint(columnNames={"uid"}</emphasis>)<co + linkends="implementEqualsByNaturalKey-1" + xml:id="implementEqualsByNaturalKey-1-co"/> , + @UniqueConstraint(columnNames={"uidNumber"})}) + public class User { + ... + String uid; + + @Column(nullable=false) + public String getUid() {return uid;} + public void setUid(String uid) {this.uid = uid;} + ... + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof User) { + return <emphasis role="bold">getUid().equals(((User) other).getUid())</emphasis>; <co + linkends="implementEqualsByNaturalKey-2" + xml:id="implementEqualsByNaturalKey-2-co"/> + } else { + return false; + } + } + @Override + public int hashCode() { + if (null == getUid()) { + return System.identityHashCode(this); + } else { + return <emphasis role="bold">getUid().hashCode()</emphasis>; <co + linkends="implementEqualsByNaturalKey-3" + xml:id="implementEqualsByNaturalKey-3-co"/> + } + } + }</programlisting> + + <calloutlist> + <callout arearefs="implementEqualsByNaturalKey-1-co" + xml:id="implementEqualsByNaturalKey-1"> + <para>Definition of property + <methodname>session3.User.getUid()</methodname> to become a + natural key.</para> + </callout> + + <callout arearefs="implementEqualsByNaturalKey-2-co" + xml:id="implementEqualsByNaturalKey-2"> + <para>Two <classname>session3.User</classname> instances + having identical + <methodname>session3.User.getUid()</methodname> values will be + considered equal.</para> + </callout> + + <callout arearefs="implementEqualsByNaturalKey-3-co" + xml:id="implementEqualsByNaturalKey-3"> + <para>The <methodname>session3.User.hashCode()</methodname> + implementation has to be changed accordingly.</para> + </callout> + </calloutlist> + </figure> + + <qandaset defaultlabel="qanda" + xml:id="quandaentry_MethodFieldAccess"> + <title>Method and field access</title> + + <qandadiv> + <qandaentry> + <question> + <para>Consider <xref + linkend="implementEqualsByNaturalKey"/>. You may get a + different runtime behaviour when using <emphasis + role="bold"><code>this.uid().equals( that.uid() + )</code></emphasis> at <coref + linkend="implementEqualsByNaturalKey-2-co"/>. Execute the + corresponding <emphasis + role="bold">session3.CompareNewlyCreated</emphasis> and + <emphasis role="bold">session3.LoadUser</emphasis> + applications and explain the result.</para> + </question> + + <answer> + <para><xref linkend="glo_JPA"/> allows lazy fetch mode + typically enabled by default. So the <code>uid</code> + attribute's initialization will be deferred until + <methodname>session3.User.getUid()</methodname> is being + called for the first time.</para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> + </section> + + <section xml:id="entityValueTypes"> + <title>Entity and value types</title> + + <para>From the viewpoint of <xref linkend="glo_ORM"/> we distinguish + two distinct types of database objects:</para> + + <glosslist> + <glossentry> + <glossterm>Value type</glossterm> + + <glossdef> + <para>An object of value type has no database identity. It will + appear in a database as a composite of a parent entity type. Its + life cycle is completely dependent on its parent.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Entity type</glossterm> + + <glossdef> + <para>Objects of this type do have their own database identity + and may exist independently of other (database) entities.</para> + </glossdef> + </glossentry> + </glosslist> + </section> + + <section xml:id="sect_MappingComponents"> + <title>Mapping components</title> + + <para>We consider a simple example. We may add an email property to + <classname>session3.User</classname>:</para> + + <programlisting language="java">... +public class User { + ... + private Email address; ...</programlisting> + + <para>Why do we use a separate class Email rather than a simple + <emphasis role="bold"><code>private String email</code></emphasis> + declaration? The answer is quite simple: We want Email instances to be + extensible and allow for method definitions like + <code>sendEmail(...)</code>:</para> + + <programlisting language="java">public class Email { + + private String emailAddress; + ... + void sendEmail(final String subject, final String content) {} +}</programlisting> + + <para>Our <code>Email</code> class may of course have more than just + one property. We don't want to email addresses to be database entities + themselves. Instead they are meant to be components of User instances. + This is achieved by:</para> + + <glosslist> + <glossentry> + <glossterm>Annotate class Email to be embeddable:</glossterm> + + <glossdef> + <programlisting language="java">package component.email; + +<emphasis role="bold">@Embeddable</emphasis> public class Email { + + private String emailAddress; + ... +}</programlisting> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>Annotate <code>emailAddress</code> to become an + embedded property:</glossterm> + + <glossdef> + <programlisting language="java">package component.email; + ... +public class User { + + private Email address; + <emphasis role="bold">@Embedded</emphasis> + public Email getEmailAddress() { return address;} + ...</programlisting> + </glossdef> + </glossentry> + </glosslist> + + <para>We may now persist <classname>component.email.User</classname> + instances:</para> + + <programlisting language="java">em.getTransaction().begin(); +{ + final User u = new User(123, "goik", "Martin Goik"); + u.setEmailAddress(new Email("goik@hdm-stuttgart.de")); + em.persist(u); +} +em.getTransaction().commit(); +</programlisting> + + <qandaset defaultlabel="qanda" xml:id="quandaentry_HomeWorAddress"> + <title>Home and work address</title> + + <qandadiv> + <qandaentry> + <question> + <para>Consider the following sketch of an + <classname>Address</classname> class:</para> + + <programlisting language="java">public class Address { + + private String street; + private String city; + private String zipcode; + ... +}</programlisting> + + <para>Extend this example to allow for two properties + <code>homeAddress</code> and <code>workAddress</code> of type + Address. You will encounter a problem concerning conflicting + database attribute names. Resolution is possible using + <code>@javax.persistence.AttributeOverrides</code> + declarations.</para> + </question> + + <answer> + <para>See <classname>component.address.Address</classname> and + <classname>component.address.User</classname>.</para> + </answer> + </qandaentry> + + <qandaentry xml:id="quandaentry_PersistChange"> + <question> + <para>Load a User instance from your database and demonstrate + that changes to this existing persistent object component's + values get persisted.</para> + </question> + + <answer> + <para>See + <classname>component.address.ModifyWorkAddress</classname>.</para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + + <section xml:id="valueSets"> + <title>Sets and lists</title> + + <para>Users may have multiple email addresses:</para> + + <programlisting language="java">public class User { + + Set<Email> email; + ...</programlisting> + + <para>Using embedded values we need an + <code>@ElementCollection</code> declaration to achieve a proper + mapping:</para> + + <programlisting language="java">package component.emails; + ... + public class User { + ... + private Set<Email> emails = new HashSet<Email>(); + <emphasis role="bold">@ElementCollection </emphasis> + public Set<Email> getEmails() { return emails; } + ...</programlisting> + + <para>This will map <classname>component.emails.Email</classname> + entries in a separate table:</para> + + <programlisting language="java">package component.emails; + ... + public class PersistUser { + public static void main(String[] args) { + final Session session = HibernateUtil.createSessionFactory("component/emails/hibernate.cfg.xml").openSession(); + { + final Transaction transaction = session.beginTransaction(); + final User u = new User(123, "goik", "Martin Goik"); + u.getEmails().add(new Email("goik@hdm-stuttgart.de")); + u.getEmails().add(new Email("goma@someserver.org")); + session.save(u); + transaction.commit(); + } ...</programlisting> + + <qandaset defaultlabel="qanda" xml:id="quandaentry_HqlSearchEmail"> + <title>Searching users by email components</title> + + <qandadiv> + <qandaentry> + <question> + <para>Create a <xref linkend="glo_HQL"/> query searching + for:</para> + + <itemizedlist> + <listitem> + <para>All Users having an email address + <email>goma@someserver.org</email>.</para> + </listitem> + + <listitem> + <para>All users having at least two email + addresses.</para> + </listitem> + </itemizedlist> + + <para>You may want to use a Hibernate Console to develop the + appropriate queries.</para> + </question> + </qandaentry> + </qandadiv> + </qandaset> + + <qandaset defaultlabel="qanda" xml:id="quandaentry_EmbeddedPostal"> + <title>Embedded postal addresses</title> + + <qandadiv> + <qandaentry> + <question> + <para>Construct a corresponding example allowing + <classname>session3.User</classname> instances to have a set + of embedded <classname>component.address.Address</classname> + elements.</para> + </question> + </qandaentry> + </qandadiv> + </qandaset> + + <qandaset defaultlabel="qanda" + xml:id="quandaentry_ComponentLifecycleDepend"> + <title>Life cycle of parents and components</title> + + <qandadiv> + <qandaentry> + <question> + <para>What happens, if an owning User instance gets deleted? + Are composed Email or Address values are implicitly being + deleted as well?</para> + </question> + </qandaentry> + </qandadiv> + </qandaset> + + <qandaset defaultlabel="qanda" + xml:id="quandaentry_OrderedComponents"> + <title>Ordered components</title> + + <qandadiv> + <qandaentry> + <question> + <para>Replace <classname>java.util.Set</classname> by + <classname>java.util.List</classname> and assure that the + order of components is being preserved in a <xref + linkend="glo_JPA"/> 2 compliant manner.</para> + </question> + </qandaentry> + </qandadiv> + </qandaset> + </section> + </section> + </section> + + <section xml:id="cache"> + <title>Caching</title> + + <figure xml:id="sda2FigJpaCacheIntro"> + <title><xref linkend="glo_JPA"/> caches.</title> + + <mediaobject> + <imageobject> + <imagedata fileref="Ref/Fig/jpacache.svg"/> + </imageobject> + </mediaobject> + </figure> + + <qandaset defaultlabel="qanda" xml:id="cacheTesting"> + <title>Testing caches</title> + + <qandadiv> + <qandaentry> + <question> + <para>Import the following Hibernate based project:</para> + + <annotation role="make"> + <para role="eclipse">Sda2/Jpa/HibernateCacheDemo</para> + </annotation> + + <orderedlist> + <listitem> + <para>Insert 10000 randomly generated + <classname>Person</classname> entries into your + database.</para> + </listitem> + + <listitem> + <para>Measure time differences for loading 1000 entities in + different sessions.</para> + </listitem> + + <listitem> + <para>Measure time differences for attribute based queries + both with query cache enabled and disabled.</para> + </listitem> + </orderedlist> + </question> + </qandaentry> + </qandadiv> + </qandaset> + </section> + + <section xml:id="orphanRemoval"> + <title>orphanRemoval vs. CascadeType.DELETE</title> + + <para>Describe the difference and construct an example.</para> + </section> + </chapter> +</part> diff --git a/Doc/Sda2/jpa.xml b/Doc/Sda2/jpa.xml deleted file mode 100644 index de3e65b8e8cf27f4e81df0238537fc39c8ab7375..0000000000000000000000000000000000000000 --- a/Doc/Sda2/jpa.xml +++ /dev/null @@ -1,2967 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<chapter version="5.1" xml:id="jpa" xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:xi="http://www.w3.org/2001/XInclude" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:ns="http://docbook.org/ns/transclusion" - xmlns:m="http://www.w3.org/1998/Math/MathML" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:db="http://docbook.org/ns/docbook"> - <title><xref linkend="glo_JPA"/></title> - - <remark>Mapping tools should be used only by someone familiar with - relational technology. O-R mapping is not meant to save developers from - understanding mapping problems or to hide them altogether. It is meant for - those who have an understanding of the issues and know what they need, but - who don't want to have to write thousands of lines of code to deal with a - problem that has already been solved.<xref linkend="bib_Keith09"/>.</remark> - - <section xml:id="configureEclipseMaven"> - <title>Configuring a Maven based Eclipse <xref linkend="glo_Java"/> - project with Hibernate</title> - - <para>We will use Maven for several purposes:</para> - - <figure xml:id="fig_reasonsUsingMaven"> - <title>Reasons for using Maven</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/mavenIntro.fig" scale="65"/> - </imageobject> - </mediaobject> - </figure> - - <para>We do explain the problem of managing transitive dependencies in - projects:</para> - - <figure xml:id="fig_transitiveDependencies"> - <title>Transitive dependencies</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/transitiveDep.fig" scale="65"/> - </imageobject> - </mediaobject> - </figure> - - <section xml:id="sect_createHibernateConfiguration"> - <title>Creating a <xref linkend="glo_JPA"/> configuration</title> - - <para><xref linkend="glo_JPA"/> is intended to provide persistence - services saving transient <xref linkend="glo_Java"/> instances to a - database. For this purpose we have to provide:</para> - - <itemizedlist> - <listitem> - <para>The type of database (Oracle, DB2, <xref - linkend="glo_Soft_Mysql"/>,...)</para> - </listitem> - - <listitem> - <para><xref linkend="glo_JDBC"/> driver class name.</para> - </listitem> - - <listitem> - <para><xref linkend="glo_JDBC"/> connection parameters</para> - - <itemizedlist> - <listitem> - <para>Server name</para> - </listitem> - - <listitem> - <para>port</para> - </listitem> - - <listitem> - <para>user name</para> - </listitem> - - <listitem> - <para>password</para> - </listitem> - </itemizedlist> - </listitem> - - <listitem> - <para>A list of classes to be mapped</para> - </listitem> - - <listitem> - <para>Parameters defining the log level, whether generated SQL code - shall be logged etc.</para> - </listitem> - </itemizedlist> - - <para><xref linkend="glo_JPA"/> offers an XML based configuration syntax - typically residing in - <filename>src/main/resources/META-INF/persistence.xml</filename>. We - show a toy example of a <filename>hibernate.cfg.xml</filename> - configuration file mapping just one class - <classname>hibintro.v1.model.User</classname> to a <xref - linkend="glo_Soft_Mysql"/> database server:</para> - - <figure xml:id="hibernateConfigurationFile"> - <title>A basic <filename>persistence.xml</filename> <xref - linkend="glo_JPA"/> configuration file.</title> - - <programlisting language="xml"><persistence - version="2.1" - xmlns="http://xmlns.jcp.org/xml/ns/persistence" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence - http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> - - <!-- - The <code>persistence.xml</code> file configures at least one persistence unit; - each unit must have a unique name. - --> - <persistence-unit name="entitytemplatePU"> - - <!-- - Hibernate will scan your classpath for mapped classes and add them automatically - to your persistence unit. - <exclude-unlisted-classes>false</exclude-unlisted-classes> - --> - <exclude-unlisted-classes>false</exclude-unlisted-classes> - - <!-- - Disabling the previous option "exclude-unlisted-classes" requires entity classes to - be listed explicitely. You may want to uncomment the following definition. - - <class>my.base.url.Airline</class> - --> - - <!-- - Standard or vendor-specific options can be set as properties on a persistence unit. - Any standard properties have the <code>javax.persistence</code> name prefix, Hibernate's - settings use <code>hibernate</code> - --> - <properties> - <!-- - JDBC database connection parameter - --> - <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> - <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/hdm?useSSL=false"/> - <property name="javax.persistence.jdbc.user" value="hdmuser"/> - <property name="javax.persistence.jdbc.password" value="XYZ"/> - - <!-- - The JPA engine should drop and re-create the SQL schema in the database - automatically when it boots. This is ideal for automated testing, when - you want to work with a clean database for every test run. - --> - <property - name="javax.persistence.schema-generation.database.action" - value="drop-and-create"/> - - <!-- - When printing SQL in logs, let Hibernate format the SQL nicely and generate - comments into the SQL string so we know why Hibernate executed the SQL statement. - --> - <property name="hibernate.format_sql" value="true"/> - <property name="hibernate.use_sql_comments" value="true"/> - - <!-- Enable Hibernate scanning for entity classes and adding them automatically - but not for hbm.xml files. --> - <property name="hibernate.archive.autodetection" value="class"/> - - </properties> - </persistence-unit> - -</persistence></programlisting> - </figure> - - <para>This file may be edited with a simple text editor. Note the - <filename>persistence_2_1.xsd</filename> schema which allows for - validation.</para> - - <para>The Eclipse <productname - xlink:href="https://www.jboss.org/tools">Jboss Tools</productname> - Eclipse plugin provides a configuration editor simplifying this task. - They may be installed on top of Eclipse <link - xlink:href="https://www.jboss.org/tools/download">in several - ways</link>. The following video shows some of its features.</para> - - <mediaobject> - <videoobject> - <videodata fileref="Ref/Video/hibernateConfig.mp4"/> - </videoobject> - </mediaobject> - </section> - </section> - - <section xml:id="sect_hibernateBasics"> - <title>A round trip working with objects</title> - - <para>Hibernate may be regarded as a persistence provider to <xref - linkend="glo_JPA"/>:</para> - - <figure xml:id="jpaPersistProvider"> - <title><xref linkend="glo_JPA"/> persistence provider</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/persistProvider.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>The following class <classname>hibintro.v1.model.User</classname> - will be used as a starting example to be mapped to a database. Notice the - <classname>javax.persistence.Entity</classname> <link - xlink:href="https://docs.oracle.com/javase/tutorial/java/javaOO/annotations.html">annotation</link> - <coref linkend="entityAnnotation"/>:</para> - - <figure xml:id="mappingUserInstances"> - <title>Mapping <classname>hibintro.v1.model.User</classname> instances - to a database.</title> - - <programlisting language="java">package hibintro.v1; - -import javax.persistence.Entity; -import javax.persistence.Id; - -/** - * A simple class intended to be mapped to a database server - * - */ -@Entity <co xml:id="entityAnnotation"/> -public class User { - - String uid; - /** - * @return The user's unique login name e.g. "goik" - */ - @Id - public String getUid() { - return uid; - } - /** - * @param uid See {@link #getUid()}. - */ - public void setUid(String uid) { - this.uid = uid; - } - String cname; - /** - * @return The user's common name e.g. "Martin Goik" - */ - public String getCname() { - return cname; - } - /** - * @param cname See {@link #getCname()}. - */ - public void setCname(String cname) { - this.cname = cname; - } - - /** - * Hibernate/JPA require a default constructor. It has has to be - * implemented if any non-default constructor has been defined - */ - public User() {} - - /** - * @param uid See {@link #getUid()}. - * @param cname See {@link #getCname()}. - */ - public User(String uid, String cname) { - this.uid = uid; - this.cname = cname; - } -}</programlisting> - </figure> - - <para>With respect to <xref linkend="hibernateConfigurationFile"/> we - notice our class <classname>hibintro.v1.model.User</classname> being - referenced:</para> - - <programlisting language="xml"><persistence ... > - - <persistence-unit name="hibintroV1PU"> - <emphasis role="bold"><class><classname>hibintro.v1.User</classname></class></emphasis> - ... - </persistence-unit> - ...</programlisting> - - <para>This line tells Hibernate to actually map - <classname>hibintro.v1.model.User</classname> to the database.</para> - - <section xml:id="persistingObjects"> - <title>Persisting objects</title> - - <para>Persisting transient objects may be achieved in various ways. - <xref linkend="glo_JDBC"/> allows to store and retrieved object - values.</para> - - <para>Having larger projects these tasks become increasingly tedious. It - is thus desired to automate these tasks while still using <trademark - xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> - for the low level transport layer.</para> - - <para>The following sections start with a single class - <classname>hibintro.v1.model.User</classname>:</para> - - <figure xml:id="fig_BasicUser"> - <title>A basic <code>User</code> class.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/classUser.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>Object relational mapping (ORM) denotes the process of mapping - instances of classes to relational table data. In our current example we - may draw a simple implementation sketch:</para> - - <figure xml:id="mappingProperties2attributes"> - <title>Mapping properties to attributes.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/mapUser.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>This is far too simplistic. What about integrity - constraints?</para> - - <figure xml:id="mappingIntegrityConstraints"> - <title>Annotating integrity constraints</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/mapUserIntegrity.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>We start with the following - <classname>hibintro.v1.model.User</classname> class lacking integrity - constraints completely:</para> - - <programlisting language="java">package hibintro.v1.model; - @Entity - public class User { - String uid; - public String getUid() {return uid;} - public void setUid(String uid) {this.uid = uid;} - - String cname; - public String getCname() {return cname;} - public void setCname(String cname) {this.cname = cname;} - - /** - * Hibernate/JPA require a default constructor. It has has to be implemented - * if any non-default constructor has been defined - */ - public User() {} - - /** - * @param uid See {@link #getUid()}. - * @param cname See {@link #getCname()}. - */ - public User(String uid, String cname) { - this.uid = uid; - this.cname = cname; - } - }</programlisting> - - <para>Persisting objects with Hibernate requires a - <classname>org.hibernate.Session</classname> instance <coref - linkend="sessionInstance"/>. It happens between the start <coref - linkend="startTransaction"/> and commit <coref - linkend="commitTransaction"/> of a transaction being derived from that - session:</para> - - <programlisting language="java">package hibintro.v1.run; - - ... - public class PersistSingleUser { - - public static void main(String[] args) { - final <classname>org.hibernate.Session</classname> session <co - xml:id="sessionInstance"/>= HibernateUtil.createSessionFactory("hibernate.cfg.xml").openSession(); - - final <classname>org.hibernate.Transaction</classname> transaction = session.beginTransaction();<co - xml:id="startTransaction"/> - - final <classname>hibintro.v1.model.User</classname> u = new User("goik", "Martin Goik"); - session.save(u); - - transaction.commit(); <co xml:id="commitTransaction"/> - } - }</programlisting> - - <para>Executing the above code yields a runtime exception:</para> - - <screen>Exception in thread "main" java.lang.ExceptionInInitializerError - at myhibernate.intro.run.PersistUser.main(PersistUser.java:14) - Caused by: org.hibernate.AnnotationException: <emphasis role="bold">No identifier specified for entity: myhibernate.intro.model.User</emphasis> - ... - at myhibernate.intro.util.HibernateUtil.buildConfiguration(HibernateUtil.java:17) - at myhibernate.intro.util.HibernateUtil.<clinit>(HibernateUtil.java:9)</screen> - - <para>This runtime error is a little bit cryptic. The missing - <quote>identifier</quote> refers to the absence of a primary key - definition already mentioned in <xref - linkend="mappingIntegrityConstraints"/>. We define a key by annotating - the <code>uid</code> property with a - <classname>javax.persistence.Id</classname> annotation <coref - linkend="primaryKeyDefinition"/>:</para> - - <programlisting language="java">package hibintro.v1.model; - - import javax.persistence.Entity; - <emphasis role="bold">import javax.persistence.Id;</emphasis> - - ... - @Entity public class User {... - <emphasis role="bold">@Id</emphasis> <co xml:id="primaryKeyDefinition"/> - public String getUid() { - return uid; - } ...</programlisting> - - <para>The careful reader will have noticed that we've annotated the - getter method rather than the property <code>uid</code> itself. - Hibernate / <xref linkend="glo_JPA"/> can work both ways. Annotating a - getter however offers additional support e.g. when logging for debugging - purposes is required.</para> - - <para>This time we are successful. Since we enabled the logging of SQL - statements in <xref linkend="hibernateConfigurationFile"/> Hibernate - shows us the corresponding <code>INSERT</code> statement:</para> - - <programlisting language="sql">Hibernate: - insert - into - User - (cname, uid) sky - values - (?, ?)</programlisting> - - <para>Notice the (?,?) part of our log: This indicates the internal - usage of <trademark - xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> - <classname>java.sql.PreparedStatement</classname> instances. Hibernate - generates the following create table statement:</para> - - <figure xml:id="fig_createTableV1User"> - <title>Database schema mapping instances of - <classname>hibintro.v1.model.User</classname>.</title> - - <programlisting language="sql">CREATE TABLE User ( - uid VARCHAR(255) NOT NULL PRIMARY KEY, - cname VARCHAR(255) - ) </programlisting> - </figure> - </section> - - <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 <coref linkend="specLoadType"/> and its primary key value - <coref linkend="specLoadPrimaryKey"/>:</para> - - <figure xml:id="loadByClassAndPrimaryKey"> - <title>Loading a single object by a primary key value.</title> - - <programlisting language="java">package hibintro.v1.run; - ... - public class RetrieveSingleUser { - ... - final Transaction transaction = session.beginTransaction(); - - 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> - </figure> - - <para>This retrieves the expected result. Buried in other log messages - we find the following SQL <quote>background</quote> statement:</para> - - <screen>... - INFO: HHH000232: Schema update complete - Hibernate: - <emphasis role="bold">select - user0_.uid as uid0_0_, - user0_.cname as cname0_0_ - from - User user0_ - where - user0_.uid=?</emphasis> - - Found user 'Martin Goik'</screen> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_MetodNullIfNoexist"> - <title>Choosing the correct method</title> - - <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>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> - - <screen>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"/> - </screen> - - <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>org.hibernate.Session.get(Class,Serializable)</methodname> - method which actually returns <code>null</code> in case a - primary key value does not exist:</para> - - <programlisting language="java">... 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 language="java">package hibintro.v1.run; - ... - public class PersistUsers { - ... - final Transaction transaction = session.beginTransaction(); - - final User users[] = {new User("wings", "Fred Wings"), - new User("eve", "Eve Briggs")} ; - for (final User u : users ) {session.save(u);} - - transaction.commit(); ...</programlisting> - - <para>Now we'd like to retrieve these objects. Hibernate offers the - <emphasis role="bold">H</emphasis>ibernate <emphasis - role="bold">Q</emphasis>uery <emphasis role="bold">L</emphasis>anguage - (<xref linkend="glo_HQL"/>) for object queries. As we will see <xref - linkend="glo_HQL"/> extends <acronym linkend="glo_SQL">SQL</acronym> - with respect to polymorphic queries. The current example does not use - inheritance leaving us with a simple <xref linkend="glo_HQL"/> query - <coref linkend="hqlFromUser"/> in - <classname>hibintro.v1.run.RetrieveAll</classname>:</para> - - <figure xml:id="retrieveAllUserByHql"> - <title>Retrieving <classname>hibintro.v1.model.User</classname> - instances by <abbrev - xlink:href="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>.</title> - - <programlisting language="java">package hibintro.v1.run; - ... - public class RetrieveAll { - ... - final Query searchUsers = session.createQuery("<emphasis role="bold">from User</emphasis>");<co - xml:id="hqlFromUser"/> - final List<User> users = (List<User>) searchUsers.list(); - for (final User u: users) { - System.out.println("uid=" + u.getUid() + ", " + u.getCname()); - }</programlisting> - </figure> - - <para>Being used to <xref linkend="glo_SQL"/>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 <xref - linkend="glo_HQL"/> query returns a set of - <classname>hibintro.v1.model.User</classname> instances:</para> - - <screen>uid=eve, Eve Briggs -uid=goik, Martin Goik -uid=wings, Fred Wings</screen> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_AnalyzeHqlResult"> - <title><xref linkend="glo_HQL"/> and <xref linkend="glo_SQL"/>, - analyzing results.</title> - - <qandadiv> - <qandaentry> - <question> - <para>We may actually retrieve attributes rather than objects. - For this purpose our query actually resembles standard <xref - linkend="glo_SQL"/> <coref linkend="hqlWithSelect"/>:</para> - - <programlisting language="java">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>Class.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 language="java">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="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev> - to offer additional features namely predicate based queries. Following - <classname>hibintro.v1.run.SelectUser</classname> we may restrict our - result set by an <xref linkend="glo_SQL"/> style <code>WHERE</code> - clause:</para> - - <programlisting language="java"> final List<User> users = (List<User>) session.createQuery( - "<emphasis role="bold">from User u where u.cname like '%e%'</emphasis>").list(); - for (final User u: users) { - System.out.println("Found user '" + u.getCname() + "'"); - }</programlisting> - - <para>This time we receive a true subset of - <classname>hibintro.v1.model.User</classname> instances:</para> - - <screen>Found user 'Eve Briggs' - Found user 'Fred Wings'</screen> - </section> - - <section xml:id="criteriaBasedQueries"> - <title>Criteria based queries</title> - - <para>Selecting Objects by <abbrev - xlink:href="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>c - queries technically means parsing <abbrev - xlink:href="https://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"> - <title>Mapping single entities and database tables</title> - - <section xml:id="transientProperties"> - <title>Transient properties</title> - - <para>We take a closer look at <xref linkend="mappingUserInstances"/> - assuming that Instances of <classname>hibintro.v1.model.User</classname> - need an additional <emphasis role="bold">GUI related</emphasis> property - <code>selected</code> <coref linkend="propertyIsSelected"/>:</para> - - <programlisting language="java">package hibintro.v2; - -import javax.persistence.Entity; -import javax.persistence.Id; - -/** - * A simple class intended to be mapped to a database server - */ -@Entity -public class User { - - String uid; - /** - * @return The user's unique login name e.g. "goik" - */ - @Id - public String getUid() { - return uid; - } - /** - * @param uid See {@link #getUid()}. - */ - public void setUid(String uid) { - this.uid = uid; - } - String cname; - /** - * @return The user's common name e.g. "Martin Goik" - */ - public String getCname() { - return cname; - } - /** - * @param cname See {@link #getCname()}. - */ - public void setCname(String cname) { - this.cname = cname; - } - - boolean selected = false; <co xml:id="propertyIsSelected"/> - - public boolean isSelected() { - return selected; - } - public void setSelected(boolean selected) { - this.selected = selected; - } - protected User() {} - - /** - * @param uid See {@link #getUid()}. - * @param cname See {@link #getCname()}. - */ - public User(String uid, String cname) { - this.uid = uid; - this.cname = cname; - } -}</programlisting> - - <para>Hibernates produces the following <abbrev - xlink:href="https://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev> - statements containing an attribute <code>selected</code> <coref - linkend="attributeSelected"/>:</para> - - <programlisting language="sql">CREATE TABLE User ( - uid VARCHAR(255) NOT NULL PRIMARY KEY, - cname VARCHAR(255), - <emphasis role="bold">selected</emphasis> <co xml:id="attributeSelected"/> BIT NOT NULL, -) </programlisting> - - <para>If we just annotate a Java class with an - <classname>javax.persistence.Entity</classname> Annotation all - properties of the class in question will be mapped. A <xref - linkend="glo_JPA"/> framework of course cannot distinguish between - transient and persistent properties. If we want a property to be - transient we have to add a <classname - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Transient.html">javax.persistence.Transient</classname> - annotation:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package hibintro.v3; - -@Entity -public class User { - ... - boolean selected = false; - @Transient <co xml:id="transientAnnotation"/> - public boolean isSelected() { return selected; } - public void setSelected(boolean selected) { this.selected = selected; } - ... -}</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">SQL</emphasis></td> - - <td><programlisting language="sql">CREATE TABLE User ( - uid VARCHAR(255) NOT NULL PRIMARY KEY, - cname VARCHAR(255) -) </programlisting></td> - </tr> - </informaltable> - - <para>The <classname - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Transient.html">javax.persistence.Transient</classname> - annotation inhibits the mapping of our property - <code>selected</code>.</para> - - <caution> - <para>When loading a <classname>hibintro.v3.User</classname> instance - from a database all transient property values are of course entirely - determined by the default constructor.</para> - </caution> - </section> - - <section xml:id="sect_mappingNullValues"> - <title>Properties and NULL values</title> - - <para>In <xref linkend="fig_createTableV1User"/> the primary key - <code>uid</code> property's value must not be <code>NULL</code>. This is - an immediate consequence of the - <classname>javax.persistence.Id</classname> annotation and the fact that - databases don't allow NULL values for key attributes.</para> - - <para>The <code>cname</code> property however may be null. Sometimes we - want to ensure the corresponding database attributes to be set, at least - carrying an empty string value. This can be achieved by adding a - <classname - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html">javax.persistence.Column</classname> - <option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html#nullable--">(nullable - = false)</option> annotation:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package hibintro.v4; - ... -@Entity public class User { - ... - String cname; - <emphasis role="bold"><classname - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html">@Column</classname>(<option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html#nullable--">nullable</option> = false)</emphasis> public String getCname() { - return cname; - } - ...</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">SQL</emphasis></td> - - <td><programlisting language="sql">CREATE TABLE User ( - uid VARCHAR(255) NOT NULL PRIMARY KEY, - cname VARCHAR(255) <emphasis role="bold">NOT NULL</emphasis> <co - xml:id="cnameDatabaseNotNull"/> -)</programlisting></td> - </tr> - </informaltable> - - <para>This results in a corresponding database constraint <coref - linkend="cnameDatabaseNotNull"/>. Attempts to store instances with null - values now fail:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package hibintro.v4; - ... - public class PersistSingleUser { - - em.getTransaction().begin(); - { - em.persist(new User("goik", "Martin Goik")); - } - em.getTransaction().commit();</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Log</emphasis></td> - - <td><screen>Exception in thread "main" javax.persistence.PersistenceException: - org.hibernate.PropertyValueException: <emphasis role="bold">not-null property references a null or transient value</emphasis> : -hibintro.v4.User.cname - at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692) - ... - at hibintro.v4.PersistSingleUser.main(PersistSingleUser.java:22)</screen></td> - </tr> - </informaltable> - - <para>The database constraint violation causes the <trademark - xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> - driver to throw this exception.</para> - </section> - - <section xml:id="mappingKeys"> - <title>Defining keys</title> - - <para>Frequently we need more than just a primary key. Starting from - <classname>hibintro.v4.User</classname> we may want to add a property - <code>uidNumber</code> adding a common requirement: On UNIX type - operation systems each user does have both a unique login name (like - <quote>goik</quote>) and a unique numerical value (like - <quote>123</quote>). We choose our primary key to be numeric <coref - linkend="uidNumberIsPrimaryKey"/>and the login name to become a second - candidate key <coref linkend="uidIsUnique"/>:</para> - - <programlisting language="java">package hibintro.v5; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - -@Entity -@Table(uniqueConstraints={<classname - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/UniqueConstraint.html">@UniqueConstraint</classname>(<option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/UniqueConstraint.html#columnNames--">columnNames</option>={"uid"})}) <co - xml:id="uidIsUnique"/> -public class User { - - int uidNumber; - @Id <co xml:id="uidNumberIsPrimaryKey"/> - public int getUidNumber() { return uidNumber; } - public void setUidNumber(int uidNumber) { this.uidNumber = uidNumber; } - - String uid; - - /** - * @return The user's unique login name e.g. "goik" - */ - @Column(nullable=false) - public String getUid() { return uid; } - - /** - * @param uid, See {@link #getUid()}. - */ - public void setUid(String uid) { this.uid = uid; } - - ... -}</programlisting> - - <para>Notice the slight difference: The property <code>uid</code> may - need a - <code>@</code><code><classname>javax.persistence.Column</classname>(nullable=false)</code> - annotation to become a candidate key. This is <emphasis>not</emphasis> - automatically inferred by the - <classname>javax.persistence.UniqueConstraint</classname> definition - <coref linkend="uidIsUnique"/>. In contrast the property - <code>uidNumber</code> is not being referenced by the preceding - <classname>javax.persistence.Table</classname> annotation but annotated - by <classname>javax.persistence.Id</classname>. Hence a - <code>nullable=false</code> is not needed.</para> - - <para>This is in accordance with <abbrev - xlink:href="https://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev>: - Attributes composing a primary key must not allow <code>NULL</code> - values but attributes only appearing in UNIQUE declarations may become - <code>NULL</code>.</para> - - <para>The resulting <abbrev - xlink:href="https://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev> - reads:</para> - - <programlisting language="sql">CREATE TABLE User ( - uidNumber INT NOT NULL PRIMARY KEY, - cname VARCHAR(255) NOT NULL, - uid VARCHAR(255) NOT NULL UNIQUE -)</programlisting> - </section> - - <section xml:id="sect_ComposedKeys"> - <title>Composed keys</title> - - <para>Composed candidate keys are often business keys. The underlying - logic defines which objects are considered to be identical based on - their values.</para> - - <para>As an example, we consider a company having several departments. - Regarding projects he following business rules shall apply:</para> - - <figure xml:id="projectBusinessRules"> - <title>Business rules for projects</title> - - <orderedlist> - <listitem> - <para>Each department must have a unique name.</para> - </listitem> - - <listitem> - <para>A project's name must be unique within the set of all - projects belonging to the same department.</para> - </listitem> - - <listitem> - <para>A project must be assigned to exactly one department.</para> - </listitem> - </orderedlist> - </figure> - - <para>Right now we defer considerations of the n:1 relationship between - departments and projects to a later chapter. Instead we focus just on - project instances and represent departments just by their integer id - values which will later become foreign keys.</para> - - <para>In addition each project receives a unique integer id value as - well. This is in accordance with the <quote>best practice</quote> rule - of defining a <link - xlink:href="https://en.wikipedia.org/wiki/Surrogate_key">surrogate - key</link> <coref linkend="projectPrimaryKeyDefinition"/> to be used as - (primary) object identifier. This immutable key will then become the - target in foreign key definitions:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package hibintro.v6; - ... -@Entity -@Table(uniqueConstraints={<classname - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/UniqueConstraint.html">@UniqueConstraint</classname>(columnNames={"name", "department"})}) <co - xml:id="projectBusinessKey"/> -public class Project { - - int id; - @Id <co xml:id="projectPrimaryKeyDefinition"/> - public int getId() { return id; } - protected void setId(int id) { this.id = id; } - - String name; - @Column(nullable=false) - public String getName() { return name; } - public void setName(String name) { this.name = name; } - - int department; - @Column(nullable=false) - public int getDepartment() { return department; } - public void setDepartment(int department) { this.department = department; } - ... -}</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting language="sql">CREATE TABLE Project ( - id int(11) NOT NULL PRIMARY KEY <coref linkend="projectPrimaryKeyDefinition"/>, - department int(11) NOT NULL, - name varchar(255) NOT NULL, - UNIQUE KEY name (name,department) <coref linkend="projectBusinessKey"/> -)</programlisting></td> - </tr> - </informaltable> - - <calloutlist> - <callout arearefs="projectPrimaryKeyDefinition"> - <para>Defining the surrogate primary key.</para> - </callout> - - <callout arearefs="projectBusinessKey"> - <para>Defining a business key composed of a project's - <code>name</code> and <code>department</code> number. This - implements our second business rule in <xref - linkend="projectBusinessRules"/>.</para> - </callout> - </calloutlist> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_IdGetterProtected"> - <title><xref linkend="glo_JPA"/> <code language="java">id</code> - getter visibility.</title> - - <qandadiv> - <qandaentry> - <question> - <para>The setter void <methodname - annotations="nojavadoc">setId(int)</methodname>in - <classname>hibintro.v6.Project</classname> has protected write - access. Explain this choice.</para> - </question> - - <answer> - <para>From an application developer's point of view the setter - should be absent: The <code>id</code> property is immutable and - should not be accessed at all.</para> - - <para>When loading an instance from a database a persistence - provider however has to set its value. Hibernate uses the - reflection-API to override the restriction being imposed by the - <code>protected</code> modifier. So why not declare it private? - Doing so may cause our IDE to flag a warning about an unused - private method.</para> - - <para>So choosing <code>protected</code> is a compromise: An - application developer cannot modify the property (unless - deriving a class) and our persistence provider can still set its - value to the database's primary key attribute value.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="nonUniqueIndexes"> - <title>Indexes (non-unique)</title> - - <para>From the viewpoint of software modelling non-unique indexes are - not part of the business logic but refer to database optimization. - Consequently <xref linkend="glo_JPA"/> has no support for non-unique - indexes.</para> - - <para>On the other hand performance matters. Hibernate and other - persistence providers offer vendor specific <xref linkend="glo_JPA"/> - extensions. We may find it useful to access - <classname>hibintro.v5.User</classname> instances having a specific - <code>cname</code> quickly. This can be achieved by adding a Hibernate - specific - <code>org.hibernate.annotations.</code><classname>org.hibernate.annotations.Table</classname> - index generating annotation <coref linkend="hibernateExtensionIndex"/> - which works on top of <xref linkend="glo_JPA"/>'s - <code>javax.persistence.</code><classname>javax.persistence.Table</classname>:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package hibintro.v7; -... -@Entity -@Table(uniqueConstraints={@UniqueConstraint(name="uidKey", columnNames={"uid"})}, - <emphasis role="bold">indexes = {<classname - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html">@Index</classname>(<option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html#name--">name</option> = "byCname", <option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html#columnList--">columnList</option>="cname", <option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html#unique--">unique</option> = false)}</emphasis> <co - xml:id="hibernateExtensionIndex"/> -) -public class User { - - int uidNumber; - @Id - public int getUidNumber() { return uidNumber; } - public void setUidNumber(int uidNumber) { this.uidNumber = uidNumber; } - - String uid; - /** - * @return The user's unique login name e.g. "goik" - */ - @Column(nullable=false) - public String getUid() { return uid; } - - /** - * @param uid See {@link #getUid()}. - */ - public void setUid(String uid) { this.uid = uid; } - ... -}</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting language="sql">CREATE TABLE User ( - uidNumber INT NOT NULL PRIMARY KEY, - cname VARCHAR(255) NOT NULL, - uid VARCHAR(255) NOT NULL UNIQUE -); - -CREATE INDEX findCname ON User (cname ASC);</programlisting></td> - </tr> - </informaltable> - </section> - - <section xml:id="sect_RenameTablesAndAttributes"> - <title>Renaming tables and attributes</title> - - <para>So far we assumed that we map classes to database tables having - identical names: A <xref linkend="glo_Java"/> class <code>User</code> is - being mapped to a relational table with identical name - <code>User</code>. Sometimes a renaming is desired. We may for example - want to access a legacy database by a newly implemented <xref - linkend="glo_Java"/> application. Choosing meaningful names may conflict - with decisions being taken when the original database design took - place.</para> - - <para>In the following example we change the database tables name from - its default User to Person <coref linkend="renameUserToPerson"/>. The - properties <code>uidNummbe</code>r and <code>cname</code> are changed to - attribute names <code>numericUid</code> <coref - linkend="renameUidNumberToNumericUid"/>and <code>fullName</code> <coref - linkend="renameCnameToFullName"/> respectively:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package hibintro.v8; - ... -@Entity -<classname xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Table.html">@Table</classname>(<option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Table.html#name--">name</option>="Person") <co - xml:id="renameUserToPerson"/> -public class User { - - int uidNumber; - @Id - <classname xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html">@Column</classname>(<option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html#name--">name</option>="numericUid") <co - xml:id="renameUidNumberToNumericUid"/> - public int getUidNumber() {return uidNumber;} - public void setUidNumber(int uidNumber) {this.uidNumber = uidNumber;} - - String uid; - @Column(nullable=false) - public String getUid() {return uid;} - public void setUid(String uid) {this.uid = uid;} - - String cname; - <classname xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html">@Column</classname>(<option - xlink:href="http://docs.oracle.com/javaee/7/api/javax/persistence/Column.html#name--">name</option>="fullName", nullable = false) <co - xml:id="renameCnameToFullName"/> - public String getCname() {return cname;} - public void setCname(String cname) {this.cname = cname;} - ...</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting language="sql">CREATE TABLE Person <coref - linkend="renameUserToPerson"/> ( - numericUid <coref linkend="renameUidNumberToNumericUid"/> int(11) NOT NULL PRIMARY KEY, - fullName <coref linkend="renameCnameToFullName"/> varchar(255) NOT NULL, - uid varchar(255) NOT NULL -)</programlisting></td> - </tr> - </informaltable> - </section> - - <section xml:id="sectChangeDefaultTypeMapping"> - <title>Changing the default type mapping</title> - - <para>Sometimes we are interested in changing <xref - linkend="glo_JPA"/>'s default type mapping strategy. For example - <trademark - xlink:href="https://www.mysql.com/about/legal/trademark.html">Mysql</trademark> - versions prior to 5.0 lack an appropriate type representing boolean - values. It was therefore quite common mapping boolean properties to - <code>CHAR(1)</code> with possible values being <code>'Y'</code> and - <code>'N'</code>. Hibernate will map boolean values to - <code>tinyint(1)</code>. Supporting older software may require to tweak - the standard mapping.</para> - - <para>Unfortunately <xref linkend="glo_JPA"/> itself does not offer any - interface for this purpose. The persistence provider may offer a - solution though. Hibernate for example allows to remap <coref - linkend="remapBooleanChar"/> types . We assume our - <classname>hibintro.v9.User</classname> class to have a - <code>boolean</code> property <code>active</code>:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package hibintro.v9; - -import org.hibernate.type.Type; - ... -public class User { - ... - public void setCname(String cname) {this.cname = cname;} - - boolean active = false; - @Type(type="yes_no") <co xml:id="remapBooleanChar"/> - public boolean isActive() {return active;} - public void setActive(boolean active) {this.active = active;} -}</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting language="sql">CREATE TABLE User ( - uidNumber int(11) NOT NULL PRIMARY KEY, - active char(1) NOT NULL, - cname varchar(255) DEFAULT NULL, - uid varchar(255) NOT NULL -)</programlisting></td> - </tr> - </informaltable> - - <para>Readers being interested in more sophisticated strategies like - mapping user defined data types to database types are advised to read - the <link - xlink:href="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch05.html#mapping-types">manual - section on Hibernate types</link>.</para> - </section> - - <section xml:id="sda2SectObjectStates"> - <title>Object states and transitions</title> - - <figure xml:id="sda2FigObjectStates"> - <title>Persistence related object states</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/persistentStates.fig"/> - </imageobject> - </mediaobject> - </figure> - </section> - - <section xml:id="sectionXmlJpaJaxb"> - <title><xref linkend="glo_XML"/> to <xref linkend="glo_JPA"/> using - <xref linkend="glo_JAXB"/></title> - - <para>Consider the following <xref linkend="glo_XML"/> dataset - example:</para> - - <programlisting language="xml"><catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="cd.xsd"> -<cd id="12"> - <title>Empire Burlesque</title> - <artist>Bob Dylan</artist> - <country>USA</country> - <company>Columbia</company> - <price>10.90</price> - <year>1985</year> -</cd> -<cd id="34"> - <title>Hide your heart</title> - <artist>Bonnie Tylor</artist> - <country>UK</country> - <company>CBS Records</company> - <price>9.90</price> - <year>1988</year> -</cd> - ...</programlisting> - - <section xml:id="sectionXmlJpaJaxbSchema"> - <title>Supplying a schema</title> - - <qandaset defaultlabel="qanda" xml:id="qandaXmlJpaJaxbSchema"> - <title>Integrity constraints</title> - - <qandadiv> - <qandaentry> - <question> - <para>Create a suitable XML schema enforcing the following - integrity constraints:</para> - - <itemizedlist> - <listitem> - <para>All elements are required.</para> - </listitem> - - <listitem> - <para>The element <tag class="starttag">price</tag> is of - decimal type having two fractional digits.</para> - </listitem> - - <listitem> - <para>The elements <tag class="starttag">year</tag> and - <code>@id</code> are of integer type.</para> - </listitem> - - <listitem> - <para>The attribute <code>@id</code> defines a key.</para> - </listitem> - </itemizedlist> - </question> - - <answer> - <programlisting language="xml"><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> - - <xs:simpleType name="money"> - <xs:restriction base="xs:decimal"> - <xs:fractionDigits value="2"/> - </xs:restriction> - </xs:simpleType> - - <xs:element name="catalog"> - <xs:complexType> - <xs:sequence> - <xs:element ref="cd" minOccurs="0" maxOccurs="unbounded"/> - </xs:sequence> - </xs:complexType> - <xs:key name="uniqueId"> - <xs:selector xpath="cd"/> - <xs:field xpath="@id"/> - </xs:key> - </xs:element> - - <xs:element name="cd"> - <xs:complexType> - <xs:sequence> - <xs:element name="title" type="xs:string"/> - <xs:element name="artist" type="xs:string"/> - <xs:element name="country" type="xs:string"/> - <xs:element name="company" type="xs:string"/> - <xs:element name="price" type="money"/> - <xs:element name="year" type="xs:int"/> - </xs:sequence> - <xs:attribute name="id" type="xs:int" use="required"/> - </xs:complexType> - </xs:element> - - </xs:schema></programlisting> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="sectionXmlJpaJaxbImport"> - <title>Importing <xref linkend="glo_XML"/> data to a RDBMS</title> - - <qandaset defaultlabel="qanda" xml:id="qandaXmlJpaJaxbImport"> - <title>Reading XML, write using <xref linkend="glo_JPA"/></title> - - <qandadiv> - <qandaentry> - <question> - <para>Read the given XML data example using <xref - linkend="glo_JAXB"/> and persist the corresponding objects to - a RDBMS. This requires the following steps:</para> - - <itemizedlist> - <listitem> - <para>Use the <xref linkend="glo_JAXB"/> schema compiler - <command - xlink:href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/xjc.html"><xref - linkend="glo_xjc"/></command> accordingly to create domain - classes corresponding to <tag - class="starttag">catalog</tag> and <tag - class="starttag">cd</tag> .</para> - </listitem> - - <listitem> - <para>Create a <classname - xlink:href="https://docs.oracle.com/javase/9/docs/api/javax/xml/bind/JAXBContext.html">JAXBContext</classname> - and an <classname - xlink:href="https://docs.oracle.com/javase/9/docs/api/javax/xml/bind/Unmarshaller.html">Unmarshaller</classname> - instance to parse valid <tag - class="starttag">catalog</tag> instances - accordingly.</para> - </listitem> - - <listitem> - <para>Supply <xref linkend="glo_JPA"/> annotations to the - <command - xlink:href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/xjc.html"><xref - linkend="glo_xjc"/></command> generated - <classname>de.hdm_stuttgart.mi.sda2.jpa.cd.domain.Catalog</classname> - class.</para> - </listitem> - </itemizedlist> - - <para>Hint: The Maven archetype <link - xlink:href="https://maven-repository.com/artifact/com.github.lalyos/standalone-jpa-eclipselink-archetype">standalone-jpa-eclipselink-archetype</link> - may help getting started with <xref linkend="glo_JPA"/> - configuration issues. You may want to add <link - xlink:href="https://repo1.maven.org/maven2/archetype-catalog.xml">http://repo1.maven.org/maven2/archetype-catalog.xml</link> - to your list of archetype repositories.</para> - </question> - - <answer> - <annotation role="make"> - <para role="eclipse">Sda2/Jpa/Cd</para> - </annotation> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> - </section> - - <section xml:id="jpaInheritance"> - <title>Inheritance</title> - - <figure xml:id="sda2FigMapInheritIntro"> - <title>Introducing inheritance mapping</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/mapInherit.svg"/> - </imageobject> - </mediaobject> - </figure> - - <para>Mapping inheritance hierarchies to relational databases means - bridging the gap between object <link - xlink:href="https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch">oriented - and relational models</link>. We start with a slightly modified example - from <xref linkend="bib_Bauer15"/>:</para> - - <figure xml:id="fig_BillingDetails"> - <title>Modelling payment.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/billing.fig"/> - </imageobject> - - <caption> - <para>Simplified Billing details example derived from <xref - linkend="bib_Bauer15"/>. Notice - <classname>inherit.v1.BillingDetails</classname> being an abstract - parent class of two concrete classes - <classname>inherit.v1.CreditCard</classname> and - <classname>inherit.v1.BankAccount</classname>. The attribute - <code>number</code> applies both to bank account and credit card - payments.</para> - </caption> - </mediaobject> - </figure> - - <para>Since the relational model lacks inheritance completely we have to - implement a database schema ourselves. We subsequently explore three main - approaches each of which having its own advantages and - disadvantages.</para> - - <section xml:id="sect_InheritTablePerClassHierarchie"> - <title>Single table per class hierarchy</title> - - <para>This approach may be considered the most simple: We just create - one database table for storing instances of arbitrary classes belonging - to the inheritance hierarchy in question:</para> - - <figure xml:id="fig_TablePerClassHierarchyData"> - <title>A single relation mapping.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/billingData.fig"/> - </imageobject> - - <caption> - <para>Fitting both <classname>inherit.v1.CreditCard</classname> - and <classname>inherit.v1.BankAccount</classname> instances into a - single relation.</para> - </caption> - </mediaobject> - </figure> - - <para>The relation may be created by the following <abbrev - xlink:href="https://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev>:</para> - - <figure xml:id="fig_TablePerClassHierarchyMapping"> - <title>Mapping the inheritance hierarchy.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/billingSql.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>We take a closer look at the generated relation. Since</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package inherit.v1; - ... - @Entity - @Inheritance(strategy=InheritanceType.<emphasis role="bold">SINGLE_TABLE</emphasis>) <co - linkends="billingMapSingleTableCallout" - xml:id="billingMapSingleTable"/> - @DiscriminatorColumn(name="dataType", discriminatorType=DiscriminatorType.STRING) <co - linkends="billingMapSingleTableDiscriminatorCallout" - xml:id="billingMapSingleTableDiscriminator"/> - abstract class BillingDetails { - @Id @GeneratedValue <co - linkends="billingMapSingleTableIdGeneratedCallout" - xml:id="billingMapSingleTableIdGenerated"/> public Long getId() ... - @Column(nullable = false, length = 32)public final String getNumber() ... - @Temporal(TemporalType.TIMESTAMP) - @Column(nullable = false) public Date getCreated() ...</programlisting> - <programlisting language="java">package inherit.v1; - ... - @Entity - @DiscriminatorValue(value = "Credit card" <co - xml:id="billingMapSingleTableDiscriminatorCredit"/>) - public class CreditCard extends BillingDetails { - ... //Nothing JPA related happens here</programlisting> <programlisting - language="java">package inherit.v1; - ... - @Entity - @DiscriminatorValue(value = "Bank account" <co - xml:id="billingMapSingleTableDiscriminatorBank"/>) - public class BankAccount extends BillingDetails { - ... //Nothing JPA related happens here</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting continuation="continues" language="sql">CREATE TABLE BillingDetails <co - linkends="billingMapSingleTableCallout" - xml:id="BillingDetailsGeneratedRelationName"/> ( - dataType varchar(31) NOT NULL, - id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, - number varchar(255) NOT NULL, <co - linkends="billingMapSingleTableBaseNotNull" - xml:id="billingMapSingleTableCalloutNumberNotNull"/> - created datetime NOT NULL, <co - linkends="billingMapSingleTableBaseNotNull" - xml:id="billingMapSingleTableCalloutCreatedNotNull"/> - cardType int(11) DEFAULT NULL, <co - linkends="billingMapSingleTableDerivedNull" - xml:id="billingMapSingleTableCardTypeNull"/> - expiration datetime DEFAULT NULL, <co - linkends="billingMapSingleTableDerivedNull" - xml:id="billingMapSingleTableExpirationNull"/> - bankName varchar(255) DEFAULT NULL, <co - linkends="billingMapSingleTableDerivedNull" - xml:id="billingMapSingleTableBankNameNull"/> - swiftcode varchar(255) DEFAULT NULL <co - linkends="billingMapSingleTableDerivedNull" - xml:id="billingMapSingleTableSwiftCodeNull"/> - )</programlisting></td> - </tr> - </informaltable> - - <calloutlist> - <callout arearefs="billingMapSingleTable" - xml:id="billingMapSingleTableCallout"> - <para>All classes of the inheritance hierarchy will be mapped to a - single table. Unless stated otherwise the <xref linkend="glo_JPA"/> - provider will choose the root class' name - (<code>BillingDetails</code>) as default value for the generated - relation's name <coref - linkend="BillingDetailsGeneratedRelationName"/>.</para> - </callout> - - <callout arearefs="billingMapSingleTableDiscriminator" - xml:id="billingMapSingleTableDiscriminatorCallout"> - <para>The <xref linkend="glo_JPA"/> provider needs a column to - distinguish the different types of database objects. We've chosen - the discriminator attribute <code>dataType</code> values to be - simple strings. Due to the definitions in <coref - linkend="billingMapSingleTableDiscriminatorCredit"/> and <coref - linkend="billingMapSingleTableDiscriminatorBank"/> database object - types are being identified by either of the two values:</para> - - <itemizedlist> - <listitem> - <para><code>Credit card</code>: object will be mapped to - <classname>inherit.v1.CreditCard</classname>.</para> - </listitem> - - <listitem> - <para><code>Bank account</code>: object will be mapped to - <classname>inherit.v1.BankAccount</classname>.</para> - </listitem> - </itemizedlist> - - <para>In a productive system the - <classname>javax.persistence.DiscriminatorType</classname> setting - will typically favour - <classname>javax.persistence.DiscriminatorType</classname><code>.INTEGER</code> - over - <classname>javax.persistence.DiscriminatorType</classname><code>.STRING</code> - unless the application in question has to deal with a legacy - database schema.</para> - </callout> - - <callout arearefs="billingMapSingleTableIdGenerated" - xml:id="billingMapSingleTableIdGeneratedCallout"> - <para>This one is unrelated to inheritance: Our primary key values - will be auto generated by the database server e.g. by - <code>SEQUENCE</code> or <code>IDENTITY</code> mechanisms if - available.</para> - </callout> - - <callout arearefs="billingMapSingleTableCalloutNumberNotNull billingMapSingleTableCalloutCreatedNotNull" - xml:id="billingMapSingleTableBaseNotNull"> - <para>Only the base class' attributes may exclude <code>NULL</code> - values.</para> - </callout> - - <callout arearefs="billingMapSingleTableCardTypeNull billingMapSingleTableExpirationNull billingMapSingleTableBankNameNull billingMapSingleTableSwiftCodeNull" - xml:id="billingMapSingleTableDerivedNull"> - <para>All derived classes' attributes must allow <code>NULL</code> - values.</para> - </callout> - </calloutlist> - - <para>We may now insert instances of - <classname>inherit.v1.BankAccount</classname> or - <classname>inherit.v1.CreditCard</classname>:</para> - - <figure xml:id="insertCreditBank"> - <title>Inserting payment information</title> - - <programlisting language="java">package inherit.v1; - ... - public class Persist { - public static void main(String[] args) throws ParseException { - ... final Transaction transaction = session.beginTransaction(); - { - final CreditCard creditCard = new CreditCard("4412 8334 4512 9416", 1, "05/18/15"); - session.save(creditCard); - - final BankAccount bankAccount = new BankAccount("1107 2 31", "Lehman Brothers", "BARCGB22"); - session.save(bankAccount); - } - transaction.commit(); ...</programlisting> - </figure> - - <section xml:id="sect_InheritTablePerClassHierarchieLoad"> - <title>Database object retrieval</title> - - <para>As in <xref linkend="retrieveAllUserByHql"/> objects being - stored by <xref linkend="insertCreditBank"/> may be queried using - <abbrev - xlink:href="https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch16.html">HQL</abbrev>.</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package inherit.v1; - ... - public class RetrieveCredit { - public static void main(String[] args) { - ... - final Transaction transaction = session.beginTransaction(); - - final Query searchCreditPayments = session.createQuery("<emphasis - role="bold">from inherit.v1.CreditCard</emphasis>"); <co - xml:id="hqlQueryCreditCard"/> - final List<CreditCard> creditCardList = (List<CreditCard>) searchCreditPayments.list(); - for (final CreditCard c: creditCardList) { - System.out.println(c); - } ...</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting continuation="continues" language="sql">INFO: HHH000232: Schema update complete - Hibernate: - select - creditcard0_.id as id0_, - creditcard0_.created as created0_, - creditcard0_.number as number0_, - creditcard0_.cardType as cardType0_, - creditcard0_.expiration as expiration0_ - from - BillingDetails creditcard0_ - where - creditcard0_.<emphasis role="bold">dataType</emphasis> <co - xml:id="hqlQueryCreditCard_dataType"/>='<emphasis - role="bold">Credit card</emphasis>' - - <emphasis role="bold">CreditCard: number=4412 8334 4512 9416, created 2013-02-19 13:09:22.0, - cardType=1, expiration=2015-05-18 00:00:00.</emphasis> <co - xml:id="hqlQueryCreditCardResultSet"/></programlisting></td> - </tr> - </informaltable> - - <para>Some Remarks: Our query asks for instances of - <classname>inherit.v2.CreditCard</classname> <coref - linkend="hqlQueryCreditCard"/>. This gets implemented as an <xref - linkend="glo_SQL"/> <code>SELECT</code> choosing datasets whose - discriminator attribute <code>value of dataType</code> <coref - linkend="hqlQueryCreditCard_dataType"/> equals <quote><code>Credit - card</code></quote>. The current result set contains just one element - <coref linkend="hqlQueryCreditCardResultSet"/> in accordance with - <xref linkend="insertCreditBank"/>.</para> - - <para>Retrieving both <classname>inherit.v1.CreditCard</classname> and - <classname>inherit.v1.BankAccount</classname> instances is - accomplished by querying for the common base class - <classname>inherit.v1.BillingDetails</classname>:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package inherit.v1; - ... - public class RetrieveAll { - ... - final Query searchBilling = session.createQuery("from <emphasis - role="bold">inherit.v1.BillingDetails</emphasis>"); - @SuppressWarnings("unchecked") - final List<BillingDetails> billingDetailsList = (List<BillingDetails>) searchBilling.list(); - for (final BillingDetails c: billingDetailsList) { - System.out.println(c); - } ...</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting continuation="continues" language="sql">INFO: HHH000232: Schema update complete - Hibernate: - select - billingdet0_.id as id0_, - ... - billingdet0_.dataType as dataType0_ - from - BillingDetails billingdet0_ - - CreditCard: number=4412 8334 4512 9416, created 2013-02-19 13:09:22.0, <co - xml:id="resultSetHeterogeneous"/> - cardType=1, expiration=2015-05-18 00:00:00.0 - BankAccount: number=1107 2 31, created 2013-02-19 13:09:22.0, - bankName=Lehman Brothers, swiftcode=BARCGB22</programlisting></td> - </tr> - </informaltable> - - <para>This is the first example of a polymorphic query yielding a - heterogeneous result set<coref - linkend="resultSetHeterogeneous"/>.</para> - </section> - - <section xml:id="sect_InheritTablePerClassHierarchieNullProblem"> - <title>Null values</title> - - <para>Our current mapping strategy limits our means to specify data - integrity constraints. It is no longer possible to disallow - <code>null</code> values for properties belonging to derived classes. - We might want to disallow <code>null</code> values in the - <code>bankName</code> property. Hibernate will generate a - corresponding database attribute <coref - linkend="require_bankNameNotNullDb"/>:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package inherit.v2; - ... - @Entity @DiscriminatorValue(value = "Bank account") - public class BankAccount extends BillingDetails { - String bankName; - @Column(<emphasis role="bold">nullable=false</emphasis>) <co - xml:id="require_bankNameNotNull"/> - public String getBankName() {return bankName;} ...</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting continuation="continues" language="sql">CREATE TABLE BillingDetails ( - id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, - bankName varchar(255) <emphasis role="bold">NOT NULL</emphasis>, <co - xml:id="require_bankNameNotNullDb"/> - ...</programlisting></td> - </tr> - </informaltable> - - <para>Looks good? Unfortunately the attempt to save a bank account - <coref linkend="saveBankAccount"/> yields a runtime exception <coref - linkend="saveBankAccountException"/>:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package inherit.v2; - ... - public class Persist { - ... - final CreditCard creditCard = new CreditCard("4412 8334 4512 9416", 1, "05/18/15"); - session.save(creditCard); - - final BankAccount bankAccount = new BankAccount("1107 2 31", "Lehman Brothers", "BARCGB22"); - session.save(bankAccount) <co xml:id="saveBankAccount"/>; ...</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting continuation="continues" language="sql">... - Feb 19, 2013 10:28:00 AM org.hibernate.tool.hbm2ddl.SchemaUpdate execute - INFO: HHH000232: Schema update complete - Hibernate: - insert - into - BillingDetails - (created, number, cardType, expiration, dataType) - values - (?, ?, ?, ?, 'Credit card') - Feb 19, 2013 10:28:00 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - WARN: SQL Error: 1364, SQLState: HY000 - Feb 19, 2013 10:28:00 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - <emphasis role="bold">ERROR: Field 'bankName' doesn't have a default value - Exception in thread "main" org.hibernate.exception.GenericJDBCException: - Field 'bankName' doesn't have a default value</emphasis> <co - xml:id="saveBankAccountException"/> - ... - at inherit.v2.Persist.main(Persist.java:28) - Caused by: java.sql.SQLException: Field 'bankName' doesn't have a default value</programlisting></td> - </tr> - </informaltable> - - <para>Conclusion: A table per class hierarchy mapping does not allow - to specify not null constraints for properties of derived - classes.</para> - - <qandaset defaultlabel="qanda" - xml:id="quandaentry_MappingFigureInherit"> - <title>Mapping figures</title> - - <qandadiv> - <qandaentry> - <question> - <para>Map the following model to a database:</para> - - <figure xml:id="modelFigureInheritance"> - <title>Figure subclasses</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/figureInherit.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>The two properties <code>xCenter</code> and - <code>yCenter</code> in the abstract base class - <code>Figure</code> represent the coordinates of the concrete - figure's center of gravity. In a drawing application this - would be considered the placement of the respective - object.</para> - - <para>The abstract method <code>getArea()</code> is meant to - be implemented without interfering with your database mapping. - Choose an integer discriminator. Test your application by - storing and loading objects.</para> - </question> - - <answer> - <para>The main difference to the current - <classname>inherit.v1.BillingDetails</classname> example is - the <classname>javax.persistence.Transient</classname> - annotation of the <code>area</code> property in - <classname>inherit.v3.Figure</classname>, - <classname>inherit.v3.Circle</classname> and - <classname>inherit.v3.Rectangle</classname>. The storage ant - retrieval applications are - <classname>inherit.v3.Persist</classname>, - <classname>inherit.v3.RetrieveRectangles</classname> and - <classname>inherit.v3.RetrieveAll</classname> are - straightforward.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> - - <section xml:id="joinedSubclass"> - <title>Joined subclasses</title> - - <para>The basic idea is to generate a normalized schema implementing - inheritance relationships by foreign keys:</para> - - <figure xml:id="joindSubclassMapping"> - <title>Joined subclass mapping.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/billingMapJoined.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>The inheritance strategy of joined subclasses <coref - linkend="strategyJoinedSubclass"/> is being defined in the abstract base - class <classname>inherit.joined.v1.BillingDetails</classname>:</para> - - <programlisting language="java">package inherit.joined.v1; - ... - @Entity - @Inheritance(strategy=InheritanceType.JOINED) <co - xml:id="strategyJoinedSubclass"/> - public abstract class BillingDetails { ... }</programlisting> - - <para>The derived classes need to provide an implementation hint in - order to identify the required foreign key <coref - linkend="referenceParentClass"/> to the parent class - <classname>inherit.joined.v1.BillingDetails</classname>:</para> - - <programlisting language="java">package inherit.joined.v1; - ... - @Entity - @PrimaryKeyJoinColumn(name="parent" <co xml:id="referenceParentClass"/>, referencedColumnName="id") - public class CreditCard extends BillingDetails { - - int cardType; - @Column(nullable=false) <co xml:id="tpcNotNullCardType"/> - public int getCardType() {return cardType;} - public void setCardType(int cardType) {this.cardType = cardType;} - - Date expiration; - @Column(nullable=false) <co xml:id="tpcNotNullexpiration"/> - public Date getExpiration() {return expiration;} - public void setExpiration(Date expiration) {this.expiration = expiration;} - ... - }</programlisting> - - <para>Notice the ability to exclude null values in <coref - linkend="tpcNotNullCardType"/> and <coref - linkend="tpcNotNullexpiration"/>.</para> - - <section xml:id="joinedSubclassRetrieve"> - <title>Retrieving Objects</title> - - <para>On the database server side object retrieval results in a more - expensive operation: A query for root class instances - of<classname>inherit.joined.v1.BillingDetails</classname> <coref - linkend="joinedQueryBillingDetails"/> of our inheritance hierarchy - results in joining all three tables <code>BillingDetails</code> <coref - linkend="joinFromBillingDetails"/>, <code>BankAccount</code> <coref - linkend="joinFromBankAccount"/> and <code>CreditCard</code> <coref - linkend="joinFromCreditCard"/>:</para> - - <informaltable border="1"> - <colgroup width="6%"/> - - <colgroup width="94%"/> - - <tr> - <td valign="top"><emphasis role="bold">Java</emphasis></td> - - <td valign="top"><programlisting language="java">package inherit.joined.v1; - ... - public class RetrieveAll { - ... - final Query searchBilling = session.createQuery("<emphasis role="bold">from inherit.tpc.v1.BillingDetails</emphasis>" <co - xml:id="joinedQueryBillingDetails"/>); - ...</programlisting></td> - </tr> - - <tr> - <td valign="top"><emphasis role="bold">Sql</emphasis></td> - - <td><programlisting continuation="continues" language="sql">Hibernate: - select - billingdet0_.id as id0_, - billingdet0_.created as created0_, - billingdet0_.number as number0_, - billingdet0_1_.bankName as bankName1_, - billingdet0_1_.swiftcode as swiftcode1_, - billingdet0_2_.cardType as cardType2_, - billingdet0_2_.expiration as expiration2_, - case - when billingdet0_1_.id is not null then 1 - when billingdet0_2_.id is not null then 2 - when billingdet0_.id is not null then 0 - end as clazz_ - from - <emphasis role="bold">BillingDetails</emphasis> billingdet0_ <co - xml:id="joinFromBillingDetails"/> - left outer join - <emphasis role="bold">BankAccount</emphasis> billingdet0_1_ <co - xml:id="joinFromBankAccount"/> - on billingdet0_.id=billingdet0_1_.id - left outer join - <emphasis role="bold">CreditCard</emphasis> billingdet0_2_ <co - xml:id="joinFromCreditCard"/> - on billingdet0_.id=billingdet0_2_.id </programlisting></td> - </tr> - </informaltable> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_IntegrityGenerate"> - <title><xref linkend="glo_JPA"/> constraints and database - integrity.</title> - - <qandadiv> - <qandaentry> - <question> - <para>Explain all integrity constraints of the Hibernate - generated schema. Will it implement the constraints corectly - on database level corresponding to the inheritance related - <xref linkend="glo_Java"/> objects? On contrary: Are there - possible database states which do not correspond to the domain - model's object constraints?</para> - </question> - - <answer> - <para>We take a look to the database schema:</para> - - <programlisting language="java">CREATE TABLE BillingDetails ( - id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY <co - linkends="inheritJoinSqlJava-1" - xml:id="inheritJoinSqlJava-1-co"/>, - created datetime NOT NULL, - number varchar(32) NOT NULL - ); - CREATE TABLE CreditCard ( - id bigint(20) NOT NULL PRIMARY KEY <co linkends="inheritJoinSqlJava-2" - xml:id="inheritJoinSqlJava-2-co"/> REFERENCES <co - linkends="inheritJoinSqlJava-3" - xml:id="inheritJoinSqlJava-3-co"/> BillingDetails, - cardType int(11) NOT NULL, - expiration datetime NOT NULL - ); - CREATE TABLE BankAccount ( - id bigint(20) NOT NULL PRIMARY KEY <co linkends="inheritJoinSqlJava-4" - xml:id="inheritJoinSqlJava-4-co"/> REFERENCES <co - linkends="inheritJoinSqlJava-4" - xml:id="inheritJoinSqlJava-5-co"/> BillingDetails, - bankName varchar(255) NOT NULL, - swiftcode varchar(255) NOT NULL - );</programlisting> - - <calloutlist> - <callout arearefs="inheritJoinSqlJava-1-co" - xml:id="inheritJoinSqlJava-1"> - <para>The table implementing the root class - <classname>inherit.joined.v1.BillingDetails</classname> of - the inheritance hierarchy will be referenced both by - <code>CreditCard</code> and <code>BankAccount</code> - datasets and thus requires a key to become addressable. - Moreover the corresponding - <classname>inherit.joined.v1.BillingDetails</classname> - class requires this attribute to be the primary key - anyway.</para> - </callout> - - <callout arearefs="inheritJoinSqlJava-2-co" - xml:id="inheritJoinSqlJava-2"> - <para>Each <code>CreditCard</code> specific set of - attributes belongs to exactly one - <code>BillingDetails</code> instance and hence the id - within our table <code>CreditCard</code> must be - unique.</para> - </callout> - - <callout arearefs="inheritJoinSqlJava-3-co" - xml:id="inheritJoinSqlJava-3"> - <para>As stated in <coref - linkend="inheritJoinSqlJava-2-co"/> each - <code>CreditCard</code> dataset must refer to its parent - <code>BillingDetails</code> instance.</para> - </callout> - - <callout arearefs="inheritJoinSqlJava-4-co inheritJoinSqlJava-5-co" - xml:id="inheritJoinSqlJava-4"> - <para>These constraints likewise describe <coref - linkend="inheritJoinSqlJava-2-co"/> and <coref - linkend="inheritJoinSqlJava-3-co"/> for - <code>BankAccount</code> datasets.</para> - </callout> - </calloutlist> - - <para>The NOT NULL constraints implement their counterpart - properties in the corresponding <xref linkend="glo_Java"/> - objects.</para> - - <para>The mapping does not cover one important integrity - constraint of our domain model: The base class - <classname>inherit.joined.v1.BillingDetails</classname> is - abstract. Thus each entry in the database must refer either to - a <classname>inherit.joined.v1.CreditCard</classname> or a - <classname>inherit.joined.v1.BankAccount</classname> instance. - But the above database schema allows for datasets to appear in - the <code>BillingDetails</code> table not being referenced by - either <code>BankAccount</code> or <code>CreditCard</code> - datasets.</para> - - <para>So the current database schema actually refers to a - domain model having a <emphasis - role="bold">concrete</emphasis> base class - <code>BillingDetails</code>.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" - xml:id="quandaentry_FigureJoinedSubclass"> - <title>Implementing figures by joined subclasses</title> - - <qandadiv> - <qandaentry> - <question> - <para>Implement the model being given in <xref - linkend="modelFigureInheritance"/> by joined - subclasses.</para> - </question> - - <answer> - <para>See - <classname>inherit.joined.v2.Figure</classname>.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> - - <section xml:id="inheritTablePerConcrete"> - <title>Table per concrete class</title> - - <para/> - </section> - </section> - - <section xml:id="jpaAssociations"> - <title>Mapping related Components and entities</title> - - <section xml:id="primaryKeyRevisit"> - <title>Primary keys revisited</title> - - <para>Following <xref linkend="bib_Bauer15"/> (p.88) we list important - properties of primary keys with respect to <quote>best practices</quote> - on top of their relational counterparts:</para> - - <itemizedlist> - <listitem> - <para>A primary key's values never change</para> - </listitem> - - <listitem> - <para>Primary key values should not have a business meaning</para> - </listitem> - - <listitem> - <para>Primary keys should be chosen to have proper indexing support - with respect to the database product in question.</para> - </listitem> - </itemizedlist> - - <para>Regarding persistence we have three different concepts related to - object identity:</para> - - <glosslist> - <glossentry> - <glossterm>Java Object identity</glossterm> - - <glossdef> - <para>The operator == checks whether two identifiers point to the - same memory address.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Java Object equality</glossterm> - - <glossdef> - <para>The <methodname>Object.equals(Object)</methodname>.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Database identity</glossterm> - - <glossdef> - <para>Two distinct datasets (tuples) are identical if all primary - key attributes have the same value.</para> - - <para>In other words: Two distinct database objects differ at - least in one of their primary key attribute values.</para> - </glossdef> - </glossentry> - </glosslist> - - <para>We recommend <xref linkend="bib_Bloch08"/> regarding Java identity - and equality of objects.</para> - - <section xml:id="objectEqualityByPrimaryKey"> - <title>Defining object equality by primary key</title> - - <para>Consider the following code:</para> - - <programlisting language="java">package session1; - ... - public class LoadUser { - ... - final User user; - { - final Session session = HibernateUtil.createSessionFactory("session1/hibernate.cfg.xml").openSession(); - final Transaction transaction = session.beginTransaction(); - user = (User) session.load(User.class, 1L); - final User user1 = (User) session.load(User.class, 1L); - - System.out.println("first transaction: user.equals(user1):" + user.equals(user1)); <co - xml:id="rereadInstance_1"/> - - transaction.commit(); - session.close(); - } - { - final Session session = HibernateUtil.createSessionFactory("session1/hibernate.cfg.xml").openSession(); - final Transaction transaction = session.beginTransaction(); - final User user2 = (User) session.load(User.class, 1L); - - System.out.println("second transaction: user.equals(user2):" + user.equals(user2)); <co - xml:id="rereadInstance_2"/> - - transaction.commit(); - session.close(); - } ...</programlisting> - - <para>At first we compare two <classname>session1.User</classname> - instances being read by the same session manager <coref - linkend="rereadInstance_1"/>. Subsequently we compare a detached - <classname>session1.User</classname> instance with a second instance - being read by a different session <coref linkend="rereadInstance_2"/>. - This yields the following result:</para> - - <screen>first transaction: user.equals(user1):true <coref - linkend="rereadInstance_1"/> -second transaction: user.equals(user2):false <coref linkend="rereadInstance_2"/></screen> - - <para>The two instances in question definitely represent the same - database entity. The two entity managers referring to different - sessions create two distinct instances within a given <xref - linkend="glo_Java"/> runtime.</para> - - <para>Since <xref linkend="glo_JPA"/> entities require an - <code>@javax.persistence.Id</code> attribute we may generally define - object equality solely based on this at tribute's value:</para> - - <programlisting language="java">package session2; - ... - public class User { - - @Id @GeneratedValue - private Long id; - ... - @Override public boolean equals(Object other) { - if (this == other) <co linkends="equalByPrimaryKey-1" - xml:id="equalByPrimaryKey-1-co"/>{ - return true; - } else if (other instanceof User) { - return getId().equals(((User) other).getId()) <co - linkends="equalByPrimaryKey-2" xml:id="equalByPrimaryKey-2-co"/>; - } else { - return false; - } - } - @Override public int hashCode() { <co linkends="equalByPrimaryKey-3" - xml:id="equalByPrimaryKey-3-co"/> - if (null == id) { - return System.identityHashCode(this); - } else { - return getId().hashCode(); - } - } - }</programlisting> - - <para>This way of defining - <methodname>session2.User.equals(java.lang.Object)</methodname> - implies that either or both of the following two conditions must be - met:</para> - - <calloutlist> - <callout arearefs="equalByPrimaryKey-1-co" - xml:id="equalByPrimaryKey-1"> - <para>Both instances must be identical.</para> - </callout> - - <callout arearefs="equalByPrimaryKey-2-co" - xml:id="equalByPrimaryKey-2"> - <para>Both instances must have the same - <methodname>session2.User.getId()</methodname> value. It is not - necessary to prepend <emphasis role="bold"><code>null != other - </code></emphasis><code>&& other instanceof User</code> - since the operator <code>instanceof</code> evaluates to - <code>false</code> if its left side is <code>null</code>.</para> - </callout> - </calloutlist> - - <caution> - <para>Do not forget to implement - <methodname>Object.hashCode()</methodname> <coref - linkend="equalByPrimaryKey-3-co" xml:id="equalByPrimaryKey-3"/> - accordingly: Two instances <code>a</code> and <code>b</code> - returning <code>a.equals(b) == true</code> equal must return an - identical hash code value <code>a.hashCode() == b.hashCode()</code> - in order to satisfy a collection's contract.</para> - - <para><xref linkend="bib_Bloch08"/> covers the contract of - <methodname>Object.hashCode()</methodname> and its close relation to - <methodname>Object.equals(Object)</methodname> in detail.</para> - </caution> - </section> - - <section xml:id="objectEqualityByNaturalKey"> - <title>Object equality by natural key</title> - - <para>Defining entity equality based on database identity suffers from - a severe deficiency: Newly created instances invariably differ from - any foreign non-identical instance regardless whether it does have a - database identity or not. We consider an example:</para> - - <programlisting language="java">package session2; - ... - public class CompareNewlyCreated { ... - - // Create two transient instances - final User a = new User(123, "goik", "Martin Goik"), <co - linkends="compareTransientById-1_2" - xml:id="compareTransientById-1-co"/> - b = new User(123, "goik", "Martin Goik"); <co - linkends="compareTransientById-1_2" - xml:id="compareTransientById-2-co"/> - - System.out.println("a.equals(b):" + a.equals(b)); <co - linkends="compareTransientById-3" - xml:id="compareTransientById-3-co"/> - - { - final Session session = HibernateUtil.createSessionFactory("session2/hibernate.cfg.xml").openSession(); - final Transaction transaction = session.beginTransaction(); - - // previously saved as new User(123, "goik", "Martin Goik"); - final User user = (User) session.load(User.class, 1L); <co - linkends="compareTransientById-4" - xml:id="compareTransientById-4-co"/> - - System.out.println("a.equals(user)):" + a.equals(user)); <co - linkends="compareTransientById-5" - xml:id="compareTransientById-5-co"/> - - transaction.commit(); - session.close(); - } ...</programlisting> - - <calloutlist> - <callout arearefs="compareTransientById-1-co compareTransientById-2-co" - xml:id="compareTransientById-1_2"> - <para><!--1 + 2-->Create two transient instances being identical - by value.</para> - </callout> - - <callout arearefs="compareTransientById-3-co" - xml:id="compareTransientById-3"> - <para><!--3-->Both instances are defined to differ by - value.</para> - </callout> - - <callout arearefs="compareTransientById-4-co" - xml:id="compareTransientById-4"> - <para><!--4-->Load a persistent entity from the database.</para> - </callout> - - <callout arearefs="compareTransientById-5-co" - xml:id="compareTransientById-5"> - <para><!--5-->Transient and persistent instances are defined to - differ by value.</para> - </callout> - </calloutlist> - - <para>Apparently this is definitely wrong: We do have unique database - index definitions. All objects in question do have common values 123 - and <code>"goik"</code> on these respective keys.</para> - </section> - - <section xml:id="equalityByNaturalKey"> - <title>Implementing <methodname>Object.equals(Object)</methodname> by - natural keys</title> - - <para>The last section's result actually provides a hint to implement - <methodname>Object.equals(Object)</methodname> in a more meaningful - way. The problem comparing transient instances occurs since a - surrogate key's value is being provided by the database server only at - the time an entity is being persisted. If at least one natural key - (sometimes referred to as <quote>business key</quote>) is being - defined this one may be used instead:</para> - - <figure xml:id="implementEqualsByNaturalKey"> - <title>Implementing <methodname>Object.equals(Object)</methodname> - by natural keys</title> - - <programlisting language="java">package session3; - - @Entity - @Table(uniqueConstraints={@<emphasis role="bold">UniqueConstraint(columnNames={"uid"}</emphasis>)<co - linkends="implementEqualsByNaturalKey-1" - xml:id="implementEqualsByNaturalKey-1-co"/> , - @UniqueConstraint(columnNames={"uidNumber"})}) - public class User { - ... - String uid; - - @Column(nullable=false) - public String getUid() {return uid;} - public void setUid(String uid) {this.uid = uid;} - ... - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof User) { - return <emphasis role="bold">getUid().equals(((User) other).getUid())</emphasis>; <co - linkends="implementEqualsByNaturalKey-2" - xml:id="implementEqualsByNaturalKey-2-co"/> - } else { - return false; - } - } - @Override - public int hashCode() { - if (null == getUid()) { - return System.identityHashCode(this); - } else { - return <emphasis role="bold">getUid().hashCode()</emphasis>; <co - linkends="implementEqualsByNaturalKey-3" - xml:id="implementEqualsByNaturalKey-3-co"/> - } - } - }</programlisting> - - <calloutlist> - <callout arearefs="implementEqualsByNaturalKey-1-co" - xml:id="implementEqualsByNaturalKey-1"> - <para>Definition of property - <methodname>session3.User.getUid()</methodname> to become a - natural key.</para> - </callout> - - <callout arearefs="implementEqualsByNaturalKey-2-co" - xml:id="implementEqualsByNaturalKey-2"> - <para>Two <classname>session3.User</classname> instances having - identical <methodname>session3.User.getUid()</methodname> values - will be considered equal.</para> - </callout> - - <callout arearefs="implementEqualsByNaturalKey-3-co" - xml:id="implementEqualsByNaturalKey-3"> - <para>The <methodname>session3.User.hashCode()</methodname> - implementation has to be changed accordingly.</para> - </callout> - </calloutlist> - </figure> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_MethodFieldAccess"> - <title>Method and field access</title> - - <qandadiv> - <qandaentry> - <question> - <para>Consider <xref linkend="implementEqualsByNaturalKey"/>. - You may get a different runtime behaviour when using <emphasis - role="bold"><code>this.uid().equals( that.uid() - )</code></emphasis> at <coref - linkend="implementEqualsByNaturalKey-2-co"/>. Execute the - corresponding <emphasis - role="bold">session3.CompareNewlyCreated</emphasis> and - <emphasis role="bold">session3.LoadUser</emphasis> - applications and explain the result.</para> - </question> - - <answer> - <para><xref linkend="glo_JPA"/> allows lazy fetch mode - typically enabled by default. So the <code>uid</code> - attribute's initialization will be deferred until - <methodname>session3.User.getUid()</methodname> is being - called for the first time.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> - - <section xml:id="entityValueTypes"> - <title>Entity and value types</title> - - <para>From the viewpoint of <xref linkend="glo_ORM"/> we distinguish two - distinct types of database objects:</para> - - <glosslist> - <glossentry> - <glossterm>Value type</glossterm> - - <glossdef> - <para>An object of value type has no database identity. It will - appear in a database as a composite of a parent entity type. Its - life cycle is completely dependent on its parent.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Entity type</glossterm> - - <glossdef> - <para>Objects of this type do have their own database identity and - may exist independently of other (database) entities.</para> - </glossdef> - </glossentry> - </glosslist> - </section> - - <section xml:id="sect_MappingComponents"> - <title>Mapping components</title> - - <para>We consider a simple example. We may add an email property to - <classname>session3.User</classname>:</para> - - <programlisting language="java">... -public class User { - ... - private Email address; ...</programlisting> - - <para>Why do we use a separate class Email rather than a simple - <emphasis role="bold"><code>private String email</code></emphasis> - declaration? The answer is quite simple: We want Email instances to be - extensible and allow for method definitions like - <code>sendEmail(...)</code>:</para> - - <programlisting language="java">public class Email { - - private String emailAddress; - ... - void sendEmail(final String subject, final String content) {} -}</programlisting> - - <para>Our <code>Email</code> class may of course have more than just one - property. We don't want to email addresses to be database entities - themselves. Instead they are meant to be components of User instances. - This is achieved by:</para> - - <glosslist> - <glossentry> - <glossterm>Annotate class Email to be embeddable:</glossterm> - - <glossdef> - <programlisting language="java">package component.email; - -<emphasis role="bold">@Embeddable</emphasis> public class Email { - - private String emailAddress; - ... -}</programlisting> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Annotate <code>emailAddress</code> to become an embedded - property:</glossterm> - - <glossdef> - <programlisting language="java">package component.email; - ... -public class User { - - private Email address; - <emphasis role="bold">@Embedded</emphasis> - public Email getEmailAddress() { return address;} - ...</programlisting> - </glossdef> - </glossentry> - </glosslist> - - <para>We may now persist <classname>component.email.User</classname> - instances:</para> - - <programlisting language="java">em.getTransaction().begin(); -{ - final User u = new User(123, "goik", "Martin Goik"); - u.setEmailAddress(new Email("goik@hdm-stuttgart.de")); - em.persist(u); -} -em.getTransaction().commit(); -</programlisting> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_HomeWorAddress"> - <title>Home and work address</title> - - <qandadiv> - <qandaentry> - <question> - <para>Consider the following sketch of an - <classname>Address</classname> class:</para> - - <programlisting language="java">public class Address { - - private String street; - private String city; - private String zipcode; - ... -}</programlisting> - - <para>Extend this example to allow for two properties - <code>homeAddress</code> and <code>workAddress</code> of type - Address. You will encounter a problem concerning conflicting - database attribute names. Resolution is possible using - <code>@javax.persistence.AttributeOverrides</code> - declarations.</para> - </question> - - <answer> - <para>See <classname>component.address.Address</classname> and - <classname>component.address.User</classname>.</para> - </answer> - </qandaentry> - - <qandaentry xml:id="quandaentry_PersistChange"> - <question> - <para>Load a User instance from your database and demonstrate - that changes to this existing persistent object component's - values get persisted.</para> - </question> - - <answer> - <para>See - <classname>component.address.ModifyWorkAddress</classname>.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <section xml:id="valueSets"> - <title>Sets and lists</title> - - <para>Users may have multiple email addresses:</para> - - <programlisting language="java">public class User { - - Set<Email> email; - ...</programlisting> - - <para>Using embedded values we need an <code>@ElementCollection</code> - declaration to achieve a proper mapping:</para> - - <programlisting language="java">package component.emails; - ... - public class User { - ... - private Set<Email> emails = new HashSet<Email>(); - <emphasis role="bold">@ElementCollection </emphasis> - public Set<Email> getEmails() { return emails; } - ...</programlisting> - - <para>This will map <classname>component.emails.Email</classname> - entries in a separate table:</para> - - <programlisting language="java">package component.emails; - ... - public class PersistUser { - public static void main(String[] args) { - final Session session = HibernateUtil.createSessionFactory("component/emails/hibernate.cfg.xml").openSession(); - { - final Transaction transaction = session.beginTransaction(); - final User u = new User(123, "goik", "Martin Goik"); - u.getEmails().add(new Email("goik@hdm-stuttgart.de")); - u.getEmails().add(new Email("goma@someserver.org")); - session.save(u); - transaction.commit(); - } ...</programlisting> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_HqlSearchEmail"> - <title>Searching users by email components</title> - - <qandadiv> - <qandaentry> - <question> - <para>Create a <xref linkend="glo_HQL"/> query searching - for:</para> - - <itemizedlist> - <listitem> - <para>All Users having an email address - <email>goma@someserver.org</email>.</para> - </listitem> - - <listitem> - <para>All users having at least two email - addresses.</para> - </listitem> - </itemizedlist> - - <para>You may want to use a Hibernate Console to develop the - appropriate queries.</para> - </question> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_EmbeddedPostal"> - <title>Embedded postal addresses</title> - - <qandadiv> - <qandaentry> - <question> - <para>Construct a corresponding example allowing - <classname>session3.User</classname> instances to have a set - of embedded <classname>component.address.Address</classname> - elements.</para> - </question> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" - xml:id="quandaentry_ComponentLifecycleDepend"> - <title>Life cycle of parents and components</title> - - <qandadiv> - <qandaentry> - <question> - <para>What happens, if an owning User instance gets deleted? - Are composed Email or Address values are implicitly being - deleted as well?</para> - </question> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_OrderedComponents"> - <title>Ordered components</title> - - <qandadiv> - <qandaentry> - <question> - <para>Replace <classname>java.util.Set</classname> by - <classname>java.util.List</classname> and assure that the - order of components is being preserved in a <xref - linkend="glo_JPA"/> 2 compliant manner.</para> - </question> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> - </section> - - <section xml:id="cache"> - <title>Caching</title> - - <figure xml:id="sda2FigJpaCacheIntro"> - <title><xref linkend="glo_JPA"/> caches.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/jpacache.svg"/> - </imageobject> - </mediaobject> - </figure> - - <qandaset defaultlabel="qanda" xml:id="cacheTesting"> - <title>Testing caches</title> - - <qandadiv> - <qandaentry> - <question> - <para>Import the following Hibernate based project:</para> - - <annotation role="make"> - <para role="eclipse">Sda2/Jpa/HibernateCacheDemo</para> - </annotation> - - <orderedlist> - <listitem> - <para>Insert 10000 randomly generated - <classname>Person</classname> entries into your - database.</para> - </listitem> - - <listitem> - <para>Measure time differences for loading 1000 entities in - different sessions.</para> - </listitem> - - <listitem> - <para>Measure time differences for attribute based queries - both with query cache enabled and disabled.</para> - </listitem> - </orderedlist> - </question> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="orphanRemoval"> - <title>orphanRemoval vs. CascadeType.DELETE</title> - - <para>Describe the difference and construct an example.</para> - </section> -</chapter> diff --git a/Doc/Sda2/sampleDbOverview.xml b/Doc/Sda2/sampleDbOverview.xml deleted file mode 100644 index 23d8d7ce218215fc17d48bb2d5a38a07c3a4b341..0000000000000000000000000000000000000000 --- a/Doc/Sda2/sampleDbOverview.xml +++ /dev/null @@ -1,778 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<part version="5.1" xml:id="sda2" xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:xi="http://www.w3.org/2001/XInclude" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:ns="http://docbook.org/ns/transclusion" - xmlns:m="http://www.w3.org/1998/Math/MathML" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:db="http://docbook.org/ns/docbook"> - <title>Structured Data and Applications 2</title> - - <chapter xml:id="ldap"> - <title><link linkend="glo_LDAP">Ldap</link> and <link - linkend="glo_JDBC">JDBC</link></title> - - <section xml:id="ldapIntro"> - <title>Getting started with <xref linkend="glo_LDAP"/></title> - - <para>Resources:</para> - - <itemizedlist> - <listitem> - <para><link - xlink:href="http://www.zytrax.com/books/ldap/ch3">Schema, - objectclasses and attributes</link></para> - </listitem> - - <listitem> - <para><link xlink:href="http://www.zytrax.com/books/ldap/apd">LDAP - Glossary</link></para> - </listitem> - </itemizedlist> - </section> - - <section xml:id="ldapSetup"> - <title>Setting up an <productname - xlink:href="http://www.openldap.org">Openldap</productname> - server</title> - - <para>The MI department provides an <productname - xlink:href="http://www.openldap.org">Openldap</productname> server at - each workstation (<productname - xlink:href="http://www.ubuntu.com/desktop">Ubuntu</productname> / Linux - only). The <xref linkend="glo_LDAP"/> manager credentials are:</para> - - <glosslist> - <glossentry> - <glossterm>Bind <xref linkend="glo_DN"/>:</glossterm> - - <glossdef> - <para><code>cn=admin,dc=hdm-stuttgart,dc=de</code></para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Bind password:</glossterm> - - <glossdef> - <para><code>XYZ</code></para> - </glossdef> - </glossentry> - </glosslist> - - <para>In addition <xref linkend="glo_Soft_ApacheDirectoryStudio"/> is in - place and may serve as a means to conveniently establish <xref - linkend="glo_LDAP"/> communications. Console geeks may use <command - xlink:href="http://tldp.org/HOWTO/LDAP-HOWTO/utilities.html">ldapmodify</command> - and friends.</para> - - <task> - <title>Set up a connection to your local <xref linkend="glo_LDAP"/> - server</title> - - <procedure> - <step> - <para>Open <xref - linkend="glo_Soft_ApacheDirectoryStudio"/>.</para> - </step> - - <step> - <para>Activate the <xref linkend="glo_LDAP"/> perspective.</para> - </step> - - <step> - <para>In the <quote>Connections</quote> window right click - <quote>New Connection ...</quote> like in <uri - xlink:href="https://directory.apache.org/studio/users-guide/ldap_browser/gettingstarted_create_connection.html">http://directory.apache.org/studio/users-guide/ldap_browser/gettingstarted_create_connection.html</uri>.</para> - </step> - - <step> - <para>Configure a connection to <parameter - xlink:href="http://www.techterms.com/definition/localhost">localhost</parameter> - using the above bind parameters.</para> - </step> - </procedure> - </task> - </section> - - <section xml:id="ldapSimpleInserts"> - <title>Adding some sample data using <xref - linkend="glo_Soft_ApacheDirectoryStudio"/></title> - - <qandaset defaultlabel="qanda" xml:id="qandaPopulateLdap"> - <title>Populating the <xref linkend="glo_DIT"/></title> - - <qandadiv> - <qandaentry> - <question> - <para>Add two departments <code>billing</code> and - <code>research</code>. Then supply corresponding user entries to - both departments by using the <link - xlink:href="https://directory.apache.org/studio/users-guide/ldap_browser/gettingstarted_browse.html">LDAP - browser view</link>. (As being mentioned previously hardcore - hackers take <link - xlink:href="http://tldp.org/HOWTO/LDAP-HOWTO/utilities.html">this - track</link> neglecting time consuming <xref linkend="glo_GUI"/> - stuff).</para> - - <para>Hint: If you do have limited understanding of <xref - linkend="glo_LDAP"/> classes an schemata you may want to create - entries containing the following <parameter - xlink:href="http://www.zytrax.com/books/ldap/apd/index.html#objectclass">objectClass</parameter> - values:</para> - - <glosslist> - <glossentry> - <glossterm>Departments <code>billing</code> and - <code>research</code>:</glossterm> - - <glossdef> - <itemizedlist> - <listitem> - <para><code - xlink:href="http://www.zytrax.com/books/ldap/ape/#organizationalunit">organizationalUnit</code> - (structural)</para> - </listitem> - </itemizedlist> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Child user entries below <code>billing</code> and - <code>research</code>:</glossterm> - - <glossdef> - <itemizedlist> - <listitem> - <para><code - xlink:href="http://www.zytrax.com/books/ldap/ape/#organizationalperson">organizationalPerson</code> - (structural) and <code - xlink:href="http://www.zytrax.com/books/ldap/ape/#posixaccount">posixAccount</code> - (auxiliary)</para> - </listitem> - </itemizedlist> - </glossdef> - </glossentry> - </glosslist> - - <para>Note that required parent <parameter - xlink:href="http://www.zytrax.com/books/ldap/apd/index.html#objectclass">objectClass</parameter> - definitions like <code>top</code> and <code - xlink:href="http://www.zytrax.com/books/ldap/ape/#person">person</code> - are being omitted here. <xref - linkend="glo_Soft_ApacheDirectoryStudio"/> will gracefully add - missing objectClasses on behalf of you automatically. The - subsequent <xref linkend="glo_LDIF"/> dump may serve as a - hint:</para> - - <programlisting language="ldif">... - <emphasis role="bold">dn: ou=billing,dc=hdm-stuttgart,dc=de</emphasis> - objectClass: top - objectClass: organizationalUnit - ou: billing - - <emphasis role="bold">dn: ou=research,dc=hdm-stuttgart,dc=de</emphasis> - objectClass: top - objectClass: organizationalUnit - ou: research - - <emphasis role="bold">dn: uid=lapinski,ou=billing,dc=hdm-stuttgart,dc=de</emphasis> - objectClass: posixAccount - objectClass: top - objectClass: person - objectClass: organizationalPerson - cn: Mee Lapinski - gidNumber: 100 - homeDirectory: /home/lapinski - sn: Lapinski - uid: lapinski - uidNumber: 1023 - ...</programlisting> - - <para>Question: What is the ratio behind adding the - <code>objectClass</code> value <code>posixAccount</code>? Hint: - Try to create a corresponding dataset having two persons with - identical names within the same department.</para> - </question> - - <answer> - <para>Your result may look like:</para> - - <figure xml:id="figureLdapTwoDepartments"> - <title>Two departments billing and research populated with - sample user entries</title> - - <screenshot> - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Screen/ldapSampleUsers.png"/> - </imageobject> - </mediaobject> - </screenshot> - </figure> - - <para>Without having the <code>objectClass</code> value - <code>posixAccount</code> the attribute <code>uid</code> would - be disallowed and could thus not be part of our <xref - linkend="glo_DN"/> values. This would leave us with solutions - like:</para> - - <programlisting language="ldif"><emphasis role="bold">dn: cn=Mee Lapinski,ou=billing,dc=hdm-stuttgart,dc=de</emphasis></programlisting> - - <para>This in turn disallows identical common names (e.g. a - second <personname>Mee Lapinski</personname>) within the same - department. Thus the auxiliary objectClass posixAccount enables - us to introduce additional mandatory <code>uid</code> attribute - being the unique identifier within a given parent scope.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="ldifImportExport"> - <title><xref linkend="glo_LDIF"/> export and import</title> - - <para>This section is intended to get acquainted with <xref - linkend="glo_LDIF"/> representation of <xref linkend="glo_LDAP"/> data - and requires successful completion of <xref - linkend="qandaPopulateLdap"/> as a prerequisite. You may want to read - <uri - xlink:href="http://www.zytrax.com/books/ldap/ch8">http://www.zytrax.com/books/ldap/ch8</uri>.</para> - - <qandaset defaultlabel="qanda" xml:id="qanda_ldifExportImport"> - <title>Exporting, modifying and importing <xref linkend="glo_LDAP"/> - data using the <xref linkend="glo_LDIF"/> interchange - representation.</title> - - <qandadiv> - <qandaentry> - <question> - <para>Export your current database state being left from <xref - linkend="qandaPopulateLdap"/> to an <xref linkend="glo_LDIF"/> - text file.</para> - - <para>Subsequently use this database dump file as a starting - point to create a <xref linkend="glo_LDIF"/> import file adding - a department <quote>pr</quote> (public relations) containing a - user <quote>Paul Simon</quote> with suitable attribute values to - the dataset.</para> - </question> - - <answer> - <para>Adding the new entries in question requires:</para> - - <screen>version: 1 - - dn: ou=pr,dc=hdm-stuttgart,dc=de - objectClass: top - objectClass: organizationalUnit - ou: pr - - dn: uid=simon,ou=pr,dc=hdm-stuttgart,dc=de - objectClass: posixAccount - objectClass: top - objectClass: person - objectClass: organizationalPerson - cn: Paul Simon - gidNumber: 130 - homeDirectory: /home/tauras - sn: Svetlana - uid: tauras - uidNumber: 1028</screen> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="ditSearch"> - <title>Searching the <xref linkend="glo_DIT"/></title> - - <para>Like relational and other database types <xref - linkend="glo_LDAP"/> provides a <link - xlink:href="http://www.zytrax.com/books/ldap/apa/search.html">query - language</link> to filter result entries.</para> - - <qandaset defaultlabel="qanda" xml:id="qanda_firstLdapQuery"> - <title>Filtering child elements</title> - - <qandadiv> - <qandaentry> - <question> - <para>Create <xref linkend="glo_LDAP"/> queries corresponding to - the following descriptions:</para> - - <orderedlist> - <listitem> - <para>All users entries within the whole <xref - linkend="glo_DIT"/> having a gidNumber value of 100.</para> - </listitem> - - <listitem> - <para>All user entries belonging to the billing department - having a <code>uid</code> value greater than 1023.</para> - </listitem> - - <listitem> - <para>All user entries within the whole <xref - linkend="glo_DIT"/> having a common name containing the - substring <quote>ei</quote>.</para> - </listitem> - - <listitem> - <para>All user entries within the whole <xref - linkend="glo_DIT"/> belonging to gidNumber == 100 or having - a <code>uid</code> value starting with letter - <quote>t</quote>.</para> - </listitem> - </orderedlist> - - <para>Hint: <xref linkend="glo_Soft_ApacheDirectoryStudio"/> - allows both for <link - xlink:href="https://directory.apache.org/studio/users-guide/ldap_browser/tools_filter_editor_dialog.html">filtering</link> - and <link - xlink:href="https://directory.apache.org/studio/users-guide/ldap_browser/gettingstarted_search.html">searching</link> - providing nifty features like attribute name completion and - syntax highlighting. For regular searches you may define:</para> - - <itemizedlist> - <listitem> - <para>The <xref linkend="glo_DIT"/> entry to start from - being identified by its <xref linkend="glo_DN"/>.</para> - </listitem> - - <listitem> - <para>The search scope being either of object, one level or - subtree.</para> - </listitem> - - <listitem> - <para>Boolean expressions based on attribute values.</para> - </listitem> - </itemizedlist> - - <para>But yes, I forgot to mention <link - xlink:href="http://tldp.org/HOWTO/LDAP-HOWTO/utilities.html">something</link>.</para> - </question> - - <answer> - <orderedlist> - <listitem> - <para><emphasis role="bold">All users entries within the - whole </emphasis><xref linkend="glo_DIT"/><emphasis - role="bold"> having a gidNumber value of - 100.</emphasis></para> - - <para>Solution: <code>(gidNumber=100)</code>, starting from - top of <xref linkend="glo_DIT"/> having subtree - scope.</para> - </listitem> - - <listitem> - <para><emphasis role="bold">All user entries belonging to - the billing department having a <code>uid</code> value - greater than 1023.</emphasis></para> - - <para>Solution: <code>(uidNumber>=1024)</code> starting - from <xref linkend="glo_DN"/> - <code>ou=billing,dc=hdm-stuttgart,dc=de</code> and scope - <code>one level</code>.</para> - - <para>Notice the expression - <code>(uidNumber>=1024)</code> in favour of the seemingly - equivalent but syntactically illegal counterpart - <code>(uidNumber>1023)</code>.</para> - </listitem> - - <listitem> - <para><emphasis role="bold">All user entries within the - whole </emphasis><xref linkend="glo_DIT"/><emphasis - role="bold"> having a common name containing the substring - <quote>ei</quote>.</emphasis></para> - - <para>Solution: <code>(cn=*ei*)</code>, starting from top of - <xref linkend="glo_DIT"/> having subtree scope.</para> - </listitem> - - <listitem> - <para><emphasis role="bold">All user entries within the - whole </emphasis><xref linkend="glo_DIT"/><emphasis - role="bold"> belonging to gidNumber == 100 or having a - <code>uid</code> value starting with letter - <quote>t</quote>.</emphasis></para> - - <para>Solution: <code>(|(gidNumber=100)(uid=t*))</code>, - starting from top of <xref linkend="glo_DIT"/> having - subtree scope.</para> - </listitem> - </orderedlist> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="unix2sql2ldap"> - <title><xref linkend="glo_UNIX"/> to <xref linkend="glo_SQL"/> to <xref - linkend="glo_LDAP"/></title> - - <para><xref linkend="glo_UNIX"/> type operating systems manage users, - groups and their respective relationships in three different text - files:</para> - - <glosslist> - <glossentry> - <glossterm><filename>/etc/passwd</filename></glossterm> - - <glossdef> - <para>Users being defined on the system:</para> - - <literallayout>root:x:0:0:root:/root:/bin/bash -daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin -bin:x:2:2:bin:/bin:/usr/sbin/nologin -sys:x:3:3:sys:/dev:/usr/sbin/nologin -sync:x:4:65534:sync:/bin:/bin/sync -...</literallayout> - - <para>We illustrate the meaning of this <xref linkend="glo_CSV"/> - (actually **character** separated) by examining the first - row:</para> - - <glosslist> - <glossentry> - <glossterm>Column 1, <code>root</code>:</glossterm> - - <glossdef> - <para>The user's unique system name as being entered at - login.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 2, x<code>:</code></glossterm> - - <glossdef> - <para>This field is not being used on current <xref - linkend="glo_UNIX"/> implementations. Historically either - the user's clear text password or its hash value was present - here. For security reasons this attribute has been moved to - a third file <filename>/etc/shadow</filename> being read - access protected to non-administrator users.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 3, <code>0</code>:</glossterm> - - <glossdef> - <para>The user's unique integer numerical - <parameter>uid</parameter> number value.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 4, <code>0</code>:</glossterm> - - <glossdef> - <para>The user's unique primary group integer numerical - <parameter>gid</parameter> number value. The value - <quote>0</quote> here refers to a group root of identical - name being defined in <filename>/etc/group</filename>, see - next section.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 5, <code>root</code>:</glossterm> - - <glossdef> - <para>The user's common name. For a regular user account - this might be <quote>Jim Beam</quote> for example.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 6, <filename>/root</filename>:</glossterm> - - <glossdef> - <para>The user's home directory. Might be /home/beam for a - user <quote>Jim Beam</quote>.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 7, <command>/bin/bash</command>:</glossterm> - - <glossdef> - <para>The user's login shell (command interpreter. This - attribute contains a reference to a command interpreter like - <command>/bin/(t)csh</command>, <command>/bin/ksh</command> - and so on.</para> - </glossdef> - </glossentry> - </glosslist> - </glossdef> - </glossentry> - - <glossentry> - <glossterm><filename>/etc/group</filename></glossterm> - - <glossdef> - <para>This file contains all groups being defined on the - system:</para> - - <literallayout>root:x:0: -daemon:x:1: -bin:x:2: -sys:x:3: -adm:x:4:syslog,mi <co xml:id="secondaryGroupmembership"/> -tty:x:5: -...</literallayout> - - <glosslist> - <glossentry> - <glossterm>Column1,root:</glossterm> - - <glossdef> - <para>The group's name</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 2, x:</glossterm> - - <glossdef> - <para>Not used</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 3, 4:</glossterm> - - <glossdef> - <para>The group's unique <parameter>gid</parameter> - number</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Column 4, <code>syslog,mi</code>:</glossterm> - - <glossdef> - <para>The set <code>{syslog,mi}</code> <coref - linkend="secondaryGroupmembership"/> defines secondary group - memberships: These two users will belong to the group - <code>adm</code> in addition to their respective primary - group definition.</para> - </glossdef> - </glossentry> - </glosslist> - </glossdef> - </glossentry> - </glosslist> - - <qandaset defaultlabel="qanda" xml:id="qandUnixToSqlToLdap"> - <title>Exporting and importing data</title> - - <qandadiv> - <qandaentry> - <question> - <para>Write two applications being able to perform the following - tasks:</para> - - <orderedlist> - <listitem> - <para>Import the previously described UNIX user and group - data ton an RDBMS using <xref linkend="glo_JDBC"/>. You will - have to define a suitable SQL schema first.</para> - </listitem> - - <listitem> - <para>Transfer RDBMS data to your local <xref - linkend="glo_LDAP"/> server using <link - xlink:href="https://docs.oracle.com/javase/jndi/tutorial/ldap/misc/url.html">JNDI</link>.</para> - </listitem> - </orderedlist> - </question> - - <answer> - <annotation role="make"> - <para role="eclipse">Sda2/UnixSqlLdap/Jndi/Unix2Rdbms</para> - </annotation> - - <annotation role="make"> - <para role="eclipse">Sda2/UnixSqlLdap/Jndi/Rdbms2Ldap</para> - </annotation> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="transactionsInJdbc"> - <title>Transactions in <xref linkend="glo_JDBC"/></title> - - <para>You may review some remarks on SQL standard isolation level - definitions:</para> - - <itemizedlist> - <listitem> - <para><xref linkend="glo_Javadoc"/>:</para> - - <itemizedlist> - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Connection.html#TRANSACTION_READ_UNCOMMITTED">TRANSACTION_READ_UNCOMMITTED</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Connection.html#TRANSACTION_READ_COMMITTED">TRANSACTION_READ_COMMITTED</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Connection.html#TRANSACTION_REPEATABLE_READ">TRANSACTION_READ_REPEATABLE_READ</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Connection.html#TRANSACTION_SERIALIZABLE">TRANSACTION_READ_SERIALIZABLE</link></para> - </listitem> - </itemizedlist> - </listitem> - - <listitem> - <para><link - xlink:href="http://www.oracle.com/technetwork/issue-archive/2005/05-nov/o65asktom-082389.html">On - Transaction Isolation Levels (Oracle)</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://technet.microsoft.com/en-us/library/ms378149(v=sql.110).aspx">Understanding - Isolation Levels (Microsoft)</link></para> - </listitem> - </itemizedlist> - - <section xml:id="accountTransferPessimistic"> - <title>Account Transfer using pessimistic concurrency control</title> - - <qandaset defaultlabel="qanda" xml:id="qandaJdbcIsolation"> - <title>Accounts and balances</title> - - <qandadiv> - <qandaentry> - <question> - <para>Consider the following simple schema of accounts keeping - customer balances:</para> - - <programlisting language="sql">CREATE TABLE Account ( - number INT NOT NULL PRIMARY KEY - ,balance INT NOT NULL - )</programlisting> - - <para>Write two GUI applications to:</para> - - <itemizedlist> - <listitem> - <para>Transfer amounts from one account to another</para> - </listitem> - - <listitem> - <para>Get the sum of all balances</para> - </listitem> - </itemizedlist> - - <informalfigure> - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Screen/accountTransferSum.png"/> - </imageobject> - </mediaobject> - </informalfigure> - </question> - - <answer> - <annotation role="make"> - <para role="eclipse">Sda2/account</para> - </annotation> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="accountTransferOptimistic"> - <title>Account Transfer using optimistic concurrency control</title> - - <figure xml:id="fig_optimisticConcurrencyControl"> - <title>Optimistic concurrency control</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/concurrentOptimistic.svg"/> - </imageobject> - </mediaobject> - </figure> - - <para>An interfering transaction obeying the protocol causes a - transaction failure:</para> - - <figure xml:id="concurrentObtimisticFail"> - <title>Failure with optimistic transactions</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/concurrentOptimisticFail.svg"/> - </imageobject> - </mediaobject> - </figure> - - <para>Considerations:</para> - - <itemizedlist> - <listitem> - <para>Race conditions, time of check to time of use</para> - </listitem> - </itemizedlist> - - <qandaset defaultlabel="qanda" xml:id="qandaTransferOptimistic"> - <title>Optimistic account transfer</title> - - <qandadiv> - <qandaentry> - <question> - <para>Implement your (pessimistic) account transfer - application <xref linkend="qandaJdbcIsolation"/> in an - optimistic manner:</para> - - <itemizedlist> - <listitem> - <para>Make sure both source and destination accounts get - protected against interfering transactions.</para> - </listitem> - - <listitem> - <para>Provide a means to definitely avoid deadlocks during - the second transaction section of a balance transfer - operation.</para> - </listitem> - - <listitem> - <para>Supply a suitable message in case of an interfering - second balance transfer</para> - </listitem> - </itemizedlist> - </question> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> - </chapter> - - <xi:include href="jpa.xml" xpointer="element(/1)"/> -</part> diff --git a/Doc/lectures.xml b/Doc/lectures.xml index 7ab060fbc05d076c65ac3c2fca2a2004345d3a8f..053fd95ae7d347baa640bbb604381a285d81c643 100644 --- a/Doc/lectures.xml +++ b/Doc/lectures.xml @@ -93,7 +93,7 @@ <xi:include href="preface.xml" xpointer="element(/1)"/> <part annotations="forumId=7;" xml:id="sd1"> - <title>Software development 1</title> + <title>113105 Software development 1</title> <xi:include href="Sd1/gettingStarted.xml" xpointer="element(/1)"/> @@ -182,11 +182,7 @@ <xi:include href="Tdoc/projects.xml" xpointer="element(/1)"/> </part> - <xi:include href="Sda2/sampleDbOverview.xml" xpointer="element(/1)"/> - -<!-- - <xi:include href="Sda2/jpa.xml" xpointer="element(/1)"/> ---> + <xi:include href="DbDevel/dbDevel.xml" xpointer="element(/1)"/> <part annotations="forumId=13;" xml:id="sdi"> <title>Software defined Infrastructure</title>