diff --git a/Doc/DbDevel/jdbc.xml b/Doc/DbDevel/jdbc.xml deleted file mode 100644 index 2d10911ce0a122874df49292805d4a26157b844b..0000000000000000000000000000000000000000 --- a/Doc/DbDevel/jdbc.xml +++ /dev/null @@ -1,3699 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<chapter annotations="slide" version="5.1" xml:id="introPersistence" - xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:xila="http://www.w3.org/2001/XInclude/local-attributes" - 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>Accessing Relational Data</title> - - <info> - <abstract> - <para>Concept of persistence</para> - - <para>Data exchange RDBMS ⇌ Java application</para> - - <para>Dealing with transactions</para> - </abstract> - </info> - - <figure xml:id="sda1_jdbc_fig_prerequisites"> - <title>Prerequisite knowledge</title> - - <itemizedlist> - <listitem> - <para><xref linkend="glo_RDBMS"/> schema and <xref - linkend="glo_Sql_DDL"/>:</para> - - <para><code>PRIMARY KEY</code>, <code>UNIQUE</code>, <code>FOREIGN - KEY</code>, <code>NOT NULL</code>, datatypes.</para> - </listitem> - - <listitem> - <para><xref linkend="glo_SQL"/>, <xref linkend="glo_Sql_DML"/>:</para> - - <para>Predicate based queries, joins.</para> - </listitem> - - <listitem> - <para>Transactions, <xref linkend="glo_ACID"/> principle:</para> - - <para>Isolation level 1 - 4.</para> - </listitem> - </itemizedlist> - </figure> - - <section xml:id="persistence"> - <title>Persistence in Object Oriented languages</title> - - <figure xml:id="sda1_jdbc_fig_persistence"> - <title>Persistence <xref linkend="bib_Bauer15"/></title> - - <para>Persistence allows an object to outlive the process that created - it.</para> - - <para>The state of the object may be stored to disk and an object with - the same state re-created at some point in the future.</para> - </figure> - - <figure xml:id="sda1_jdbc_fig_transientJavaClass"> - <title><xref linkend="glo_Java"/> transient instances</title> - - <programlisting language="java">public class User { - String commonName; // Common name e.g. 'Joe Bix' - String uid; // Unique login name e.g. 'bix' - ... // getters, setters and other stuff - -} -//------------------------------------ -// Thread lifespan (transient instance) -User u = new User("Joe Bix", "bix");</programlisting> - </figure> - - <figure xml:id="sda1_jdbc_fig_persistentObjects"> - <title><xref linkend="glo_RDBMS"/> persistent records</title> - - <programlisting language="sql">CREATE TABLE User( - commonName CHAR(80) - ,uid CHAR(10) PRIMARY KEY -); --- Persistent record (see <emphasis role="red">D</emphasis>urability in <xref - linkend="glo_ACID"/>) -INSERT INTO User VALUES('Joe Bix', 'bix');</programlisting> - </figure> - - <figure xml:id="processObjPersist"> - <title>Persisting transient <code>User</code> instances</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/persistence.fig" scale="65"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_jdbc_fig_javaRdbmsCommunication"> - <title>Observations</title> - - <itemizedlist> - <listitem> - <para>Processes in disjoint address spaces:</para> - - <orderedlist> - <listitem> - <para><trademark - xlink:href="http://www.oracle.com/technetwork/java/javase">JRE</trademark> - runtime.</para> - </listitem> - - <listitem> - <para><xref linkend="glo_RDBMS"/> server.</para> - </listitem> - </orderedlist> - </listitem> - - <listitem> - <para>Multiple runtimes possible (<abbrev - xlink:href="https://secure.php.net">PHP</abbrev>)</para> - </listitem> - - <listitem> - <para><quote>save</quote> and <quote>load</quote> denote - communications across OS boundaries.</para> - </listitem> - </itemizedlist> - </figure> - </section> - - <section xml:id="jdbcIntro"> - <title>Introduction to <xref linkend="glo_JDBC"/></title> - - <info> - <abstract> - <para>Writing data by java.sql.Statement</para> - - <para>Reading data using ResultSet</para> - - <para>Dealing with transactions</para> - </abstract> - </info> - - <section xml:id="jdbcWrite"> - <title>Write access, principles</title> - - <info> - <abstract> - <para>JDBC architecture</para> - - <para>Important interfaces in java.sql</para> - - <para>Write data using java.sql.Statement</para> - </abstract> - </info> - - <para>Connecting an application to a database by establishing a - connection between client and database server:</para> - - <figure xml:id="jdbcClientServer"> - <title>Networking between clients and database server</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/clientserv.fig"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_jdbc_fig_jdbcFeatures"> - <title><xref linkend="glo_JDBC"/> features</title> - - <itemizedlist> - <listitem> - <para>Protocol connecting database client and server.</para> - </listitem> - - <listitem> - <para>Vendor dependent implementations.</para> - </listitem> - </itemizedlist> - </figure> - - <para>So <trademark - xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> - is just one among a whole bunch of protocol implementations connecting - database servers and applications. Consequently <trademark - xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> - is expected to appear in the lower layer of multi-tier applications. We - take a three-tier application as a starting point:</para> - - <figure xml:id="jdbcThreeTier"> - <title><trademark - xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> - in a three-tier application</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/jdbcThreeTier.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>We may add an additional layer. Web applications are typically - being build on top of an application server (<productname - xlink:href="https://www.ibm.com/software/de/websphere/">WebSphere</productname>, - <productname - xlink:href="https://glassfish.java.net">Glassfish</productname>, - <productname - xlink:href="https://www.jboss.org/jbossas">Jboss</productname>,...) - providing additional services:</para> - - <figure xml:id="jdbcFourTier"> - <title><trademark - xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> - connecting application server and database.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/jdbcFourTier.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>Opening a connection to a database server requires:</para> - - <figure xml:id="sda1_jdbc_fig_jdbcOpenPrerequisites"> - <title><xref linkend="glo_JDBC"/> connection parameter</title> - - <orderedlist> - <listitem xml:id="ItemJdbcProtocol"> - <para>Database server type i.e. <productname - xlink:href="http://www.oracle.com/us/products/database">Oracle</productname>, - <productname - xlink:href="https://en.wikipedia.org/wiki/IBM_Db2">DB2</productname>, - <productname - xlink:href="http://www-01.ibm.com/software/data/informix">Informix</productname>, - <xref linkend="glo_Soft_Postgresql"/>, <productname - xlink:href="https://www.mysql.com">Mysql</productname> - <abbrev>etc.</abbrev> due to vendor specific <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - protocol implementations.</para> - </listitem> - - <listitem> - <para>Server <link - xlink:href="https://en.wikipedia.org/wiki/Domain_Name_System">DNS</link> - name or IP number.</para> - </listitem> - - <listitem> - <para>Server service's port number.</para> - </listitem> - - <listitem xml:id="itemJdbcDatabaseName"> - <para>The database name within the given server.</para> - </listitem> - - <listitem> - <para>Optional: A database user's account name and - password.</para> - </listitem> - </orderedlist> - </figure> - - <para>Items <xref linkend="ItemJdbcProtocol"/> - <xref - linkend="itemJdbcDatabaseName"/> will be encapsulated into a so called - <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - <link - xlink:href="https://en.wikipedia.org/wiki/Uniform_Resource_Locator">URL</link>. - We consider a typical example corresponding to the previous parameter - list:</para> - - <figure xml:id="jdbcUrlComponents"> - <title>Components of a <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - URL</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/jdbcurl.fig" scale="65"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_fig_jdbc_UrlRfc"> - <title><xref linkend="glo_IETF"/> Uniform Resource Identifier</title> - - <para><uri - xlink:href="https://www.ietf.org/rfc/rfc2396.txt">https://www.ietf.org/rfc/rfc2396.txt</uri>:</para> - - <programlisting language="bnf">absoluteURI = scheme ":" ( hier_part | opaque_part ) - -hier_part = ( net_path | abs_path ) [ "?" query ] - -net_path = "//" authority [ abs_path ] - -abs_path = "/" path_segments -...</programlisting> - </figure> - - <figure xml:id="sda1_jdbc_fig_protocolExamples"> - <title><xref linkend="glo_URL"/> examples</title> - - <itemizedlist> - <listitem> - <para><code>http://www.hdm-stuttgart.de/aaa</code></para> - </listitem> - - <listitem> - <para><code>http://someserver.com:8080/someResource</code></para> - - <para>Non-standard port 8080</para> - </listitem> - - <listitem> - <para><code>ftp://mirror.mi.hdm-stuttgart.de/Firmen</code></para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_subProtocolExamples"> - <title>Sub protocol examples</title> - - <informaltable border="0"> - <colgroup width="23%"/> - - <colgroup width="77%"/> - - <tr> - <th>Database</th> - - <th><xref linkend="glo_JDBC"/> URI</th> - </tr> - - <tr> - <td valign="top">PostgreSQL</td> - - <td - valign="top">jdbc:postgresql://<HOST>:<PORT>/[database]</td> - </tr> - - <tr> - <td valign="top">MySQL</td> - - <td - valign="top">jdbc:mysql://[host][:port]/[database][?p1=v1]...</td> - </tr> - - <tr> - <td valign="top">Oracle</td> - - <td - valign="top">jdbc:oracle:thin:[user/password]@[host][:port]:SID</td> - </tr> - - <tr> - <td valign="top">DB2</td> - - <td - valign="top">jdbc:db2://<HOST>:<PORT>/[database]</td> - </tr> - - <tr> - <td valign="top">Derby</td> - - <td valign="top">jdbc:derby://[host][:port]/[database]</td> - </tr> - - <tr> - <td valign="top">MS. SQL S.</td> - - <td - valign="top">jdbc:sqlserver://host[:port];user=xxx;password=xyz</td> - </tr> - - <tr> - <td valign="top">Sybase</td> - - <td - valign="top">jdbc:sybase:Tds:<HOST>:<PORT>/[database]</td> - </tr> - </informaltable> - </figure> - - <figure xml:id="sda1_jdbc_fig_nonStandardPorts"> - <title>No standard port assignments ...</title> - - <informaltable border="1"> - <colgroup width="35%"/> - - <colgroup width="65%"/> - - <tr> - <td valign="top"><glosslist> - <glossentry> - <glossterm>Postgresql:</glossterm> - - <glossdef> - <para>5432</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>IBM DB2:</glossterm> - - <glossdef> - <para>50000</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Oracle:</glossterm> - - <glossdef> - <para>1521</para> - </glossdef> - </glossentry> - </glosslist></td> - - <td valign="top"><itemizedlist> - <listitem> - <para>No official <xref linkend="glo_IETF"/> standard port - assignments</para> - </listitem> - - <listitem> - <para>Vendor specific defaults</para> - </listitem> - - <listitem> - <para>Explicit port specification required</para> - </listitem> - </itemizedlist></td> - </tr> - </informaltable> - </figure> - - <figure xml:id="sda1_jdbc_fig_postgresqlLinuxStandardPort"> - <title><productname>... but Postgresql</productname> made it into - Linux</title> - - <screen>>grep postgresql /etc/services -postgresql 5432/tcp postgres # PostgreSQL Database -postgresql 5432/udp postgres</screen> - </figure> - - <para>Writing <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - based applications follows a simple scheme:</para> - - <figure xml:id="sda1_fig_jdbcArchitecture"> - <title><xref linkend="glo_JDBC"/> architecture</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/jdbcarch.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>From a programmer's point of view the <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html">java.sql.DriverManager</classname> - is a bootstrapping object: Other objects like <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html">Connection</classname> - instances are being created from this central and unique object.</para> - - <figure xml:id="sda1_jdbc_fig_jdbcBootstrap"> - <title><classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html">DriverManager</classname>: - Bootstrapping connections</title> - - <itemizedlist> - <listitem> - <para>Bootstrapping object.</para> - </listitem> - - <listitem> - <para><classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html">java.sql.DriverManager</classname> - shipped with <xref linkend="glo_JRE"/>.</para> - </listitem> - - <listitem> - <para>Interfacing <xref linkend="glo_JRE"/> and <xref - linkend="glo_JDBC"/> driver.</para> - </listitem> - - <listitem> - <para>Provides instances of <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html">java.sql.Connection</classname>.</para> - </listitem> - - <listitem> - <para>See <xref linkend="exerciseJdbcWhyInterface"/>.</para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_mysqlConnection"> - <title>Example: <productname>Mysql</productname> connection - implementation</title> - - <itemizedlist> - <listitem> - <para>Interface <classname - xlink:href="https://github.com/mysql/mysql-connector-j/blob/release/5.1/src/com/mysql/jdbc/MySQLConnection.java#L35">MySQLConnection</classname> - <code>extends</code> <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html">java.sql.Connection</classname></para> - </listitem> - - <listitem> - <para>Class <classname - xlink:href="https://github.com/mysql/mysql-connector-j/blob/release/5.1/src/com/mysql/jdbc/ConnectionImpl.java#L71">ConnectionImpl</classname> - implements <classname - xlink:href="https://github.com/mysql/mysql-connector-j/blob/release/5.1/src/com/mysql/jdbc/MySQLConnection.java#L35">MySQLConnection</classname></para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_driverLibraries"> - <title>Driver libraries</title> - - <itemizedlist> - <listitem> - <para><filename>postgresql-42.1.4.jar</filename></para> - </listitem> - - <listitem> - <para><filename>mysql-connector-java-x.y.z.jar</filename></para> - </listitem> - - <listitem> - <para><filename>ojdbc6.jar</filename></para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_driverLibrariesMaven"> - <title>Driver libraries by <xref linkend="glo_Maven"/></title> - - <itemizedlist> - <listitem> - <programlisting language="xml"><groupId>postgresql</groupId> -<artifactId>postgresql</artifactId> -<version>9.1-901-1.jdbc4</version></programlisting> - </listitem> - - <listitem> - <programlisting language="xml"><groupId>com.oracle</groupId> <emphasis - role="red"><!-- requires access credentials --></emphasis> -<artifactId>ojdbc7</artifactId> -<version>12.1.0</version></programlisting> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_driverUnavailable"> - <title>Driver unavailable</title> - - <itemizedlist> - <listitem> - <programlisting language="none">conn = DriverManager.getConnection( - "jdbc:postgresql<emphasis role="red">l</emphasis>://localhost/hdm", "hdmuser", "XYZ");</programlisting> - </listitem> - - <listitem> - <para><screen>java.sql.SQLException: No suitable driver found for - jdbc:postgresqll://localhost/hdm - - at java.sql.DriverManager.getConnection(DriverManager.java:689) - at java.sql.DriverManager.getConnection(DriverManager.java:247) - ...</screen></para> - </listitem> - </itemizedlist> - </figure> - - <para><xref linkend="sda1_fig_jdbcArchitecture"/> does not show details - about the relations between <classname>java.sql.Connection</classname>, - <classname>java.sql.Statement</classname> and - <classname>java.sql.ResultSet</classname> objects. We start by giving a - rough description of these three interfaces' tasks and - responsibilities:</para> - - <figure xml:id="sda1_jdbc_fig_ConnectionInterface"> - <title><classname>Connection</classname> interface</title> - - <glosslist> - <glossentry> - <glossterm><classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html">java.sql.Connection</classname></glossterm> - - <glossdef> - <itemizedlist> - <listitem> - <para>Holding a permanent database server connection - .</para> - </listitem> - - <listitem> - <para>Stateful protocol.</para> - </listitem> - - <listitem> - <para>Per connection properties: <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#setTransactionIsolation(int)">Isolation - level</link>, <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#setAutoCommit(boolean)">auto - commit</link>,...</para> - </listitem> - - <listitem> - <para><methodname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#rollback()">rollback()</methodname> - / <methodname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#commit()">commit()</methodname>.</para> - </listitem> - </itemizedlist> - </glossdef> - </glossentry> - </glosslist> - </figure> - - <figure xml:id="sda1_jdbc_fig_StatementInterface"> - <title><classname>Statement</classname> interface</title> - - <glosslist> - <glossentry> - <glossterm><classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html">java.sql.Statement</classname></glossterm> - - <glossdef> - <para>Two distinct operation classes:</para> - - <glosslist> - <glossentry> - <glossterm><code - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#executeUpdate(java.lang.String)">executeUpdate()</code></glossterm> - - <glossdef> - <para><code>INSERT</code>, <code>UPDATE</code>, - <code>DELETE</code>: Integer return code</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm><code - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#executeQuery(java.lang.String)">executeQuery()</code></glossterm> - - <glossdef> - <para><code>SELECT</code>: Returning <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html">java.sql.ResultSet</classname>, - see <xref linkend="jdbcRead"/>.</para> - </glossdef> - </glossentry> - </glosslist> - </glossdef> - </glossentry> - </glosslist> - </figure> - - <figure xml:id="jdbcObjectCreation"> - <title><trademark - xlink:href="http://www.oracle.com/technetwork/java/javase/jdbc">JDBC</trademark> - instances and relationships.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/jdbcObjectRelation.fig"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_jdbc_fig_importantConnectionMethods"> - <title>Important <classname>Connection</classname> methods</title> - - <itemizedlist> - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#createStatement()">createStatement()</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#setAutoCommit(boolean)">setAutoCommit()</link>, - <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#getAutoCommit()">getAutoCommit()</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#getWarnings()">getWarnings()</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#isClosed()">isClosed()</link>, - <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#isValid(int)">isValid(int - timeout)</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#rollback()">rollback()</link>, - <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#commit()">commit()</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#close()">close()</link></para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_importantStatementMethods"> - <title>Important <classname>Statement</classname> methods</title> - - <itemizedlist> - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#executeUpdate(java.lang.String)">executeUpdate(String - sql)</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#getConnection()">getConnection()</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#getResultSet()">getResultSet()</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#close()">close()</link> - and <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#isClosed()">isClosed()</link></para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_connectionThreads"> - <title><xref linkend="glo_JDBC"/> and threading.</title> - - <para>From <link - xlink:href="https://docs.oracle.com/cd/A97335_02/apps.102/a83724/tips1.htm">JDBC - and Multithreading</link>:</para> - - <blockquote> - <para><quote>Because all Oracle JDBC API methods are synchronized, - if two threads try to use the connection object simultaneously, then - one will be forced to wait until the other one finishes its - use.</quote></para> - </blockquote> - - <para>Consequence:</para> - - <itemizedlist> - <listitem> - <para>Use one <classname>java.sql.Connection</classname> per - thread.</para> - </listitem> - - <listitem> - <para>Use <link - xlink:href="https://www.developer.com/java/data/understanding-jdbc-connection-pooling.html">connection - pooling</link> e.g. <link - xlink:href="http://www.mchange.com/projects/c3p0">c3po</link>.</para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_connectionPooling"> - <title><xref linkend="glo_JDBC"/> connection pooling</title> - - <programlisting language="java">try (final Connection conn = - <link xlink:href="https://www.developer.com/java/data/understanding-jdbc-connection-pooling.html">C3P0DataSource</link>.getInstance().getConnection()) { - - final PreparedStatement pstmt = conn.create...; - ... - pstmt.executeUpdate(); - // Auto close connection, back to pool. -} catch (SQLException e) { - e.printStackTrace(); -}</programlisting> - </figure> - </section> - - <section xml:id="writeAccessCoding"> - <title>Write access, coding!</title> - - <info> - <abstract> - <para>Providing a driver based on maven.</para> - - <para>Specifying unit tests.</para> - </abstract> - </info> - - <para><xref linkend="glo_JDBC"/> applications require a per project - driver configuration:</para> - - <figure xml:id="sda1_fig_configurePostgresqlMaven"> - <title><filename>pom.xml</filename> driver <emphasis - role="red">runtime</emphasis> scope</title> - - <programlisting language="xml">... -<dependency> - <groupId>postgresql</groupId> - <artifactId>postgresql</artifactId> - <version>9.1-901-1.jdbc4</version> - <scope><emphasis role="red">runtime</emphasis></scope> -</dependency> ...</programlisting> - </figure> - - <qandaset defaultlabel="qanda" xml:id="sda1_qanda_whyScopeRuntime"> - <title>Why <tag class="starttag">scope</tag><code>runtime</code><tag - class="endtag">scope</tag>?</title> - - <qandadiv> - <qandaentry> - <question> - <para>Why is the <xref linkend="glo_JDBC"/> <link - linkend="sda1_fig_configurePostgresqlMaven">driver - dependency</link> not being required at compile time?</para> - </question> - - <answer> - <para>According to <xref linkend="sda1_fig_jdbcArchitecture"/> a - <xref linkend="glo_JDBC"/> based application requires just - interfaces rather than classes at compile time. The actual - instances of <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html">java.sql.Connection</classname>, - <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html">java.sql.Statement</classname> - and friends will be created during the bootstrap process - starting from <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html">java.sql.DriverManager</classname>.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <para>So how does it actually work with respect to coding? We first - prepare a database table:</para> - - <figure xml:id="figSchemaPerson"> - <title><code>Person</code> table</title> - - <programlisting language="sql"><emphasis role="strong">CREATE</emphasis> <emphasis - role="strong">TABLE</emphasis> Person ( - name CHAR(20) - ,email CHAR(20) <emphasis>UNIQUE</emphasis> -)</programlisting> - </figure> - - <figure xml:id="sda1_fig_addPersonDataset"> - <title>Objective: insert person record</title> - - <itemizedlist> - <listitem> - <para><xref linkend="glo_Java"/> application executing:</para> - - <programlisting language="xml">INSERT INTO Person VALUES('Jim', 'jim@foo.org')</programlisting> - </listitem> - - <listitem> - <para>No data returned (No <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html">java.sql.ResultSet</classname>).</para> - </listitem> - - <listitem> - <para>Success / failure related database return parameter.</para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_fig_jdbcSimpleWrite"> - <title><trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - backed data insert</title> - - <programlisting language="java">// <link - xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/ws/eclipse/Jdbc/src/main/java/sda/jdbc/intro/SimpleInsert.java#L22">Step 1: Open connection to database server</link> -final Connection conn = DriverManager.<link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html#getConnection(java.lang.String)">getConnection</link> ( - "jdbc:postgresql://localhost/hdm", // Connection parameter URL - "hdmuser", // Username - "XYZ"); // Password - -// Step 2: Create a Statement instance -final Statement stmt = conn.<link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#createStatement()">createStatement()</link>; - -// Step 3: Execute the desired INSERT -final int updateCount = stmt.<link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#executeUpdate(java.lang.String)">executeUpdate</link>( - "INSERT INTO Person VALUES('Jim', 'jim@foo.org')"); - -// Step 4: Give feedback to the end user -System.out.println("Successfully inserted " + updateCount + " dataset(s)");</programlisting> - </figure> - - <figure xml:id="sda1_figJ_dbcSimpleWriteResult"> - <title>Result</title> - - <itemizedlist> - <listitem> - <para>Execution yields:</para> - - <screen>Successfully inserted 1 dataset(s)</screen> - </listitem> - - <listitem> - <para>Note: The database server <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#executeUpdate(java.lang.String)">returns</link> - the number of inserted / modified / deleted inserted - datasets.</para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_fig_jdbcTwiceConfig"> - <title>Two <xref linkend="glo_JDBC"/> configurations</title> - - <orderedlist> - <listitem> - <para><xref linkend="glo_IDE"/> level.</para> - </listitem> - - <listitem> - <para>Project level (<xref linkend="glo_Maven"/>).</para> - </listitem> - </orderedlist> - </figure> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_DupInsert"> - <title>Exception on inserting objects</title> - - <qandadiv> - <qandaentry> - <question> - <para>Set up <xref linkend="sda1_fig_jdbcSimpleWrite"/> as a - <xref linkend="glo_Maven"/> project yourself. Create a - corresponding table prior to executing your application.</para> - - <para>Execute your application twice. What happens? Give an - explanation.</para> - </question> - - <answer> - <para>We require a database table <filename - xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/P/Sda1/Jdbc/Insert/Minimum/src/main/resources/schema.sql">resources/schema.sql</filename> - prior to execution:</para> - - <programlisting language="sql">CREATE TABLE Person ( - name char(80) - ,email CHAR(20) UNIQUE -)</programlisting> - - <para>Building the executable jar and running - de.hdm_stuttgart.sda1.insert.SimpleInsert yields:</para> - - <screen>goik@goiki Minimum> mvn package -[INFO] Scanning for projects... -... -[INFO] Replacing .../Jdbc/Insert/Minimum/target/insert_user-0.1.jar - with .../Jdbc/Insert/Minimum/target/insert_user-0.1-shaded.jar - -goik@goiki Minimum> java -jar /.../Jdbc/Insert/Minimum/target/insert_user-0.1.jar -<emphasis role="red">Successfully inserted 1 dataset(s)</emphasis></screen> - - <para>We may check our database:</para> - - <screen>MariaDB [hdm]> SELECT * FROM Person; -+------+-------------+ -| name | email | -+------+-------------+ -| Jim | jim@foo.org | -+------+-------------+ -1 row in set (0.00 sec)</screen> - - <para>A second invocation results in a runtime error:</para> - - <para>The exception relates to a constraint violation with - respect to the <code>UNIQUE</code> attribute <code>email</code> - in our schema definition file - <filename>resources/schema.sql</filename>: We cannot add a - second entry having the same value - <code>'jim@foo.org'</code>.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <figure xml:id="sda1_jdbc_fig_appDeficencies"> - <title><xref linkend="sda1_fig_jdbcSimpleWrite"/> deficiencies</title> - - <glosslist> - <glossentry> - <glossterm>Missing exception handling:</glossterm> - - <glossdef> - <programlisting language="none">public static void main(String[] args) - <emphasis role="red">throws SQLException</emphasis> { ...</programlisting> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Hard coded connection parameters:</glossterm> - - <glossdef> - <programlisting language="none">... = DriverManager.getConnection ( - "<emphasis role="red">jdbc:postgresql://localhost/hdm</emphasis>", //JDBC URL - "<emphasis role="red">hdmuser</emphasis>", // Username - "<emphasis role="red">XYZ</emphasis>") // Password</programlisting> - </glossdef> - </glossentry> - </glosslist> - </figure> - - <figure xml:id="sda1_jdbc_fig_jdbcPropertiesPrinciple"> - <title>Why properties?</title> - - <itemizedlist> - <listitem> - <para>Connection parameter changes require recompilation!</para> - </listitem> - - <listitem> - <para>Parameters should be configurable.</para> - </listitem> - </itemizedlist> - - <blockquote> - <para>Possible solution: <xref linkend="glo_Java"/> - properties.</para> - </blockquote> - </figure> - - <figure xml:id="sda1_jdbc_fig_propertyExternalization"> - <title><filename>message.properties</filename> string - externalization</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/externalize.fig"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_jdbc_fig_propertyExternalizationCode"> - <title>Properties code sketch</title> - - <glosslist> - <glossentry> - <glossterm>Properties key / value file - <filename>resources/jdbc.properties</filename></glossterm> - - <glossdef> - <programlisting language="properties"><emphasis role="red">jdbcurl</emphasis>=jdbc:postgresql://localhost/hdm -<emphasis role="red">username</emphasis>=hdmuser -<emphasis role="red">password</emphasis>=XYZ</programlisting> - </glossdef> - </glossentry> - </glosslist> - - <glosslist> - <glossentry> - <glossterm>ResourceBundle reading properties</glossterm> - - <glossdef> - <programlisting language="none">// resources/<emphasis - role="red">jdbc</emphasis>.properties -ResourceBundle jdbcProperties = ResourceBundle.getBundle("<emphasis role="red">jdbc</emphasis>");</programlisting> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Using ResourceBundle</glossterm> - - <glossdef> - <programlisting language="none">... Connection conn = DriverManager.getConnection( - jdbcProperties.getString("<emphasis role="red">jdbcurl</emphasis>"), - jdbcProperties.getString("<emphasis role="red">username</emphasis>"), - jdbcProperties.getString("<emphasis role="red">password</emphasis>"));</programlisting> - </glossdef> - </glossentry> - </glosslist> - </figure> - - <figure xml:id="sda1_jdbc_fig_IdeaExternalizeStrings"> - <title><xref linkend="glo_Soft_IntellijIDEA"/> <link - xlink:href="https://www.jetbrains.com/help/idea/extracting-hard-coded-string-literals.html">settings</link>, - <link - xlink:href="https://www.jetbrains.com/help/idea/recognizing-hard-coded-string-literals.html">preconditions</link></title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/externalizeStrings.png"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_jdbc_fig_sqlUnitTests"> - <title>Database related unit test phases</title> - - <orderedlist> - <listitem> - <para><emphasis role="red">Set up:</emphasis> Test - preparation.</para> - - <itemizedlist> - <listitem> - <para>Open database connection</para> - </listitem> - - <listitem> - <para>Create a required schema.</para> - </listitem> - - <listitem> - <para>Optional: Insert initial data.</para> - </listitem> - </itemizedlist> - </listitem> - - <listitem> - <para><emphasis role="red">Test:</emphasis> Execute <xref - linkend="glo_JDBC"/> CRUD / SELECT operations.</para> - </listitem> - - <listitem> - <para><emphasis role="red">Tear down:</emphasis></para> - - <itemizedlist> - <listitem> - <para>Drop schema</para> - </listitem> - - <listitem> - <para>Close database connection.</para> - </listitem> - </itemizedlist> - </listitem> - </orderedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_dbUnitTestImplement"> - <title>Implementing unit tests</title> - - <programlisting language="none"><link - xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/P/Sda1/Jdbc/Insert/MinimumTest/src/test/java/de/hdm_stuttgart/sda1/insert/InsertTest.java">public class InsertTest</link> { - static private Connection conn; - static private Statement stmt; - - @BeforeClass <co linkends="sda1_jdbc_fig_dbUnitTestImplement-1" - xml:id="sda1_jdbc_fig_dbUnitTestImplement-1-co"/> static public void initDatabase() throws SQLException { - conn = DriverManager.getConnection( - SimpleInsert.jdbcProperties.getString("jdbcurl"), - SimpleInsert.jdbcProperties.getString("username"),...); - <emphasis role="red">ScriptUtils.executeSqlScript(conn, new ClassPathResource("schema.sql"));</emphasis> - stmt = conn.createStatement();} - - @Test <co linkends="sda1_jdbc_fig_dbUnitTestImplement-2" - xml:id="sda1_jdbc_fig_dbUnitTestImplement-2-co"/> - public void test_010_insertJill() throws SQLException { - Assert.assertEquals(1, SimpleInsert.insertPerson( - stmt, "Jill", "jill@programmer.org")); - } -@AfterClass <co linkends="sda1_jdbc_fig_dbUnitTestImplement-3" - xml:id="sda1_jdbc_fig_dbUnitTestImplement-3-co"/> static public void releaseDatabase() - throws SQLException {conn.close();}</programlisting> - </figure> - - <calloutlist> - <callout arearefs="sda1_jdbc_fig_dbUnitTestImplement-1-co" - xml:id="sda1_jdbc_fig_dbUnitTestImplement-1"> - <para>Set up phase.</para> - </callout> - - <callout arearefs="sda1_jdbc_fig_dbUnitTestImplement-2-co" - xml:id="sda1_jdbc_fig_dbUnitTestImplement-2"> - <para>Test execution phase.</para> - </callout> - - <callout arearefs="sda1_jdbc_fig_dbUnitTestImplement-3-co" - xml:id="sda1_jdbc_fig_dbUnitTestImplement-3"> - <para>Tear down phase.</para> - </callout> - </calloutlist> - - <figure xml:id="sda1_jdbc_fig_dbSpringSupport"> - <title><link xlink:href="https://spring.io">Spring</link> is your - friend</title> - - <para>Getting <emphasis role="red" - xlink:href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/datasource/init/ScriptUtils.html#executeSqlScript-java.sql.Connection-org.springframework.core.io.Resource-">ScriptUtils.executeSqlScript(...)</emphasis> - to work:</para> - - <programlisting language="xml"><dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-jdbc</artifactId> - <version>5.3.1</version> - <emphasis role="red"><scope>test</scope></emphasis> -</dependency></programlisting> - </figure> - - <figure xml:id="sda1_jdbc_fig_sqlTestingProjectLayout"> - <title>Project layout</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/sqlUnitTestLayout.png"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_fig_jdbcCloseConnection"> - <title>Closing connections</title> - - <programlisting language="java">final Connection conn = DriverManager.getConnection(...); -... // CRUD operations -conn.close(); // Important! Wanna use a <link - linkend="sda1_jdbc_connectionPooling">connection pool</link> instead?</programlisting> - </figure> - - <figure xml:id="sda1_fig_jdbcAutoCloseConnection"> - <title>Employ <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/AutoCloseable.html">AutoCloseable</classname></title> - - <para>Using <link - xlink:href="https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html">try-with-resources - statement</link>.</para> - - <programlisting language="java">try (final Connection conn = DriverManager.getConnection(...) { - ... // CRUD operations -} catch (SQLException e) {...}</programlisting> - </figure> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_DupInsertUnitTest"> - <title>Interactive inserts, connection properties, error handling and - unit tests</title> - - <qandadiv> - <qandaentry> - <question> - <para>Extend the previous example by adding support for - interactive insert of person data. <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/util/Scanner.html">java.util.Scanner</classname> - is your friend:</para> - - <para>Ask the end user to enter name and email addresses of - persons until being satisfied. In case of constraint violations - issue a warning rather then terminating the application as - in:</para> - - <mediaobject> - <videoobject> - <videodata fileref="Ref/Jdbc/Insert/personRecords.webm" - format="video/webm"/> - </videoobject> - </mediaobject> - - <para>Moreover <xref linkend="quandaentry_DupInsert"/> does not - yet contain any tests: SQL schema or application modifications - may lead to inconsistencies. Provide the following tests:</para> - - <orderedlist> - <listitem> - <para>Inserting multiple <classname>Person</classname> - records.</para> - </listitem> - - <listitem> - <para>Trying to insert Person records containing email - duplicates.</para> - </listitem> - </orderedlist> - - <tip> - <orderedlist> - <listitem> - <para>Use a connection property file for both your - application and related unit tests.</para> - </listitem> - - <listitem> - <para>Consider catching <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/SQLIntegrityConstraintViolationException.html">java.sql.SQLIntegrityConstraintViolationException</classname> - in case of email duplicates.</para> - </listitem> - - <listitem> - <para>Implement a separate method doing the actual INSERT - operations. This method can be used both in your - application and unit tests:</para> - - <programlisting language="java">/** - * <p>Try inserting new Person record.</p> - * - * @param statement To be used for SQL INSERT attempt. - * @param name Person's name - * @param email Person's email - * @return Inserted reord count: 1 on successful INSERT, - * 0 in case of duplicate email violating UNIQUE constraint. - * - * @throws SQLException To be thrown in case of - * non - {@link SQLIntegrityConstraintViolationException} - * errors. - */ -static public int insertPerson( - final Statement statement, final String name, final String email) - throws SQLException {...}</programlisting> - </listitem> - </orderedlist> - </tip> - </question> - - <answer> - <para>Our last exercise's database schema <filename - xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/P/Sda1/Jdbc/Insert/Minimum/src/main/resources/schema.sql">resources/schema.sql</filename> - may remain untouched. Solution:</para> - - <annotation role="make"> - <para role="eclipse">P/Sda1/Jdbc/Insert/MinimumTest</para> - </annotation> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" xml:id="qandaXmldata2relational"> - <title>Avoiding intermediate <xref linkend="glo_SQL"/> file - export</title> - - <qandadiv> - <qandaentry> - <question> - <para>In <xref linkend="quandaentry_SqlFromXml"/> shows a <xref - linkend="glo_SAX"/> application transforming XML product catalog - instances into a series of SQL statements. Implement a <xref - linkend="glo_JDBC"/> based application which reads the following - type of data and writes it to a relational database:</para> - - <programlisting language="xml"><catalog> - <item orderNo="3218">Swinging headset</item> - <item orderNo="9921">200W Stereo Amplifier</item> -</catalog></programlisting> - - <para>Error handling may be implemented by simply issuing a - corresponding message before exiting the application. In order - to assure data integrity transferring data shall be realized in - an <quote>all or nothing</quote> fashion by grouping all - <code>INSERT</code>s into a single transaction. You may want to - read about <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#setAutoCommit(boolean)">setAutoCommit(boolean - autoCommit)</link> and <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#commit()">commit()</link> - for this purpose.</para> - </question> - - <answer> - <annotation role="make"> - <para role="eclipse">P/Sda1/catalog2rdbms</para> - </annotation> - - <para>This solution requires a <command>mvn</command> - <option>install</option> on dependent project - <quote>saxerrorhandler</quote>:</para> - - <annotation role="make"> - <para role="eclipse">P/Sda1/saxerrorhandler</para> - </annotation> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" xml:id="exerciseJdbcWhyInterface"> - <title>Interfaces and classes in <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark></title> - - <qandadiv> - <qandaentry> - <question> - <para>The <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - standard mostly defines interfaces like <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html">Connection</classname> - and <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html">Statement</classname>. - Why are these not being defined as classes? Moreover why is - <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html">DriverManager</classname> - being defined as a class rather than an interface?</para> - - <para>You may want to supply code examples backing your - argumentation.</para> - </question> - - <answer> - <para>Figure <xref linkend="sda1_fig_jdbcArchitecture"/> depicts - <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - being a vendor independent architecture. Oracle for example may - implement a class - <classname>com.oracle.jdbc.OracleConnection</classname>:</para> - - <programlisting annotations="nojavadoc" language="java">package com.oracle.jdbc; - -import java.sql.Connection; -import java.sql.Statement; -import java.sql.SQLException; - -public class OracleConnection implements Connection { - -... - -Statement createStatement(int resultSetType, - int resultSetConcurrency) - throws SQLException) { - // Implementation omitted here due to - // limited personal hacking capabilities - ... -} -... -}</programlisting> - - <para>Using <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - interfaces rather than vendor specific classes allows for - decoupling an application from a specific database platform. It - requires a database vendor's implementation not to be exposed to - our own <xref linkend="glo_Java"/> code but to be encapsulated - by a set of interfaces.</para> - - <para>Regarding the special role of <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html">DriverManager</classname> - we notice the need for a starting point: An application requires - an initial entry point for accessing a given framework. In - theory (<emphasis role="bold">BUT NOT IN PRACTICE!!!</emphasis>) - the following (ugly) code might be possible:</para> - - <programlisting language="java">package my.personal.application; - -import java.sql.Connection; -import java.sql.Statement; -import java.sql.SQLException; - -public someClass { - - public void someMethod(){ - - Connection conn = <emphasis role="bold">new OracleConnection()</emphasis>; // bad idea! - ... - } - ... -}</programlisting> - - <para>The problem with this approach is its explicit constructor - call: Whenever we want to use another database we have two - possibilities:</para> - - <itemizedlist> - <listitem> - <para>Modify and recompile / redeploy our code.</para> - </listitem> - - <listitem> - <para>Introduce some sort of dispatch mechanism supporting a - fixed (albeit not extensible!) set of databases - beforehand:</para> - - <programlisting language="java">public void someMethod(final String vendor){ - - final Connection conn; - - switch(vendor) { - case "ORACLE": - conn = new OracleConnection(); - break; - - case "DB2": - conn = new Db2Connection(); - break; - - default: - conn = null; - break; - } - ... -}</programlisting> - - <para>Each time adding a new database requires extending the - above code.</para> - </listitem> - </itemizedlist> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_Close"> - <title>Closing <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - connections</title> - - <qandadiv> - <qandaentry> - <question> - <para>Why is it important to call the <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html#close()">close()</link> - method for <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html">Connection</classname> - and / or <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Statement.html">Statement</classname> - instances?</para> - </question> - - <answer> - <para>A <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - connection ties network resources (socket connections). These - may get used up if e.g. new connections are being established - within a loop.</para> - - <para>The situation is similar to memory leaks in programming - languages lacking a garbage collector.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_DriverDispatch"> - <title>Driver dispatch mechanism</title> - - <qandadiv> - <qandaentry> - <question> - <para>In exercise <xref linkend="exerciseJdbcWhyInterface"/> we - saw a hypothetic way to resolve the interface/class resolution - problem by using a switch clause. How is this - <code>switch</code> clause's logic actually realized in a - <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - based application? (<quote>behind the scenes</quote>)</para> - - <para>Hint: Read the documentation of - <classname>java.sql.DriverManager</classname>.</para> - </question> - - <answer> - <para>Prior to opening a Connection a <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - driver registers itself at the - <classname>java.sql.DriverManager</classname>. For this purpose - the standard defines the <code language="java">static</code> - <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html#registerDriver(java.sql.Driver)">registerDriver(Driver)</link> - method. On success driver is being added to an internal - dictionary:</para> - - <informaltable border="1"> - <col width="20%"/> - - <col width="30%"/> - - <tr> - <th>protocol</th> - - <th>driver instance</th> - </tr> - - <tr> - <td>jdbc:postgresql</td> - - <td>Postgresql driver instance</td> - </tr> - - <tr> - <td>jdbc:oracle</td> - - <td>Oracle driver instance</td> - </tr> - - <tr> - <td>...</td> - - <td>...</td> - </tr> - </informaltable> - - <para>So whenever the method <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/DriverManager.html#getConnection(java.lang.String,java.lang.String,java.lang.String)">getConnection()</link> - is being called the - <classname>java.sql.DriverManager</classname> will scan the - <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - URL and isolate the protocol part. If we start with - <code>jdbc:postgresql://someserver.com:5432/someDatabase</code> - this is just <code>jdbc:postgresql</code>. The value is then - being looked up in the above table of registered drivers to - choose an appropriate instance or null otherwise. This way our - hypothetic switch including the default value null is actually - implemented.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="jdbcSecurity"> - <title><xref linkend="glo_JDBC"/> and security</title> - - <info> - <abstract> - <para>Attack vectors.</para> - - <para>Sanitizing user input</para> - - <para>Solution by PreparedStatement</para> - </abstract> - </info> - - <section xml:id="jdbcSecurityNetwork"> - <title>Network sniffing</title> - - <para>Sniffing <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - network traffic is one possibility for intruders to compromise - database applications. This requires physical access to either - of:</para> - - <itemizedlist> - <listitem> - <para>Server host</para> - </listitem> - - <listitem> - <para>Client host</para> - </listitem> - - <listitem> - <para>intermediate hub, switch or router.</para> - </listitem> - </itemizedlist> - - <figure xml:id="figJdbcSniffing"> - <title>Sniffing a <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - connection by an intruder.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/jdbcSniffing.multi.svg"/> - </imageobject> - </mediaobject> - </figure> - - <para>We demonstrate a possible attack by analyzing the network - traffic between our application shown in <xref - linkend="sda1_fig_jdbcSimpleWrite"/> and the <productname - xlink:href="https://www.mysql.com">Mysql</productname> database - server. Prior to starting the application we set up <productname - xlink:href="https://www.wireshark.org">Wireshark</productname> for - filtered capturing:</para> - - <figure xml:id="sda1_jdbc_fig_jdbcAttackWireshark"> - <title>Setting up <productname - xlink:href="https://www.wireshark.org">Wireshark</productname></title> - - <informaltable border="0"> - <colgroup width="75%"/> - - <colgroup width="25%"/> - - <tr> - <td valign="top"><itemizedlist> - <listitem> - <para>Database server and <xref linkend="glo_JDBC"/> - client on same machine.</para> - </listitem> - - <listitem> - <para>Connecting to the <varname>loopback</varname> (lo) - interface only.</para> - - <para>(Sufficient since client connects to - <varname>localhost</varname>)</para> - </listitem> - - <listitem> - <para>Capture packets of type <acronym - xlink:href="https://en.wikipedia.org/wiki/Transmission_Control_Protocol">TCP</acronym> - having port number 3306.</para> - </listitem> - </itemizedlist></td> - - <td valign="top"><mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/wireshark.png"/> - </imageobject> - </mediaobject></td> - </tr> - </informaltable> - </figure> - - <figure xml:id="sda1_jdbc_fig_jdbcWiresharkResults"> - <title>Capturing results</title> - - <screen>[... -5.5.24-0ubuntu0.12.04.1.%...X*e?I1ZQ...................e,F[yoA5$T[N.mysql_native_password. - A...........!.......................<emphasis role="red">hdmuser</emphasis> <co - xml:id="tcpCaptureUsername"/>......U.>S.%..~h...!.xhdm............j..../* - - ... <emphasis role="red">INSERT INTO Person VALUES('Jim', 'jim@foo.org')</emphasis> <co - xml:id="tcpCaptureSqlInsert"/>6... - .&.<emphasis role="red">#23000Duplicate entry 'jim@foo.org' for key 'email'</emphasis> <co - xml:id="tcpCaptureErrmsg"/></screen> - - <calloutlist> - <callout arearefs="tcpCaptureUsername"> - <para><varname>username</varname> initiating database - connection.</para> - </callout> - - <callout arearefs="tcpCaptureSqlInsert"> - <para><code>INSERT(...)</code> statement.</para> - </callout> - - <callout arearefs="tcpCaptureErrmsg"> - <para>Resulting error message sent back to the client.</para> - </callout> - </calloutlist> - - <para>Password?</para> - </figure> - - <figure xml:id="sda1_jdbc_fig_jdbcMysqlSecurity"> - <title><xref linkend="glo_Soft_Mysql"/> security</title> - - <para>What about the missing password?</para> - - <para><link - xlink:href="https://dev.mysql.com/doc/refman/5.7/en/security-against-attack.html">Making - MySQL Secure Against Attackers</link>:</para> - - <blockquote> - <para>When you connect to a MySQL server, you should use a - password.</para> - - <para>The password is not transmitted in clear text over the - connection.</para> - </blockquote> - </figure> - - <para>So regarding our (current) <productname - xlink:href="https://www.mysql.com">Mysql</productname> implementation - the impact of this attack type is somewhat limited but still severe: - All data being transmitted between client and server may be disclosed. - This typically comprises sensible data as well. Possible - solutions:</para> - - <figure xml:id="sda1_jdbc_fig_jdbcSecurityImpact"> - <title><xref linkend="glo_Soft_Mysql"/> security</title> - - <itemizedlist> - <listitem> - <para>Data exchange client to server nearly fully - disclosed.</para> - </listitem> - - <listitem> - <para><productname - xlink:href="https://www.mysql.com">Mysql</productname> mitigates - the attack type's severity</para> - </listitem> - - <listitem> - <para>Possible solutions:</para> - - <itemizedlist> - <listitem> - <para>Encrypted tunnel between client and server: like e.g. - <link - xlink:href="https://www.debianadmin.com/howto-use-ssh-local-and-remote-port-forwarding.html">ssh - port forwarding</link> or <link - xlink:href="https://en.wikipedia.org/wiki/Virtual_private_network">VPN</link>.</para> - </listitem> - - <listitem> - <para>Use <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - driver supporting <xref linkend="glo_TLS"/>.</para> - </listitem> - </itemizedlist> - </listitem> - </itemizedlist> - </figure> - </section> - - <section xml:id="sqlInjection"> - <title>SQL injection</title> - - <figure xml:id="figSqlAssemble"> - <title>Assembling SQL</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/sqlconstruct.multi.svg"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="figSqlInject"> - <title>SQL injection principle</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/sqlinject.multi.svg"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_fig_preventRadar"> - <title>Preventing traffic tickets</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/radar.jpg"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_fig_littleBobbyTables"> - <title><link xlink:href="http://xkcd.com/327">Trouble at - school</link></title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/exploits_of_a_mom.png"/> - </imageobject> - </mediaobject> - </figure> - - <para>Before diving into technical details we shed some light on the - possible impact of this common attack type:</para> - - <figure xml:id="figHeartlandSecurityBreach"> - <title>SQL injection impact</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/heartland.fig"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_jdbc_sqlInjectionRelevance"> - <title>SQL injection relevance, <xref - linkend="bib_Clarke09"/></title> - - <blockquote> - <para>Many people say they know what SQL injection is, but all - they have heard about or experienced are trivial examples.</para> - - <para>SQL injection is one of the most devastating vulnerabilities - to impact a business, as it can lead to exposure of all of the - sensitive information stored in an application's database, - including handy information such as usernames, passwords, names, - addresses, phone numbers, and credit card details.</para> - </blockquote> - </figure> - - <qandaset defaultlabel="qanda" xml:id="sqlInjectDropTable"> - <title>Attack from the dark side</title> - - <qandadiv> - <qandaentry> - <question> - <para>Use your <xref linkend="quandaentry_DupInsertUnitTest"/> - application and the idea of <xref linkend="figSqlInject"/> to - launch an SQL injection attack. We provide some hints:</para> - - <orderedlist> - <listitem> - <para>Executing multi-line statements may require explicit - <code>COMMIT</code> statements:</para> - - <programlisting language="sql">INSERT INTO Person VALUES (...);DROP TABLE Person;<emphasis - role="red">COMMIT</emphasis>;...</programlisting> - </listitem> - - <listitem> - <para>You may use either of the two input fields - <quote>name</quote> or <quote>email</quote> to inject - arbitrary SQL code.</para> - </listitem> - </orderedlist> - </question> - - <answer> - <para>Logging tells us about SQL code being generated when - inserting a record based on e.g. user <emphasis - role="red">Eve</emphasis> having an email <emphasis - role="red">eve@my.org</emphasis>:</para> - - <programlisting language="sql">main INFO insert.SimpleInsert - Executing «INSERT INTO Person VALUES('<emphasis - role="red">Eve</emphasis>', '<emphasis role="red">eve@my.org</emphasis>')»</programlisting> - - <para>We craft our first input <code>username</code> replacing - <emphasis role="red">Eve</emphasis> to launch our - attack:</para> - - <programlisting language="sql"><emphasis role="red">Eve', 'eve@my.org');DROP TABLE Person;COMMIT;INSERT INTO Person VALUES('jim</emphasis></programlisting> - - <para>A corresponding dialog reads:</para> - - <screen>MinimumTest> java -jar /ma/goik/GoikLectures/P/Sda1/Jdbc/Insert/MinimumTest/target/insert_user-0.1.jar -Enter a person's name or 'x' to exit: <emphasis role="red">Eve', 'eve@my.org');DROP TABLE Person;INSERT INTO Person VALUES('jim</emphasis> -Enter <emphasis role="red">Eve', 'eve@my.org');DROP TABLE Person;INSERT INTO Person VALUES('jim's</emphasis> email or 'x' to exit: jim@company.com -</screen> - - <screen>java -jar /home/goik/.m2/repository/de/hdm_stuttgart/sda1/insert/insert_user/0.2/insert_user-0.2.jar -Enter a person's name or 'x' to exit: <emphasis role="red">Eve', 'eve@my.org');DROP TABLE Person;COMMIT;INSERT INTO Person VALUES('jim</emphasis> -Enter <emphasis role="red">Eve', 'eve@my.org');DROP TABLE Person;COMMIT;INSERT INTO Person VALUES('jim's</emphasis> email or 'x' to exit: sd@de -Exception in thread "main" org.postgresql.util.PSQLException: ERROR: relation "person" does not exist - Position: 13 - at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2103) - at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1836)</screen> - - <para>This <quote>successfully</quote> kills our - <code>Person</code> table:</para> - - <screen>goik@goikschlepptop MinimumTest> cat A1.log -main INFO insert.SimpleInsert - Executing «INSERT INTO Person VALUES('Eve', 'eve@my.org');DROP TABLE Person;COMMIT;INSERT INTO Person VALUES('jim', 'jim@company.com')» -main ERROR insert.SimpleInsert - General database connection problem: -java.sql.SQLSyntaxErrorException: Table 'hdm.Person' doesn't exist - at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:112) ~[insert_user-0.1.jar:?] -...</screen> - - <para>According to the message text the table - <code>Person</code> gets dropped as expected. Thus the - subsequent (second) <code>INSERT</code> action is then bound - to fail.</para> - - <para>In practice this result may be avoided: The database - user in question will (hopefully!) not have sufficient - permissions to drop the whole table. Use <code>GRANT</code> / - <code>REVOKE</code> statements accordingly!</para> - - <para>Malicious modifications by INSERT, UPDATE or DELETE - statements of data records are still possible though!</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="sanitizeUserInput"> - <title>Sanitizing user input</title> - - <para>There are at least two general ways to deal with the disastrous - result of <xref linkend="sqlInjectDropTable"/>:</para> - - <itemizedlist> - <listitem> - <para>Keep the database server from interpreting user input - completely. This is probably the best way and will be discussed in - <xref linkend="sectPreparedStatements"/>.</para> - </listitem> - - <listitem> - <para>Let the application check and process user input. Dangerous - user input may be modified prior to being embedded in SQL - statements or being rejected completely.</para> - </listitem> - </itemizedlist> - - <para>The first method is definitely superior in most cases. There are - however cases where the restrictions being implied are too severe. We - may for example choose dynamically which tables shall be accessed. So - an SQL statement's structure rather than just its predicates is - affected by user input. There are at least two standard procedures - dealing with this problem:</para> - - <glosslist> - <glossentry> - <glossterm>Input Filtering</glossterm> - - <glossdef> - <para>In the simplest case we check a user's input by regular - expressions. An example is an input field in a login window - representing a system user name. Legal input may allows letters - and digits only. Special characters, whitespace etc. are - typically prohibited. The input does have a minimum length of - one character. A maximum length may be imposed as well. So we - may choose the regular expression <code>[A-Za-z0-9]+</code> to - check valid user names.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm><foreignphrase>Whitelisting</foreignphrase></glossterm> - - <glossdef> - <para>In many cases Input fields only allow a restricted set of - values. Consider an input field for names of planets. An - application may keep a dictionary table to validate user - input:</para> - - <informaltable border="1"> - <col width="10%"/> - - <col width="5%"/> - - <tr> - <td>Mercury</td> - - <td>1</td> - </tr> - - <tr> - <td>Venus</td> - - <td>2</td> - </tr> - - <tr> - <td>Earth</td> - - <td>3</td> - </tr> - - <tr> - <td>...</td> - - <td>...</td> - </tr> - - <tr> - <td>Neptune</td> - - <td>9</td> - </tr> - - <tr> - <td><emphasis role="bold">Default:</emphasis></td> - - <td><emphasis role="bold">0</emphasis></td> - </tr> - </informaltable> - - <para>So if a user enters a valid planet name a corresponding - number representing this particular planet will be sent to the - database. If the user enters an invalid string an error message - may be raised.</para> - - <para>In a GUI in many situations this may be better - accomplished by presenting the list of planets to choose from. - In this case a user has no chance to enter invalid or even - malicious code.</para> - </glossdef> - </glossentry> - </glosslist> - - <para>So we have an <quote>interceptor</quote> sitting between user - input fields and SQL generating code:</para> - - <figure xml:id="figInputFiltering"> - <title>Validating user input prior to dynamically composing SQL - statements.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/filtering.fig"/> - </imageobject> - </mediaobject> - </figure> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_RegexpUse"> - <title>Using regular expressions in <xref - linkend="glo_Java"/></title> - - <qandadiv> - <qandaentry> - <question> - <para>This exercise is a preparation for <xref - linkend="exercisefilterUserInput"/>. The aim is to deal with - regular expressions and to use them in <xref - linkend="glo_Java"/>. If you don't know yet about regular - expressions / pattern matching you may want to read either - of:</para> - - <itemizedlist> - <listitem> - <para><link - xlink:href="http://www.aivosto.com/vbtips/regex.html">Regular - expressions - An introduction</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="http://www.codeproject.com/Articles/939/An-Introduction-to-Regular-Expressions">An - Introduction to Regular Expressions</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="http://www.regular-expressions.info/tutorial.html">Regular - Expression Tutorial</link></para> - </listitem> - </itemizedlist> - - <para>Complete the implementation of the following - skeleton:</para> - - <programlisting language="java">... -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public static void main(String[] args) { - final String [] wordList = new String [] {"Eric", "126653BBb", "_login","some text"}; - final String [] regexpList = new String[] {"[A-K].*", "[^0-9]+.*", "_[a-z]+", ""}; - - for (final String word: wordList) { - for (final String regexp: regexpList) { - testMatch(word, regexp); - } - } -} - -/** - * Matching a given word by a regular expression. A log message is being - * written to stdout. - * - * Hint: The implementation is based on the explanation being given in the - * introduction to {@link Pattern} - * - * @param word This string will be matched by the subsequent argument. - * @param regexp The regular expression tested to match the previous argument. - * @return true if regexp matches word, false otherwise. - */ -public static boolean testMatch(final String word, final String regexp) { -.../* to be implemented by <emphasis role="bold">**YOU**</emphasis> */ -}</programlisting> - - <para>As being noted in the <xref linkend="glo_Java"/> above - you may want to read the documentation of class - <classname>java.util.regex.Pattern</classname>. The intended - output of the above application is:</para> - - <screen>The expression '[A-K].*' matches 'Eric' -The expression '[^0-9]+.*' ... -...</screen> - </question> - - <answer> - <para>A possible implementation is given by:</para> - - <programlisting language="java">import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * This class is intended to gain some basic experience with - * regular expressions and their usage in Java - * - * @author goik - * - */ -public class RegexpPrimer { - - - public static void main(String[] args) { - final String [] wordList = new String [] {"Eric", "126653BBb", "_login","some text"}; - final String [] regexpList = new String[] {"[A-K].*", "[^0-9]+.*", "_[a-z]+", ""}; - - for (final String word: wordList) { - for (final String regexp: regexpList) { - testMatch(word, regexp); - } - } - } - - /** - * Matching a given word by a regular expression. A log message is being - * written to stdout. - * - * Hint: The implementation is based on the explanation being given in the - * introduction to {@link Pattern} - * - * @param word This string will be matched by the subsequent argument. - * @param regexp The regular expression tested to match the previous argument. - * @return true if regexp matches word, false otherwise. - */ - public static boolean testMatch(final String word, final String regexp) { - Pattern p = Pattern.compile(regexp); - Matcher m = p.matcher(word); - if (m.matches()) { - System.out.println("The expression '" + regexp + "' matches '" + word + "'"); - return true; - } else { - System.out.println("The expression '" + regexp + "' does not match '" + word + "'"); - return false; - } - } -}</programlisting> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" xml:id="exercisefilterUserInput"> - <title>Input validation by regular expressions</title> - - <qandadiv> - <qandaentry> - <question> - <para>The application of <xref linkend="sqlInjectDropTable"/> - proved to be vulnerable to SQL injection. Sanitize the two - user input field's values to prevent such behaviour.</para> - - <itemizedlist> - <listitem> - <para>Find appropriate validators to check both - <code>username</code> and <code>email</code>. Some - hints:</para> - - <glosslist> - <glossentry> - <glossterm><code>username</code></glossterm> - - <glossdef> - <para>Regarding SQL injection the <quote>;</quote> - character is among the most critical. You may want - to exclude certain special characters like (,) and - similar as well by means of a regular expression. - This doesn't harm since their presence in a user's - name is most likely a typo rather then any sensitive - input. On the other hand it becomes increasingly - hard to construct suitable injection attack - strings.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm><code>email</code></glossterm> - - <glossdef> - <para>There are tons of <quote>ultimate</quote> - regular expressions available to check email - addresses. Remember that rather avoiding - <quote>wrong</quote> email addresses the present - task is to avoid SQL injection. So find a reasonable - one which may be too permissive regarding RFC email - syntax rules but sufficient to secure your - application.</para> - - <para>A concise definition of an email's syntax is - being given in <link - xlink:href="https://tools.ietf.org/html/rfc5322#section-3.4.1">RFC5322</link>. - Its implementation is beyond scope of the current - lecture. Moreover it is questionable whether E-mail - clients and mail transfer agents implement strict - RFC compliance.</para> - </glossdef> - </glossentry> - </glosslist> - - <para>Both regular expressions must cover the whole user - input from the beginning to the end. This can be achieved - by using <code>^ ... $</code>.</para> - </listitem> - - <listitem> - <para>The <xref linkend="glo_Java"/> standard class - <classname>javax.swing.InputVerifier</classname> may help - you validating user input.</para> - </listitem> - - <listitem> - <para>The following screen shot may provide an idea for - GUI realization and user interaction in case of errors. Of - course the submit button's action should be disabled in - case of erroneous input. The user should receive a helpful - error message instead.</para> - - <figure xml:id="figInsertValidate"> - <title>Error message being presented to the - user.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/insertValidate.screen.png"/> - </imageobject> - - <caption> - <para>In the current example the trailing - <quote>;</quote> within the E-Mail field is - invalid.</para> - </caption> - </mediaobject> - </figure> - </listitem> - </itemizedlist> - - <tip> - <para>Vaadin does provide regular expression based - validation support. You may want to consider <classname - xlink:href="https://vaadin.com/api/com/vaadin/data/validator/EmailValidator.html">EmailValidator</classname> - instead.</para> - </tip> - </question> - - <answer> - <annotation role="make"> - <para role="eclipse">P/Sda1/InsertGui/V8</para> - </annotation> - - <para>Validation will be based on both on regular expressions - and Vaadins built in <classname - xlink:href="https://vaadin.com/api/com/vaadin/data/validator/EmailValidator.html">EmailValidator</classname>:</para> - - <programlisting language="java"> @Override - protected void init(final VaadinRequest vaadinRequest) { - - ... - // Sanitizing user names by regular expression - nameField.addValidator(new RegexpValidator("[^;\"'()]+", - "Sorry but this does not appear to be a user's name")); - - // Adding an input validator for sanitizing username and e-mail input values. - // Caveat: Vaadin's validator will refuse e.g. "domainregistry@de" - // and may generally be non-RFC compliant. - emailField.addValidator(new EmailValidator("Not a valid email")); - ... -} -... - - void conditionallyActivateInsertButton() { - final boolean isValid = - 0 < nameField.getValue().trim().length() && - nameField.isValid() && - - // empty fields are not being validated! - 0 < emailField.getValue().trim().length() && - <emphasis role="bold">emailField.isValid();</emphasis> - ...</programlisting> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="sectPreparedStatements"> - <title><classname>java.sql.PreparedStatement</classname></title> - - <para>Sanitizing user input is a means to secure an application. The - <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - standard however provides a mechanism being superior regarding the - purpose of protecting applications against SQL injection attacks. We - shed some light on our current mechanism sending SQL statements to a - database server:</para> - - <figure xml:id="sqlTransport"> - <title>SQL statements in <xref linkend="glo_Java"/> applications get - parsed at the database server</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/sqlTransport.fig"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_jdbc_fig_interpretSqlQuestions"> - <title>Two questions</title> - - <orderedlist> - <listitem> - <para>What happens when executing structural identical SQL - statements (differing only by attribute values) - repeatedly?</para> - - <para>E.g. inserting thousands of records of identical - structure.</para> - </listitem> - - <listitem> - <para>Is this architecture adequate with respect to security - concerns?</para> - </listitem> - </orderedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_interpretSqlPerformance"> - <title>Addressing performance</title> - - <programlisting language="sql">INSERT INTO Person VALUES ('Jim', 'jim@q.org') -INSERT INTO Person VALUES ('Eve', 'eve@y.org') -INSERT INTO Person VALUES ('Pete', 'p@rr.com') -...</programlisting> - - <para>Wasting time parsing SQL over and over again!</para> - </figure> - - <figure xml:id="sda1_jdbc_fig_interpretSqlPerformanceMitigation"> - <title>Addressing performance mitigation</title> - - <programlisting language="sql">INSERT INTO Person VALUES - ('Jim', 'jim@q.org'), - ('Eve', 'eve@y.org'), - ('Pete', 'p@rr.com') ... ;</programlisting> - - <para>Dealing with large record counts even this option may become - questionable.</para> - </figure> - - <figure xml:id="sda1_jdbc_fig_interpretSqlSecurity"> - <title>Addressing security</title> - - <para>The database server's interpreter may interpret an attacker's - malicious code among with intended <xref linkend="glo_SQL"/>.</para> - </figure> - - <figure xml:id="sda1_jdbc_fig_interpretSqlSecurityPerformanceSolution"> - <title>Solution: Use - <classname>java.sql.PreparedStatement</classname></title> - - <itemizedlist> - <listitem> - <para>Parsing happens only once.</para> - </listitem> - - <listitem> - <para>Reuse per record.</para> - </listitem> - - <listitem> - <para>Avoids parsing contained «payload» / user input.</para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sqlTransportPrepare"> - <title><classname>PreparedStatement</classname> principle.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/sqlTransportPrepare.fig"/> - </imageobject> - </mediaobject> - </figure> - - <para>Prepared statements are an example for parameterized SQL - statements which do exist in various programming languages. When using - <classname>java.sql.PreparedStatement</classname> instances we - actually have three distinct phases:</para> - - <figure xml:id="sda1_jdbc_fig_parameterizedSql"> - <title>Three phases using parameterized queries</title> - - <orderedlist> - <listitem> - <para xml:id="exerciseGuiWritePrepared"><classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> - instance creation: Parsing <xref linkend="glo_SQL"/> statement - possibly containing place holders.</para> - </listitem> - - <listitem> - <para>Set values of all placeholder values: No <xref - linkend="glo_SQL"/> parsing happens.</para> - </listitem> - - <listitem> - <para>Execute the statement.</para> - </listitem> - </orderedlist> - - <para>Steps 2. and 3. may be repeated without requiring re-parsing - <xref linkend="glo_SQL"/> statements thus saving database server - resources.</para> - </figure> - - <para>Our introductory toy application <xref - linkend="sda1_fig_jdbcSimpleWrite"/> may be rewritten using <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> - objects:</para> - - <figure xml:id="sda1_jdbc_fig_preparedStatementExample"> - <title><classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> - example</title> - - <programlisting language="java">final Connection conn = DriverManager.getConnection (... - -final PreparedStatement pStmt = conn.prepareStatement( - "INSERT INTO Person VALUES(<emphasis role="bold">?, ?</emphasis>)");<co - xml:id="listPrepCreate"/> - -pStmt.setString(1, "Jim");<co xml:id="listPrepSet1"/> -pStmt.setString(2, "jim@foo.org");<co xml:id="listPrepSet2"/> - -final int updateCount = pStmt.executeUpdate();<co xml:id="listPrepExec"/> - -System.out.println("Successfully inserted " + updateCount + " dataset(s)");</programlisting> - </figure> - - <calloutlist> - <callout arearefs="listPrepCreate"> - <para>An instance of - <classname>java.sql.PreparedStatement</classname> is being - created. Notice the two question marks representing two place - holders for string values to be inserted in the next step.</para> - </callout> - - <callout arearefs="listPrepSet1 listPrepSet2"> - <para>Fill in the two placeholder values being defined at <coref - linkend="listPrepCreate"/>.</para> - - <caution> - <para>Since half the world of programming folks will index a - list of n elements starting from 0 to n-1, <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - apparently counts from 1 to n. Working with <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - would have been too easy otherwise!</para> - </caution> - </callout> - - <callout arearefs="listPrepExec"> - <para>Execute the beast! Notice the empty parameter list. No SQL - is required since we already prepared it in <coref - linkend="listPrepCreate"/>.</para> - </callout> - </calloutlist> - - <figure xml:id="sda1_jdbc_fig_preparedSecurityResult"> - <title>Injection attempt example</title> - - <programlisting language="sql">Jim', 'jim@c.com');DROP TABLE Person;INSERT INTO Person VALUES('Joe</programlisting> - - <para>Attacker's injection text simply becomes part of the database - server's content.</para> - - <para>Problem solved!</para> - </figure> - - <figure xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport"> - <title>No dynamic table support</title> - - <programlisting language="java">PreparedSatatement statement = - connection.prepareStatement("SELECT ? <co - linkends="sda1_jdbc_fig_preparedNoDynamicTableSupport-1.2" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-1.2-co"/> from ?" <co - linkends="sda1_jdbc_fig_preparedNoDynamicTableSupport-2.2" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-2.2-co"/>); -statement.setString(1, "birthday") <co - linkends="sda1_jdbc_fig_preparedNoDynamicTableSupport-3.2" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-3.2-co"/>; -statement.setString(2, "Persons") <co - linkends="sda1_jdbc_fig_preparedNoDynamicTableSupport-4.2" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-4.2-co"/>; -ResultSet rs = statement.executeQuery() <co - linkends="sda1_jdbc_fig_preparedNoDynamicTableSupport-5" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-5-co"/>;</programlisting> - - <para>In a nutshell: <emphasis role="red">Only attribute value - literals may be parameterized.</emphasis></para> - </figure> - - <calloutlist> - <callout arearefs="sda1_jdbc_fig_preparedNoDynamicTableSupport-1.2-co" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-1.2"> - <para>Providing an attributes name as parameter.</para> - </callout> - - <callout arearefs="sda1_jdbc_fig_preparedNoDynamicTableSupport-2.2-co" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-2.2"> - <para>Providing the table name to be queried as parameter.</para> - </callout> - - <callout arearefs="sda1_jdbc_fig_preparedNoDynamicTableSupport-3.2-co" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-3.2"> - <para>Setting the desired attributes name intending:</para> - - <programlisting language="none">SELECT <emphasis role="red">birthday</emphasis> FROM ...</programlisting> - </callout> - - <callout arearefs="sda1_jdbc_fig_preparedNoDynamicTableSupport-4.2-co" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-4.2"> - <para>Setting the table name to be queried intending:</para> - - <programlisting language="none">SELECT birthday FROM <emphasis - role="red">Persons</emphasis></programlisting> - </callout> - - <callout arearefs="sda1_jdbc_fig_preparedNoDynamicTableSupport-5-co" - xml:id="sda1_jdbc_fig_preparedNoDynamicTableSupport-5"> - <para>Fails: Only attribute value literals are allowed.</para> - </callout> - </calloutlist> - - <qandaset defaultlabel="qanda" xml:id="exerciseSqlInjectPrepare"> - <title>Prepared Statements to keep the barbarians at the - gate</title> - - <qandadiv> - <qandaentry> - <question> - <para>Use <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> - objects to sanitize your flawed <xref - linkend="quandaentry_DupInsertUnitTest"/> implementation being - susceptible to <xref linkend="glo_SQL"/> injection - attacks.</para> - - <para>When you are done repeat your injection attempt from - <xref linkend="sqlInjectDropTable"/>. You may require larger - string lengths in your <xref linkend="glo_SQL"/> schema for - accommodating the injection string.</para> - </question> - - <answer> - <para>See:</para> - - <annotation role="make"> - <para role="eclipse">P/Sda1/Jdbc/Insert/Prepared</para> - </annotation> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> - - <section xml:id="jdbcRead"> - <title>Read Access</title> - - <figure xml:id="sda1_jdbc_fig_readAndWrite"> - <title><xref linkend="glo_JDBC"/> read and write</title> - - <itemizedlist> - <listitem> - <para><code language="sql">CREATE</code> / <code - language="sql">UPDATE</code> / <code - language="sql">DELETE</code></para> - - <para>client modifies database server data:</para> - - <programlisting language="java">int result = statement.executeUpdate("UPDATE Person ...");</programlisting> - </listitem> - - <listitem> - <para><code language="sql">SELECT</code></para> - - <para>client receives copies of database server data:</para> - - <programlisting language="java">ResultSet result = statement.executeQuery("SELECT ... FROM Person ...");</programlisting> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="jdbcReadWrite"> - <title>Server / client object's life cycle</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/jdbcReadWrite.fig"/> - </imageobject> - </mediaobject> - </figure> - - <figure xml:id="sda1_jdbc_fig_readContainer"> - <title><xref linkend="glo_JDBC"/> record container</title> - - <itemizedlist> - <listitem> - <para>No standard Collections container e.g. <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/util/List.html">java.util.List</classname>.</para> - </listitem> - - <listitem> - <para><quote>Own</quote> container standard <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html">java.sql.ResultSet</classname> - holding transient database.</para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="figJdbcRead"> - <title>Reading data from a database server.</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/jdbcread.fig" scale="65"/> - </imageobject> - </mediaobject> - </figure> - - <para>We take an example. Suppose our database contains a table of our - friends' nicknames and their respective birth dates:</para> - - <figure xml:id="sda1_jdbc_fig_friendsBirthDates"> - <title>Names and birth dates of friends</title> - - <informaltable border="0"> - <tr> - <td valign="top"><programlisting language="sql">CREATE TABLE Friends ( - id INTEGER NOT NULL PRIMARY KEY - ,nickname char(10) - ,birthdate DATE -);</programlisting></td> - - <td valign="top"><programlisting language="sql">INSERT INTO Friends VALUES - (1, 'Jim', '1991-10-10') - ,(2, 'Eve', '2003-05-24') - ,(3, 'Mick','2001-12-30') - ;</programlisting></td> - </tr> - </informaltable> - </figure> - - <figure xml:id="sda1_jdbc_fig_codeReadingFriendsData"> - <title>Accessing friend's database records</title> - - <programlisting language="java">final Connection conn = DriverManager.getConnection (...); -final Statement stmt = conn.createStatement(); -<emphasis role="bold">// Step 3: Creating the client side JDBC container holding our data records</emphasis> -<emphasis role="bold">final ResultSet data = stmt.executeQuery("SELECT * FROM Friends");</emphasis> <co - linkends="listingJdbcRead-1" xml:id="listingJdbcRead-1-co"/> - -<emphasis role="bold">// Step 4: Dataset iteration -while (data.next()) {</emphasis> <co linkends="listingJdbcRead-2" - xml:id="listingJdbcRead-2-co"/> -<emphasis role="bold"> System.out.println(data.getInt("id")</emphasis> <co - linkends="listingJdbcRead-3" xml:id="listingJdbcRead-3-co"/> - <emphasis role="bold"> + ", " + data.getString("nickname")</emphasis> <co - linkends="listingJdbcRead-3" xml:id="listingJdbcRead-4-co"/> - <emphasis role="bold">+ ", " + data.getString("birthdate"));</emphasis> <co - linkends="listingJdbcRead-3" xml:id="listingJdbcRead-5-co"/> -}</programlisting> - </figure> - - <calloutlist> - <callout arearefs="listingJdbcRead-1-co" xml:id="listingJdbcRead-1"> - <para>As being mentioned in the introduction to this section the - <trademark - xlink:href="https://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</trademark> - standard provides a container interface rather than using - <classname>java.util.List</classname> or similar.</para> - </callout> - - <callout arearefs="listingJdbcRead-2-co" xml:id="listingJdbcRead-2"> - <para>Calling <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#next()">next()</link> - prior to actually accessing data on the client side is mandatory! - The <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#next()">next()</link> - method positions an internal iterator to the first element of our - dataset unless the latter being empty. Follow the link address and - **read** the documentation.</para> - </callout> - - <callout arearefs="listingJdbcRead-3-co listingJdbcRead-4-co listingJdbcRead-5-co" - xml:id="listingJdbcRead-3"> - <para>The access methods have to be chosen according to matching - types. An overview of database/<xref linkend="glo_Java"/> type - mappings is being given in <uri - xlink:href="https://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html">https://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html</uri>.</para> - </callout> - </calloutlist> - - <figure xml:id="sda1_jdbc_fig_ResultSetStates"> - <title>Important <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html">ResultSet</classname> - states</title> - - <glosslist> - <glossentry> - <glossterm>New: <code language="java">resultSet = - statement.executeQuery(...)</code></glossterm> - - <glossdef> - <para>Caution: Data not yet accessible!</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Cursor positioned: <code - language="java">resultSet.next()</code> returning <code - language="java">true</code></glossterm> - - <glossdef> - <para>Data accessible until <code - language="java">resultSet.next()</code>returns <code - language="java">false</code>.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>Closed: <code language="java">resultSet.next()</code> - returning <code language="java">false</code></glossterm> - - <glossdef> - <para>Caution: Data not longer accessible!</para> - </glossdef> - </glossentry> - </glosslist> - </figure> - - <figure xml:id="sda1_jdbc_fig_Sql2JdbcTypeConversion"> - <title><xref linkend="glo_JDBC"/> to <xref linkend="glo_Java"/> type - conversions</title> - - <informaltable border="1"> - <tr> - <th><xref linkend="glo_JDBC"/> Type</th> - - <th><xref linkend="glo_Java"/> type</th> - </tr> - - <tr> - <td valign="top"><code language="sql">CHAR</code>, <code - language="sql">VARCHAR</code>, <code - language="sql">LONGVARCHAR</code></td> - - <td valign="top"><classname>String</classname></td> - </tr> - - <tr> - <td valign="top"><code language="sql">NUMERIC</code>, <code - language="sql">DECIMAL</code></td> - - <td valign="top"><classname>java.math.BigDecimal</classname></td> - </tr> - - <tr> - <td valign="top"><code language="sql">BIT</code></td> - - <td valign="top"><code language="java">boolean</code></td> - </tr> - - <tr> - <td valign="top"><code language="sql">TINYINT</code></td> - - <td valign="top"><code language="java">byte</code></td> - </tr> - - <tr> - <td valign="top"><code language="sql">...</code></td> - - <td valign="top"><code language="java">...</code></td> - </tr> - </informaltable> - - <para>Shamelessly copied from <link - xlink:href="https://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html#1051555">JDBC - Types Mapped to Java Types</link>.</para> - </figure> - - <figure xml:id="sda1_jdbc_fig_Jdbc2SqlTypeConversion"> - <title><xref linkend="glo_Java"/> to <xref linkend="glo_JDBC"/> type - conversions</title> - - <informaltable border="1"> - <tr> - <th><xref linkend="glo_Java"/> Type</th> - - <th><xref linkend="glo_JDBC"/> type</th> - </tr> - - <tr> - <td valign="top"><classname>String</classname></td> - - <td valign="top"><code language="sql">CHAR</code>, <code - language="sql">VARCHAR</code>, <code - language="sql">LONGVARCHAR</code></td> - </tr> - - <tr> - <td valign="top"><classname>java.math.BigDecimal</classname></td> - - <td valign="top"><code language="sql">NUMERIC</code></td> - </tr> - - <tr> - <td valign="top"><code language="java">boolean</code></td> - - <td valign="top"><code language="sql">BIT</code></td> - </tr> - - <tr> - <td valign="top"><code language="sql">...</code></td> - - <td valign="top"><code language="java">...</code></td> - </tr> - </informaltable> - - <para>Shamelessly copied from <link - xlink:href="https://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html#1033804">Java - Types Mapped to JDBC Types</link>.</para> - </figure> - - <figure xml:id="sda1_jdbc_fig_ResultSetTypedAccess"> - <title>Fixed type accessors</title> - - <programlisting language="java">int getInt​(int columnIndex) -double getDouble​(int columnIndex) -Date getDate​(int columnIndex) -...</programlisting> - </figure> - - <figure xml:id="sda1_jdbc_fig_ResultSetGetObject"> - <title>Polymorphic accessors</title> - - <programlisting language="java">Object getObject​(int columnIndex)</programlisting> - - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#getObject(int)">Gets - the value of the designated column in the current row of this - ResultSet object as an Object in the Java programming - language.</link></para> - </figure> - - <figure xml:id="sda1_jdbc_fig_ResultSetByColumnString"> - <title>Access by column name</title> - - <informaltable border="0"> - <tr> - <td valign="top"><programlisting language="java">final int id = - resultSet.getInt("id"); -final String nickName = - resultSet.getString("nickname"); -final Date birthDate = - resultSet.getDate​("birthdate");</programlisting></td> - - <td valign="top"><programlisting language="sql">CREATE TABLE Friends ( - id INTEGER NOT NULL PRIMARY KEY - ,nickname char(10) - ,birthdate DATE -);</programlisting></td> - </tr> - </informaltable> - </figure> - - <figure xml:id="sda1_jdbc_fig_ResultSetByColumnIndex"> - <title>Access by column index</title> - - <informaltable border="0"> - <tr> - <td valign="top"><programlisting language="java">final int id = - resultSet.getInt(1); -final String nickName = - resultSet.getString(2); -final Date birthDate = - resultSet.getDate(3);</programlisting></td> - - <td valign="top"><programlisting language="sql">CREATE TABLE Friends ( - id INTEGER NOT NULL PRIMARY KEY - ,nickname char(10) - ,birthdate DATE -);</programlisting></td> - </tr> - </informaltable> - </figure> - - <para>We now present a series of exercises thereby exploring important - aspects of <xref linkend="glo_JDBC"/> read access.</para> - - <section xml:id="sectGetterTypeConversion"> - <title>Getter methods and type conversion</title> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_JdbcTypeConversion"> - <qandadiv> - <qandaentry> - <question> - <para>Apart from type mappings the <xref linkend="glo_JDBC"/> - access methods like <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#getString(int)">getString()</link> - may also be used for type conversion. Modify <xref - linkend="sda1_jdbc_fig_codeReadingFriendsData"/> by:</para> - - <itemizedlist> - <listitem> - <para>Read the database attribute <code>id</code> by <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#getString(java.lang.String)">getString(String)</link>.</para> - </listitem> - - <listitem> - <para>Read the database attribute nickname by <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#getInt(java.lang.String)">getInt(String)</link>.</para> - </listitem> - </itemizedlist> - - <para>What do you observe?</para> - </question> - - <answer> - <para>Modifying our iteration loop:</para> - - <programlisting language="java">// Step 4: Dataset iteration -while (data.next()) { - System.out.println(data.<emphasis role="bold">getString</emphasis>("id") <co - linkends="jdbcReadWrongType-1" - xml:id="jdbcReadWrongType-1-co"/> - + ", " + data.<emphasis role="bold">getInt</emphasis>("nickname") <co - linkends="jdbcReadWrongType-2" - xml:id="jdbcReadWrongType-2-co"/> - + ", " + data.getString("birthdate")); -}</programlisting> - - <para>We observe:</para> - - <calloutlist> - <callout arearefs="jdbcReadWrongType-1-co" - xml:id="jdbcReadWrongType-1"> - <para>Calling <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#getString(int)">getString()</link> - for a database attribute of type INTEGER does not cause - any trouble: The value gets silently converted to a string - value.</para> - </callout> - - <callout arearefs="jdbcReadWrongType-2-co" - xml:id="jdbcReadWrongType-2"> - <para>Calling <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#getInt(java.lang.String)">getInt(String)</link> - for the database field of type CHAR yields an (expected) - Exception:</para> - </callout> - </calloutlist> - - <screen>Exception in thread "main" java.sql.SQLException: Invalid value for getInt() - 'Jim' - at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073) -...</screen> - - <para>We may however provide <quote>compatible</quote> data - records:</para> - - <programlisting language="sql">DELETE FROM Friends; -INSERT INTO Friends VALUES (1, <emphasis role="bold">'31'</emphasis>, '1991-10-10');</programlisting> - - <para>This time our application executes perfectly - well:</para> - - <screen>1, 31, 1991-10-10</screen> - - <para>Conclusion: The <xref linkend="glo_JDBC"/> driver - performs a conversion from a string type to an integer similar - like the <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Integer.html#parseInt(java.lang.String)">parseInt(String)</link> - method.</para> - - <para>The next series of exercises aims on a more powerful - implementation of our person data insertion application in - <xref linkend="sda1SectGuiAuthenticateTheRealMcCoy"/>.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <figure xml:id="sda1_jdbc_fig_ResultSetHandlingNull"> - <title>Problem: <code language="java">null</code> value - ambiguity</title> - - <programlisting language="java">final int count = resultSet.getInt("numProducts");</programlisting> - - <para>Problem: Two possibilities in case of <code - language="java">count == 0</code>:</para> - - <orderedlist> - <listitem> - <para>DB attribute <property>numProducts</property> is 0 - (zero).</para> - </listitem> - - <listitem> - <para>DB attribute <property>numProducts</property> is <code - language="sql">null</code>.</para> - </listitem> - </orderedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_ResultSetHandlingNullResolve"> - <title>Resolving <code language="java">null</code> value - ambiguity</title> - - <programlisting language="java">final int count = resultSet.getInt("numProducts"); - -if (resultSet.wasNull()) { -... -} else { -... -}</programlisting> - - <para>See <methodname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#wasNull()">wasNull()</methodname>.</para> - </figure> - </section> - - <section xml:id="sectHandlingNullValues"> - <title>Handling NULL values.</title> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_HandlingNull"> - <qandadiv> - <qandaentry> - <question> - <para>The attribute <code>birthday</code> in our database - table Friends allows <code>NULL</code> values:</para> - - <programlisting language="sql">INSERT INTO Friends VALUES - (1, 'Jim', '1991-10-10') - ,(2, <emphasis role="bold"> NULL</emphasis>, '2003-5-24') - ,(3, 'Mick', '2001-12-30');</programlisting> - - <para>Starting our current application yields:</para> - - <screen>1, Jim, 1991-10-10 -2, null, 2003-05-24 -3, Mick, 2001-12-30</screen> - - <para>This might be confuses with a person having the nickname - <quote>null</quote>. Instead we would like to have:</para> - - <screen>1, Jim, 1991-10-10 -2, -Name unknown- , 2003-05-24 -3, Mick, 2001-12-30</screen> - - <para>Extend the current code of - <classname>sda.jdbc.intro.SimpleRead</classname> to produce - the above result in case of nickname <code>NULL</code> - values.</para> - - <para>Hint: Read the documentation of <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/ResultSet.html#wasNull()">wasNull()</link>.</para> - </question> - - <answer> - <para>A possible implementation is being given in - <classname>sda.jdbc.intro.v1.SimpleRead</classname>.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="sda1SectRelationadatal2Xml"> - <title>Reversing <xref linkend="glo_XML"/> to Rdbms</title> - - <qandaset defaultlabel="qanda" xml:base="qandaRelationaldata2Xml" - xml:id="qandaRelationaldata2Xml"> - <qandadiv> - <qandaentry> - <question> - <para>Reverse exercise <xref - linkend="qandaXmldata2relational"/> to read Rdbms data via - <xref linkend="glo_JDBC"/> and export corresponding XML data - using Jdom. You will need two database tables describing each - product among with at least one description. Consider the - following schema among with some sample data:</para> - - <programlisting language="sql">DROP TABLE IF EXISTS Description; -DROP TABLE IF EXISTS Product; - -CREATE TABLE Product ( - id INTEGER NOT NULL - ,name VARCHAR(255) NOT NULL - ,age SMALLINT - ,PRIMARY KEY(id) -); - -CREATE TABLE Description ( - product INTEGER NOT NULL - ,orderIndex int NOT NULL -- preserving the order of descriptions belonging to a given product - ,text VARCHAR(255) NOT NULL - ,UNIQUE(product, orderIndex) - ,FOREIGN KEY(product) REFERENCES Product(id) -); - --- example data corresponding to products.xml -- - --- A single product lacking age property -- -INSERT INTO Product (id, name) VALUES (1, 'Monkey Picked Tea'); - -INSERT INTO Description VALUES(1, 0, 'Picked only by specially trained monkeys.'); -INSERT INTO Description VALUES(1, 1, 'Rare wild Chinese tea.'); - -INSERT INTO Product VALUES (2, '4-Person Instant Tent', 15); -INSERT INTO Description VALUES(2, 0, 'Exclusive WeatherTec system.'); -INSERT INTO Description VALUES(2, 1, '4-person, 1-compartment tent.'); -INSERT INTO Description VALUES(2, 2, 'Pre-attached tent poles.');</programlisting> - - <orderedlist> - <listitem> - <para>Explain the ratio of the <code>UNIQUE(product, - orderIndex)</code> constraint in - <classname>Description</classname>.</para> - </listitem> - - <listitem> - <para>Write a <xref linkend="glo_JDBC"/> application which - reads from your RDBMS data and exports a corresponding - <xref linkend="glo_XML"/> instance:</para> - - <programlisting language="xml"><catalog> - <product id="1"> - <name>Monkey Picked Tea</name> - <description>Picked only by specially trained monkeys</description> - <description>Rare wild Chinese tea.</description> - </product> - <product id="2"> - <name>4-Person Instant Tent</name> - <description>Exclusive WeatherTec system.</description> - <description>4-person, 1-compartment tent.</description> - <description>Pre-attached tent poles.</description> - <age>15</age> - </product> -</catalog></programlisting> - - <para>Use <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/PreparedStatement.html">PreparedStatement</link> - instances throughout your application.</para> - </listitem> - </orderedlist> - </question> - - <answer> - <orderedlist> - <listitem> - <para>Without <code>UNIQUE(product, orderIndex)</code> the - following state would become possible:</para> - - <literallayout>+---------+------------------------------+------------+ -| product | text | orderIndex | -+---------+------------------------------+------------+ -| ... | ... | ... | -| 2 | Exclusive WeatherTec system. | 1 | -| 2 | 4-person, 1-compartment tent.| 1 | -| ... | ... | ... | -+---------+------------------------------+------------+</literallayout> - - <para>The presence of two distinct descriptions having - identical orderIndex does not enforce the required - order.</para> - </listitem> - - <listitem> - <para>See:</para> - - <annotation role="make"> - <para role="eclipse">P/Sda1/rdbms2catalog</para> - </annotation> - </listitem> - </orderedlist> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - - <section xml:id="sda1SectCatalog2html"> - <title>Generating HTML from both XML and relational input data</title> - - <qandaset defaultlabel="qanda" xml:id="sda1QandaCatalogRdbms"> - <qandadiv> - <qandaentry> - <question> - <para>We want to extend the transformation <xref - linkend="xml2xml"/> by reading price information from a RDBMS. - Consider the following schema:</para> - - <programlisting language="sql">CREATE TABLE Product( - orderNo CHAR(10) NOT NULL PRIMARY KEY - ,price DECIMAL (9,2) NOT NULL -); - -INSERT INTO Product VALUES('x-223', 330.20); -INSERT INTO Product VALUES('w-124', 110.40); -</programlisting> - - <para>Adding prices to your final HTML output may be - implemented the following way:</para> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/xml2html.fig"/> - </imageobject> - </mediaobject> - - <para>You may start by implementing <emphasis>and - testing</emphasis> the following methods of a RDBMS - interfacing class using <xref linkend="glo_JDBC"/>:</para> - - <programlisting language="java">public class DbAccess { - - /** Open a connection to a RDBMS. - * @param jdbcUrl The database server's address - * @param userName The user's name - * @param password The user's password - */ - public void connect(final String jdbcUrl, - final String userName, final String password) { - ... - } - /** Read the price from the RDBMS for a given article number. In a "real" - * application we'd use an integer type rather than a string. - * @param articleNumber - * The number of the article for which we search pricing information. - * @return - * The price if available, else an error message. - */ - public String readPrice(final String articleNumber) { - ... - } - /** - * Close the database connection. - */ - public void close() { - ... - } - } -}</programlisting> - - <tip> - <para>You may want to write a small testbed assuring RDBMS - access functionality working properly prior to integrating - it into your <xref linkend="glo_DOM"/> application producing - HTML output.</para> - </tip> - - <para>Then extend <xref linkend="xml2xml"/> by introducing a - new method <methodname>addPrices(final Document - catalog)</methodname> which adds prices to the <acronym - xlink:href="https://www.w3.org/DOM">DOM</acronym> tree - accordingly.</para> - </question> - - <answer> - <para>The additional functionality on top of <xref - linkend="xml2xml"/> is represented by a method - <methodname>de.hdm_stuttgart.mi.sda1.rdbmsxml2html.XmlRdbms2Html.addPrices()</methodname>. - This method modifies the <acronym - xlink:href="https://www.w3.org/DOM">DOM</acronym> input tree - prior to applying the XSL by inserting data received from the - RDBMS:</para> - - <annotation role="make"> - <para role="eclipse">P/Sda1/rdbmsXml2Html</para> - </annotation> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> - - <section xml:id="sda1_jdbc_sect_surrogateKeys"> - <title>Handling surrogate keys</title> - - <figure xml:id="sda1_jdbc_fig_userGroup"> - <title>Users and groups</title> - - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Jdbc/Users/userModel.svg"/> - </imageobject> - </mediaobject> - </figure> - </section> - - <section xml:id="sda1_jdbc_tarnsactions"> - <title>Transactions</title> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_AutoCommit"> - <title><xref linkend="glo_JDBC"/> and transactions</title> - - <figure xml:id="sda1_jdbc_fig_transactionIsolationLevel"> - <title>Isolation level</title> - - <itemizedlist> - <listitem> - <para><emphasis role="bold">Level 0</emphasis>: Prevent other - transactions from changing data that has already been modified - by an uncommitted transaction.</para> - - <para>Other transactions can read uncommitted data resulting in - <quote>dirty reads</quote>.</para> - </listitem> - - <listitem> - <para><emphasis role="bold">Level 1</emphasis>: Prevents dirty - reads. (Default on many <xref linkend="glo_RDBMS"/>)</para> - </listitem> - - <listitem> - <para><emphasis role="bold">Level 2</emphasis>: prevents - non-repeatable reads.</para> - </listitem> - - <listitem> - <para><emphasis role="bold">Level 3</emphasis>: Data read by one - transaction is valid until the end of that transaction, - preventing phantom rows.</para> - </listitem> - </itemizedlist> - </figure> - - <qandaset defaultlabel="qanda" xml:id="sda1_jdbc_qandaIs1Vs2"> - <title>Isolation level 1 vs. 2</title> - - <qandadiv> - <qandaentry> - <question> - <para>What is the difference between Isolation level 1 and - two?</para> - </question> - - <answer> - <para>Consider the following schedule being prohibited in - isolation level 1:</para> - - <informaltable border="0"> - <tr> - <th>Transaction A</th> - - <th>Transaction B</th> - </tr> - - <tr> - <td valign="top">-</td> - - <td valign="top">begin transaction</td> - </tr> - - <tr> - <td valign="top">begin transaction</td> - - <td valign="top">-</td> - </tr> - - <tr> - <td valign="top">-</td> - - <td valign="top">write X</td> - </tr> - - <tr> - <td valign="top">read X (Dirty read)</td> - - <td valign="top">-</td> - </tr> - - <tr> - <td valign="top">-</td> - - <td valign="top">rollback transaction</td> - </tr> - - <tr> - <td valign="top">...</td> - - <td valign="top">-</td> - </tr> - </informaltable> - - <para>Isolation level 2 will prohibit successfully committed - intermediate results as well:</para> - - <informaltable border="0"> - <tr> - <th>Transaction A</th> - - <th>Transaction B</th> - </tr> - - <tr> - <td valign="top">begin transaction</td> - - <td valign="top">-</td> - </tr> - - <tr> - <td valign="top">read X</td> - - <td valign="top">-</td> - </tr> - - <tr> - <td valign="top">-</td> - - <td valign="top">begin transaction</td> - </tr> - - <tr> - <td valign="top">-</td> - - <td valign="top">write X</td> - </tr> - - <tr> - <td valign="top">-</td> - - <td valign="top">commit transaction</td> - </tr> - - <tr> - <td valign="top">read X</td> - - <td valign="top">-</td> - </tr> - - <tr> - <td valign="top">commit</td> - - <td valign="top">-</td> - </tr> - </informaltable> - - <para>Note that the second <quote>read X</quote> operation in - transaction A will access successfully committed data - presumably different to its first <quote>read X</quote> - result. This is compatible with isolation level 1 since no - <quote>dirty</quote> data is being involved. Isolation level 2 - prevents this behaviour.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <figure xml:id="sda1_jdbc_fig_jdbcIsolationLevelApi"> - <title><xref linkend="glo_JDBC"/> Isolation level</title> - - <itemizedlist> - <listitem> - <para>Transaction unsupported: <property - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#TRANSACTION_NONE">Connection.TRANSACTION_NONE</property></para> - </listitem> - - <listitem> - <para>Level 0: <property - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#TRANSACTION_READ_COMMITTED">Connection.TRANSACTION_READ_COMMITTED</property></para> - </listitem> - - <listitem> - <para>Level 1: <property - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#TRANSACTION_READ_UNCOMMITTED">Connection.TRANSACTION_READ_UNCOMMITTED</property></para> - </listitem> - - <listitem> - <para>Level 2: <property - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#TRANSACTION_REPEATABLE_READ">Connection.TRANSACTION_REPEATABLE_READ</property></para> - </listitem> - - <listitem> - <para>Level 2: <property - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#TRANSACTION_SERIALIZABLE">Connection.TRANSACTION_SERIALIZABL</property></para> - </listitem> - </itemizedlist> - </figure> - - <figure xml:id="sda1_jdbc_fig_jdbcSetIsolationLevel"> - <title>Setting the isolation level</title> - - <programlisting language="java">connection.setTransactionIsolation​(Connection.TRANSACTION_READ_COMMITTED);</programlisting> - - <para>See <code - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#TRANSACTION_READ_COMMITTED">Connection.TRANSACTION_READ_COMMITTED</code> - and <methodname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#setTransactionIsolation(int)">setTransactionIsolation</methodname>.</para> - - <note> - <para>Will become effective when starting next transaction.</para> - </note> - </figure> - - <qandadiv> - <qandaentry> - <question> - <itemizedlist> - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#setAutoCommit(boolean)">How - does the method setAutoCommit()</link> relate to <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#commit()">commit()</link> - and <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#rollback()">rollback()</link>? - Is there a <quote>start transaction</quote> method on - offer?</para> - </listitem> - - <listitem> - <para>How may we group individual <xref linkend="glo_SQL"/> - statement into transactions?</para> - </listitem> - </itemizedlist> - </question> - - <answer> - <para>A connection's default state is <code>autocommit == - true</code>. In this state each individual <xref - linkend="glo_SQL"/> statement (<code>SELECT</code>, - <code>UPDATE</code>, ...) defines a separate transaction.</para> - - <para>The <xref linkend="glo_JDBC"/> API does not provide a - <quote>start transaction</quote> equivalent. Instead - transactions are being started implicitly and last until <code - language="java">connection</code>.<methodname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#commit()">commit()</methodname> - is being executed.</para> - - <para>Grouping two or more <xref linkend="glo_SQL"/> statements - into a transaction in turn requires:</para> - - <orderedlist> - <listitem> - <para>Calling - <code>connection.setAutoComit(false)</code>.</para> - </listitem> - - <listitem> - <para>All subsequent <xref linkend="glo_SQL"/> statements - will implicitly become part of the <quote>current</quote> - transaction till either of the following three events - happen:</para> - - <orderedlist numeration="loweralpha"> - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#commit()">commit()</link></para> - </listitem> - - <listitem> - <para><link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#rollback()">rollback()</link></para> - </listitem> - - <listitem> - <para>The transaction gets aborted by the database - server. This may for example happen in case of a - deadlock conflict with a second transaction.</para> - </listitem> - </orderedlist> - - <note> - <para>Both <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#commit()">commit()</link> - and <link - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/Connection.html#rollback()">rollback()</link> - are being initiated from the client side whereas aborting - a transaction happens on behalf of the database - server.</para> - </note> - </listitem> - </orderedlist> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - - <qandaset defaultlabel="qanda" xml:id="quandaentry_AbortTran"> - <title>Aborted transactions</title> - - <qandadiv> - <qandaentry> - <question> - <para>In the previous exercise we mentioned the possibility of a - transaction aborts being issued by a database server. Which - responsibility arises for an application programmer?</para> - - <tip> - <para>How may an implementation become aware of an «abort - transaction» event?</para> - </tip> - </question> - - <answer> - <para>On aborting a transaction a database server will cause the - corresponding <xref linkend="glo_JDBC"/> client to throw a - <classname - xlink:href="https://docs.oracle.com/javase/10/docs/api/java/sql/SQLException.html">SQLException</classname>. - An application is obliged to implement a sensible <code - language="java">catch(...)</code> clause.</para> - </answer> - </qandaentry> - </qandadiv> - </qandaset> - </section> - </section> -</chapter>