1. JDBC简介
JDBC(Java Database Connectivity):Java数据库连接。官方定义了一套操作所有关系型数据库的接口,各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们使用这套接口JDBC编程,而真正执行的代码是驱动jar包中的实现类。
2. JDBC基本使用
使用步骤:
- 导入驱动jar包如:mysql-connector-java-5.1.46.jar
- 注册驱动:Class.forName("com.mysql.jdbc.Driver");
- 使用DriverManager静态方法获取数据库的连接对象Connection:getConnection("url", "user", "password");
- 定义SQL语句:SQL的参数使用?作为占位符
- 使用Connection对象获取SQL语句执行对象Statement:prepareStatement(String sql)
- 使用PreparedStatement对象setXxx方法给?赋值:setObject(int parameterIndex, Object x)
- 执行SQL语句,获取结果集对象ResultSet
- 处理结果集
- 释放资源:结果集对象、SQL语句执行对象、连接对象
2.1 DriverManager
- 驱动管理对象
-
注册驱动:告诉程序该使用哪一个数据库驱动jar
1. 注册给定的驱动程序DriverManager static void registerDriver(Driver driver) 示例: DriverManager.registerDriver(new Driver()); 2. 在com.mysql.jdbc.Driver类中存在静态代码块: static { try { // 第一次使用Driver时就会注册驱动 java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } 通过上面静态代码块可以看出: 只要加载Driver到内存就可以注册驱动了: Class.forName("com.mysql.jdbc.Driver"); 注:mysql5之后的驱动jar包可以省略注册驱动的步骤
-
获取数据库连接:
static Connection getConnection(String url, String user, String password) url:指定连接的路径 语法:连接方式:数据库厂商名://IP(域名):端口号/数据库名 例如:url=jdbc:mysql://localhost:3306/db01 若MySQL版本太高,与JDBC不兼容: url=jdbc:mysql://localhost:3306/db01?useSSL=false user:用户名 password:密码
2.2 Connection
- 数据库连接对象
-
获取执行sql的对象:
Statement createStatement() PreparedStatement prepareStatement(String sql)
-
管理事务:
1. 开启事务:setAutoCommit(boolean autoCommit) 参数为false代表取消自动提交,即开启事务 一般在执行SQL之前开启事务 2. 提交事务:commit() 一般当所有SQL都执行完后提交事务 3. 回滚事务:rollback() 一般在catch中回滚事务 抓异常时,最好选得大一些 4. 设置当前连接的事务隔离级别 void setTransactionIsolation(int level) 1:Connection.TRANSACTION_READ_UNCOMMITTED 2:Connection.TRANSACTION_READ_COMMITTED 4:Connection.TRANSACTION_REPEATABLE_READ 8:Connection.TRANSACTION_SERIALIZABLE
-
获取包含数据库元数据的对象:
DatabaseMetaData包含此Connection对象所连接的数据库的元数据 DatabaseMetaData getMetaData()
2.3 Statement
- 执行sql的对象
-
执行SQL的方法:
// 可以执行任意的SQL(了解) boolean execute(String sql) // 执行DML(insert、update、delete)、DDL(create,alter、drop) // 返回值:DML返回影响的行数,DDL返回0;可以判断DML语句是否执行成功,返回值>0的则执行成功,反之则失败 int executeUpdate(String sql) // 执行DQL(select) ResultSet executeQuery(String sql)
-
子类PreparedStatement:
SQL注入问题:在拼接SQL语句时,有一些SQL的特殊关键字参与字符串的拼接,会造成安全性问题
使用PreparedStatement对象可以解决SQL注入问题
预编译的SQL:参数使用?作为占位符
-
获取包含参数元数据的对象:
ParameterMetaData getParameterMetaData()
2.4 ResultSet
结果集对象,封装查询结果
-
常用方法:
// 游标向下移动一行并判断当前行是否是最后一行末尾(是否有数据) // 最初光标位于第一行之前 boolean next() // 获取数据 // Xxx代表数据类型 getXxx(参数) 参数为int:列的编号,从1开始 参数为String:列名称 使用示例: while (resultSet.next()) { System.out.println(resultSet.getInt(1)); } // 获取包含结果集元数据的对象 ResultSetMetaData getMetaData()
2.5 自定义JDBC工具类
-
自定义JDBC工具类:
resources下的jdbcutils.properties文件内容示例: driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/db01?useSSL=false username=root password=123456 import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; /** * 自定义JDBC工具类 * * @author 刘浩鹏 * @date 2020/11/28 */ public class JdbcUtils { private static String driverClassName; private static String url; private static String username; private static String password; // 使用静态代码块:只会执行一次 static { // 读取配置文件,获取值 InputStream is = null; try { // 1. 创建Properties对象 Properties properties = new Properties(); // 2. 加载配置文件 is = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbcutils.properties"); properties.load(is); // 3. 获取键的值并赋值 driverClassName = properties.getProperty("driverClassName"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); // 4. 注册驱动 Class.forName(driverClassName); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } // 获取连接,返回连接对象 public static Connection getConnection() { Connection connection = null; // 使调用者想处理就处理,不想处理就继续由上层调用/JVM处理 try { connection = DriverManager.getConnection(url, username, password); } catch (SQLException e) { e.printStackTrace(); // 将编译期异常转成运行期异常抛出,调用者想处理就处理 throw new RuntimeException(e.getMessage()); } return connection; } // 释放资源:Connection、Statement、ResultSet,哪一个没有则传递null public static void close(Connection connection, Statement statement, ResultSet resultSet) { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
-
使用自定义的JDBC工具类:
在数据库创建测试表user: -- 创建user表 CREATE TABLE user( id INT PRIMARY KEY AUTO_INCREMENT COMMENT "id", username VARCHAR(255) NOT NULL DEFAULT "" COMMENT "用户名", password VARCHAR(16) NOT NULL DEFAULT "123456" COMMENT "密码", balance DECIMAL(16, 3) NOT NULL DEFAULT 0.0 COMMENT "余额" )ENGINE=INNODB, CHARSET=utf8, COMMENT="用户表"; -- 添加用户 INSERT INTO user VALUES (NULL, 'user1', 'password1', 1000), (NULL, 'user2', 'password2', 2000), (NULL, 'user3', 'password3', 3000); 使用自定义的JDBC工具类: import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class JdbcUtilsTest { public static void main(String[] args) throws SQLException { // 1. 使用自定义JDBC工具类获取数据库连接 Connection connection = JdbcUtils.getConnection(); // 2. 定义SQL String sql = "SELECT id, username, balance FROM user WHERE id = ?"; // 3. 获取SQL语句执行对象 PreparedStatement ps = connection.prepareStatement(sql); // 4. 给?赋值 ps.setObject(1, 1); // 5. 执行SQL语句,获取结果集对象ResultSet ResultSet resultSet = ps.executeQuery(); // 6. 处理结果集 while (resultSet.next()) { int id = resultSet.getInt("id"); String username = resultSet.getString("username"); double balance = resultSet.getDouble("balance"); System.out.println(id + "\t" + username + "\t" + balance); } // 7. 使用自定义JDBC工具类释放资源:结果集对象、SQL语句执行对象、连接对象 JdbcUtils.close(connection, ps, resultSet); /* 结果: 1 user1 1000.0 */ } }
3. 数据库连接池
数据库连接池就是一个存放数据库连接的容器(链表,增删快)
-
数据连接池的工作机制:
- 程序启动时会在连接池中创建一定数量的连接,需要时从连接池中申请一个连接,使用完将连接归还给连接池
- 若当前连接池中没有空闲连接,则判断已使用的连接数是否达到最大连接数
- 若没达到最大连接数就创建一定数量的连接供使用
- 若达到最大连接数就让当前请求等待,直到有连接归还
- 若等待超时则报错或强制使用时间最久的那个连接归还
- 连接池会控制空闲连接数小于最大空闲连接数,多余的空闲连接会被销毁
-
所有连接池的标准接口:javax.sql.DataSource
- 获取连接:getConnection()
- 归还连接:Connection.close()
- 如果连接对象Connection是从连接池中获取的,那么调用Connection.close(),则不会再关闭连接了,而是归还连接(动态代理)
3.1 c3p0
官网:https://www.mchange.com/projects/c3p0/
c3p0数据库连接池使用步骤:
导入jar包如:c3p0-0.9.5.2.jar(依赖mchange-commons-java-0.2.12.jar)或c3p0-0.9.1.2.jar
-
定义配置文件:
- 名称:必须为c3p0-config.xml
- 路径:必须放在src或resources下
c3p0-config.xml配置文件样例:
com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/db01?useSSL=false root 123456 5 10 3000 com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/db02 root 123456 5 8 1000 -
创建数据库连接池对象ComboPooledDataSource
// 使用默认配置 DataSource ds = new ComboPooledDataSource(); // 使用指定名称配置 DataSource ds2 = new ComboPooledDataSource("otherc3p0");
通过连接池对象获取连接:getConnection()
3.2 druid
官网:https://druid.apache.org/
druid(阿里提供)下载地址:https://github.com/alibaba/druid
druid数据库连接池使用步骤:
导入jar包如:druid-1.1.6.jar
-
定义配置文件:
- Properties形式
- 可以是任意名称,可以放在任意目录下
druid.properties配置文件样例: driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/db01?useSSL=false username=root password=123456 # 初始化连接数量 initialSize=5 # 最大连接数量 maxActive=10 # 最大等待时间 maxWait=3000
加载配置文件Properties
通过工厂类DruidDataSourceFactory获取DataSource对象:createDataSource(Properties properties)
根据DataSource对象获取连接:getConnection()
自定义Druid连接池的工具类:
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DruidUtils {
// 定义成员变量DataSource
private static DataSource ds;
private static ThreadLocal threadLocal = new ThreadLocal<>();
// 使用静态代码块:只会执行一次
static {
InputStream is = null;
try {
// 1. 加载配置文件
is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties = new Properties();
properties.load(is);
// 2. 获取DataSource
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 获取连接池
public static DataSource getDataSource() {
return ds;
}
// 获取连接
public static Connection getConnection() {
Connection connection = null;
try {
connection = ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
return connection;
}
// 从ThreadLocal中获取连接
public static Connection getConnectionFromThreadLocal() {
Connection connection = threadLocal.get();
if (connection == null) {
connection = getConnection();
threadLocal.set(connection);
}
return connection;
}
// 释放资源
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4. DbUtils
- Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
- 使用Commons DbUtils需导入jar包,如:commons-dbutils-1.6.jar
- 使用步骤:
- 创建QueryRunner对象
- 定义SQL语句:SQL的参数使用?作为占位符
- 创建一个JavaBean类来准备接收结果集中的记录
- 调用QueryRunner对象执行SQL语句的方法获取结果集:update或query
- 处理结果集
4.1 QueryRunner类
QueryRunner类是执行SQL语句的核心类,自动维护连接
-
提供数据源:
构造: // 创建核心类,并提供数据源,内部自己维护Connection QueryRunner(DataSource ds) 方法: // 执行DML语句 int update(String sql, Object... params) // 执行DQL语句并将查询结果封装到对象中
T query(String sql, ResultSetHandler rsh, Object... params) -
提供连接:使用事务时需要提供连接
构造: // 创建核心类,没有提供数据源,在进行具体操作时需要手动提供Connection QueryRunner() 方法: // 使用提供的Connection,完成DML语句 int update(Connection conn, String sql, Object... params) // 使用提供的Connection,执行DQL语句,并将查询结果封装到对象中
T query(Connection conn, String sql, ResultSetHandler rsh, Object... params)
4.2 ResultSetHandler接口
ResultSetHandler接口的实现类将ResultSet转换为其他对象
4.2.1 JavaBean
JavaBean就是一个POJO(Plain Ordinary Java Object),常用于封装数据,其特征有:
- 是一个public类
- 提供无参构造
- 私有化字段并提供get/set方法
- 建议实现Serializable接口
- 建议重写toString方法,便于打印
- 属性如果是基本类型,建议声明成对应的包装类型
-
JavaBean示例:
import lombok.Data; import java.math.BigDecimal; // 为了方便,使用lombok的Data注解在编译时自动生成get/set等方法 @Data public class User { private int id; private String username; private String password; private BigDecimal balance; }
4.2.2 结果集的转换
-
BeanHandler类:将结果集中的第一条记录封装到指定的JavaBean中
构造: BeanHandler(Class
type) -
BeanListHandler类:将结果集中每一条记录封装到指定的JavaBean中,然后再将这些JavaBean封装到一个List集合中
构造: BeanListHandler(Class
type) MapHandler类:将结果集中第一条记录封装到一个Map集合中,字段名为key,字段值为value
MapListHandler类:将结果集中第一条记录封装到一个Map集合中,然后再将这些Map封装到一个List集合中
-
ColumnListHandler类:将结果集中的指定列封装到一个List集合中
构造: ColumnListHandler(String columnName)
-
ScalarHandler类:将结果集中第一条记录的指定列封装到一个对象;常用来处理单值查询结果,如执行select语句后,结果集只有1个的情况
将一个ResultSet列转换为一个对象的实现。这个类是线程安全的。 构造: // 获取第一条记录的第一列 ScalarHandler() // 获取第一条记录的指定列 ScalarHandler(String columnName)
-
处理结果集示例
import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.ScalarHandler; import java.sql.SQLException; public class DbUtilsTest { public static void main(String[] args) throws SQLException { // 1. 创建QueryRunner对象,使用自定义的Druid工具类获取数据库连接池对象 QueryRunner qr = new QueryRunner(DruidUtils.getDataSource()); // 2. 定义SQL语句:SQL的参数使用?作为占位符 String sql = "SELECT count(id) FROM user WHERE id > ?"; Object[] params = {0}; // 3. 调用QueryRunner对象执行SQL语句的方法获取结果集:update或query Long id = qr.query(sql, new ScalarHandler
(1), params); // 4. 处理结果集 System.out.println(id); /* 结果: 3 */ } }
4.3 DbUtils工具类
-
定义了关闭资源与事务处理的方法
// 关闭资源 static void closeQuietly(Connection conn, Statement stmt, ResultSet rs) // 提交事务并关闭连接,如果为null避免关闭 static void commitAndCloseQuietly(Connection conn) // 回滚事务并关闭连接,如果为null避免关闭 static void rollbackAndCloseQuietly(Connection conn)
若有错误或补充,欢迎私信