From 8d757de0bd8a2c59d035af7f493c0e53b2bd90ff Mon Sep 17 00:00:00 2001 From: Martin Goik <goik@hdm-stuttgart.de> Date: Tue, 19 Dec 2017 13:50:49 +0100 Subject: [PATCH] Insert Person records by PreparedStatement --- Doc/Sda1/jdbc.xml | 289 +++++++----------- P/Sda1/Jdbc/Insert/Prepared/.gitignore | 8 + P/Sda1/Jdbc/Insert/Prepared/pom.xml | 95 ++++++ .../sda1/insert/PreparedInsert.java | 101 ++++++ .../src/main/resources/jdbc.properties | 3 + .../Prepared/src/main/resources/log4j2.xml | 21 ++ .../Prepared/src/main/resources/schema.sql | 6 + .../hdm_stuttgart/sda1/insert/InsertTest.java | 64 ++++ P/pom.xml | 3 + 9 files changed, 415 insertions(+), 175 deletions(-) create mode 100644 P/Sda1/Jdbc/Insert/Prepared/.gitignore create mode 100644 P/Sda1/Jdbc/Insert/Prepared/pom.xml create mode 100644 P/Sda1/Jdbc/Insert/Prepared/src/main/java/de/hdm_stuttgart/sda1/insert/PreparedInsert.java create mode 100644 P/Sda1/Jdbc/Insert/Prepared/src/main/resources/jdbc.properties create mode 100644 P/Sda1/Jdbc/Insert/Prepared/src/main/resources/log4j2.xml create mode 100644 P/Sda1/Jdbc/Insert/Prepared/src/main/resources/schema.sql create mode 100644 P/Sda1/Jdbc/Insert/Prepared/src/test/java/de/hdm_stuttgart/sda1/insert/InsertTest.java diff --git a/Doc/Sda1/jdbc.xml b/Doc/Sda1/jdbc.xml index 464b6a926..4af660137 100644 --- a/Doc/Sda1/jdbc.xml +++ b/Doc/Sda1/jdbc.xml @@ -2306,8 +2306,7 @@ public class RegexpPrimer { </section> <section xml:id="sectPreparedStatements"> - <title><classname>java.sql.PreparedStatement</classname> - objects</title> + <title><classname>java.sql.PreparedStatement</classname></title> <para>Sanitizing user input is a means to secure an application. The <trademark @@ -2328,57 +2327,78 @@ public class RegexpPrimer { </mediaobject> </figure> - <para>This architecture raises two questions:</para> + <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? This - may happen e.g. inside a loop when thousands of records sharing - identical structure are being inserted into a database.</para> - </listitem> + <orderedlist> + <listitem> + <para>What happens when executing structural identical SQL + statements (differing only by attribute values) + repeatedly?</para> - <listitem> - <para>Is this architecture adequate with respect to security - concerns?</para> - </listitem> - </orderedlist> + <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> - <para>The first question is related to performance: Parsing statements - being identical despite the properties being contained within is a - waste of resources. We consider the transfer of records between - different databases:</para> + <figure xml:id="sda1_jdbc_fig_interpretSqlPerformance"> + <title>Addressing performance</title> - <programlisting language="sql">INSERT INTO Person VALUES ('Jim', 'jim@q.org') + <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>In this case it does not make sense to repeatedly parse - identical SQL statements. We may however use single - <code>INSERT</code> statements with multiple data - records:<programlisting language="sql">INSERT INTO Person VALUES + <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> + ('Pete', 'p@rr.com') ... ;</programlisting> - <para>Dealing with large record counts even this option may become - questionable.</para> + <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 second question is related to our current security topic: - The database server's interpreter may interpret an attacker's - malicious code as well.</para> + <para>The database server's interpreter may interpret an attacker's + malicious code among with intended <xref linkend="glo_SQL"/>.</para> + </figure> - <para>Both topics are being addressed by - <classname>java.sql.PreparedStatement</classname> objects. Basically - these objects allow for separation of an SQL statements <emphasis - role="bold">structure</emphasis> from parameter values being contained - within. The scenario given in <xref linkend="sqlTransport"/> may be - implemented as:</para> + <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>Using <classname>java.sql.PreparedStatement</classname> - objects.</title> + <title><classname>java.sql.PreparedStatement</classname> + principle.</title> <mediaobject> <imageobject> @@ -2392,58 +2412,55 @@ INSERT INTO Person VALUES ('Pete', 'p@rr.com') <classname>java.sql.PreparedStatement</classname> instances we actually have three distinct phases:</para> - <orderedlist> - <listitem> - <para xml:id="exerciseGuiWritePrepared">Creating an instance of - <classname - xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname>. - The SQL statement possibly containing place holders gets - parsed.</para> - </listitem> + <figure xml:id="sda1_jdbc_fig_parameterizedSql"> + <title>Three phases using parameterized queries</title> - <listitem> - <para>Setting all placeholder values. This does not involve any - further SQL syntax parsing.</para> - </listitem> + <orderedlist> + <listitem> + <para xml:id="exerciseGuiWritePrepared"><classname + xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> + instance creation: Parsing <xref linkend="glo_SQL"/> statement + possibly containing place holders.</para> + </listitem> - <listitem> - <para>Execute the statement.</para> - </listitem> - </orderedlist> + <listitem> + <para>Set values of all placeholder values: No <xref + linkend="glo_SQL"/> parsing happens.</para> + </listitem> - <para>Steps 2. and 3. may be repeated as often as desired without any - re-parsing of SQL statements thus saving resources on the database - server side.</para> + <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/9/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> objects:</para> - <programlisting language="java">sda.jdbc.intro.v1; -... -public class SimpleInsert { - - public static void main(String[] args) throws SQLException { - - final Connection conn = DriverManager.getConnection (... + <figure xml:id="sda1_jdbc_fig_preparedStatementExample"> + <title><classname + xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> + example</title> + + <programlisting language="java">final Connection conn = DriverManager.getConnection (... - // Step 2: Create a PreparedStatement instance - final PreparedStatement pStmt = conn.prepareStatement( - "INSERT INTO Person VALUES(<emphasis role="bold">?, ?</emphasis>)");<co - xml:id="listPrepCreate"/> +final PreparedStatement pStmt = conn.prepareStatement( + "INSERT INTO Person VALUES(<emphasis role="bold">?, ?</emphasis>)");<co + xml:id="listPrepCreate"/> - // Step 3a: Fill in desired attribute values - pStmt.setString(1, "Jim");<co xml:id="listPrepSet1"/> - pStmt.setString(2, "jim@foo.org");<co xml:id="listPrepSet2"/> +pStmt.setString(1, "Jim");<co xml:id="listPrepSet1"/> +pStmt.setString(2, "jim@foo.org");<co xml:id="listPrepSet2"/> - // Step 3b: Execute the desired INSERT - final int updateCount = pStmt.executeUpdate();<co xml:id="listPrepExec"/> +final int updateCount = pStmt.executeUpdate();<co xml:id="listPrepExec"/> - // Step 4: Give feedback to the enduser - System.out.println("Successfully inserted " + updateCount + " dataset(s)"); - } -}</programlisting> +System.out.println("Successfully inserted " + updateCount + " dataset(s)"); </programlisting> + </figure> <calloutlist> <callout arearefs="listPrepCreate"> @@ -2474,15 +2491,16 @@ public class SimpleInsert { </callout> </calloutlist> - <para>The problem of SQL injection disappears completely when using - <classname - xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> - instances. An attacker may safely enter offending strings like:</para> + <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> - <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>The above string will be taken <quote>as is</quote> and thus - simply becomes part of the database server's content.</para> + <para>Problem solved!</para> + </figure> <qandaset defaultlabel="qanda" xml:id="exerciseSqlInjectPrepare"> <title>Prepared Statements to keep the barbarians at the @@ -2491,108 +2509,29 @@ public class SimpleInsert { <qandadiv> <qandaentry> <question> - <para>In <xref linkend="sqlInjectDropTable"/> we found our - implementation in <xref - linkend="sda1SectUserInitiatedConnect"/> to be vulnerable with - respect to SQL injection. Rather than sanitizing user input - you shall use <classname + <para>Use <classname xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> - objects to secure the application.</para> - - <tip> - <itemizedlist> - <listitem> - <para>You only have to change the implementation - of:</para> - - <annotation role="make"> - <para - role="eclipse">Sda1/PersistenceHandler/Statement</para> - </annotation> - </listitem> - - <listitem> - <para>You may reuse your unit tests.</para> - </listitem> - </itemizedlist> - </tip> + 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>Due to our separation of GUI and persistence handling we - only need to re-implement - <classname>PersistenceHandler</classname> replacing <classname - xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/Statement.html">Statement</classname> - by <classname - xlink:href="https://docs.oracle.com/javase/9/docs/api/java/sql/PreparedStatement.html">PreparedStatement</classname> - instances:</para> - - <annotation role="make"> - <para - role="eclipse">Sda1/PersistenceHandler/PreparedStatement</para> - </annotation> - - <para>Regarding our GUI we only need two changes:</para> - - <orderedlist> - <listitem> - <para>Removing our sanitizing code.</para> - </listitem> - - <listitem> - <para>Update our re - implementation's dependency version - number <coref linkend="sda1ProgPreparedPersistVersion"/> - to reflect the required library change:</para> - - <programlisting language="xml">... -<dependency> - <groupId>de.hdm_stuttgart.mi.persistence</groupId> - <artifactId>persistencehandler</artifactId> - <emphasis role="bold"><version>4</version></emphasis> <co - xml:id="sda1ProgPreparedPersistVersion"/> -</dependency></programlisting> - </listitem> - </orderedlist> - - <para>This yields:</para> + <para>See:</para> <annotation role="make"> - <para role="eclipse">Sda1/InsertGui/V10</para> + <para role="eclipse">Sda1/Jdbc/Insert/Prepared</para> </annotation> - - <para>We may now safely enter offending strings like:</para> - - <programlisting language="sql">Jim', 'jim@c.com');DROP TABLE Person;INSERT INTO Person VALUES('Joe</programlisting> - - <para>This time the input value is taken <quote>as is</quote> - and our database server responds with a different error - message:</para> - - <informalfigure> - <mediaobject> - <imageobject> - <imagedata fileref="Ref/Fig/sqlInjectPrepare.screen.png"/> - </imageobject> - </mediaobject> - </informalfigure> - - <para>Reason: The offending string exceeds the attribute - <code>name's </code>length. We may enlarge this value enabling - the <code>INSERT</code> operation:</para> - - <programlisting language="sql">CREATE TABLE Person ( - name char(<emphasis role="bold">80</emphasis>) <emphasis role="bold">-- a little bit longer --</emphasis> - ,email CHAR(20) UNIQUE -);</programlisting> </answer> </qandaentry> </qandadiv> </qandaset> - - <para>You have followed the track of test-driven development. This - allowed to safely change the implementation of - <classname>PersistenceHandler</classname> without worrying to much - about possible side-effects.</para> </section> </section> diff --git a/P/Sda1/Jdbc/Insert/Prepared/.gitignore b/P/Sda1/Jdbc/Insert/Prepared/.gitignore new file mode 100644 index 000000000..7913c9bee --- /dev/null +++ b/P/Sda1/Jdbc/Insert/Prepared/.gitignore @@ -0,0 +1,8 @@ +/dependency-reduced-pom.xml +/A1.log +/.settings +/.idea +/*.iml +/.classpath +/.project +/target diff --git a/P/Sda1/Jdbc/Insert/Prepared/pom.xml b/P/Sda1/Jdbc/Insert/Prepared/pom.xml new file mode 100644 index 000000000..9d6e1bfd4 --- /dev/null +++ b/P/Sda1/Jdbc/Insert/Prepared/pom.xml @@ -0,0 +1,95 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>de.hdm_stuttgart.sda1.insert</groupId> + <artifactId>insert_user_prepared</artifactId> + <version>0.3</version> + <packaging>jar</packaging> + <description>PreparedStatements.</description> + + <name>insert_user</name> + + <url>https://freedocs.mi.hdm-stuttgart.de</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <version>8.0.8-dmr</version> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>2.9.1</version> + </dependency> + + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-jdbc</artifactId> + <version>5.0.2.RELEASE</version> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <plugins> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.7.0</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.10.4</version> + <configuration /> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.1.0</version> + <configuration> + <transformers> + <transformer + implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <manifestEntries> + <Main-Class>de.hdm_stuttgart.sda1.insert.PreparedInsert</Main-Class> + </manifestEntries> + </transformer> + </transformers> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + </execution> + </executions> + </plugin> + + </plugins> + </build> +</project> diff --git a/P/Sda1/Jdbc/Insert/Prepared/src/main/java/de/hdm_stuttgart/sda1/insert/PreparedInsert.java b/P/Sda1/Jdbc/Insert/Prepared/src/main/java/de/hdm_stuttgart/sda1/insert/PreparedInsert.java new file mode 100644 index 000000000..305729897 --- /dev/null +++ b/P/Sda1/Jdbc/Insert/Prepared/src/main/java/de/hdm_stuttgart/sda1/insert/PreparedInsert.java @@ -0,0 +1,101 @@ +package de.hdm_stuttgart.sda1.insert; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLIntegrityConstraintViolationException; + +import java.util.ResourceBundle; +import java.util.Scanner; + +/** + * <p>A JDBC "getting started" example inserting person's data into a table</p> + * + * <p>Caveat: No error handling whatsoever is being supplied!</p> + * + * <p>Preconditions:</p> + * + * <ul> + * <li>A database matching {@link #main(String[])}'s connection setup must be configured.</li> + * <li>A database table »Person« corresponding to this project's file »resources/schema.sql« must exist.</li> + * </ul> + * + */ +public class PreparedInsert { + + static private final Logger log = LogManager.getLogger (PreparedInsert.class); + + static final ResourceBundle jdbcProperties = ResourceBundle.getBundle("jdbc"); + + static final String insertUserStatement = "INSERT INTO Person VALUES(?, ?)"; + + /** + * Asking for user input inserting related person records into database. + * + * @param args unused + * + */ + public static void main(String[] args) { + + try (final Connection conn = DriverManager.getConnection( + jdbcProperties.getString("jdbcurl"), + jdbcProperties.getString("username"), + jdbcProperties.getString("password"))) { + handleUserInput(conn.prepareStatement(insertUserStatement)); + } catch (final SQLException e) { + log.error("General database connection problem:", e); + } + } + static private void handleUserInput(final PreparedStatement statement) throws SQLException { + try (final Scanner scanner = new Scanner(System.in)) { + + while(true) { + System.out.print("Enter a person's name or 'x' to exit: "); + final String name = scanner.nextLine().trim(); + if (name.equals("x")) { + break; + } + System.out.print("Enter " + name + "'s email or 'x' to exit: "); + final String email = scanner.nextLine().trim(); + if (email.equals("x")) { + break; + } + final int insertCount = insertPerson(statement, name, email); + if (1 == insertCount) { + System.out.println("Successfully inserted new user '" + name + "'\n"); + } else { + System.out.println("Insertion failed, duplicate email '" + email + "' ?\n"); + } + } + System.out.println("Bye!"); + } + } + /** + * <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 java.sql.SQLIntegrityConstraintViolationException} + * errors. + */ + static public int insertPerson(final PreparedStatement statement, final String name, final String email) + throws SQLException { + statement.setString(1, name); + statement.setString(2, email); + log.info("Executing '" + insertUserStatement + "' using " + name + " and " + email); + + try { + return statement.executeUpdate(); + } catch (final SQLIntegrityConstraintViolationException ex) { + log.info("Constraint violation, possibly inserting duplicate email '" + email + "'"); + return 0; + } + } +} \ No newline at end of file diff --git a/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/jdbc.properties b/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/jdbc.properties new file mode 100644 index 000000000..d8fef888f --- /dev/null +++ b/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/jdbc.properties @@ -0,0 +1,3 @@ +jdbcurl=jdbc:mysql://localhost:3306/hdm?allowMultiQueries=true +password=XYZ +username=hdmuser \ No newline at end of file diff --git a/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/log4j2.xml b/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/log4j2.xml new file mode 100644 index 000000000..3273a0681 --- /dev/null +++ b/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/log4j2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <File name="A1" fileName="A1.log" append="false"> + <PatternLayout pattern="%t %-5p %c{2} - %m%n"/> + </File> + <Console name="STDOUT" target="SYSTEM_OUT"> + <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> + </Console> + </Appenders> + <Loggers> + + <!-- You my want to define class or package level per-logger rules --> + <Logger name="de.hdm_stuttgart.sda1.insert.App" level="debug"> + <AppenderRef ref="A1"/> + </Logger> + <Root level="info"> + <AppenderRef ref="A1"/> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/schema.sql b/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/schema.sql new file mode 100644 index 000000000..73b3bb149 --- /dev/null +++ b/P/Sda1/Jdbc/Insert/Prepared/src/main/resources/schema.sql @@ -0,0 +1,6 @@ +DROP TABLE IF EXISTS Person; + +CREATE TABLE Person ( + name char(100) + ,email VARCHAR(100) UNIQUE +); \ No newline at end of file diff --git a/P/Sda1/Jdbc/Insert/Prepared/src/test/java/de/hdm_stuttgart/sda1/insert/InsertTest.java b/P/Sda1/Jdbc/Insert/Prepared/src/test/java/de/hdm_stuttgart/sda1/insert/InsertTest.java new file mode 100644 index 000000000..566ef4836 --- /dev/null +++ b/P/Sda1/Jdbc/Insert/Prepared/src/test/java/de/hdm_stuttgart/sda1/insert/InsertTest.java @@ -0,0 +1,64 @@ +package de.hdm_stuttgart.sda1.insert; + +import org.junit.*; +import org.junit.runners.MethodSorters; +import org.springframework.core.io.ClassPathResource; +import org.springframework.jdbc.datasource.init.ScriptUtils; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; + + +/** + * Testing insert operations. + */ + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) // SQL inserts require fixed execution order. +public class InsertTest { + + static private Connection conn; + static private PreparedStatement stmt; + + /** + * Init connection + * @throws java.sql.SQLException Unable to establish connection. + */ + @BeforeClass + static public void initDatabase() throws SQLException { + + conn = DriverManager.getConnection( + PreparedInsert.jdbcProperties.getString("jdbcurl"), + PreparedInsert.jdbcProperties.getString("username"), + PreparedInsert.jdbcProperties.getString("password")); + ScriptUtils.executeSqlScript(conn, new ClassPathResource("schema.sql")); + stmt = conn.prepareStatement(PreparedInsert.insertUserStatement); + } + + @AfterClass + static public void releaseDatabase() throws SQLException { + conn.close(); + } + + @Test + public void test_010_insertJill() throws SQLException { + Assert.assertEquals(1, PreparedInsert.insertPerson(stmt, "Jill", "jill@programmer.org")); + } + + @Test + public void test_020_insertDave() throws SQLException { + Assert.assertEquals(1, PreparedInsert.insertPerson(stmt, "Dave", "dave@genius.org")); + } + + + /** + * Insert fails due to UNIQUE constraint on email value »jill@programmer.org« + * + */ + @Test + public void test_030_insertJillAgain() throws SQLException { + Assert.assertEquals(0, PreparedInsert.insertPerson(stmt, "Jill", "jill@programmer.org")); + } + +} \ No newline at end of file diff --git a/P/pom.xml b/P/pom.xml index 9401a5369..96767e5b1 100644 --- a/P/pom.xml +++ b/P/pom.xml @@ -114,6 +114,9 @@ <module>Sda1/Streams/Template</module> <module>Sda1/Jdbc/Users/Plain</module> + <module>Sda1/Jdbc/Insert/Minimum</module> + <module>Sda1/Jdbc/Insert/MinimumTest</module> + <module>Sda1/Jdbc/Insert/Prepared</module> <module>Sda1/Jdom/Catalog</module> <module>Sda1/Jdom/Catalog2Xhtml</module> -- GitLab