π
JDBC(Java Database Connectivity)is astandardised APIor anabstractionof howdatabase connectionsshould 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 connectionflexible database selectionsincreased maintainability at the database levelJDBC 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);
}
π‘
Transactionis a unit of alogical operationin 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)