文章目录标题

  • 1 JDBC 概述
    • 1.1 基本介绍
    • 1.2 模拟 JDBC
    • 1.3 JDBC 带来的好处
    • 1.4 JDBC API
  • 2 JDBC 快速入门
    • 2.1 JDBC 程序编写步骤
    • 2.2 JDBC 第一个程序
  • 3 获取数据库连接 5 种方式
    • 3.1 方式 1
    • 3.2 方式 2
    • 3.3 方式 3
    • 3.4 方式 4
    • 3.5 方式 5
    • 3.6 课堂练习
  • 4 ResultSet[结果集]
    • 4.1 基本介绍
    • 4.2 应用实例
  • 5 Statement
    • 5.1 基本介绍
    • 5.2 应用实例
  • 6 PreparedStatement【预处理】
    • 6.1 基本介绍
    • 6.2 预处理好处
    • 6.3 应用案例
      • 6.3.1 预处理查询案例
      • 6.3.2 预处理DML案例
    • 6.4 课堂练习
  • 7 JDBC 的相关 API 小结
  • 8 封装 JDBCUtils 【得到连接, 关闭连接】
    • 8.1 说明
    • 8.2 代码实现
    • 8.3 实际使用使用工具类 JDBCUtils
  • 9 事务
    • 9.1 基本介绍
    • 9.2 应用实例
    • 9.3 不使用事务可能出现的问题模拟-模拟经典的转账业务
    • 9.4 使用事务解决上述问题-模拟经典的转账业务
  • 10 批处理
    • 10.1 基本介绍
    • 10.2 应用实例
  • 11数据库连接池
    • 11.1 5k 次连接数据库问题
    • 11.2 传统获取 Connection 问题分析
    • 11.3 数据库连接池基本介绍
    • 11.4 数据库连接池种类
    • 11.5 C3P0 应用实例
    • 11.6 Druid(德鲁伊)应用实例
    • 11.7 将 JDBCUtils 工具类改成 Druid(德鲁伊)实现
  • 12Apache—DBUtils
    • 12.1 先分析一个问题
    • 12.2 用自己的土方法来解决
    • 12.3 基本介绍
    • 12.4 应用实例
    • 12.5 表和 JavaBean 的类型映射关系
  • 13 DAO 和增删改查通用方法-BasicDao
    • 13.1 先分析一个问题
    • 13.2 基本说明
    • 13.3 BasicDAO 应用实例

1 JDBC 概述

1.1 基本介绍

  1. JDBC 为访问不同数据库提供了统一的接口,为使用者屏蔽了细节问题。

  2. Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。

  3. JDBC的基本原理图【重要!】

  1. 模拟JDBC

1.2 模拟 JDBC

package com.xjs.jdbc.myjdbc;/*** @author 谢家升* @version 1.0* 我们规定的jdbc接口(方法)*/
public interface JdbcInterface {//连接public Object getConnection() ;//crudpublic void crud();//关闭连接public void close();
}
package com.xjs.jdbc.myjdbc;/*** @author 谢家升* @version 1.0* mysql 数据库实现了jdbc接口 [模拟] 【mysql厂商开发】*/
public class MysqlJdbcImpl implements  JdbcInterface{@Overridepublic Object getConnection() {System.out.println("得到 mysql 的连接");return null;}@Overridepublic void crud() {System.out.println("完成 mysql 增删改查");}@Overridepublic void close() {System.out.println("关闭 mysql 的连接");}
}
package com.xjs.jdbc.myjdbc;/*** @author 谢家升* @version 1.0* 模拟oracle数据库实现 jdbc*/
public class OracleJdbcImpl implements  JdbcInterface {@Overridepublic Object getConnection() {System.out.println("得到 oracle的连接 升级");return null;}@Overridepublic void crud() {System.out.println("完成 对oracle的增删改查");}@Overridepublic 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;/*** @author 谢家升* @version 1.0*/
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 带来的好处


1.4 JDBC API

2 JDBC 快速入门

2.1 JDBC 程序编写步骤

  1. 注册驱动 - 加载 Driver 类
  2. 获取连接 - 得到 Connection
  3. 执行增删改查 - 发送 sql 给 MySQL 执行
  4. 释放资源 - 关闭相关连接

2.2 JDBC 第一个程序

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;/*** @author 谢家升* @version 1.0* 这是第一个Jdbc 程序,完成简单的操作*/
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(),属于静态加载,灵活性差,依赖性强
//---推出---> 方式2Calss 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-INF\services\java.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;/*** @author 谢家升* @version 1.0* 分析java 连接mysql的5中方式*/
public class JdbcConn {//方式1@Testpublic 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@Testpublic 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 进行统一管理@Testpublic void connect03() throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException {//使用反射加载DriverClass<?> aClass = Class.forName("com.mysql.jdbc.Driver");Driver driver = (Driver) aClass.newInstance();//创建url 和 user 和 passwordString 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 自动完成注册驱动,简化代码//这种方式获取连接是使用的最多,推荐使用@Testpublic void connect04() throws ClassNotFoundException, SQLException {//使用反射加载了 Driver类//在加载 Driver类时,完成注册/*源码: 1. 静态代码块,在类加载时,会执行一次.2. DriverManager.registerDriver(new Driver());3. 因此注册driver的工作已经完成static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}*/Class.forName("com.mysql.jdbc.Driver");//创建url 和 user 和 passwordString 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更加灵活@Testpublic 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完成以下功能

  1. 创建 news 表
  2. 使用 JDBC 添加 5 条数据
  3. 修改 id = 1 的记录,将 content 改成一个新的消息
  4. 删除 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;/*** @Author: 谢家升* @Version: 1.0*/
public class Exercise {public static void main(String[] args) throws IOException, SQLException {/*参考前面的代码,使用==方式5==完成以下功能1. 创建 news 表2. 使用 JDBC 添加 5 条数据3. 修改 id = 1 的记录,将 content 改成一个新的消息4. 删除 id = 3 的记录*///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 基本介绍

  1. 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
  2. ResultSet对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前
  3. next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false,因此可以在while循环中使用循环来遍历结果集

4.2 应用实例

【在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;/*** @Author: 谢家升* @Version: 1.0* 演示select 语句返回 ResultSet ,并取出结果*/
@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.得到statementStatement 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对象/*+----+-----------+-----+---------------------+| id | name      | sex | borndate            |+----+-----------+-----+---------------------+|  2 | 刘德华    | 男  | 1970-12-12 00:00:00 ||  3 | 周星驰    | 男  | 1974-01-01 00:00:00 |+----+-----------+-----+---------------------+*//*阅读debug 代码 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 对象的结构】如下图所示:

5 Statement

5.1 基本介绍

  1. Statement 对象用于执行静态 sql 语句并返回生成的结果的对象。
  2. 在连接建立后,需要对数据库进行访问,执行命令或是 SQL 语句,可以通过:
  • Statement 【存在 SQL 注入】
  • PreparedStatement 【预处理】
  • CallableStatement 【存储过程】
  1. Statement对象执行 SQL 语句,存在SQL注入风险
  2. SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句,恶意攻击数据库。
  3. 要防范 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 adminWHERE NAME = 'tom' AND pwd = '123'-- SQL
-- 输入用户名 为  1' or
-- 输入万能密码 为 or '1'= '1
SELECT * FROM adminWHERE NAME = '1' OR' AND pwd = 'OR '1'= '1'
SELECT * FROM admin

5.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;/*** @author 谢家升* @version 1.0* 演示statement 的注入问题*/
@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注入,这里需要用nextLineSystem.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. 得到StatementStatement statement = connection.createStatement();//4. 组织SqLString 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 基本介绍

  1. PreparedStatement 执行的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数。setXxx() 方法有两个参数,第一个参数是要设置的 SQL语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值
  2. 调用 excuteQuery(),返回 ResultSet 对象
  3. 调用 excuteUpdate(),执行更新,包括:增、删、改

【看 PreparedStatement类图】

6.2 预处理好处

  1. 不再使用 + 拼接 sql 语句,减少了语法错误
  2. 有效解决了 sql注入问题
  3. 大大减少了编译次数,效率较高

6.3 应用案例

6.3.1 预处理查询案例

package com.xjs.jdbc.preparedstatement_;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;/*** @author 谢家升* @version 1.0* 演示PreparedStatement使用*/
@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注入,这里需要用nextLineSystem.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;/*** @author 谢家升* @version 1.0* 演示PreparedStatement使用 dml语句*/
@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 语句使用  executeUpdateint rows = preparedStatement.executeUpdate();System.out.println(rows > 0 ? "执行成功" : "执行失败");//关闭连接preparedStatement.close();connection.close();}
}

6.4 课堂练习

要求:

  1. 创建 admin 表
  2. 使用 preparedStatement 添加 5 条数据
  3. 修改 tom 的记录,将 name 改成 king
  4. 删除一条记录
  5. 查询全部记录,并显示在控制台
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;/*** @Author: 谢家升* @Date: 2022/1/26-01-26-15:37* @Version: 1.0*/
@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得到preparedStatementPreparedStatement 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 实际使用使用工具类 JDBCUtils

package com.xjs.jdbc.utils;import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;/*** @author 谢家升* @version 1.0* 这是一个工具类,完成 mysql的连接和关闭资源*/
public class JDBCUtils {//定义相关的属性(4个), 因为只需要一份,因此,我们做出staticprivate static String user; //用户名private static String password; //密码private static String url; //urlprivate 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);}}//连接数据库, 返回Connectionpublic static Connection getConnection() {try {return DriverManager.getConnection(url, user, password);} catch (SQLException e) {//1. 将编译异常转成 运行异常//2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便.throw new RuntimeException(e);}}//关闭相关资源/*1. ResultSet 结果集2. Statement 或者 PreparedStatement3. Connection4. 如果需要关闭资源,就传入对象,否则传入 null*/public static void close(ResultSet set, Statement statement, Connection connection) {//判断是否为nulltry {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.*;/*** @Author: 谢家升* @Date: 2022/1/26-01-26-21:11* @Version: 1.0*/
@SuppressWarnings({"all"})
public class JDBCUtilsUse {@Testpublic void testDML() {//1.得到连接Connection connection = null;//2.组织sqlString 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);}}@Testpublic void testSelect() {//1.连接数据库Connection connection = null;//2.组织SqlString sql = "select * from actor where id = ?";//3.得到preparedStstementPreparedStatement preparedStatement = null;//得到结果集ResultSet resultSet = null;try {connection = JDBCUtils.getConnection();preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1, 3);//给?号赋值//执行查询,得到resultSetresultSet = 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);/*表所有数据:2 黄渤  男   1970-12-12  1113    周星驰 男   1974-01-01  222*//*控制台输出结果:3 周星驰 男   1974-01-01  222*/}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.close(resultSet,preparedStatement,connection);}}}

9 事务

9.1 基本介绍

  1. JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不是回滚。
  2. JDBC程序中为了让多个 SQL 语句作为一个整体执行,需要使用事务
  3. 调用 Connection 的 setAutoCommit(false) 可以取消自动提交事务
  4. 在所有的 SQL 语句都成功执行后,调用 Connection 的 commit() 方法提交事务
  5. 在其中某个操作失败或出现异常时,调用 Connection 的 rollback() 方法回滚事务

9.2 应用实例

模拟经典的转账业务

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);

建表如下图所示:↓

9.3 不使用事务可能出现的问题模拟-模拟经典的转账业务

package com.xjs.jdbc.transaction_;import com.xjs.jdbc.utils.JDBCUtils;
import org.junit.Test;import java.sql.Connection;
import java.sql.PreparedStatement;/*** @Author: 谢家升* @Date: 2022/1/27-01-27-8:42* @Version: 1.0* 演示 JDBC 中事务的使用*/
@SuppressWarnings({"all"})
public class Transaction_ {//没有使用事务.@Testpublic void noTransaction() {//操作转账的业务//1. 得到连接Connection connection = null;//2. 组织sqlString 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是默认自动提交事务//执行sqlpreparedStatement = connection.prepareStatement(sql);preparedStatement.executeUpdate();//执行第1条Sqlint i = 1/0;//这里会抛出一个异常,下面的第2条sql将无法执行...preparedStatement = connection.prepareStatement(sql2);preparedStatement.executeUpdate();//执行第2条Sql} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.close(null,preparedStatement,connection);}}}


9.4 使用事务解决上述问题-模拟经典的转账业务

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;/*** @Author: 谢家升* @Date: 2022/1/27-01-27-8:42* @Version: 1.0* 演示 JDBC 中事务的使用*/
@SuppressWarnings({"all"})
public class Transaction_ {//使用事务处理@Testpublic void useTransaction () {//操作转账的业务//1. 得到连接Connection connection = null;//2. 组织sqlString 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);//此时相当于开启了一个事务//执行sqlpreparedStatement = connection.prepareStatement(sql);preparedStatement.executeUpdate();//执行第1条Sqlint 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);}}}




10 批处理

10.1 基本介绍

  1. 当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。
  2. JDBC 的批量处理语句包括下面这些方法:
  • addBatch():添加需要批量处理的SQL语句或参数
  • executeBatch():执行批量处理语句
  • clearBatch():清空批处理包的语句
  1. JDBC连接MySQL时,如果要使用批处理功能,请在 url 中加参数 ?rewriteBatchedStatements=true
  2. 批处理往往和PreparedStatement一起搭配使用,既可以减少编译次数,又减少运行次数,效率大大提高

10.2 应用实例

  1. 演示向 admin2 表中添加5000条数据,看看使用批处理耗时多久
  2. 注意:需要修改配置文件 【mysql.properties】中 url=jdbc:mysql://localhost:3306/数据库?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;/*** @Author: 谢家升* @Date: 2022/1/27-01-27-10:19* @Version: 1.0* 演示 Java 批处理*/
@SuppressWarnings({"all"})
public class Batch_ {//传统方法,添加5000条数据到admin2@Testpublic 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);}//使用批处理@Testpublic 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();/*//1. //第一就创建 ArrayList - elementData => Object[]//2. elementData => Object[] 就会存放我们预处理的sql语句//3. 当elementData满后,就按照1.5扩容//4. 当添加到指定的值后,就executeBatch//5. 批量处理会减少我们发送sql语句的网络开销,而且减少编译次数,因此效率提高public void addBatch() throws SQLException {synchronized(this.checkClosed().getConnectionMutex()) {if (this.batchedArgs == null) {this.batchedArgs = new ArrayList();}for(int i = 0; i < this.parameterValues.length; ++i) {this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);}this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));}}*/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 次连接数据库问题

  1. 编写程序完成连接MySQL 5000次的操作
  2. 看看有什么问题,耗时多久 ===> 数据库连接池
package com.xjs.jdbc.datasource;import com.xjs.jdbc.utils.JDBCUtils;
import org.junit.Test;import java.sql.Connection;/*** @Author: 谢家升* @Date: 2022/1/27-01-27-17:21* @Version: 1.0*/
public class ConQuestion {//代码 连接mysql 5000次@Testpublic 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 问题分析

  1. 传统的 JDBC 数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证 IP地址,用户名和密码(0.05s ~ 1s 时间)需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多系统资源,容易造成服务器崩溃。
  2. 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。
  3. 传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄露,mysql崩溃。
  4. 解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool)

11.3 数据库连接池基本介绍

  1. 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从 “缓冲池” 中取出一个,使用完毕后再放回去。
  2. 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重建建立一个。
  3. 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中


11.4 数据库连接池种类

  1. JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource只是一个接口,该接口通常由第三方提供实现【提供 .jar】
  2. C3P0 数据库连接池,速度相对较慢,稳定性不错(hibernate,spring)
  3. DBCP 数据库连接池,速度相对C3P0较快,但不稳定
  4. Proxool 数据库连接池,有监控连接池状态的功能,稳定性较C3P0差一点
  5. BoneCP 数据库连接池,速度快
  6. Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxool优点于一身的数据库连接池

11.5 C3P0 应用实例

韩老师视频链接: 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;/*** @Author: 谢家升* @Date: 2022/1/27-01-27-20:28* @Version: 1.0* 演示 C3P0 的使用*/
@SuppressWarnings({"all"})
public class C3P0_ {//方式1: 相关参数,在程序中指定user, url , password等@Testpublic 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 耗时=438System.out.println("c3p0 5000次连接mysql 耗时=" + (end - start));}//第二种方式 使用配置文件模板来完成//1. 将c3p0 提供的 c3p0.config.xml 拷贝到 src目录下//2. 该文件指定了连接数据库和连接池的相关参数@Testpublic void testC3P0_02() throws SQLException {ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("xjs");//测试5000次连接mysqllong 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的第二种方式 耗时=375System.out.println("c3p0的第二种方式(500000) 耗时=" + (end - start));//1970}}

【将c3p0 提供的 c3p0.config.xml 拷贝到 src目录下】

<c3p0-config>
<!--  数据源名称代表连接池  --><named-config name="xjs">
<!-- 驱动类 --><property name="driverClass">com.mysql.jdbc.Driver</property><!-- url--><property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/hsp_db02</property><!-- 用户名 --><property name="user">root</property><!-- 密码 --><property name="password">hsp</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>

11.6 Druid(德鲁伊)应用实例

韩老师视频链接: 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;/*** @Author: 谢家升* @Date: 2022/1/27-01-27-21:36* @Version: 1.0* 测试 Druid (德鲁伊)的使用*/
@SuppressWarnings({"all"})
public class Druid_ {@Testpublic 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 耗时=359System.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;/*** @Author: 谢家升* @Date: 2022/1/28-01-28-8:53* @Version: 1.0* 基于druid数据库连接池的工具类*/
@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;/*** @Author: 谢家升* @Date: 2022/1/28-01-28-9:09* @Version: 1.0*/
@SuppressWarnings({"all"})
public class JDBCUtilsByDruidUse {@Testpublic void TestSelect() {//1.得到连接Connection connection = null;//2.组织sqlString sql = "select * from actor where id >= ?";//3.得到preparedStatementPreparedStatement preparedStatement = null;//4.得到resultSet结果集ResultSet resultSet = null;try {connection = JDBCUtilsByDruid.getConnection();System.out.println(connection.getClass());//此处connection的运行类型: com.alibaba.druid.pool.DruidPooledConnectionpreparedStatement = 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

点击查看=> 韩老师视频链接.

12.1 先分析一个问题

  1. 关闭 connection 后,resultSet 结果集无法使用
  2. resultSet 不利于数据的管理
  3. 示意图

12.2 用自己的土方法来解决

先创建 Actor 类,用于封装 actor表中各列信息

package com.xjs.jdbc.datasource;import java.util.Date;/*** @Author: 谢家升* @Date: 2022/1/28-01-28-11:21* @Version: 1.0* Actor 对象和 actor表的记录对应*/
@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;}@Overridepublic 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;/*** @Author: 谢家升* @Date: 2022/1/28-01-28-9:09* @Version: 1.0*/
@SuppressWarnings({"all"})
public class JDBCUtilsByDruidUse {//注意:@Test 测试的方法不能有返回值,否则将报一个异常,//这里我选择写一次main方法来测试public static void main(String[] args) {new JDBCUtilsByDruidUse().testSelectToArrayList();}//使用土方法来解决ResultSet =封装=> Arraylist//@Testpublic ArrayList<Actor> testSelectToArrayList() {//1.得到连接Connection connection = null;//2.组织sqlString sql = "select * from actor where id >= ?";//3.得到preparedStatementPreparedStatement preparedStatement = null;//4.得到resultSet结果集ResultSet resultSet = null;//创建ArrayList对象,存放actor对象ArrayList<Actor> arrayList = new ArrayList<>();try {connection = JDBCUtilsByDruid.getConnection();System.out.println(connection.getClass());//此处connection的运行类型: com.alibaba.druid.pool.DruidPooledConnectionpreparedStatement = 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 基本介绍

  1. commons-dbutils 是 Apache 组织提供的一个开源 JDBC 工具类库,它是对 JDBC 的封装,使用 dbutils 能极大的简化 jdbc 编码的工作量【真的】
  2. 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;/*** @author 谢家升* @Date: 2022/1/28-01-28-13:58* @version 1.0*/
@SuppressWarnings({"all"})
public class DBUtils_USE {//使用apache-DBUtils 工具类 + druid 完成对表的crud操作@Testpublic void testQueryMany() throws SQLException { //返回结果是多行的情况//1. 得到 连接 (druid)Connection connection = JDBCUtilsByDruid.getConnection();//2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的jar , 加入到本Project//3. 创建 QueryRunnerQueryRunner 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/*** 分析 queryRunner.query方法:* public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {*         PreparedStatement stmt = null;//定义PreparedStatement*         ResultSet rs = null;//接收返回的 ResultSet*         Object result = null;//返回ArrayList**         try {*             stmt = this.prepareStatement(conn, sql);//创建PreparedStatement*             this.fillStatement(stmt, params);//对sql 进行 ? 赋值*             rs = this.wrap(stmt.executeQuery());//执行sql,返回resultset*             result = rsh.handle(rs);//返回的resultset --封装--> arrayList[result] [使用到反射,对传入class对象处理]*         } catch (SQLException var33) {*             this.rethrow(var33, sql, params);*         } finally {*             try {*                 this.close(rs);//关闭resultset*             } finally {*                 this.close((Statement)stmt);//关闭preparedstatement对象*             }*         }**         return result;*     }*/List<Actor> 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 完成 返回的结果是单行记录(单个对象)@Testpublic void testQuerySingle() throws SQLException {//1. 得到 连接 (druid)Connection connection = JDBCUtilsByDruid.getConnection();//2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的jar , 加入到本Project//3. 创建 QueryRunnerQueryRunner queryRunner = new QueryRunner();//4. 就可以执行相关的方法,返回单个对象String sql = "select * from actor where id = ?";// 解读:// 因为我们返回的单行记录<--->单个对象 , 使用的Hander 是 BeanHandlerActor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 3);System.out.println(actor);// 释放资源JDBCUtilsByDruid.close(null, null, connection);}//演示apache-dbutils + druid 完成查询结果是单行单列-返回的就是object@Testpublic void testScalar() throws SQLException {//1. 得到 连接 (druid)Connection connection = JDBCUtilsByDruid.getConnection();//2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的jar , 加入到本Project//3. 创建 QueryRunnerQueryRunner queryRunner = new QueryRunner();//4. 就可以执行相关的方法,返回单行单列 , 返回的就是ObjectString sql = "select name from actor where id = ?";//解读: 因为返回的是一个对象, 使用的handler 就是 ScalarHandlerObject obj = queryRunner.query(connection, sql, new ScalarHandler(), 3);System.out.println(obj);// 释放资源JDBCUtilsByDruid.close(null, null, connection);}//演示apache-dbutils + druid 完成 dml (update, insert ,delete)@Testpublic void testDML() throws SQLException {//1. 得到 连接 (druid)Connection connection = JDBCUtilsByDruid.getConnection();//2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的jar , 加入到本Project//3. 创建 QueryRunnerQueryRunner 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 的类型映射关系

  1. 字符串类型,表中的 char()、varchar() 对应Java中的 String类型。
  2. 数值类型,表中的 int、double 对应Java中的 包装类(Integer、Double…)因为MySQL中所有类型都有可能是null,而 Java 中只有引用数据类型才有 null 值。
  3. 日期类型,表中的 date 对应 Java 中 java.utils 包下的 Date 。

13 DAO 和增删改查通用方法-BasicDao

13.1 先分析一个问题

apache-dbutils + Druid 简化了 JDBC 开发,但还有不足:

  1. sql 语句是固定的,不能通过参数传入,通用性不好,需要进行改进,更方便执行 增删改查
  2. 对于 select 操作,如果有返回值,返回类型不能固定,需要使用泛型
  3. 将来的表很多,业务需求复杂,不可能只靠一个 Java 类完成
  4. 引出 ===> BasicDAO 画出 示意图,看看在实际开发中,应该如何处理

13.2 基本说明

  1. DAO:data access object 数据访问对象
  2. 这样的通用类,称为 BasicDao,是专门和数据库交互的,即完成对数据库(表)的 crud 操作
  3. 在 BasicDao 的基础上,实现一张表对应一个 Dao,更好的完成功能,比如 Actor 表 - Actor.java 类(javabean)- ActorDao.java

13.3 BasicDAO 应用实例

完成简单设计
com.xjs.dao_

  1. com.xjs.dao_.utils //工具类
  2. com.xjs.dao_.domain //javabean
  3. com.xjs.dao_.dao //存放 XxxDAO 和 BasicDAO
  4. com.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;/*** @author xjs* @version 1.0* 基于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();}//关闭连接, 再次强调: 在数据库连接池技术中,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;/*** @author 谢家升* @version 1.0* Actor 对象和 actor表的记录对应**/
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;}@Overridepublic 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;/*** @author 谢家升* @version 1.0* 开发BasicDAO , 是其他DAO的父类*/
public class BasicDAO<T> { //泛型指定具体类型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);}}//返回多个对象(即查询的结果是多行), 针对任意表/**** @param sql sql 语句,可以有 ?* @param clazz 传入一个类的Class对象 比如 Actor.class* @param parameters 传入 ? 的具体的值,可以是多个* @return 根据Actor.class 返回对应的 ArrayList 集合*/public List<T> queryMulti(String sql, Class<T> clazz, Object... parameters) {Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return qr.query(connection, sql, new BeanListHandler<T>(clazz), parameters);} catch (SQLException e) {throw  new RuntimeException(e); //将编译异常->运行异常 ,抛出} finally {JDBCUtilsByDruid.close(null, null, connection);}}//查询单行结果 的通用方法public T querySingle(String sql, Class<T> clazz, Object... parameters) {Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return  qr.query(connection, sql, new BeanHandler<T>(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;/*** @author 谢家升* @version 1.0*/
public class ActorDAO extends BasicDAO<Actor> {//由于继承了 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;/*** @author 谢家升* @version 1.0*/
public class TestDAO {//测试ActorDAO 对actor表crud操作@Testpublic void testActorDAO() {ActorDAO actorDAO = new ActorDAO();//1. 查询List<Actor> 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, deleteint update = actorDAO.update("insert into actor values(null, ?, ?, ?, ?)", "张无忌", "男", "2000-11-11", "999");System.out.println(update > 0 ? "执行成功" : "执行没有影响表");}
}

JDBC 和数据库连接池相关推荐

  1. Java -- JDBC 学习--数据库连接池

    JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模式开 ...

  2. JavaWeb:JDBC之数据库连接池

    JDBC系列阅读 JavaWeb:用JDBC操作数据库 JavaWeb:JDBC之事务 JavaWeb:JDBC之数据库连接池 使用JDBC实现水果超市管理系统 1. 池参数(所有池参数都有默认值) ...

  3. Java JDBC和数据库连接池 韩顺平老师自学笔记

    JDBC和数据库连接池 JDBC 概述 基本介绍 原理示意图 代码示例 JdbcInterface 模拟Java公司提供给其它数据库厂商的接口,供给调用 TestJdbc 模拟一个类来实现数据库的调用 ...

  4. 【JDBC】数据库连接池技术

    文章目录 一.数据库连接池技术 二.多种开源的数据库连接池 一.数据库连接池技术 1.数据库连接池的基本思想︰ 就是为数据库连接建立一个"缓冲池".预先在缓冲池中放入一定数量的连接 ...

  5. JDBC以及数据库连接池的使用

    文章目录 JDBC 步骤 数据库连接池 1.概念 2.接口规范方法 3.第三方数据库连接池技术 C3p0 Druid:由阿里提供 Druid工具类 JDBC 概念 ​ JDBC是sun公司提供的一套用 ...

  6. 从JDBC到数据库连接池

    文章目录 基本的JDBC操作方式 基本的JDBC操作在高并发的情况下带来的问题 第三方应该具备条件 连接池的初步设计 C3P0从数据源获取到连接的过程 如何从C3P0获取连接池状态信息 基本的JDBC ...

  7. 基于JDBC的数据库连接池技术研究与应用

    引言 近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机 应用程序已从传统的桌面应用转到Web应用.基于B/S(Browser/Server)架构的3层开 ...

  8. 基于JDBC的数据库连接池高效管理策略

    2019独角兽企业重金招聘Python工程师标准>>> 介绍 在使用Java语言进行和数据库有关的的应用开发中,一般都使用JDBC来进行和数据库的交互,其中有一个关键的概念就是Con ...

  9. JDBC【数据库连接池、DbUtils框架、分页】

    1.数据库连接池 什么是数据库连接池 简单来说:数据库连接池就是提供连接的... 为什么我们要使用数据库连接池 数据库的连接的建立和关闭是非常消耗资源的 频繁地打开.关闭连接造成系统性能低下 编写连接 ...

  10. kylin调优,项目中错误总结,知识点总结,kylin jdbc driver + 数据库连接池druid + Myba

    首先给大家分享一个巨牛巨牛的人工智能教程,是我无意中发现的.教程不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵段子,像看小说一样,哈哈-我正在学习中,觉得太牛了,所以分享给大家!点这里可以跳转 ...

最新文章

  1. layui跳转html如何带参数,Layui跳转页面代码(可携带复杂参数)
  2. laravel mysql sum查询并排行_必看!PHP常见面试题——MySQL篇(二)
  3. _BLOCK_TYPE_IS_VALID错误
  4. Python协程:从yield/send到async/await
  5. LeetCode_树类
  6. spring boot 整合redis实现方法缓存
  7. boost::multi_array模块实现创建负步幅视图的小测试
  8. 经典C语言程序100例之八
  9. .依存句法分析--提取用户评论
  10. Python Json读写(json模块)(转载)
  11. 详解JavaScript中的Url编码/解码,表单提交中网址编码
  12. python把经纬度生成shp,利用Python实现Shp格式向GeoJSON的转换方法
  13. 高中计算机平面设计计划书,日照师范学校计算机平面设计专业教学方案.doc
  14. Jquery中stop()的用法
  15. Andriod Studio下载安装教程
  16. 初学者须知 常见Web前端开发工具有哪些
  17. 扫地机器人扫水泥地板有用吗_拖地机器人好用吗—拖地机器人的优点介绍
  18. Python基础——类属性、类方法、异常
  19. 60项基于深度学习的SLAM顶会开源方案汇总(上篇)
  20. SYN Flood攻击原理及防御技术

热门文章

  1. 贝叶斯网络与EM算法
  2. USB-PPI数据电缆驱动
  3. 拟物化设计与扁平化设计
  4. 设置虚拟机dns服务器域名,域名服务器DNS的设置实验
  5. MLX90614修改地址
  6. 什么是Photoshop中的图层和蒙版?
  7. 【周五松土】私家萝卜与坑
  8. 转——韩寒:一个流传多年的谣言
  9. 第二章--第二节:注释
  10. HTML实例网页代码 简单的个人博客网站设计与实现 (div+css)