JDBC 为访问不同数据库提供了统一的接口,为使用者屏蔽了细节问题。
Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种 *** 作。
JDBC的基本原理图【重要!】
模拟JDBC
package com.xjs.jdbc.myjdbc; public interface JdbcInterface { //连接 public Object getConnection() ; //crud public void crud(); //关闭连接 public void close(); }
package com.xjs.jdbc.myjdbc; public class MysqlJdbcImpl implements JdbcInterface{ @Override public Object getConnection() { System.out.println("得到 mysql 的连接"); return null; } @Override public void crud() { System.out.println("完成 mysql 增删改查"); } @Override public void close() { System.out.println("关闭 mysql 的连接"); } }
package com.xjs.jdbc.myjdbc; public class OracleJdbcImpl implements JdbcInterface { @Override public Object getConnection() { System.out.println("得到 oracle的连接 升级"); return null; } @Override public void crud() { System.out.println("完成 对oracle的增删改查"); } @Override public void close() { System.out.println("关闭 oracle的连接"); } }
package com.xjs.jdbc.myjdbc; import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; import java.util.Scanner; public class TestJDBC { public static void main(String[] args) throws Exception { //完成对mysql的 *** 作 JdbcInterface jdbcInterface = new MysqlJdbcImpl(); jdbcInterface.getConnection(); //通过接口来调用实现类[动态绑定] jdbcInterface.crud(); jdbcInterface.close(); //完成对oracle的 *** 作 System.out.println("=============================="); jdbcInterface = new OracleJdbcImpl(); jdbcInterface.getConnection(); //通过接口来调用实现类[动态绑定] jdbcInterface.crud(); jdbcInterface.close(); } }1.3 JDBC 带来的好处
- 注册驱动 - 加载 Driver 类获取连接 - 得到 Connection执行增删改查 - 发送 sql 给 MySQL 执行释放资源 - 关闭相关连接
CREATE TABLE actor( id INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(32) NOT NULL DEFAULT '', sex CHAR(1) NOT NULL DEFAULT '女', borndate DATETIME, phone VARCHAR(12) ) SELECt * FROM actor
【前置工作】:将mysql 驱动引入到我们的项目中
视频链接: jdbc快速入门.
package com.xjs.jdbc; import com.mysql.jdbc.Driver; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class Jdbc01 { public static void main(String[] args) throws SQLException { //前置工作: 在项目下创建一个文件夹比如 libs // 将 mysql.jar 拷贝到该目录下,点击 add as Library... 加入到项目中 //1. 注册驱动 Driver driver = new Driver(); //创建driver对象 //2. 得到连接 // 解读: //(1) jdbc:mysql:// 规定好表示协议,通过jdbc的方式连接mysql //(2) localhost 主机,可以是ip地址 //(3) 3306 表示mysql监听的端口 //(4) hsp_db02 连接到mysql dbms 的哪个数据库 //(5) mysql的连接本质就是前面学过的socket连接 String url = "jdbc:mysql://localhost:3306/hsp_db02"; //将 用户名和密码放入到Properties 对象 Properties properties = new Properties(); //说明 user 和 password 是规定好,后面的值根据实际情况写 properties.setProperty("user", "root");// 用户 properties.setProperty("password", "hsp"); //密码 Connection connect = driver.connect(url, properties); //3. 执行sql //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(); } }3 获取数据库连接 5 种方式 3.1 方式 1
//获取Driver实现类对象 Driver driver = new com.mysql.jdbc.Driver(); String url = "jdbc:mysql://localhost:3306/jdbc_db"; Properties info = new Properties(); info.setProperties("user","root");//用户名 info.setProperties("password","hsp");//密码 Connection conn = driver.connect(url,info); System.out.println(conn);3.2 方式 2
//方式1 会直接使用 com.mysql.jdbc.Driver(),属于静态加载,灵活性差,依赖性强 //---推出---> 方式2 Calss aClass = Class.forName("com.mysql.jdbc.Driver"); Driver driver = (Driver) aClass.newInstance(); String url = "jdbc:mysql://localhost:3306/jdbc_db"; Properties info = new Properties(); info.setProperties("user","root"); info.setProperties("password","hsp"); Connection conn = driver.connect(url,info); System.out.println(conn);3.3 方式 3
//使用DriverManager替换Driver Class aClass = Class.forName("com.mysql.jdbc.Driver"); Driver driver = (Driver) aClass.newInstance(); String url = "jdbc:mysql://localhost:3306/hsp_db02"; String user = "root"; String password = "hsp"; DriverManager.registerDriver(driver);//注册Driver驱动 Connection conn = DriverManager.getConnection(url,user,password); System.out.println(conn);3.4 方式 4
//使用Class.forName 自动完成注册驱动,简化代码 ==> 分析源码 Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/hsp_db02"; String user = "root"; String password = "hsp"; Connection conn = DriverManager.getConnection(url,user,password); System.out.println(conn);
> 特别提示: > 1. mysql驱动5.1.6可以无需Class.forName("com.mysql.jdbc.Driver"); > 2. 从jdk1.5以后使用了jdbc4, 不再需要显示调用Class.forName()注册驱动, > 而是自动调用驱动jar包下 meta-INFservicesjava.sql.Driver文本中的类名称去注册 > 3.建议还是写上 Class.forName("com.mysql.jdbc.Driver"),更加明确3.5 方式 5
//使用配置文件,连接数据库更灵活 1. Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/hsp_db02","user","hsp"); 这其中的字符串 各个值,比如端口,数据库,用户名,密码为了方便, 我们可以将信息写入到 mysql.properties 配置文件中,方便 *** 作
【配置文件 mysql.propertise 内容如下】
user=root password=hsp driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/hsp_db02
//通过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"); Class.forName(driver);//建议写上 Connection connection = DriverManager.getConnection(url, user, password); System.out.println("方式5 " + connection);
【五种方式代码如下】
package com.xjs.jdbc; import com.mysql.jdbc.Driver; import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; public class JdbcConn { //方式1 @Test public void connect01() throws SQLException { Driver driver = new Driver(); //创建driver对象 String url = "jdbc:mysql://localhost:3306/hsp_db02"; //将 用户名和密码放入到Properties 对象 Properties properties = new Properties(); //说明 user 和 password 是规定好,后面的值根据实际情况写 properties.setProperty("user", "root");// 用户 properties.setProperty("password", "hsp"); //密码 Connection connect = driver.connect(url, properties); System.out.println(connect); //com.mysql.jdbc.JDBC4Connection@6c629d6e } //方式2 @Test public void connect02() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException { //使用反射加载Driver类 , 动态加载,更加的灵活,减少依赖性 Class> aClass = Class.forName("com.mysql.jdbc.Driver"); Driver driver = (Driver)aClass.newInstance(); String url = "jdbc:mysql://localhost:3306/hsp_db02"; //将 用户名和密码放入到Properties 对象 Properties properties = new Properties(); //说明 user 和 password 是规定好,后面的值根据实际情况写 properties.setProperty("user", "root");// 用户 properties.setProperty("password", "hsp"); //密码 Connection connect = driver.connect(url, properties); System.out.println("方式2=" + connect); //com.mysql.jdbc.JDBC4Connection@6c629d6e } //方式3 使用DriverManager 替代 driver 进行统一管理 @Test public void connect03() throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException { //使用反射加载Driver Class> aClass = Class.forName("com.mysql.jdbc.Driver"); Driver driver = (Driver) aClass.newInstance(); //创建url 和 user 和 password String url = "jdbc:mysql://localhost:3306/hsp_db02"; String user = "root"; String password = "hsp"; DriverManager.registerDriver(driver);//注册Driver驱动 Connection connection = DriverManager.getConnection(url, user, password); System.out.println("第三种方式=" + connection); //com.mysql.jdbc.JDBC4Connection@5ecddf8f } //方式4: 使用Class.forName 自动完成注册驱动,简化代码 //这种方式获取连接是使用的最多,推荐使用 @Test public void connect04() throws ClassNotFoundException, SQLException { //使用反射加载了 Driver类 //在加载 Driver类时,完成注册 Class.forName("com.mysql.jdbc.Driver"); //创建url 和 user 和 password String url = "jdbc:mysql://localhost:3306/hsp_db02"; String user = "root"; String password = "hsp"; Connection connection = DriverManager.getConnection(url, user, password); System.out.println("第4种方式~ " + connection); //com.mysql.jdbc.JDBC4Connection@6c629d6e } //方式5 , 在方式4的基础上改进,增加配置文件,让连接mysql更加灵活 @Test public void connect05() throws IOException, ClassNotFoundException, SQLException { //通过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"); Class.forName(driver);//建议写上 Connection connection = DriverManager.getConnection(url, user, password); System.out.println("方式5 " + connection); //com.mysql.jdbc.JDBC4Connection@6c629d6e } }3.6 课堂练习
参考前面的代码,使用方式5完成以下功能
- 创建 news 表使用 JDBC 添加 5 条数据修改 id = 1 的记录,将 content 改成一个新的消息删除 id = 3 的记录
package com.xjs.jdbc; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class Exercise { public static void main(String[] args) throws IOException, SQLException { //1.加载驱动 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"); //2.获得连接 Connection connection = DriverManager.getConnection(url, user, password); //3.执行sql //创建 news 表 //String sql = "create table news (id int primary key , content varchar(32) not null default '')"; //使用 JDBC 添加 5 条数据 //String sql = "insert into news values (1,'北京新闻'),(2,'上海新闻'),(3,'深圳新闻'),(4,'湖北新闻'),(5,'中央新闻')"; //修改 id = 1 的记录,将 content 改成一个新的消息 //String sql = "update news set content = '十堰新闻' where id = 1"; //删除 id = 3 的记录 String sql = "delete from news where id = 3"; Statement statement = connection.createStatement(); int i = statement.executeUpdate(sql); System.out.println("i= "+i); System.out.println(i>0?" *** 作成功~":"失败..."); //4.关闭连接释放资源 statement.close(); connection.close(); } }4 ResultSet[结果集] 4.1 基本介绍
- 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成ResultSet对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false,因此可以在while循环中使用循环来遍历结果集
【在dos窗口下查询的 actor 表的数据如下】
package com.xjs.jdbc.resultSet_; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.*; import java.util.Properties; @SuppressWarnings({"all"}) public class ResultSet_ { public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException { //通过Properties对象获取配置文件的信息 //读取 mysql.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.得到statement Statement statement = connection.createStatement(); //4.执行sql //先添加两条数据 //String sql = "insert into actor values (null,'刘德华','男','1970-12-12','111')"; //String sql = "insert into actor values (null,'周星驰','男','1974-1-1','222')"; //查询语句 String sql = "select id,name,sex,borndate from actor"; //执行给定的SQL语句,该语句返回单个 ResultSet对象 //dml语句 //int i = statement.executeUpdate(sql); //System.out.println(i>0?"成功~":"失败..."); //执行给定的SQL语句,该语句返回单个 ResultSet对象 //select语句 ResultSet resultSet = statement.executeQuery(sql); //5. 使用while取出数据 while (resultSet.next()) { int id = resultSet.getInt(1);//获取该行的第1列值 //int id = resultSet.getInt("id"); 通过列名来获取值, 推荐 String name = resultSet.getString(2);//获取该行的第2列值 String sex = resultSet.getString(3); Date borndate = resultSet.getDate(4); //输出 System.out.println(id + "t" + name + "t" + sex + "t" + borndate); } //6.关闭资源 statement.close(); connection.close(); } }
【阅读debug 代码 resultSet 对象的结构】如下图所示:
- Statement 对象用于执行静态 sql 语句并返回生成的结果的对象。在连接建立后,需要对数据库进行访问,执行命令或是 SQL 语句,可以通过:
Statement 【存在 SQL 注入】PreparedStatement 【预处理】CallableStatement 【存储过程】
- Statement对象执行 SQL 语句,存在SQL注入风险SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句,恶意攻击数据库。要防范 SQL注入,只要用 PreparedStatement(从Statement扩展而来)取代 Statement 就可以了。
-- 演示sql 注入 -- 创建一张表 CREATE TABLE admin ( -- 管理员表 NAME VARCHAR(32) NOT NULL UNIQUE, pwd VARCHAR(32) NOT NULL DEFAULT '') CHARACTER SET utf8; -- 添加数据 INSERT INTO admin VALUES('tom', '123'); -- 查找某个管理是否存在 SELECT * FROM admin WHERe NAME = 'tom' AND pwd = '123' -- SQL -- 输入用户名 为 1' or -- 输入万能密码 为 or '1'= '1 SELECt * FROM admin WHERe NAME = '1' OR' AND pwd = 'OR '1'= '1' SELECt * FROM admin5.2 应用实例
【说明】scanner.next():当接收到 空格或者 '就是表示结束, 而scanner.nextLine()是接收到回车表示结束
package com.xjs.jdbc.statement_; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.sql.*; import java.util.Properties; import java.util.Scanner; @SuppressWarnings({"all"}) public class Statement_ { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); //让用户输入管理员名和密码 System.out.print("请输入管理员的名字: "); //next(): 当接收到 空格或者 '就是表示结束, 而nextLine()是接收到回车表示结束 String admin_name = scanner.nextLine(); // 特别说明,如果希望看到SQL注入,这里需要用nextLine System.out.print("请输入管理员的密码: "); String admin_pwd = scanner.nextLine(); //通过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. 得到Statement Statement statement = connection.createStatement(); //4. 组织SqL String sql = "select name , pwd from admin where name ='" + admin_name + "' and pwd = '" + admin_pwd + "'"; ResultSet resultSet = statement.executeQuery(sql); if (resultSet.next()) { //如果查询到一条记录,则说明该管理存在 System.out.println("恭喜, 登录成功"); } else { System.out.println("对不起,登录失败"); } //关闭连接 resultSet.close(); statement.close(); connection.close(); } }6 PreparedStatement【预处理】 6.1 基本介绍
- PreparedStatement 执行的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数。setXxx() 方法有两个参数,第一个参数是要设置的 SQL语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值调用 excuteQuery(),返回 ResultSet 对象调用 excuteUpdate(),执行更新,包括:增、删、改
【看 PreparedStatement类图】
- 不再使用 + 拼接 sql 语句,减少了语法错误有效解决了 sql注入问题大大减少了编译次数,效率较高
package com.xjs.jdbc.preparedstatement_; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.sql.*; import java.util.Properties; import java.util.Scanner; @SuppressWarnings({"all"}) public class PreparedStatement_ { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); //让用户输入管理员名和密码 System.out.print("请输入管理员的名字: "); //next(): 当接收到 空格或者 '就是表示结束 String admin_name = scanner.nextLine(); // 说明,如果希望看到SQL注入,这里需要用nextLine System.out.print("请输入管理员的密码: "); String admin_pwd = scanner.nextLine(); //通过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 = "select name , pwd from admin where name =? and pwd = ?"; //3.2 preparedStatement 对象是实现了 PreparedStatement 接口的实现类的对象 PreparedStatement preparedStatement = connection.prepareStatement(sql); //3.3 给 ? 赋值 preparedStatement.setString(1, admin_name); preparedStatement.setString(2, admin_pwd); //4. 执行 select 语句使用 executeQuery // 如果执行的是 dml(update, insert ,delete) executeUpdate() // 这里执行 executeQuery ,不要在写 sql, ResultSet resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { //如果查询到一条记录,则说明该管理存在 System.out.println("恭喜, 登录成功"); } else { System.out.println("对不起,登录失败"); } //关闭连接 resultSet.close(); preparedStatement.close(); connection.close(); } }6.3.2 预处理DML案例
package com.xjs.jdbc.preparedstatement_; import java.io.FileInputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Properties; import java.util.Scanner; @SuppressWarnings({"all"}) public class PreparedStatementDML_ { public static void main(String[] args) throws Exception { //看 PreparedStatement类图 Scanner scanner = new Scanner(System.in); //让用户输入管理员名和密码 System.out.print("请输删除管理员的名字: "); //next(): 当接收到 空格或者 '就是表示结束 String admin_name = scanner.nextLine(); // 说明,如果希望看到SQL注入,这里需要用nextLine // System.out.print("请输入管理员的新密码: "); // String admin_pwd = scanner.nextLine(); //通过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 = ?"; //3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象 PreparedStatement preparedStatement = connection.prepareStatement(sql); //3.3 给 ? 赋值 preparedStatement.setString(1, admin_name); //preparedStatement.setString(2, admin_name); //4. 执行 dml 语句使用 executeUpdate int rows = preparedStatement.executeUpdate(); System.out.println(rows > 0 ? "执行成功" : "执行失败"); //关闭连接 preparedStatement.close(); connection.close(); } }6.4 课堂练习
要求:
- 创建 admin 表使用 preparedStatement 添加 5 条数据修改 tom 的记录,将 name 改成 king删除一条记录查询全部记录,并显示在控制台
package com.xjs.jdbc.preparedStatement_; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.*; import java.util.Properties; import java.util.Scanner; @SuppressWarnings({"all"}) public class Exercise { public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException { //接收用户输入 Scanner scanner = new Scanner(System.in); // System.out.println("请输入用户名:"); // String admin_name = scanner.nextLine(); // System.out.println("请输入密码:"); // String admin_pwd = scanner.nextLine(); //通过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"); //1.注册driver驱动 Class.forName(driver); //2.得到连接 Connection connection = DriverManager.getConnection(url, user, password); //3.组织sql语句 //① 创建admin表 // String sql = "create table admin (name varchar(32) not null unique," + // "pwd varchar(32) not null default '')"; //② 使用 preparedStatement 添加 5 条数据 // String sql = "insert into admin values (?,?)"; //③ 修改 tom 的记录,将 name 改成 king // String sql = "update admin set name = ? where name = ?"; //④ 删除一条记录 // String sql = "delete from admin where name = ?"; //⑤ 查询全部记录,并显示在控制台 String sql = "select * from admin"; //3.2得到preparedStatement PreparedStatement preparedStatement = connection.prepareStatement(sql); //3.3 给?赋值 //preparedStatement.setString(1,admin_name); //preparedStatement.setString(2,admin_pwd); //4.执行sql //dml语句 // int i = preparedStatement.executeUpdate(); // System.out.println(i>0?"执行成功":"执行失败"); //select语句 ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { String name = resultSet.getString(1);//获取该行第1列数据 String pwd = resultSet.getString(2);//获取该行第2列数据 System.out.println(name+"t"+pwd); } //5.关闭资源 resultSet.close(); preparedStatement.close(); connection.close(); } }7 JDBC 的相关 API 小结 8 封装 JDBCUtils 【得到连接, 关闭连接】 8.1 说明
在 jdbc *** 作中,获取连接和释放资源是经常使用到的,可以将其封装到 JDBC 连接的工具类 JDBCUtils
8.2 代码实现 8.3 实际使用使用工具类 JDBCUtilspackage com.xjs.jdbc.utils; import java.io.FileInputStream; import java.io.IOException; import java.sql.*; import java.util.Properties; public class JDBCUtils { //定义相关的属性(4个), 因为只需要一份,因此,我们做出static private static String user; //用户名 private static String password; //密码 private static String url; //url private static String driver; //驱动名 //在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); } } //连接数据库, 返回Connection public static Connection getConnection() { try { return DriverManager.getConnection(url, user, password); } catch (SQLException e) { //1. 将编译异常转成 运行异常 //2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便. throw new RuntimeException(e); } } //关闭相关资源 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); } } }
package com.xjs.jdbc.utils; import org.junit.Test; import java.sql.*; @SuppressWarnings({"all"}) public class JDBCUtilsUse { @Test public void testDML() { //1.得到连接 Connection connection = null; //2.组织sql String sql = "update actor set name = ? where id = ?"; //得到preparedStatement对象 PreparedStatement preparedStatement = null; try { connection = JDBCUtils.getConnection(); preparedStatement = connection.prepareStatement(sql); //给?占位符赋值,(将刘德华改为黄渤) preparedStatement.setString(1, "黄渤"); preparedStatement.setInt(2, 2); //执行 int i = preparedStatement.executeUpdate(); System.out.println("受影响行数= " + i); } catch (SQLException e) { e.printStackTrace(); } finally { //关闭资源 JDBCUtils.close(null, preparedStatement, connection); } } @Test public void testSelect() { //1.连接数据库 Connection connection = null; //2.组织Sql String sql = "select * from actor where id = ?"; //3.得到preparedStstement PreparedStatement preparedStatement = null; //得到结果集 ResultSet resultSet = null; try { connection = JDBCUtils.getConnection(); preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1, 3);//给?号赋值 //执行查询,得到resultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { //int id = resultSet.getInt(1); int id = resultSet.getInt("id");//推荐 //String name = resultSet.getString(2); String name = resultSet.getString("name");//推荐 //String sex = resultSet.getString(3); String sex = resultSet.getString("sex"); //Date borndate = resultSet.getDate(4); Date borndate = resultSet.getDate("borndate"); //String phone = resultSet.getString(5); String phone = resultSet.getString("phone"); System.out.println(id + "t" + name + "t" + sex + "t" + borndate + "t" + phone); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(resultSet,preparedStatement,connection); } } }9 事务 9.1 基本介绍
- JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不是回滚。JDBC程序中为了让多个 SQL 语句作为一个整体执行,需要使用事务调用 Connection 的 setAutoCommit(false) 可以取消自动提交事务在所有的 SQL 语句都成功执行后,调用 Connection 的 commit() 方法提交事务在其中某个 *** 作失败或出现异常时,调用 Connection 的 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);
建表如下图所示:↓
package com.xjs.jdbc.transaction_; import com.xjs.jdbc.utils.JDBCUtils; import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; @SuppressWarnings({"all"}) public class Transaction_ { //没有使用事务. @Test public void noTransaction() { // *** 作转账的业务 //1. 得到连接 Connection connection = null; //2. 组织sql String sql = "update account set balance = balance - 100 where id = 1"; String sql2 = "update account set balance = balance + 100 where id = 2"; //3.创建 preparedStatement 对象 PreparedStatement preparedStatement = null; try { connection = JDBCUtils.getConnection();// 在默认情况下,connection是默认自动提交事务 //执行sql preparedStatement = connection.prepareStatement(sql); preparedStatement.executeUpdate();//执行第1条Sql int i = 1/0;//这里会抛出一个异常,下面的第2条sql将无法执行... preparedStatement = connection.prepareStatement(sql2); preparedStatement.executeUpdate();//执行第2条Sql } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(null,preparedStatement,connection); } } }
package com.xjs.jdbc.transaction_; import com.xjs.jdbc.utils.JDBCUtils; import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; @SuppressWarnings({"all"}) public class Transaction_ { //使用事务处理 @Test public void useTransaction () { // *** 作转账的业务 //1. 得到连接 Connection connection = null; //2. 组织sql String sql = "update account set balance = balance - 100 where id = 1"; String sql2 = "update account set balance = balance + 100 where id = 2"; //3.创建 preparedStatement 对象 PreparedStatement preparedStatement = null; try { connection = JDBCUtils.getConnection();// 在默认情况下,connection是默认自动提交事务 //这里我们将connection设置为不自动提交事务 connection.setAutoCommit(false);//此时相当于开启了一个事务 //执行sql preparedStatement = connection.prepareStatement(sql); preparedStatement.executeUpdate();//执行第1条Sql int i = 1/0;//这里会抛出一个异常,下面的第2条sql将无法执行... preparedStatement = connection.prepareStatement(sql2); preparedStatement.executeUpdate();//执行第2条Sql //这里提交事务 connection.commit(); } catch (Exception e) { //如果程序发生异常,在这里我们有机会回滚撤销 System.out.println("执行发生了异常,撤销执行的sql"); try { //rollback()方法如果没有指定参数(savepoint),默认回滚到事务开启的状态 connection.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } e.printStackTrace(); } finally { JDBCUtils.close(null,preparedStatement,connection); } } }
- 当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。JDBC 的批量处理语句包括下面这些方法:
addBatch():添加需要批量处理的SQL语句或参数executeBatch():执行批量处理语句clearBatch():清空批处理包的语句
- JDBC连接MySQL时,如果要使用批处理功能,请在 url 中加参数 ?rewriteBatchedStatements=true批处理往往和PreparedStatement一起搭配使用,既可以减少编译次数,又减少运行次数,效率大大提高
- 演示向 admin2 表中添加5000条数据,看看使用批处理耗时多久注意:需要修改配置文件 【mysql.properties】中 url=jdbc:mysql://local:3302/数据库?rewriteBatchedStatements=true
create table admin2( id int primary key auto_increment, username varchar(32) not null, password varchar(32) not null);
package com.xjs.jdbc.batch_; import com.xjs.jdbc.utils.JDBCUtils; import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; @SuppressWarnings({"all"}) public class Batch_ { //传统方法,添加5000条数据到admin2 @Test public void noBatch() throws SQLException { Connection connection = JDBCUtils.getConnection(); String sql = "insert into admin2 values(null,?,?)"; PreparedStatement preparedStatement = connection.prepareStatement(sql); System.out.println("开始执行..."); long start = System.currentTimeMillis();//开始计时 for (int i = 0; i < 5000; i++) {//循环执行5000次 preparedStatement.setString(1, "jack" + i); preparedStatement.setString(2, "666"); preparedStatement.executeUpdate();//执行 } System.out.println("执行完成~"); long end = System.currentTimeMillis(); System.out.println("传统方式共耗时=" + (end - start));//传统方式共耗时=3377 //关闭连接 JDBCUtils.close(null, preparedStatement, connection); } //使用批处理 @Test public void useBath() throws SQLException { Connection connection = JDBCUtils.getConnection(); String sql = "insert into admin2 values(null,?,?)"; PreparedStatement preparedStatement = connection.prepareStatement(sql); System.out.println("开始执行..."); long start = System.currentTimeMillis();//开始计时 for (int i = 0; i < 5000; i++) {//添加5000条数据 preparedStatement.setString(1, "jack" + i); preparedStatement.setString(2, "666"); //将sql语句储到批处理包中 preparedStatement.addBatch(); //看底层源码,debug---> addBatch(); if ((i + 1) % 1000 == 0) { preparedStatement.executeBatch();//批量执行1000条数据 preparedStatement.clearBatch();//清空批处理包数据 } } System.out.println("执行完成~"); long end = System.currentTimeMillis();//结束时间 System.out.println("使用批处理共耗时=" + (end - start));//使用批处理共耗时=94 //关闭连接 JDBCUtils.close(null, preparedStatement, connection); } }11数据库连接池 11.1 5k 次连接数据库问题
- 编写程序完成连接MySQL 5000次的 *** 作看看有什么问题,耗时多久 ===> 数据库连接池
package com.xjs.jdbc.datasource; import com.xjs.jdbc.utils.JDBCUtils; import org.junit.Test; import java.sql.Connection; public class ConQuestion { //代码 连接mysql 5000次 @Test public void testCon() { //看看连接-关闭 connection 会耗用多久 long start = System.currentTimeMillis(); System.out.println("开始连接....."); for (int i = 0; i < 5000; i++) { //使用传统的jdbc方式,得到连接 Connection connection = JDBCUtils.getConnection(); //做一些工作,比如得到PreparedStatement ,发送sql //.......... //关闭 JDBCUtils.close(null, null, connection); } long end = System.currentTimeMillis(); System.out.println("传统方式5000次 耗时=" + (end - start));//传统方式5000次 耗时=7099 } }11.2 传统获取 Connection 问题分析
- 传统的 JDBC 数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证 IP地址,用户名和密码(0.05s ~ 1s 时间)需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接 *** 作将占用很多系统资源,容易造成服务器崩溃。每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄露,mysql崩溃。解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool)
- 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从 “缓冲池” 中取出一个,使用完毕后再放回去。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重建建立一个。当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中
- JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource只是一个接口,该接口通常由第三方提供实现【提供 .jar】C3P0 数据库连接池,速度相对较慢,稳定性不错(hibernate,spring)DBCP 数据库连接池,速度相对C3P0较快,但不稳定Proxool 数据库连接池,有监控连接池状态的功能,稳定性较C3P0差一点BoneCP 数据库连接池,速度快Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxool优点于一身的数据库连接池
韩老师视频链接: C3P0测试.
使用代码实现C3P0数据库连接池,配置文件(c3p0.config.xml)放在src目录下
package com.xjs.jdbc.datasource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import java.beans.PropertyVetoException; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; @SuppressWarnings({"all"}) public class C3P0_ { //方式1: 相关参数,在程序中指定user, url , password等 @Test 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"); //给数据源 comboPooledDataSource 设置相关的参数 //注意:连接管理是由 comboPooledDataSource 来管理 comboPooledDataSource.setDriverClass(driver); comboPooledDataSource.setJdbcUrl(url); comboPooledDataSource.setUser(user); comboPooledDataSource.setPassword(password); //设置初始化连接数 comboPooledDataSource.setInitialPoolSize(10); //最大连接数 comboPooledDataSource.setMaxPoolSize(50); //测试连接池的效率, 测试对mysql 5000次 *** 作 long start = System.currentTimeMillis(); for (int i = 0; i < 5000; i++) { Connection connection = comboPooledDataSource.getConnection(); //这个方法就是从 DataSource 接口实现的 //System.out.println("连接OK"); connection.close(); } long end = System.currentTimeMillis(); //c3p0 5000次连接mysql 耗时=438 System.out.println("c3p0 5000次连接mysql 耗时=" + (end - start)); } //第二种方式 使用配置文件模板来完成 //1. 将c3p0 提供的 c3p0.config.xml 拷贝到 src目录下 //2. 该文件指定了连接数据库和连接池的相关参数 @Test public void testC3P0_02() throws SQLException { ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("xjs"); //测试5000次连接mysql long start = System.currentTimeMillis(); System.out.println("开始执行...."); for (int i = 0; i < 500000; i++) { Connection connection = comboPooledDataSource.getConnection(); //System.out.println("连接OK~"); connection.close(); } long end = System.currentTimeMillis(); //c3p0的第二种方式 耗时=375 System.out.println("c3p0的第二种方式(500000) 耗时=" + (end - start));//1970 } }
【将c3p0 提供的 c3p0.config.xml 拷贝到 src目录下】
11.6 Druid(德鲁伊)应用实例com.mysql.jdbc.Driver jdbc:mysql://127.0.0.1:3306/hsp_db02 root hsp 5 10 5 50 5 2
韩老师视频链接: Druid测试.
使用代码实现Druid(德鲁伊)数据库连接池
【加入 配置文件 druid.properties , 将该文件拷贝项目的 src 目录】
#key=value driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/hsp_db02?rewriteBatchedStatements=true username=root password=hsp #initial connection Size initialSize=10 #min idle connection size minIdle=5 #max active connection size maxActive=20 #max wait time (5000 mil seconds) maxWait=5000
package com.xjs.jdbc.datasource; import com.alibaba.druid.pool.DruidDataSourceFactory; import org.junit.Test; import javax.sql.DataSource; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.Connection; import java.util.Properties; @SuppressWarnings({"all"}) public class Druid_ { @Test public void testDruid() throws Exception { //1. 加入 Druid jar包 //2. 加入 配置文件 druid.properties , 将该文件拷贝项目的src目录 //3. 创建Properties对象, 读取配置文件 Properties properties = new Properties(); properties.load(new FileInputStream("src\druid.properties")); //4. 创建一个指定参数的数据库连接池, Druid连接池 DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); long start = System.currentTimeMillis(); for (int i = 0; i < 500000; i++) { Connection connection = dataSource.getConnection(); System.out.println(connection.getClass()); //System.out.println("连接成功!"); connection.close(); } long end = System.currentTimeMillis(); //druid连接池 操作5000 耗时=359 System.out.println("druid连接池 操作500000 耗时=" + (end - start));//510 } }11.7 将 JDBCUtils 工具类改成 Druid(德鲁伊)实现
package com.xjs.jdbc.datasource; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.FileInputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; @SuppressWarnings({"all"}) public class JDBCUtilsByDruid { private static DataSource dataSource; //在静态代码块完成 dataSource初始化 static { Properties properties = new Properties(); try { properties.load(new FileInputStream("src\druid.properties")); dataSource = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { throw new RuntimeException(e); } } //编写getConnection方法 public static Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } //关闭连接, 再次强调: 在数据库连接池技术中,close 不是真的断掉连接 //而是把使用的Connection对象放回连接池 //由于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); } } }
测试该工具类的使用:
package com.xjs.jdbc.datasource; import org.junit.Test; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; @SuppressWarnings({"all"}) public class JDBCUtilsByDruidUse { @Test public void TestSelect() { //1.得到连接 Connection connection = null; //2.组织sql String sql = "select * from actor where id >= ?"; //3.得到preparedStatement PreparedStatement preparedStatement = null; //4.得到resultSet结果集 ResultSet resultSet = null; try { connection = JDBCUtilsByDruid.getConnection(); System.out.println(connection.getClass()); //此处connection的运行类型: com.alibaba.druid.pool.DruidPooledConnection preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1,3);//给?赋值 resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); String sex = resultSet.getString("sex"); Date borndate = resultSet.getDate("borndate"); String phone = resultSet.getString("phone"); System.out.println(id+"t"+name+"t"+sex+"t"+borndate+"t"+phone); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtilsByDruid.close(resultSet,preparedStatement,connection); } } }12Apache—DBUtils
韩老师链接: link.
12.1 先分析一个问题- 关闭 connection 后,resultSet 结果集无法使用resultSet 不利于数据的管理示意图
先创建 Actor 类,用于封装 actor表中各列信息
package com.xjs.jdbc.datasource; import java.util.Date; @SuppressWarnings({"all"}) public class Actor {//Javabean, POJO, Domain对象 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; } 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 "nActor{" + "id=" + id + ", name='" + name + ''' + ", sex='" + sex + ''' + ", borndate=" + borndate + ", phone='" + phone + ''' + '}'; } }
测试方法如下:
package com.xjs.jdbc.datasource; import org.junit.Test; import java.sql.*; import java.util.ArrayList; @SuppressWarnings({"all"}) public class JDBCUtilsByDruidUse { //注意:@Test 测试的方法不能有返回值,否则将报一个异常, //这里我选择写一次main方法来测试 public static void main(String[] args) { new JDBCUtilsByDruidUse().testSelectToArrayList(); } //使用土方法来解决ResultSet =封装=> Arraylist //@Test public ArrayList testSelectToArrayList() { //1.得到连接 Connection connection = null; //2.组织sql String sql = "select * from actor where id >= ?"; //3.得到preparedStatement PreparedStatement preparedStatement = null; //4.得到resultSet结果集 ResultSet resultSet = null; //创建ArrayList对象,存放actor对象 ArrayList arrayList = new ArrayList<>(); try { connection = JDBCUtilsByDruid.getConnection(); System.out.println(connection.getClass()); //此处connection的运行类型: com.alibaba.druid.pool.DruidPooledConnection preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1,1);//给?赋值 resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); String sex = resultSet.getString("sex"); Date borndate = resultSet.getDate("borndate"); String phone = resultSet.getString("phone"); //把得到的resultset 的记录,封装到 Actor对象,放入到list集合 arrayList.add(new Actor(id,name,sex,borndate,phone)); } System.out.println("arraylist集合数据:"+arrayList); for(Actor actor : arrayList) {//可以选择性的只取出我们需要的字段信息 System.out.println("id=" + actor.getId() + "t" + actor.getName()); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtilsByDruid.close(resultSet,preparedStatement,connection); } //因为ArrayList 和 connection 没有任何关联,所以该集合可以复用. return arrayList; } }12.3 基本介绍
- commons-dbutils 是 Apache 组织提供的一个开源 JDBC 工具类库,它是对 JDBC 的封装,使用 dbutils 能极大的简化 jdbc 编码的工作量【真的】DBUtils 类:
QueryRunner 类:该类封装了 SQL 的执行,是线程安全的。可以实现增、删、改、查、批处理。使用 QueryRunner 类实现查询ResultSetHandleer 接口:该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式
12.4 应用实例使用 DBUtils + 数据库连接池(德鲁伊)方式,完成对表 actor 的 crud
package com.xjs.jdbc.datasource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import org.junit.jupiter.api.Test; import java.sql.*; import java.util.ArrayList; import java.util.List; @SuppressWarnings({"all"}) public class DBUtils_USE { //使用apache-DBUtils 工具类 + druid 完成对表的crud *** 作 @Test 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 * from actor where id >= ?"; // 注意: sql 语句也可以查询部分列 String sql = "select id, name from actor where id >= ?"; // 解读: //(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 List list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1); System.out.println("输出集合的信息"); for (Actor actor : list) { System.out.print(actor); } //释放资源 JDBCUtilsByDruid.close(null, null, connection); } //演示 apache-dbutils + druid 完成 返回的结果是单行记录(单个对象) @Test 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), 3); System.out.println(actor); // 释放资源 JDBCUtilsByDruid.close(null, null, connection); } //演示apache-dbutils + druid 完成查询结果是单行单列-返回的就是object @Test 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(), 3); System.out.println(obj); // 释放资源 JDBCUtilsByDruid.close(null, null, connection); } //演示apache-dbutils + druid 完成 dml (update, insert ,delete) @Test 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"); int affectedRow = queryRunner.update(connection, sql, 1000 ); System.out.println(affectedRow > 0 ? "执行成功" : "执行没有影响到表"); // 释放资源 JDBCUtilsByDruid.close(null, null, connection); } }12.5 表和 JavaBean 的类型映射关系
- 字符串类型,表中的 char()、varchar() 对应Java中的 String类型。数值类型,表中的 int、double 对应Java中的 包装类(Integer、Double…)因为MySQL中所有类型都有可能是null,而 Java 中只有引用数据类型才有 null 值。日期类型,表中的 date 对应 Java 中 java.utils 包下的 Date 。
apache-dbutils + Druid 简化了 JDBC 开发,但还有不足:
- sql 语句是固定的,不能通过参数传入,通用性不好,需要进行改进,更方便执行 增删改查对于 select *** 作,如果有返回值,返回类型不能固定,需要使用泛型将来的表很多,业务需求复杂,不可能只靠一个 Java 类完成引出 ===> BasicDAO 画出 示意图,看看在实际开发中,应该如何处理
- DAO:data access object 数据访问对象这样的通用类,称为 BasicDao,是专门和数据库交互的,即完成对数据库(表)的 crud *** 作在 BasicDao 的基础上,实现一张表对应一个 Dao,更好的完成功能,比如 Actor 表 - Actor.java 类(javabean)- ActorDao.java
完成简单设计
com.xjs.dao_
- com.xjs.dao_.utils //工具类com.xjs.dao_.domain //javabeancom.xjs.dao_.dao //存放 XxxDAO 和 BasicDAOcom.xjs.dao_.test //写测试类
package com.xjs.dao_.utils; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.FileInputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; 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(); } //关闭连接, 再次强调: 在数据库连接池技术中,close 不是真的断掉连接 //而是把使用的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); } } }
package com.xjs.dao_.domain; import java.util.Date; public class Actor { //Javabean, POJO, Domain对象 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; } 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 "nActor{" + "id=" + id + ", name='" + name + ''' + ", sex='" + sex + ''' + ", borndate=" + borndate + ", phone='" + phone + ''' + '}'; } }
package com.xjs.dao_.dao; import com.xjs.dao_.utils.JDBCUtilsByDruid; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import java.sql.Connection; import java.sql.SQLException; import java.util.List; public class BasicDAO{ //泛型指定具体类型 private QueryRunner qr = new QueryRunner(); //开发通用的dml方法, 针对任意的表 public int update(String sql, Object... parameters) { Connection connection = null; try { connection = JDBCUtilsByDruid.getConnection(); int update = qr.update(connection, sql, parameters); return update; } catch (SQLException e) { throw new RuntimeException(e); //将编译异常->运行异常 ,抛出 } finally { JDBCUtilsByDruid.close(null, null, connection); } } //返回多个对象(即查询的结果是多行), 针对任意表 public List queryMulti(String sql, Class clazz, Object... parameters) { Connection connection = null; try { connection = JDBCUtilsByDruid.getConnection(); return qr.query(connection, sql, new BeanListHandler (clazz), parameters); } catch (SQLException e) { throw new RuntimeException(e); //将编译异常->运行异常 ,抛出 } finally { JDBCUtilsByDruid.close(null, null, connection); } } //查询单行结果 的通用方法 public T querySingle(String sql, Class clazz, Object... parameters) { Connection connection = null; try { connection = JDBCUtilsByDruid.getConnection(); return qr.query(connection, sql, new BeanHandler (clazz), parameters); } catch (SQLException e) { throw new RuntimeException(e); //将编译异常->运行异常 ,抛出 } finally { JDBCUtilsByDruid.close(null, null, connection); } } //查询单行单列的方法,即返回单值的方法 public Object queryScalar(String sql, Object... parameters) { Connection connection = null; try { connection = JDBCUtilsByDruid.getConnection(); return qr.query(connection, sql, new ScalarHandler(), parameters); } catch (SQLException e) { throw new RuntimeException(e); //将编译异常->运行异常 ,抛出 } finally { JDBCUtilsByDruid.close(null, null, connection); } } }
package com.xjs.dao_.dao; import com.xjs.dao_.domain.Actor; public class ActorDAO extends BasicDAO { //由于继承了 BasicDAO,所以: //1. 就有 BasicDAO 的方法 //2. 根据业务需求,可以编写特有的方法. }
package com.xjs.dao_.test; import com.xjs.dao_.dao.ActorDAO; import com.xjs.dao_.domain.Actor; import org.junit.jupiter.api.Test; import java.util.List; public class TestDAO { //测试ActorDAO 对actor表crud *** 作 @Test public void testActorDAO() { ActorDAO actorDAO = new ActorDAO(); //1. 查询 List actors = actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 1); System.out.println("===查询结果==="); for (Actor actor : actors) { System.out.println(actor); } //2. 查询单行记录 Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 6); System.out.println("====查询单行结果===="); System.out.println(actor); //3. 查询单行单列 Object o = actorDAO.queryScalar("select name from actor where id = ?", 6); System.out.println("====查询单行单列值==="); System.out.println(o); //4. dml *** 作 insert ,update, delete int update = actorDAO.update("insert into actor values(null, ?, ?, ?, ?)", "张无忌", "男", "2000-11-11", "999"); System.out.println(update > 0 ? "执行成功" : "执行没有影响表"); } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)