> 文章列表 > JDBC重点

JDBC重点

JDBC重点

JDBC初识

  • DriverManager

    1. 将第三方数据库厂商的实现驱动jar注册到程序中
    2. 可以根据数据库连接信息获取connection
  • Connection

    • 和数据库建立的连接,在连接对象上,可以多次执行数据库curd动作

    • 可以获取statement和 preparedstatement,callablestatement对象

  • Statement | PreparedStatement | CallableStatement

    • 具体发送SQL语句到数据库管理软件的对象

    • 不同发送方式稍有不同! **preparedstatement **使用为重点!

  • Result

    • 面向对象思维的产物(抽象成数据库的查询结果表)

    • 存储DQL查询数据库结果的对象

    • 需要我们进行解析,获取具体的数据库数据

JDBC重点

3.2 jdbc基本使用步骤分析(6步)

  1. 注册驱动
  2. 获取连接
  3. 创建发送sql语句对象
  4. 发送sql语句,并获取返回结果
  5. 结果集解析
  6. 资源关闭

###基于statement实现查询(演示步骤)

* TODO: 步骤总结 (6)*    1. 注册驱动*    2. 获取连接*    3. 创建statement*    4. 发送SQL语句,并获取结果*    5. 结果集解析*    6. 关闭资源*/
public class JdbcBasePart {public static void main(String[] args) throws SQLException {//1.注册驱动/*** TODO: 注意*   Driver -> com.mysql.cj.jdbc.Driver*/DriverManager.registerDriver(new Driver());//2.获取连接/*** TODO: 注意*   面向接口编程*   java.sql 接口 = 实现类*   connection 使用java.sql.Connection接口接收*/Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu","root","root");//3.创建小车Statement statement = connection.createStatement();//4.发送SQL语句String sql = "select id,account,password,nickname from t_user ;";ResultSet resultSet =  statement.executeQuery(sql);//5.结果集解析while (resultSet.next()){int id = resultSet.getInt("id");String account = resultSet.getString("account");String password = resultSet.getString("password");String nickname = resultSet.getString("nickname");System.out.println(id+"::"+account+"::"+password+"::"+nickname);}//6.关闭资源  【先开后关】resultSet.close();statement.close();connection.close();}}
  • 存在问题
    1. SQL语句需要字符串拼接,比较麻烦

    2. 只能拼接字符串类型,其他的数据库类型无法处理

    3. 可能发生注入攻击

      动态值充当了SQL语句结构,影响了原有的查询结果!

3.5 基于preparedStatement方式优化

利用preparedStatement解决上述案例注入攻击SQL语句拼接问题! (重点掌握)


public class JdbcPreparedStatementLoginPart {public static void main(String[] args) throws ClassNotFoundException, SQLException {//1.输入账号和密码Scanner scanner = new Scanner(System.in);String account = scanner.nextLine();String password = scanner.nextLine();scanner.close();//2.jdbc的查询使用//注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取连接Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "root");//创建preparedStatement//connection.createStatement();//TODO 需要传入SQL语句结构//TODO 要的是SQL语句结构,动态值的部分使用 ? ,  占位符!//TODO ?  不能加 '?'  ? 只能替代值,不能替代关键字和容器名String sql = "select * from t_user where account = ? and password = ? ;";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值//给占位符赋值! 从左到右,从1开始!/***  int 占位符的下角标*  object 占位符的值*/preparedStatement.setObject(2,password);preparedStatement.setObject(1,account);//这哥们内部完成SQL语句拼接!//执行SQL语句即可ResultSet resultSet = preparedStatement.executeQuery();//preparedStatement.executeUpdate()//进行结果集对象解析if (resultSet.next()){//只要向下移动,就是有数据 就是登录成功!System.out.println("登录成功!");}else{System.out.println("登录失败!");}//关闭资源resultSet.close();preparedStatement.close();connection.close();}}

JDBC重点

3.6基于preparedStatement演示curd

  • 数据库数据插入
/*** 插入一条用户数据!* 账号: test* 密码: test* 昵称: 测试*/
@Test
public void testInsert() throws Exception{//注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取连接Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "root");//TODO: 切记, ? 只能代替 值!!!!!  不能代替关键字 特殊符号 容器名String sql = "insert into t_user(account,password,nickname) values (?,?,?);";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值preparedStatement.setString(1, "test");preparedStatement.setString(2, "test");preparedStatement.setString(3, "测试");//发送SQL语句int rows = preparedStatement.executeUpdate();//输出结果System.out.println(rows);//关闭资源closepreparedStatement.close();connection.close();
}
  • 数据库数据修改
/*** 修改一条用户数据!* 修改账号: test的用户,将nickname改为tomcat*/
@Test
public void testUpdate() throws Exception{//注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取连接Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "root");//TODO: 切记, ? 只能代替 值!!!!!  不能代替关键字 特殊符号 容器名String sql = "update t_user set nickname = ? where account = ? ;";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值preparedStatement.setString(1, "tomcat");preparedStatement.setString(2, "test");//发送SQL语句int rows = preparedStatement.executeUpdate();//输出结果System.out.println(rows);//关闭资源closepreparedStatement.close();connection.close();
}
  • 数据库数据删除
/*** 删除一条用户数据!* 根据账号: test*/
@Test
public void testDelete() throws Exception{//注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取连接Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "root");//TODO: 切记, ? 只能代替 值!!!!!  不能代替关键字 特殊符号 容器名String sql = "delete from t_user where account = ? ;";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值preparedStatement.setString(1, "test");//发送SQL语句int rows = preparedStatement.executeUpdate();//输出结果System.out.println(rows);//关闭资源closepreparedStatement.close();connection.close();
}
  • 数据库数据查询
/*** 查询全部数据!*   将数据存到List<Map>中*   map -> 对应一行数据*      map key -> 数据库列名或者别名*      map value -> 数据库列的值* TODO: 思路分析*    1.先创建一个List<Map>集合*    2.遍历resultSet对象的行数据*    3.将每一行数据存储到一个map对象中!*    4.将对象存到List<Map>中*    5.最终返回** TODO:*    初体验,结果存储!*    学习获取结果表头信息(列名和数量等信息)*/
@Test
public void testQueryMap() throws Exception{//注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取连接Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "root");//TODO: 切记, ? 只能代替 值!!!!!  不能代替关键字 特殊符号 容器名String sql = "select id,account,password,nickname from t_user ;";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值 本次没有占位符,省略//发送查询语句ResultSet resultSet = preparedStatement.executeQuery();//创建一个集合List<Map> mapList = new ArrayList<>();//获取列信息对象ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();while (resultSet.next()) {Map map = new HashMap();for (int i = 1; i <= columnCount; i++) {map.put(metaData.getColumnLabel(i), resultSet.getObject(i));}mapList.add(map);}System.out.println(mapList);//关闭资源closepreparedStatement.close();connection.close();resultSet.close();
}

3.7 preparedStatement使用方式总结

  • 使用步骤总结
//1.注册驱动//2.获取连接//3.编写SQL语句//4.创建preparedstatement并且传入SQL语句结构//5.占位符赋值//6.发送SQL语句,并且获取结果 //7.结果集解析//8.关闭资源
  • 使用API总结
//1.注册驱动
方案1: 调用静态方法,但是会注册两次
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
方案2: 反射触发
Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接Connection connection = DriverManager.getConnection();3 (String url,String user,String password)
2 (String url,Properties info(user password))
1 (String url?user=账号&password=密码 )//3.创建statement//静态
Statement statement = connection.createStatement();
//预编译
PreparedStatement preparedstatement = connection.preparedStatement(sql语句结构);//4.占位符赋值preparedstatement.setObject(?的位置 从左到右 从1开始,值)//5.发送sql语句获取结果int rows = executeUpdate(); //非DQL
Resultset = executeQuery(); //DQL//6.查询结果集解析//移动光标指向行数据 next();  if(next())  while(next())
//获取列的数据即可   get类型(int 列的下角标 从1开始 | int 列的label (别名或者列名))
//获取列的信息   getMetadata(); ResultsetMetaData对象 包含的就是列的信息getColumnCount(); | getCloumnLebal(index)
//7.关闭资源
close(); 
### 4.3 jdbc中数据库事务实现- 章节目标使用jdbc代码,添加数据库事务动作!开启事务事务提交 / 事务回滚- 事务概念回顾```Java
// 事务概念数据库事务就是一种SQL语句执行的缓存机制,不会单条执行完毕就更新数据库数据,最终根据缓存内的多条语句执行结果统一判定!一个事务内所有语句都成功及事务成功,我们可以触发commit提交事务来结束事务,更新数据!一个事务内任意一条语句失败,及事务失败,我们可以触发rollback回滚结束事务,数据回到事务之前状态!举个例子: 临近高考,你好吃懒做,偶尔还瞎花钱,父母也只会说'你等着!',待到高考完毕!成绩600+,翻篇,庆祝!成绩200+,翻旧账,男女混合双打!//优势允许我们在失败情况下,数据回归到业务之前的状态! //场景**一个业务****涉及****多条修改****数据库语句!**例如: 经典的转账案例,转账业务(加钱和减钱)   批量删除(涉及多个删除)批量添加(涉及多个插入)     // 事务特性1. 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 2. 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。3. 隔离性(Isolation)事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。4. 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响// 事务类型自动提交 : 每条语句自动存储一个事务中,执行成功自动提交,执行失败自动回滚! (MySQL)手动提交:  手动开启事务,添加语句,手动提交或者手动回滚即可!// sql开启事务方式针对自动提交: 关闭自动提交即可,多条语句添加以后,最终手动提交或者回滚! (推荐)SET autocommit = off; //关闭当前连接自动事务提交方式# 只有当前连接有效# 编写SQL语句即可SQLSQLSQL#手动提交或者回滚 【结束当前的事务】COMMIT / ROLLBACK ;  手动开启事务: 开启事务代码,添加SQL语句,事务提交或者事务回滚! (不推荐)// 呼应jdbc技术try{connection.setAutoCommit(false); //关闭自动提交了//注意,只要当前connection对象,进行数据库操作,都不会自动提交事务//数据库动作!//statement - 单一的数据库动作 c u r d connection.commit();}catch(Execption e){connection.rollback();}
  • 数据库表数据
-- 继续在atguigu的库中创建银行表
CREATE TABLE t_bank(id INT PRIMARY KEY AUTO_INCREMENT COMMENT '账号主键',account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号',money  INT UNSIGNED COMMENT '金额,不能为负值') ;INSERT INTO t_bank(account,money) VALUES('ergouzi',1000),('lvdandan',1000);
  • 代码结构设计

  • jdbc事务实现

    • 测试类

public class BankTest {@Testpublic void testBank() throws Exception {BankService bankService = new BankService();bankService.transfer("ergouzi", "lvdandan",500);}}
- BankService

public class BankService {/*** 转账业务方法* @param addAccount  加钱账号* @param subAccount  减钱账号* @param money  金额*/public void transfer(String addAccount,String subAccount, int money) throws ClassNotFoundException, SQLException {System.out.println("addAccount = " + addAccount + ", subAccount = " + subAccount + ", money = " + money);//注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取连接Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "root");int flag = 0;//利用try代码块,调用daotry {//开启事务(关闭事务自动提交)connection.setAutoCommit(false);BankDao bankDao = new BankDao();//调用加钱 和 减钱bankDao.addMoney(addAccount,money,connection);System.out.println("--------------");bankDao.subMoney(subAccount,money,connection);flag = 1;//不报错,提交事务connection.commit();}catch (Exception e){//报错回滚事务connection.rollback();throw e;}finally {connection.close();}if (flag == 1){System.out.println("转账成功!");}else{System.out.println("转账失败!");}}}
- BankDao

public class BankDao {/*** 加钱方法* @param account* @param money* @param connection 业务传递的connection和减钱是同一个! 才可以在一个事务中!* @return 影响行数*/public int addMoney(String account, int money,Connection connection) throws ClassNotFoundException, SQLException {String sql = "update t_bank set money = money + ? where account = ? ;";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值preparedStatement.setObject(1, money);preparedStatement.setString(2, account);//发送SQL语句int rows = preparedStatement.executeUpdate();//输出结果System.out.println("加钱执行完毕!");//关闭资源closepreparedStatement.close();return rows;}/*** 减钱方法* @param account* @param money* @param connection 业务传递的connection和加钱是同一个! 才可以在一个事务中!* @return 影响行数*/public int subMoney(String account, int money,Connection connection) throws ClassNotFoundException, SQLException {String sql = "update t_bank set money = money - ? where account = ? ;";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值preparedStatement.setObject(1, money);preparedStatement.setString(2, account);//发送SQL语句int rows = preparedStatement.executeUpdate();//输出结果System.out.println("减钱执行完毕!");//关闭资源closepreparedStatement.close();return rows;}
}

4.3 jdbc中数据库事务实现

  • 章节目标

    使用jdbc代码,添加数据库事务动作!

    开启事务

    事务提交 / 事务回滚

  • 事务概念回顾

// 事务概念数据库事务就是一种SQL语句执行的缓存机制,不会单条执行完毕就更新数据库数据,最终根据缓存内的多条语句执行结果统一判定!一个事务内所有语句都成功及事务成功,我们可以触发commit提交事务来结束事务,更新数据!一个事务内任意一条语句失败,及事务失败,我们可以触发rollback回滚结束事务,数据回到事务之前状态!举个例子: 临近高考,你好吃懒做,偶尔还瞎花钱,父母也只会说'你等着!',待到高考完毕!成绩600+,翻篇,庆祝!成绩200+,翻旧账,男女混合双打!//优势允许我们在失败情况下,数据回归到业务之前的状态! //场景**一个业务****涉及****多条修改****数据库语句!**例如: 经典的转账案例,转账业务(加钱和减钱)   批量删除(涉及多个删除)批量添加(涉及多个插入)     // 事务特性1. 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 2. 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。3. 隔离性(Isolation)事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。4. 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响// 事务类型自动提交 : 每条语句自动存储一个事务中,执行成功自动提交,执行失败自动回滚! (MySQL)手动提交:  手动开启事务,添加语句,手动提交或者手动回滚即可!// sql开启事务方式针对自动提交: 关闭自动提交即可,多条语句添加以后,最终手动提交或者回滚! (推荐)SET autocommit = off; //关闭当前连接自动事务提交方式# 只有当前连接有效# 编写SQL语句即可SQLSQLSQL#手动提交或者回滚 【结束当前的事务】COMMIT / ROLLBACK ;  手动开启事务: 开启事务代码,添加SQL语句,事务提交或者事务回滚! (不推荐)// 呼应jdbc技术try{connection.setAutoCommit(false); //关闭自动提交了//注意,只要当前connection对象,进行数据库操作,都不会自动提交事务//数据库动作!//statement - 单一的数据库动作 c u r d connection.commit();}catch(Execption e){connection.rollback();}
  • 数据库表数据
-- 继续在atguigu的库中创建银行表
CREATE TABLE t_bank(id INT PRIMARY KEY AUTO_INCREMENT COMMENT '账号主键',account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号',money  INT UNSIGNED COMMENT '金额,不能为负值') ;INSERT INTO t_bank(account,money) VALUES('ergouzi',1000),('lvdandan',1000);
  • 代码结构设计

  • jdbc事务实现

    • 测试类

public class BankTest {@Testpublic void testBank() throws Exception {BankService bankService = new BankService();bankService.transfer("ergouzi", "lvdandan",500);}}
- BankService

public class BankService {/*** 转账业务方法* @param addAccount  加钱账号* @param subAccount  减钱账号* @param money  金额*/public void transfer(String addAccount,String subAccount, int money) throws ClassNotFoundException, SQLException {System.out.println("addAccount = " + addAccount + ", subAccount = " + subAccount + ", money = " + money);//注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取连接Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu", "root", "root");int flag = 0;//利用try代码块,调用daotry {//开启事务(关闭事务自动提交)connection.setAutoCommit(false);BankDao bankDao = new BankDao();//调用加钱 和 减钱bankDao.addMoney(addAccount,money,connection);System.out.println("--------------");bankDao.subMoney(subAccount,money,connection);flag = 1;//不报错,提交事务connection.commit();}catch (Exception e){//报错回滚事务connection.rollback();throw e;}finally {connection.close();}if (flag == 1){System.out.println("转账成功!");}else{System.out.println("转账失败!");}}}
- BankDao

public class BankDao {/*** 加钱方法* @param account* @param money* @param connection 业务传递的connection和减钱是同一个! 才可以在一个事务中!* @return 影响行数*/public int addMoney(String account, int money,Connection connection) throws ClassNotFoundException, SQLException {String sql = "update t_bank set money = money + ? where account = ? ;";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值preparedStatement.setObject(1, money);preparedStatement.setString(2, account);//发送SQL语句int rows = preparedStatement.executeUpdate();//输出结果System.out.println("加钱执行完毕!");//关闭资源closepreparedStatement.close();return rows;}/*** 减钱方法* @param account* @param money* @param connection 业务传递的connection和加钱是同一个! 才可以在一个事务中!* @return 影响行数*/public int subMoney(String account, int money,Connection connection) throws ClassNotFoundException, SQLException {String sql = "update t_bank set money = money - ? where account = ? ;";PreparedStatement preparedStatement = connection.prepareStatement(sql);//占位符赋值preparedStatement.setObject(1, money);preparedStatement.setString(2, account);//发送SQL语句int rows = preparedStatement.executeUpdate();//输出结果System.out.println("减钱执行完毕!");//关闭资源closepreparedStatement.close();return rows;}
}

五、国货之光Druid连接池技术使用

5.1连接性能消耗问题分析

     ** 贪污和浪费是极大的犯罪**** - 毛爷爷**** connection可以复用!** (年纪轻轻你就范了 浪费连接的罪啊!)

5.2 数据库连接池作用

**总结缺点:**
(1)不使用数据库连接池,每次都通过DriverManager获取新连接,用完直接抛弃断开,
连接的利用率太低,太浪费。
(2)对于数据库服务器来说,压力太大了。我们数据库服务器和Java程序对连接数也无法控制
,很容易导致数据库服务器崩溃。**我们就希望能管理连接。**
- 我们可以建立一个连接池,这个池中可以容纳一定数量的连接对象,一开始,我们可以先替用户先创建好一些连接对象,等用户要拿连接对象时,就直接从池中拿,不用新建了,这样也可以节省时间。然后用户用完后,放回去,别人可以接着用。
- 可以提高连接的使用率。当池中的现有的连接都用完了,**那么连接池可以向服务器申请新的连接放到池中。**
- 直到池中的连接达到“最大连接数”,就不能在申请新的连接了,如果没有拿到连接的用户只能等待。

5.3市面常见连接池产品和对比

JDBC 的数据库连接池使用 javax.sql.**DataSource**接口进行规范,**所有**的第三方**连接池****都实现此接口**,自行添加具体实现!也就是说,**所有连接池获取连接的和回收连接方法都一样**,不同的只有性能和扩展功能!- DBCP 是Apache提供的数据库连接池,速度相对c3p0较快,但因自身存在BUG- C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以- Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点- Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,**妥妥国货之光!!!!**

5.4国货之光druid连接池使用

记得导入druid工具类jar

  • 硬编码方式(了解,不推荐)
/*** 创建druid连接池对象,使用硬编码进行核心参数设置!*   必须参数: 账号*             密码*             url*             driverClass*   非必须参数:*           初始化个数*           最大数量等等  不推荐设置*/
@Test
public void druidHard() throws SQLException {DruidDataSource dataSource = new DruidDataSource();//设置四个必须参数dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setUrl("jdbc:mysql:///day01");//获取连接Connection connection = dataSource.getConnection();// JDBC的步骤//回收连接connection.close();
}
  • 软编码方式

    • 外部配置

      存放在src/druid.properties

# druid连接池需要的配置参数,key固定命名
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=root
url=jdbc:mysql:///atguigu
- druid声明代码
/*** 不直接在java代码编写配置文件!* 利用工厂模式,传入配置文件对象,创建连接池!* @throws Exception*/
@Test
public void druidSoft() throws Exception {Properties properties = new Properties();InputStream ips = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");properties.load(ips);DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
}
  • druid配置(了解)
配置 缺省 说明
name 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
jdbcUrl 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 连接数据库的用户名
password 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis 有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun 不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls 物理连接初始化的时候执行的sql
exceptionSorter 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

6.3高级应用层封装BaseDao

基本上每一个数据表都应该有一个对应的DAO接口及其实现类,发现对所有表的操作(增、删、改、查)代码重复度很高,所以可以抽取公共代码,给这些DAO的实现类可以抽取一个公共的父类,我们称为BaseDao

public abstract class BaseDao {/*通用的增、删、改的方法String sql:sqlObject... args:给sql中的?设置的值列表,可以是0~n*/protected int update(String sql,Object... args) throws SQLException {
//        创建PreparedStatement对象,对sql预编译Connection connection = JDBCTools.getConnection();PreparedStatement ps = connection.prepareStatement(sql);//设置?的值if(args != null && args.length>0){for(int i=0; i<args.length; i++) {ps.setObject(i+1, args[i]);//?的编号从1开始,不是从0开始,数组的下标是从0开始}}//执行sqlint len = ps.executeUpdate();ps.close();//这里检查下是否开启事务,开启不关闭连接,业务方法关闭!//没有开启事务的话,直接回收关闭即可!if (connection.getAutoCommit()) {//回收JDBCTools.free();}return len;}/*通用的查询多个Javabean对象的方法,例如:多个员工对象,多个部门对象等这里的clazz接收的是T类型的Class对象,如果查询员工信息,clazz代表Employee.class,如果查询部门信息,clazz代表Department.class,*/protected <T> ArrayList<T> query(Class<T> clazz,String sql, Object... args) throws Exception {//        创建PreparedStatement对象,对sql预编译Connection connection = JDBCTools.getConnection();PreparedStatement ps = connection.prepareStatement(sql);//设置?的值if(args != null && args.length>0){for(int i=0; i<args.length; i++) {ps.setObject(i+1, args[i]);//?的编号从1开始,不是从0开始,数组的下标是从0开始}}ArrayList<T> list = new ArrayList<>();ResultSet res = ps.executeQuery();/*获取结果集的元数据对象。元数据对象中有该结果集一共有几列、列名称是什么等信息*/ResultSetMetaData metaData = res.getMetaData();int columnCount = metaData.getColumnCount();//获取结果集列数//遍历结果集ResultSet,把查询结果中的一条一条记录,变成一个一个T 对象,放到list中。while(res.next()){//循环一次代表有一行,代表有一个T对象T t = clazz.newInstance();//要求这个类型必须有公共的无参构造//把这条记录的每一个单元格的值取出来,设置到t对象对应的属性中。for(int i=1; i<=columnCount; i++){//for循环一次,代表取某一行的1个单元格的值Object value = res.getObject(i);//这个值应该是t对象的某个属性值//获取该属性对应的Field对象
//                String columnName = metaData.getColumnName(i);//获取第i列的字段名String columnName = metaData.getColumnLabel(i);//获取第i列的字段名或字段的别名Field field = clazz.getDeclaredField(columnName);field.setAccessible(true);//这么做可以操作private的属性field.set(t, value);}list.add(t);}res.close();ps.close();//这里检查下是否开启事务,开启不关闭连接,业务方法关闭!//没有开启事务的话,直接回收关闭即可!if (connection.getAutoCommit()) {//回收JDBCTools.free();}return list;}protected <T> T queryBean(Class<T> clazz,String sql, Object... args) throws Exception {ArrayList<T> list = query(clazz, sql, args);if(list == null || list.size() == 0){return null;}return list.get(0);}
}