Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
User
instances
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
DriverManager
:
Bootstrapping connections
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Connection
interface
Lecture notes |
Pdf slides |
|
Statement
interface
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Connection
methods
Lecture notes |
Pdf slides |
|
Statement
methods
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
pom.xml
driver runtime scope
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Person
table
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
message.properties
string
externalization
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
AutoCloseable
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
(#1 of 3) |
Lecture notes |
Pdf slides |
(#2 of 3) |
Lecture notes |
Pdf slides |
(#3 of 3) |
Lecture notes |
Pdf slides |
(#1 of 1) |
Lecture notes |
Pdf slides |
(#1 of 2) |
Lecture notes |
Pdf slides |
(#2 of 2) |
java.sql.PreparedStatement
PreparedStatement
principle.
PreparedStatement
example
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
ResultSet
states
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
null
value
ambiguity
Lecture notes |
Pdf slides |
|
null
value
ambiguity
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Lecture notes |
Pdf slides |
|
Concept of persistence
Data exchange RDBMS ⇌ Java application
Dealing with transactions
Persistence allows an object to outlive the process that created it.
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.
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");
CREATE TABLE User( commonName CHAR(80) ,uid CHAR(10) PRIMARY KEY ); -- Persistent record (see Durability in ACID) INSERT INTO User VALUES('Joe Bix', 'bix');
JDBC architecture
Important interfaces in java.sql
Write data using java.sql.Statement
Protocol connecting database client and server.
Vendor dependent implementations.
https://www.ietf.org/rfc/rfc2396.txt
:
absoluteURI = scheme ":" ( hier_part | opaque_part )
hier_part = ( net_path | abs_path ) [ "?" query ]
net_path = "//" authority [ abs_path ]
abs_path = "/" path_segments
...
http://www.hdm-stuttgart.de/aaa
http://someserver.com:8080/someResource
Non-standard port 8080
ftp://mirror.mi.hdm-stuttgart.de/Firmen
Database | JDBC™ URI |
---|---|
PostgreSQL | jdbc:postgresql://<HOST>:<PORT>/[database] |
MySQL | jdbc:mysql://[host][:port]/[database][?p1=v1]... |
Oracle | jdbc:oracle:thin:[user/password]@[host][:port]:SID |
DB2 | jdbc:db2://<HOST>:<PORT>/[database] |
Derby | jdbc:derby://[host][:port]/[database] |
MS. SQL S. | jdbc:sqlserver://host[:port];user=xxx;password=xyz |
Sybase | jdbc:sybase:Tds:<HOST>:<PORT>/[database] |
|
|
>grep postgresql /etc/services postgresql 5432/tcp postgres # PostgreSQL Database postgresql 5432/udp postgres
Bootstrapping object.
java.sql.DriverManager
shipped with JRE™.
Provides instances of java.sql.Connection
.
Interface MySQLConnection
extends
java.sql.Connection
Class ConnectionImpl
implements MySQLConnection
postgresql-42.1.4.jar
mysql-connector-java-x.y.z.jar
ojdbc6.jar
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901-1.jdbc4</version>
<groupId>com.oracle</groupId> <!-- requires access credentials -->
<artifactId>ojdbc7</artifactId>
<version>12.1.0</version>
conn = DriverManager.getConnection(
"jdbc:postgresqll://localhost/hdm", "hdmuser", "XYZ");
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) at de.hdm_stuttgart.mi.sda1.DatabaseTest.initDatabase(DatabaseTest.java:34) ...
java.sql.Connection
Holding a permanent database server connection .
Stateful protocol.
Per connection properties: Isolation level, auto commit,...
java.sql.Statement
Two distinct operation classes:
executeUpdate()
INSERT
, UPDATE
,
DELETE
: Integer return code
executeQuery()
SELECT
: Returning java.sql.ResultSet
,
see the section called “Read Access”.
From JDBC and Multithreading:
“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.”
Consequence:
Use one java.sql.Connection
per
thread.
Use connection pooling e.g. c3po.
try (final Connection conn = C3P0DataSource.getInstance().getConnection()) { final PreparedStatement pstmt = conn.create...; ... pstmt.executeUpdate(); // Auto close connection, back to pool. } catch (SQLException e) { e.printStackTrace(); }
Providing a driver based on maven.
Specifying unit tests.
...
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901-1.jdbc4</version>
<scope>runtime</scope>
</dependency> ...
CREATE TABLE Person ( name CHAR(20) ,email CHAR(20) UNIQUE )
Java™ application executing:
INSERT INTO Person VALUES('Jim', 'jim@foo.org')
No database read required (No java.sql.ResultSet
).
Success / failure related database return parameter.
// Step 1: Open connection to database server final Connection conn = DriverManager.getConnection ( "jdbc:postgresql://localhost/hdm", // Connection parameter URL "hdmuser", // Username "XYZ"); // Password // Step 2: Create a Statement instance final Statement stmt = conn.createStatement(); // Step 3: Execute the desired INSERT final int updateCount = stmt.executeUpdate( "INSERT INTO Person VALUES('Jim', 'jim@foo.org')"); // Step 4: Give feedback to the end user System.out.println("Successfully inserted " + updateCount + " dataset(s)");
Execution yields:
Successfully inserted 1 dataset(s)
Note: The database server returns the number of inserted / modified / deleted datasets.
public static void main(String[] args)
throws SQLException { ...
... = DriverManager.getConnection ( "jdbc:postgresql://localhost/hdm", //JDBC URL "hdmuser", // Username "XYZ") // Password
Connection parameter changes require recompilation!
Parameters should be configurable.
Possible solution: Java™ properties.
resources/jdbc.properties
jdbcurl=jdbc:postgresql://localhost/hdm username=hdmuser password=XYZ
// resources/jdbc.properties ResourceBundle jdbcProperties = ResourceBundle.getBundle("jdbc");
... Connection conn = DriverManager.getConnection( jdbcProperties.getString("jdbcurl"), jdbcProperties.getString("username"), jdbcProperties.getString("password"));
Set up: Test preparation.
Open database connection
Create a required schema.
Optional: Insert initial data.
Test: Execute JDBC™ CRUD / SELECT operations.
Tear down:
Drop schema
Close database connection.
public class InsertTest { static private Connection conn; static private Statement stmt; @BeforeClass ❶ static public void initDatabase() throws SQLException { conn = DriverManager.getConnection( SimpleInsert.jdbcProperties.getString("jdbcurl"), SimpleInsert.jdbcProperties.getString("username"),...); ScriptUtils.executeSqlScript(conn, new ClassPathResource("schema.sql")); stmt = conn.createStatement();} @Test ❷ public void test_010_insertJill() throws SQLException { Assert.assertEquals(1, SimpleInsert.insertPerson( stmt, "Jill", "jill@programmer.org")); } @AfterClass ❸ static public void releaseDatabase() throws SQLException {conn.close();}
Getting ScriptUtils.executeSqlScript(...) to work:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
final Connection conn = DriverManager.getConnection(...); ... // CRUD operations conn.close(); // Important! Wanna use a connection pool instead?
Using try-with-resources statement.
try (final Connection conn = DriverManager.getConnection(...) {
... // CRUD operations
} catch (SQLException e) {...}
[... 5.5.24-0ubuntu0.12.04.1.%...X*e?I1ZQ...................e,F[yoA5$T[N.mysql_native_password. A...........!.......................hdmuser ❶......U.>S.%..~h...!.xhdm............j..../* ... INSERT INTO Person VALUES('Jim', 'jim@foo.org') ❷6... .&.#23000Duplicate entry 'jim@foo.org' for key 'email' ❸
|
|
|
|
Resulting error message sent back to the client. |
Password?
What about the missing password?
Making MySQL Secure Against Attackers:
When you connect to a MySQL server, you should use a password.
The password is not transmitted in clear text over the connection.
Data exchange client to server nearly fully disclosed.
Mysql mitigates the attack type's severity
Possible solutions:
Encrypted tunnel between client and server: like e.g. ssh port forwarding or VPN.
Irrelevant e.g. within DMZ.
Many people say they know what SQL injection is, but all they have heard about or experienced are trivial examples.
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 user's names, passwords, names, addresses, phone numbers, and credit card details.
This is the preferred way eliminating security issues
completely as being discussed in the section called “java.sql.PreparedStatement
”.
May not be possible in legacy applications due to required efforts.
Malicious user input is being rejected from being embedded into SQL statements.
Regular expression matching user names.
Regular expression | User input |
---|---|
[a-zA-Z]+ |
Matches
“Jennifer ” |
Does not match “DROP TABLE
Users ” |
What happens when executing thousands of SQL statements having identical structure?
Is this architecture adequate with respect to security concerns?
INSERT INTO Person VALUES ('Jim', 'jim@q.org')
INSERT INTO Person VALUES ('Eve', 'eve@y.org')
INSERT INTO Person VALUES ('Pete', 'p@rr.com')
...
Wasting time parsing SQL over and over again!
INSERT INTO Person VALUES
('Jim', 'jim@q.org'),
('Eve', 'eve@y.org'),
('Pete', 'p@rr.com') ... ;
Dealing with large record counts even this option may become questionable.
The database server's interpreter may interpret an attacker's malicious code among with intended SQL.
User input is being interpreted by the database server's interpreter.
User input filtering my be incomplete / tedious.
User input being excluded from parsing.
Allows for reuse per record.
PreparedStatement
instance creation: Parsing SQL statement
possibly containing place holders.
Set values of all placeholder values: SQL values are not being parsed.
Execute the statement.
Steps 2. and 3. may be repeated without re-parsing the underlying SQL statement thereby saving database server resources.
final Connection conn = DriverManager.getConnection (...
final PreparedStatement pStmt = conn.prepareStatement(
"INSERT INTO Person VALUES(?, ?)");❶
pStmt.setString(1, "Jim");❷
pStmt.setString(2, "jim@foo.org");❸
final int updateCount = pStmt.executeUpdate();❹
System.out.println("Successfully inserted " + updateCount + " dataset(s)");
Jim', 'jim@c.com');DROP TABLE Person;INSERT INTO Person VALUES('Joe
Attacker's injection text simply becomes part of the database server's content.
Problem solved!
In a nutshell: Only attribute value literals may be parameterized.
CREATE
/ UPDATE
/ DELETE
client modifies database server data:
int result = statement.executeUpdate("UPDATE Person ...");
SELECT
client receives copies of database server data:
ResultSet result = statement.executeQuery("SELECT ... FROM Person ...");
No standard Collections container e.g. java.util.List
.
“Own” collection java.sql.ResultSet
holding transient database object copies.
|
|
final Connection conn = DriverManager.getConnection (...); final Statement stmt = conn.createStatement(); // Step 3: Creating the client side JDBC container holding our data records final ResultSet data = stmt.executeQuery("SELECT * FROM Friends"); ❶ // Step 4: Dataset iteration while (data.next()) { ❷ System.out.println(data.getInt("id") ❸ + ", " + data.getString("nickname") ❹ + ", " + data.getString("birthdate")); ❺ }
resultSet =
statement.executeQuery(...)
Caution: Data not yet accessible!
resultSet.next()
returning true
Data accessible until resultSet.next()
returns false
.
resultSet.next()
returning false
Caution: Data not longer accessible!
JDBC™ Type | Java™ type |
---|---|
CHAR , VARCHAR , LONGVARCHAR |
String |
NUMERIC , DECIMAL |
java.math.BigDecimal |
BIT |
boolean |
TINYINT |
byte |
... |
... |
Shamelessly copied from JDBC Types Mapped to Java Types.
Java™ Type | JDBC™ type |
---|---|
String |
CHAR , VARCHAR , LONGVARCHAR |
java.math.BigDecimal |
NUMERIC |
boolean |
BIT |
... |
... |
Shamelessly copied from Java Types Mapped to JDBC Types.
int getInt(int columnIndex)
double getDouble(int columnIndex)
Date getDate(int columnIndex)
...
Object getObject(int columnIndex)
Best SQL to Java type match.
|
|
Caveat: May impact performance.
|
|
Caveat: Error prone on schema evolution.
final int count = resultSet.getInt("numProducts");
Problem: Two possibilities in case of count == 0
:
DB attribute numProducts is 0 (zero).
DB attribute numProducts is null
.
final int count = resultSet.getInt("numProducts");
if (resultSet.wasNull()) {
...
} else {
...
}
See wasNull()
.
server=db.somedomain.org port=3306 ... driver=mariadb-java-client-3.3.3.jar |
|
import java.sql.Driver;
...
public class DriverShim implements Driver {
private Driver driver;
DriverShim(Driver driver) {
this.driver = driver;
}
@Override
public Connection connect(String s, Properties properties) throws SQLException {
return driver.connect(s, properties);
}
@Override
public boolean acceptsURL(String u) throws SQLException {
return driver.acceptsURL(u);
}
...
}
Level 0: Prevent other transactions from changing data that has already been modified by an uncommitted transaction.
Other transactions can read uncommitted data resulting in “dirty reads”.
Level 1: Prevents dirty reads. (Default on many RDBMS)
Level 2: prevents non-repeatable reads.
Level 3: Data read by one transaction is valid until the end of that transaction, preventing phantom rows.
Transaction unsupported: Connection.TRANSACTION_NONE
Level 2: Connection.TRANSACTION_SERIALIZABL
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
See Connection.TRANSACTION_READ_COMMITTED
and setTransactionIsolation
.
Note: Setting will become effective when starting next transaction.