π
JDBC
(Java Database Connectivity)
is astandardised API
or anabstraction
of howdatabase connections
should be controlled over theJava context
.
JDBC
is almost all comprised of interfaces
that have to be implemented individually depending on varying vendors where followed by polymorphism
, various implementations of the database connections
all work.
Hence, as JDBC
handles all the low-level database-related logics
, the developers can access different databases
in the uniformed appoaches, implying that the developers can focus on the business logics
than the database connections
.
JDBC
enables:
simplified database connection
flexible database selections
increased maintainability at the database level
JDBC
is executed following the underneath steps:
connect to the database via JDBC Driver
.execute SQL via Statement instance
.acquire the results as ResultSet instance
.where these will be discussed in detail in the following subsections.
Databases
in JDBC
can be connected via driver classes
implementing the Driver interface
. Driver class
is an interface
between Java
and database software (process)
in which varying database vendors
implement their own database driver class
and JDBC
just runs such driver class
to establish a connection to the database
.
A good example of Driver classes
are, MySQL
, Hikari
PostgreSQL
, and etc
where these Driver classes
all implement the below connect() method
. Typically required properties or info for database connections are URL
, username
, and password
and these can vary depending on the implemented Driver class
.
Driver Interface
public interface Driver {
...
Connection connect(String URL, java.util.Properties info)
throws SQLException;
...
Connection instance
can be either acquired via DriverManager class
or via Datasource class
where it is specifically used with the DataSourceUtil class
.
Below are good practices of the DriverManager class
and DataSourceUtils class
and its actual implementations.
DriverManager Class (use case)
private Connection getConnection() {
return DriverManager.getConnection(DB_URL, USER, PASS);
}
DriverManager Class
public class DriverManager {
...
// Worker method called by the public getConnection() methods.
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
if (callerCL == null || callerCL == ClassLoader.getPlatformClassLoader()) {
callerCL = Thread.currentThread().getContextClassLoader();
}
if (url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
ensureDriversInitialized();
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for (DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if (isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.driver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
...
}
DataSourceUtils Class (use case)
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource);
}
DataSource Interface
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password)
throws SQLException;
After acquiring the Connection instance
from a getConnection() method
, SQL
could run after either calling on createStatement()
or prepareStatement() methods
where these methods all return an instance
implementing the Statement interface
.
Statement (use case)
PreparedStatement pstmt = null;
String sql = "select * from member where name = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
Statement instance
generated via createStatement() method
and PreparedStatement instance
generated via prepareStatement() method
varies, where the later PreparedStatement
allows dynamic parameter bindings through setString() method
. PreparedStatement
is also prevents potential SQL Injections
by running String type encodings
as illustrated below.
JdbcPreparedStatement setString()
public class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement {
public void setString(int var1, String var2) throws SQLException {
try {
if (this.isDebugEnabled()) {
this.debugCode("setString(" + var1 + ", " + quote(var2) + ')');
}
this.setParameter(var1, (Value)(var2 == null ? ValueNull.INSTANCE : ValueVarchar.get(var2, this.conn)));
} catch (Exception var4) {
throw this.logAndConvert(var4);
}
}
}
TraceObject quote()
protected static String quote(String var0) {
return StringUtils.quoteJavaString(var0);
}
StringUtils quoteJavaString()
public static String quoteJavaString(String var0) {
if (var0 == null) {
return "null";
} else {
StringBuilder var1 = (new StringBuilder(var0.length() + 2)).append('"');
javaEncode(var0, var1, false);
return var1.append('"').toString();
}
}
StringUtils javaEncode()
public static void javaEncode(String var0, StringBuilder var1, boolean var2) {
int var3 = var0.length();
for(int var4 = 0; var4 < var3; ++var4) {
char var5 = var0.charAt(var4);
switch (var5) {
case '\t':
var1.append("\\t");
break;
case '\n':
var1.append("\\n");
break;
case '\f':
var1.append("\\f");
break;
case '\r':
var1.append("\\r");
break;
case '"':
var1.append("\\\"");
break;
case '\'':
if (var2) {
var1.append('\'');
}
var1.append('\'');
break;
case '\\':
var1.append("\\\\");
break;
default:
if (var5 >= ' ' && var5 < 128) {
var1.append(var5);
} else {
var1.append("\\u").append(HEX[var5 >>> 12]).append(HEX[var5 >>> 8 & 15]).append(HEX[var5 >>> 4 & 15]).append(HEX[var5 & 15]);
}
}
}
}
ResultSet instance
representing the result of the provided SQL query
can be acquired from running the executeQuery() method
from the generated Statement instances
. And the specific results can be accessed via the next() method
and get_type methods
as specified in the ResultSet interface
.
get_type methods
allows either a column index
or a column label
to access the data
.
Example
ResultSet rs = null;
...
rs = pstmt.executeQuery();
List<Member> members = new ArrayList<>();
while(rs.next()) {Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
members.add(member);
}
π‘
Transaction
is a unit of alogical operation
in thedatabase
.
A transaction
groups one or more database modification statements
(like INSERT
, UPDATE
, or DELETE
), ensuring that they are executed as a single unit. This means either all operations succeed together, or none of them are applied, thus preserving database integrity
.
In JDBC
, transactions
can be managed using the autoCommit property
of the Connection instance
. By default, autoCommit
is set to true
, meaning every SQL
statement is immediately committed to the database
as soon as it is executed. To use transactions
, autoCommit
can be turned off, and the commits
can be controlled manually.
If an error
occurs, the catch block
handles it by calling conn.rollback()
, which undoes all changes since autoCommit
was disabled otherwise if all statements succeed, conn.commit()
is called, applying all changes to the database
.
Example
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TransactionExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "user";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// Disable auto-commit mode
conn.setAutoCommit(false);
try {
// Start the transaction
String sql1 = "UPDATE accounts SET balance = balance - 100 WHERE account_id = ?";
try (PreparedStatement stmt1 = conn.prepareStatement(sql1)) {
stmt1.setInt(1, 1); // Debit $100 from account 1
stmt1.executeUpdate();
}
String sql2 = "UPDATE accounts SET balance = balance + 100 WHERE account_id = ?";
try (PreparedStatement stmt2 = conn.prepareStatement(sql2)) {
stmt2.setInt(1, 2); // Credit $100 to account 2
stmt2.executeUpdate();
}
// Commit the transaction if all operations succeed
conn.commit();
System.out.println("Transaction committed successfully.");
} catch (SQLException e) {
// Roll back if an error occurs
conn.rollback();
System.out.println("Transaction rolled back due to error: " + e.getMessage());
} finally {
// Restore default auto-commit behavior
conn.setAutoCommit(true);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
μ΅λ²κ· μ JSP 2.3 μΉ νλ‘κ·Έλλ°: κΈ°μ΄λΆν° μ€κΈκΉμ§
F-Lab (1)
F-Lab (2)