一、JDBC概述
1、基本介绍
-
JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题。
-
Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。
二、JDBC快速入门
1、JDBC API JDBC API是一系列的接口,它统一和规范了应用程序与数据库的连接、执行SQL语句,并到得到返回结果等各类操作,相关类和接口在java.sq与javax.sql包中
2、JDBC程序编写步骤
-
注册驱动-加载Driver 类
-
获取连接-得到Connection
-
执行增删改查-发送SQL给mysql执行
-
释放资源–关闭相关连接
3、通过jdbc对表actor进行添加,删除和修改操作
//前置工作,将mysql-connector-java-5.1.37-bin.jar添加到项目中
public class jdbc01 {
public static void main(String[] args) throws SQLException {
//1.注册驱动-加载 Driver类
Driver driver = new Driver();
//2.获取连接-得到Connection
//(1) jdbc:mysql:规定好表示协议,通过jdbc的方式连接mysql
//(2) localhost:主机,可以是ip地址
//(3)3306:表示mysql监听的端口
//(4) db02:连接到mysql dbms的哪个数据库
String url = "jdbc:mysql://127.0.0.1:3306/db01";
//将用户名和密码放入到Properties 对象
Properties properties = new Properties();
//说明user和 password是规定好,后面的值根据实际情况写
properties.setProperty("user", "root");// 用户
properties.setProperty("password","123456");//密码
//创建连接
Connection connect = driver.connect(url, properties);
//3.执行增删改查-发送SQL给mysql执行
String sql = "insert into actor values(null, '刘德华', '男', '1970-11-11', '110')";
//String sql = "update actor set name='周星驰' where id = 1";
// String sql = "delete from actor where id = 1";
// statement 用于执行静态 SQL语句并返回其生成的结果的对象
Statement statement = connect.createStatement();
int rows = statement.executeUpdate(sql);
// 如果是 dml 语句,返回的就是影响行数
System.out.println(rows > 0 ? "成功" : "失败");
//4.释放资源–关闭相关连接
statement.close();
connect.close();
}
}
三、创建数据库连接5种方式
第一种方法:使用Driver类
public void connect01() throws SQLException{
//加载驱动类
Driver driver = new Driver();
String url = "jdbc:mysql://127.0.0.1:3306/db01";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password","123456");
//创建连接
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
第二种方法:使用反射机制
public void connect02() throws ClassNotFoundException, IllegalAccessError, IllegalAccessException, InstantiationException, SQLException {
//使用反射,创建Driver驱动对象
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
//创建url、user、password
String url = "jdbc:mysql://127.0.0.1:3306/db01";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password","123456");
//创建连接
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
第三种方法:使用DriverManager 替代 driver 进行统一管理
public void connect03() throws SQLException,ClassNotFoundException,IllegalAccessError, IllegalAccessException, InstantiationException{
//使用DriverManager替换Driver
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
//创建url、user、password
String url = "jdbc:mysql://127.0.0.1:3306/db01";
String user = "root";
String password = "123456";
//注册DriverManager驱动
DriverManager.registerDriver(driver);
//创建数据库连接
Connection connect = DriverManager.getConnection(url, user, password);
System.out.println(connect);
}
第四种方法:使用Class.forName自动完成注册驱动
public void connect04() throws SQLException,ClassNotFoundException,IllegalAccessError{
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/db01";
String user = "root";
String password = "123456";
//创建数据库连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
mysql驱动5.1.6可以无需CLass.forName("com.mysql.jdbc.Driver"),它会自动调用驱动jar包下META-INF\services\java.sql .Driver 文本中的类名称去注册;建议还是写上 CLass.forName("com.mysql.jdbc.Driver""),更加明确
第五种方法:创建配置文件,使用Class.forName自动完成注册驱动
配置文件 mysql.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db01?rewriteBatchedStatements=true
user=root
password=123456
public void connect05() throws IOException,SQLException,ClassNotFoundException,IllegalAccessError{
//通过Properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src/mysql.properties")); //获取相关的值
String driver = properties.getProperty("driver");
String url = properties.getProperty ("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password" );
//注册驱动
Class.forName(driver);
//创建数据库连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
四、ResultSet类 [结果集]
1、基本介绍
-
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
-
ResultSet对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前
-
next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false ,因此可以在while循环中使用循环来遍历结果集
-
previous方法将光标移动到上一行,并且由于在ResultSet对象中没有更多行时返回false ,因此可以在while循环中使用循环来遍历结果集
2、结果集的使用
//通过Properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src/mysql.properties"));
String driver = properties.getProperty("driver");
String url = properties.getProperty ("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password" );
// 1.注册驱动
Class.forName(driver);
// 2.创建数据库连接
Connection conn = DriverManager.getConnection(url, user, password);
// 3.得到 Statement
Statement statement = connection.createStatement();
// 4.书写sql语句
String sql = "select id, name , sex, borndate from actor";
// 5.执行给定的 SQL语句,该语句返回单个 ResultSet结果集
ResultSet resultSet = statement.executeQuery(sql);
// 6.输出 结果集的数据
while (resultSet.next()) { // .next()让光标向后移动,如果没有更多行,则返回 false
// 通过列名来获取值, 推荐
int id = resultSet.getInt(1); //获取该行的第 1 列
String name = resultSet.getString(2); //获取该行的第 2 列
String sex = resultSet.getString(3);
Date date = resultSet.getDate(4);
System.out.println(id + "\t" + name + "\t" + sex + "\t" + date);
}
// 7.关闭连接
resultSet.close();
statement.close();
connection.close();
五、Statement类(会出现SQL注入)
1、基本介绍
-
Statement对象用于执行静态SQL语句并返回其生成的结果的对象
-
在连接建立后,需要对数据库进行访问,执行命名或是SQL语句,可以通过 .Statement 【存在SQL注入】 .PreparedStatement 【预处理】 .CallableStatement 【存储过程】
-
Statement对象执行SQL语句,存在SQL注入风险
-
SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库。sql_injection.sql
-
要防范SQL注入,只要用 PreparedStatement(从Statement扩展而来)取代 Statement就可以了
2、Statement类的操作
public class Statement_ {
public static void main(String[] args) throws Exception {
//通过 Properties 对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src/mysql.properties"));
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
// 1.注册驱动
Class.forName(driver);
// 2.得到连接
Connection connection = DriverManager.getConnection(url, user, password);
// 3.得到Statement
Statement statement = connection.createStatement();
// 4.SQL语句
String sql = "select name from admin where name = 'Jom' and pwd = '123';
// 5.执行sql语句,并返回单个结果集
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
//如果查询到一条记录,则说明该管理存在
System.out.println("恭喜, 登录成功");
} else {
System.out.println("对不起,登录失败");
}
//关闭连接
resultSet.close();
statement.close();
connection.close();
}
}
六、PreparedStatement类(预处理)
1、基本介绍:
-
PreparedStatement 执行的SQL 语中的参数用问号(?)来表示,调用PreparedStatement对象的setXxx()方法来设置这些参数。
-
调用setXxx(),设置sql语句中?的值,该方法有两个参数,第一个参数是 ?的索引(从1开始),第二个是参数的值
-
调用executeQuery(),返回ResultSet 对象
-
调用executeUpdate(),执行更新,包括增、删、修改
2、预处理好处:
-
不再使用+拼接sql语句,减少语法错误
-
有效的解决了sql注入问题!
-
大大减少了编译次数,效率较高
3、⭐PreparedStatement类的操作(推荐使用)
public class PreparedStatementDML_ {
public static void main(String[] args) throws Exception {
//通过 Properties 对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src/mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
// 1.注册驱动
Class.forName(driver);
// 2.得到连接
Connection connection = DriverManager.getConnection(url, user, password);
// 3.得到 PreparedStatement
// 3.1 书写sql语句, sql语句的 ? 就相当于占位符
//String sql = "insert into admin values(?, ?)";
//String sql = "update admin set pwd = ? where name = ?";
//String sql = "delete from admin where name = ?";
String sql = "select name, pwd from admin where name = ? and pwd = ?";
// 3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 3.3 给 ? 赋值
preparedStatement.setString(1, 'Jom');
preparedStatement.setString(2, '123');
//4. 执行 dml 语句使用 executeUpdate
int rows = preparedStatement.executeUpdate();
System.out.println(rows > 0 ? "执行成功" : "执行失败");
//关闭连接
preparedStatement.close();
connection.close();
}
}
*七、JDBC API小结
DriverManager驱动管理类的方法
getConnection(url, user, pwd) 获取到连接
Connection接口的方法
createStatement 创建Statement对象 preparedStatement(sql) 生成预处理对象 executeUpdate(sql) 执行dml语句,返回影响的行数 executeQuery(sql) 执行查询,返回ResultSet对象 execute(sql) 执行任意的sql,返回布尔值
Statement接口的方法
executeUpdate(sql) 执行dml语句,返回影响的行数 executeQuery(sql) 执行查询,返回ResultSet对象 execute(sql) 执行任意的sql,返回布尔值
PreparedStatement接口的方法
executeUpdate() 执行dml executeQuery() 执行查询,返回ResultSet execute() 执行任意sql ,返回布尔 setXxx(占位符索引,占位符的值), 解决SQL注入 setObject(占位符索引,占位符的值)
ResultSet(结果集)的方法
next() 向下移动一行,同时如果没有下一行,返回false previous() 向上移动一行,如果没有上一行,返回false getXxx(列的索引列名) 返回对应列的值,接收类型是Xxx getObject(列的索引列名) 返回对应列的值,接收类型为Object
八、封装JDBC的Utils工具类
1、封装JDBCUtils【获取连接,关闭连接】 说明:在jdbc操作中,获取连接和释放资源是经常使用到,可以将其封装JDBC连接的工真类JDBCUtils。
配置文件mysql.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db01?rewriteBatchedStatements=true
user=root
password=123456
public class JDBCUtils {
// 1.定义相关的属性(4个), 因为只需要一份,因此,我们做出 static
private static String user; //用户名
private static String password; //密码
private static String url; //url
private static String driver; //驱动名
// 2.在 static 代码块初始化属性
static {
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driver = properties.getProperty("driver");
} catch (IOException e) {
// 1. 将编译异常转成 运行异常
// 2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便.
throw new RuntimeException(e);
}
}
// 3.连接数据库, 返回 Connection
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
//1. 将编译异常转成 运行异常
// 2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便.
throw new RuntimeException(e);
}
}
// 4.关闭相关资源
/**
* 如果需要关闭资源,就传入对象,否则传入 null
* @param set --> ResultSet 结果集
* @param statement --> Statement 或者 PreparedStatement
* @param connection --> Connection
*/
public static void close(ResultSet set, Statement statement, Connection connection) {
//判断是否为 null
try {
if (set != null) {
set.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
//将编译异常转成运行异常抛出
throw new RuntimeException(e);
}
}
}
2、使用JDBCUtils工具类
public class JDBCUtils_Use {
// select
public void testSelect() {
String sql = "select * from actor where id = ?";
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet set = null;
try {
// 1.得到连接
connection = JDBCUtils.getConnection();
// 2.创建 PreparedStatement 对象
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 1); //给 ? 赋值
set = preparedStatement.executeQuery(); // 执行, 得到结果集
// 输出结果集
while (set.next()) {
int id = set.getInt("id");
String name = set.getString("name");
String sex = set.getString("sex");
Date borndate = set.getDate("borndate");
String phone = set.getString("phone");
System.out.println(id + "\t" + name + "\t" + sex + "\t" + borndate + "\t" + phone);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.close(set, preparedStatement, connection);
}
}
// insert, update, delete
public void testDML() {
String sql = "update actor set name = ? where id = ?";
// 测试 delete 和 insert ,自己玩.
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
// 1.获得数据库连接
connection = JDBCUtils.getConnection();
// 2.创建 PreparedStatement 对象
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "周星驰"); //给占位符赋值
preparedStatement.setInt(2, 4);
preparedStatement.executeUpdate(); //执行sql语句
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.close(null, preparedStatement, connection);
}
}
}
*九、事务
1、基本介绍
-
JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务: 每次执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
-
JDBC程序中为了让多个SQL语句作为一个整体执行,需要使用事务
-
调用Connection的setAutoCommit(false)可以取消自动提交事务
-
在所有的SQL语句都成功执行后,调用commit();方法 提交事务
-
在其中某个操作失败或出现异常时,调用rollback();方法 回滚事务
创建表
create table account( id int primary key auto increment, name varchar(32)not null default ", balance double not null default 0) character set utf8; insert into account values(null, '马云',3000); insert into account values(null, '马化腾',10000);
事务的使用
/**
* 演示 jdbc 中如何使用事务 - 操作转账的业务
* 事务就是每执行一个执行sql语句,执行成功会向数据库提交,执行失败会进行回滚
*/
public class Transaction_ {
public void useTransaction() {
//2. 组织一个 sql
String sql = "update account set balance = balance - 100 where id = 1";
String sql2 = "update account set balance = balance + 100 where id = 2";
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
// 1.获得数据库连接
connection = JDBCUtils.getConnection();
// 2.开启了事务
// 将 connection 设置为不自动提交,在默认情况下,connection 是默认自动提交
connection.setAutoCommit(false);
// 3.创建 PreparedStatement 对象
// 3.1执行第 1 条 sql
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
int i = 1 / 0; // 抛出异常
// 3.2执行第 3 条 sql
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
// 4.提交事务
connection.commit();
} catch (SQLException e) {
System.out.println("执行发生了异常,撤销执行的 sql");
// 这里我们可以进行回滚,即撤销执行的 SQL,默认回滚到事务开始的状态.
try {
// 5.进行回滚
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
} finally {
// 关闭资源
JDBCUtils.close(null, preparedStatement, connection);
}
}
}
十、批处理
1、基本介绍
-
当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。
-
JDBC的批量处理语句包括下面方法: addBatch():添加需要批量处理的SQL语句或参数 executeBatch():执行批量处理语句; clearBatch():清空批处理包的语句
-
JDBC连接MySQL时,如果要使用批处理功能,请再url中加参数?rewriteBatchedStatements=true
-
批处理往往和PreparedStatement一起搭配使用,可以既减少编译次数,又减少运行次数,效率大大提高
2、preparedStatement.addBatch() 的源码分析:
-
第一就创建 ArrayList - elementData => Object[]
-
elementData => Object[] 就会存放我们预处理的 sql 语句
-
当 elementData 满后,就按照 1.5 扩容
-
当添加到指定的值后,就 executeBatch
-
批量处理会减少我们发送 sql 语句的网络开销,而且减少编译次数,因此效率提高。
3、批处理的使用
// 使用批量方式添加数据
public class Batch_ {
public void batch() throws Exception {
// 1.获取数据库连接
Connection connection = JDBCUtils.getConnection();
//SQL语句
String sql = "insert into admin2 values(null, ?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 一共添加5000条数据
for (int i = 0; i < 5000; i++) {
// 给sql语句的 ? 赋值
preparedStatement.setString(1, "jack" + i);
preparedStatement.setString(2, "123");
//将 sql 语句加入到批处理包中
preparedStatement.addBatch();
//当数据达到 1000条记录时,在批量执行
if ((i + 1) % 1000 == 0) {
preparedStatement.executeBatch(); //执行批处理包中的数据
preparedStatement.clearBatch(); //清空批处理包中的数据
}
}
// 关闭连接
JDBCUtils.close(null, preparedStatement, connection);
}
}
*十一、数据库连接池
1、传统连接的弊端分析
-
传统的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将Connection 加载到内存中, 再验证IP地址,用户名和密码(0.05s~1s时间)。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃。
-
每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。
-
传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL崩溃。
-
解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool) 。
2、数据库连接池基本介绍
-
预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
-
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
-
当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中
3、数据库连接池种类
-
JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现[提供.jar]
-
C3P0数据库连接池,速度相对较慢,稳定性不错(hibernate, spring)
-
DBCP数据库连接池,速度相对c3p0较快,但不稳定
-
Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
-
BoneCP 数据库连接池,速度快
-
⭐Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxool优点于一身的数据库连接池。
C3P0演示 - 使用c3p0数据库连接池
配置文件 mysql.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db01?rewriteBatchedStatements=true
user=root
password=123456
配置文件模板 c3p0.config.xml
<c3p0-config>
<!-- 数据源名称代表连接池 -->
<named-config name="Connection_pool">
<!-- 驱动类 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!-- url-->
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/db01</property>
<!-- 用户名 -->
<property name="user">root</property>
<!-- 密码 -->
<property name="password">123456</property>
<!-- 每次增长的连接数-->
<property name="acquireIncrement">5</property>
<!-- 初始的连接数 -->
<property name="initialPoolSize">10</property>
<!-- 最小连接数 -->
<property name="minPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">50</property>
<!-- 可连接的最多的命令对象数 -->
<property name="maxStatements">5</property>
<!-- 每个连接对象可连接的最多的命令对象数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
操作
//前置:添加c3p0的jar
public class C3P0_ {
//第一种方式:使用配置文件来完成
public void testC3P0_01() throws Exception {
//1. 创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2. 通过配置文件mysql.properties 获取相关连接的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
// 3.给数据源 comboPooledDataSource 设置相关的参数
// 注意:连接管理是由 comboPooledDataSource 来管理
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//c3p0数据库连接池的设置
comboPooledDataSource.setInitialPoolSize(10); //设置初始化连接数
comboPooledDataSource.setMaxPoolSize(50); //最大连接数
//测试连接池的效率, 对mysql数据库进行 5000次 连接操作
for (int i = 0; i < 5000; i++) {
//从连接池中获取连接
Connection connection = comboPooledDataSource.getConnection();
//将连接放回连接池
connection.close();
}
}
//第二种方式:使用配置文件模板来完成
//1. 将 c3p0 提供的 c3p0.config.xml 拷贝到 src 目录下
//2. 该文件指定了连接数据库和连接池的相关参数
@Test
public void testC3P0_02() throws SQLException {
// 1.创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("Connection_pool");
//测试连接池的效率, 对mysql数据库进行 5000次 连接操作
for (int i = 0; i < 500000; i++) {
//从连接池中获取连接
Connection connection = comboPooledDataSource.getConnection();
//将连接放回连接池
connection.close();
}
}
}
Druid(德鲁伊)演示-使用Druid(德鲁伊)数据库连接池(⭐推荐使用)
配置文件 druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db01?rewriteBatchedStatements=true
username=root
password=123456
initialSize=10
minIdle=5
maxActive=20
maxWait=5000
操作
// 前置:添加 Druid的jar 包
// 创建配置文件 druid.properties, 将该文件拷贝项目的 src 目录
public class Druid_ {
public void testDruid() throws Exception {
// 1.创建 Properties 对象, 读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\druid.properties"));
// 2.创建一个指定参数的数据库连接池, Druid 连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//测试连接池的效率, 对mysql数据库进行 5000次 连接操作
for (int i = 0; i < 500000; i++) {
//从连接池中获取连接
Connection connection = dataSource.getConnection();
//将连接放回连接池
connection.close();
}
}
}
十二、Apache-DBUtils
1、结果集复用
-
关闭connection后,resultSet结果集无法使用
-
结果集不利于数据管理[只能用一次]
-
使用返回信息也不方便
解决复用问题:将结果集封装到ArrayList<Actor>中
一个actor对象对应一条acts表记录,actor对象放入到ArrayList集合
Actor { 属性: id , name,sex, borndate,phone }
2、Apache—DBUtils 基本介绍:
commons-dbutils 是 Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量。
-
QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理
-
使用QueryRunner类实现查询
-
ResultSetHandler接口:该接口用于处理java.sqlI.ResultSet,将数据按要求转换为另一种形式,
3、DbUtils类的方法
-
ArrayHandler: 把结果集中的第一行数据转成对象数组。
-
ArrayListHandler: 把结果集中的每一行数据都转成一个数组,再存放到List中.
-
BeanHandler: 将结果集中的第一行数据封装到一个对应的JavaBean实例中。
-
BeanListHandler: 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
-
ColumnListHandler: 将结果集中某一列的数据存放到List中。
-
KeyedHandler(name): 将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key.
-
MapHandler: 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
-
MapListHandler: 将结果集中的每一行数据都封装到一个Map里,然后再存放到List
4、应用实例
actor类
/**
* actor对象 和 actor表的记录 对应
*/
public class Actor {
private Integer id;
private String name;
private String sex;
private Date borndate;
private String phone;
public Actor() {}
public Actor(Integer id, String name, String sex, Date borndate, String phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.borndate = borndate;
this.phone = phone;
}
//getX()和setX()方法
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getSex() {return sex;}
public void setSex(String sex) {this.sex = sex;}
public Date getBorndate() {return borndate;}
public void setBorndate(Date borndate) {this.borndate = borndate;}
public String getPhone() {return phone;}
public void setPhone(String phone) {this.phone = phone;}
@Override
public String toString() {
return "Actor{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", borndate=" + borndate +
", phone='" + phone + '\'' +
'}';
}
}
//使用 DBUtils+数据连接池(德鲁伊)方式,完成对表actor的crud
public void testQueryMany() throws SQLException { //返回结果是多行的情况
// 1.得到 连接 (druid)
Connection connection = JDBCUtilsByDruid.getConnection();
// 2.使用 DBUtils 类和接口 , 先引入DBUtils 相关的 jar , 加入到本 Project
// 3.创建 QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 4.就可以执行相关的方法,返回ArrayList 结果集
String sql = "select id, name from actor where id >= ?";
List<Actor> list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
// 解读 query()方法
//(1) query 方法就是执行 sql 语句,得到 resultset ---封装到 --> ArrayList 集合中
//(2) 返回集合
//(3) connection: 连接
//(4) sql: 执行的 sql 语句
//(5) new BeanListHandler<>(Actor.class): 在将 resultset -> Actor 对象 -> 封装到 ArrayList
// 底层使用反射机制 去获取Actor 类的属性,然后进行封装
//(6) 1 就是给 sql 语句中的? 赋值,可以有多个值,因为是可变参数Object... params
//(7) 底层得到的 resultset ,会在 query 关闭, 关闭 PreparedStatment
System.out.println("输出集合的信息");
for (Actor actor : list) {
System.out.print(actor);
}
//释放资源
JDBCUtilsByDruid.close(null, null, connection);
}
//演示 apache-dbutils + druid 完成 返回的结果是单行记录(单个对象)
public void testQuerySingle() throws SQLException {
// 1.得到 连接 (druid)
Connection connection = JDBCUtilsByDruid.getConnection();
// 2.使用 DBUtils 类和接口 , 先引入DBUtils 相关的 jar , 加入到本 Project
// 3.创建 QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 4.就可以执行相关的方法,返回单个对象
String sql = "select * from actor where id = ?";
// 因为我们返回的单行记录<--->单个对象 , 使用的Hander 是 BeanHandler
Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 1);
System.out.println(actor);
// 释放资源
JDBCUtilsByDruid.close(null, null, connection);
}
//演示 apache-dbutils + druid 完成查询结果是单行单列-返回的就是 object
public void testScalar() throws SQLException {
// 1.得到 连接 (druid)
Connection connection = JDBCUtilsByDruid.getConnection();
// 2.使用 DBUtils 类和接口 , 先引入DBUtils 相关的 jar , 加入到本 Project
// 3.创建 QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 4.就可以执行相关的方法,返回单行单列 , 返回的就是Object
String sql = "select name from actor where id = ?";
//老师解读: 因为返回的是一个对象, 使用的 handler 就是 ScalarHandler
Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 1);
System.out.println(obj);
// 释放资源
JDBCUtilsByDruid.close(null, null, connection);
}
//演示 apache-dbutils + druid 完成 dml (update, insert ,delete)
public void testDML() throws SQLException {
//1. 得到 连接 (druid)
Connection connection = JDBCUtilsByDruid.getConnection();
//2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的 jar , 加入到本 Project
//3. 创建 QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4. 这里组织 sql 完成 update, insert delete
// String sql = "update actor set name = ? where id = ?";
String sql = "insert into actor values(null, ?, ?, ?, ?)";
// String sql = "delete from actor where id = ?";
//解读
//(1) 执行 dml 操作是 queryRunner.update()
//(2) 返回的值是受影响的行数 (affected: 受影响)
int affectedRow = queryRunner.update(connection, sql, "林青霞", "女", "1966-10-10", "116");
System.out.println(affectedRow > 0 ? "执行成功" : "执行没有影响到表");
// 释放资源
JDBCUtilsByDruid.close(null, null, connection);
}
将Druid(德鲁伊)封装成工具类
public class JDBCUtilsByDruid {
private static DataSource ds;
//在静态代码块完成 ds 初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//编写 getConnection 方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// 将 Connection 对象放回连接池
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (
SQLException e) {
throw new RuntimeException(e);
}
}
}
解决结果集重复使用的问题
public class JDBCUtilsByDruid_USE {
// 使用老师的土方法来解决 ResultSet =封装=> Arraylist
public ArrayList<Actor> testSelectToArrayList() {
// 1.组织一个 sql
String sql = "select * from actor where id >= ?";
// 2.创建ArrayList 对象,存放 actor 对象
ArrayList<Actor> list = new ArrayList<>();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet set = null;
try {
// 3.得到连接
connection = JDBCUtilsByDruid.getConnection();
// 4.创建 PreparedStatement 对象
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 1); //给?号赋值
set = preparedStatement.executeQuery(); // 执行, 得到结果集
//遍历该结果集,取出字段并存储到 Actor对象
while (set.next()) {
int id = set.getInt("id");
String name = set.getString("name");//getName()
String sex = set.getString("sex");//getSex()
Date borndate = set.getDate("borndate");
String phone = set.getString("phone");
//把得到的 resultset 的记录,封装到 Actor 对象,放入到 list 集合
list.add(new Actor(id, name, sex, borndate, phone));
}
//将集合数据输出
System.out.println("list 集合数据=" + list);
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtilsByDruid.close(set, preparedStatement, connection);
}
//因为ArrayList 和 connection 没有任何关联,所以该集合可以复用.
return list;
}
}
十三、DAO和通用增删改查
apache-dbutils + Druid简化了JDBC开发,但还有不足;
-
SQL语句是固定,不能通过参数传入,通用性不好,需要进行改进,更方便执行增删改查
-
对于select 操作,如果有返回值,返回类型不能固定,需要使用泛型
-
将来的表很多,业务需求复杂,不可能只靠一个Java类完成
-
引出=》BasicDAO画出示意图,看看在实际开发中,应该如何处理
AppView层:界面层,调用server层的类,得到结果 Server层:业务层,组织sql,并调用XXXDao完成需求(actorServer、orderServer...) BasicDAO层: BasicDAO:将各个DAO共同操作放到这里 XXXDAO:完成对XXX表的增删改查 domian层:XXX类【javabean,domain,pojo】
2、基本说明
-
DAO: data access object数据访问对象
-
这样的通用类,称为BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作。
-
在BaiscDao的基础上,实现一张表对应一个Dao,更好的完成功能,比如 Customer表-Customer.java类(javabean)-CustomerDao.java
3、实例
工具库类
JDBCUtilsByDruid -基于 druid 数据库连接池的工具类
domain层
Actor类-actor对象的属性对应表的字段
dao层
BasicDAO类 -各个DAO共同操作放到这里, 是其他DAO的父类, 使用到 apache-dbutils ActorDAO类 -继承BasicDAO类,完成对XXX表的增删改查
用户层
TestDAO类 -测试ActorDAO 对 actor 表 crud 操作