JDBC介绍
百度百科:Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。
个人理解:
没有JDBC:因为各个数据库软件都是自成体系,对外提供服务时没有统一标准,不像http接口,因为http协议的存在及约束,对接一个新接口非常方便。且数据库连接本身也非常复杂,当我们需要连接不同数据库时,自己手写连接不现实,使用数据库厂商提供的工具连接自然可以,但开发限制较大,不同数据库的连接,执行,结果集处理均不能保证一致,应用切换数据库代价更是无比巨大,可能换一个数据库,数据库连接查询及结果处理的代码全部需要重写,bug也会更多。
JDBC意义:JAVA提供统一的数据库访问API,仅仅是一套标准,一系列规范化的操作。且不提供具体实现,而是不同数据库开发商根据jdbc标准提供具体实现,如何实现不需要开发者关心。这样可以做到,对数据库的连接,执行,结果集处理使用同一套规范与标准,提高开发效率与容错率,降低开发难度及与数据库的耦合度。
JDBC使用与测试
造数据
建表
CREATE TABLE `t_test`(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`code` VARCHAR(32) DEFAULT NULL COMMENT '编码',
`name` VARCHAR(64) DEFAULT NULL COMMENT '名称',
PRIMARY KEY (`id`)
)
插入数据
INSERT INTO t_test(CODE, NAME) VALUES(‘ZS’,‘张三’), (‘LS’,‘李四’), (‘ZL’,‘张龙’), (‘ZH’,‘赵虎’);
查询
示例代码
public class MainTest {
public static void main(String[] args) throws Exception {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.创建连接
Connection mysqlConnection = DriverManager.getConnection(
"jdbc:mysql://host:port/dbName",
"username ",
"password"
);
// 3.创建statement,用于执行静态sql
Statement statement = mysqlConnection.createStatement();
// 4.执行查询sql,会返回结果集
String sql = "select * from t_test;";
ResultSet resultSet = statement.executeQuery(sql);
// 5.读取结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("code")+" : "+resultSet.getString("name"));
}
}
}
解读
上述使用jdbc执行一个查询并处理返回值共分五步:
- 加载驱动
- 创建连接
- 创建statement,用于执行静态sql
- 执行查询sql,会返回结果集
- 读取结果集
为何要加载驱动
Class.forName(“com.mysql.cj.jdbc.Driver”);
这一步根据字面意思,使用Class类,去加载名为“com.mysql.cj.jdbc.Driver”的类,即加载mysql的驱动。乍一看简直是一脸懵逼,这句代码就能加载驱动了?而且为什么要加载驱动?没这么写过代码呀!
试着慢慢分析吧,首先Class.forName !
Java class在没有被需要用到时,是不会被加载到JVM内存中。当我们new一个从没用过的类时,JVM首先会去找这个类有么有在内存中,没有的话会先去加载这个类,然后
com.mysql.cj.jdbc.Driver源码:
package com.mysql.cj.jdbc;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
/**
* Construct a new driver and register it with DriverManager
*
* @throws SQLException
* if a database error occurs.
*/
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
static代码块会在类被加载时执行,其他也没了,而static中就一句代码java.sql.DriverManager.registerDriver(new Driver());
注册了一个实现java.sql.Driver的mysql驱动,即把mysql厂商提供的驱动注册到了驱动管理器中,具体作用肯定是供后续使用,这里暂时无法明确用途。
创建连接
Connection mysqlConnection = DriverManager.getConnection(
"jdbc:mysql://host:port/dbName",
"username ",
"password"
);
创建连接阶段会根据提供的数据库信息进行数据库连接创建,这一步就需要连接具体的mysql数据库了,除了用户名、密码、数据库地址信息,似乎没有看到如何告诉jvm需要连mysql还是oracle或者其他数据库,url中倒是有mysql字样,难道用的这个连的?
先看下创建连接的代码:
// 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;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// 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.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");
}
Connection con = aDriver.driver.connect(url, info);
这里可以看到,遍历已注册的驱动,挨个调用驱动创建连接,这里java只提供了接口,即Driver.connect.并没有提供具体实现,而是各个数据库厂商自己提供实现,提供如何去连接他们的数据库,返回一个可用的连接。
而这个驱动注册就是第一步加载驱动的意义:需要主动把厂商的驱动注册到jdbc的驱动管理器中,这样用户创建数据库连接时,jdbc的驱动管理器就使用已注册的驱动去创建对应数据库的连接。
如果项目中同时连接多种数据库,如果区分需要连哪个呢?
Jdbc不做决策,而是遍历所有驱动,挨个去创建连接,谁先创建成功了,就返回谁的连接。
注意
当前jdbc已经不需要手动加载驱动了,新的驱动管理器的静态代码块中已经自动进行了驱动加载。
public class DriverManager {
…
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
…
}
方式为遍历寻找所有java.sql.Driver接口的实现类,然后挨个执行Class.forName(XXX);
增删改查
private static void test_query() throws Exception {
String sql = "select * from t_test;";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
System.out.println(resultSet.getString("code")+" : "+resultSet.getString("name"));
}
}
private static void test_insert() throws Exception {
String sql = "INSERT INTO t_test(CODE, NAME) VALUES('ZS','张三'), ('LS','李四'), ('ZL','张龙'), ('ZH','赵虎');";
boolean result = statement.execute(sql);
System.out.println(result);
}
private static void test_update() throws Exception {
transaction(false);
String sql = "UPDATE t_test set name='张66' where code='ZS'";
boolean result = statement.execute(sql);
System.out.println(result);
commit();
}
private static void test_delete() throws Exception {
String sql = "DELETE FROM t_test WHERE code IN ('ZS', 'LS', 'ZL', 'ZH');";
boolean result = statement.execute(sql);
System.out.println(result);
}
事务控制
事务控制在connection层,默认是自动提交的,如果需要进行事务控制及业务回滚,需要将自动提交事务设置为false,然后可以在发生异常或者业务错误的情况下手动执行rollback回滚事务,或者在执行完成后手动执行commit提交事务。
// 将自动提交事务设置为false
mysqlConnection.setAutoCommit(false);
try{
// do something
} catch (SQLException e) {
e.printStackTrace();
//手动执行rollback回滚事务
mysqlConnection.rollback();
}
//手动执行commit提交事务
mysqlConnection.commit();