admin管理员组文章数量:1122850
连接池
连接池和DBUtils
第一章-自定义连接池
知识点-连接池概念
1.目标
- 能够理解连接池解决现状问题的原理
2.讲解
2.1为什么要使用连接池
Connection对象在JDBC使用的时候就会去创建一个对象,使用结束以后就会将这个对象给销毁了(close).每次创建和销毁对象都是耗时操作.需要使用连接池对其进行优化.
程序初始化的时候,初始化多个连接,将多个连接放入到池(集合)中.每次获取的时候,都可以直接从连接池中进行获取.使用结束以后,将连接归还到池中.
2.2.生活里面的连接池例子
-
老方式:
下了地铁需要骑车, 跑去生产一个, 然后骑完之后,直接把车销毁了.
-
连接池方式 摩拜单车:
骑之前, 有一个公司生产了很多的自行车, 下了地铁需要骑车, 直接扫码使用就好了, 然后骑完之后, 还回去
2.3连接池原理【重点】
- 程序一开始就创建一定数量的连接,放在一个容器(集合)中,这个容器称为连接池。
- 使用的时候直接从连接池中取一个已经创建好的连接对象, 使用完成之后 归还到池子
- 如果池子里面的连接使用完了, 还有程序需要使用连接, 先等待一段时间(eg: 3s), 如果在这段时间之内有连接归还, 就拿去使用; 如果还没有连接归还, 新创建一个, 但是新创建的这一个不会归还了
- 集合选择LinkedList
- 增删比较快
- LinkedList里面的removeFirst()和addLast()方法和连接池的原理吻合
3.小结
- 使用连接池的目的: 可以让连接得到复用, 避免浪费
自定义连接池-初级版本
1.目标
根据连接池的原理, 使用LinkedList自定义连接池
2.分析
- 创建一个类MyDataSource, 定义一个集合LinkedList
- 程序初始化的时候, 创建5个连接 存到LinkedList
- 定义getConnection() 从LinkedList取出Connection返回
- 定义addBack()方法归还Connection到LinkedList
3.实现
/*** 包名:com.itheima.customer.datasource** @author Leevi* 日期2020-07-06 09:37* 自定义连接池的第一个版本* 1. 创建一个容器,存放连接* 2. 默认往容器中存放5个连接* 在构造函数中编写代码* 3. 提供一个方法,让调用者获取连接* 4. 提供一个方法,让调用者归还连接** 当前第一个版本存在的问题:* 1. 新创建的连接(原本没有在连接池中的连接)也会归还回连接池* 2. 连接池使用的耦合性太高了,不便于以后项目切换连接池*/
public class MyDataSource {private LinkedList<Connection> connectionPool = new LinkedList<>();public MyDataSource() {//初始化往connectionPool中存放5个连接for (int i = 0; i < 5; i++) {try {//创建一个连接Connection conn = DriverManager.getConnection("jdbc:mysql:///day20?characterEncoding=utf8", "root", "123");//将连接添加到connectionPool中connectionPool.add(conn);} catch (SQLException e) {e.printStackTrace();}}}/*** 获取连接的方法* @return*/public Connection getConn() throws SQLException {//如果容器中有连接,则从容器中获取,如果容器中没有连接,就直接新创建连接Connection conn = null;if (connectionPool.size() > 0){//从容器中获取连接conn = connectionPool.removeFirst();}else {//则新创建连接conn = DriverManager.getConnection("jdbc:mysql:///day20?characterEncoding=utf8", "root", "123");}return conn;}public void addBack(Connection connection){//归还连接:其实就是将要归还的那个连接,添加到容器的尾部connectionPool.addLast(connection);}
}
4.小结
- 创建一个类MyDataSource, 定义一个集合LinkedList
- 程序初始化(静态代码块)里面 创建5个连接存到LinkedList
- 定义提供Connection的方法
- 定义归还Connection的方法
自定义连接池-进阶版本
1.目标
实现datasource完成自定义连接池
2.分析
在初级版本版本中, 我们定义的方法是getConnection(). 因为是自定义的.如果改用李四的自定义的连接池,李四定义的方法是getAbc(), 那么我们的源码就需要修改, 这样不方便维护. 所以sun公司定义了一个接口datasource,让自定义连接池有了规范
3.讲解
3.1datasource接口概述
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商(用户)需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
3.2代码实现
/*** 包名:com.itheima.customer.datasource** @author Leevi* 日期2020-07-06 10:01* 自定义连接池的第二版:* 编写连接池,并且实现官方的DataSource接口** 存在的问题:DataSource中并没有提供归还连接的方法*/
public class MyDataSource2 implements DataSource{private LinkedList<Connection> connectionPool = new LinkedList<>();public MyDataSource2() {//初始化往connectionPool中存放5个连接for (int i = 0; i < 5; i++) {try {//创建一个连接Connection conn = DriverManager.getConnection("jdbc:mysql:///day20?characterEncoding=utf8", "root", "123");//将连接添加到connectionPool中connectionPool.add(conn);} catch (SQLException e) {e.printStackTrace();}}}@Overridepublic Connection getConnection() throws SQLException {//如果容器中有连接,则从容器中获取,如果容器中没有连接,就直接新创建连接Connection conn = null;if (connectionPool.size() > 0){//从容器中获取连接conn = connectionPool.removeFirst();}else {//则新创建连接conn = DriverManager.getConnection("jdbc:mysql:///day20?characterEncoding=utf8", "root", "123");}return conn;}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}
}
4.小结
4.1编写连接池遇到的问题
- 实现DataSource接口后,addBack()不能调用了.
- 能不能不引入新的api,直接调用之前的connection.close(),但是这个close不是关闭,是归还
4.2解决办法
-
继承
- 条件:可以控制父类, 最起码知道父类的名字
-
装饰者模式
- 作用:改写已存在的类的某个方法或某些方法
- 条件:
- 增强类和被增强类实现的是同一个接口
- 增强类里面要拿到被增强类的引用
-
动态代理
自定义连接池-终极版本
1.目标
使用装饰者模式改写connection的close()方法, 让connection归还
2.讲解
2.1自定义连接池终极版本
2.1.1分析
增强connection的close()方法, 其它的方法逻辑不改
- 创建WrapperConnection实现Connection
- 在WrapperConnection里面需要得到被增强的connection对象(通过构造方法传进去)
- 改写close()的逻辑, 变成归还
- 其它方法的逻辑, 还是调用被增强connection对象之前的逻辑
2.2.2实现
- WrapperConnection
/*** 包名:com.itheima.customer.connection** @author Leevi* 日期2020-07-06 10:16* 依赖倒置原则:* 尽量依赖抽象,不依赖具体*/
public class WrapperConnection implements Connection{private Connection connection;private LinkedList<Connection> connectionPool;public WrapperConnection(Connection connection,LinkedList<Connection> connectionPool) {this.connection = connection;this.connectionPool = connectionPool;}@Overridepublic void close() throws SQLException {//将当前这个连接归还回原来那个连接池容器connectionPool.addLast(this);}@Overridepublic Statement createStatement() throws SQLException {return connection.createStatement();}@Overridepublic PreparedStatement prepareStatement(String sql) throws SQLException {return connection.prepareStatement(sql);}@Overridepublic CallableStatement prepareCall(String sql) throws SQLException {return null;}@Overridepublic String nativeSQL(String sql) throws SQLException {return null;}@Overridepublic void setAutoCommit(boolean autoCommit) throws SQLException {connection.setAutoCommit(autoCommit);}@Overridepublic boolean getAutoCommit() throws SQLException {return false;}@Overridepublic void commit() throws SQLException {connectionmit();}@Overridepublic void rollback() throws SQLException {connection.rollback();}//....其它方法省略
}
- MyDataSource03
/*** 包名:com.itheima.customer.datasource** @author Leevi* 日期2020-07-06 10:01* 自定义连接池的第三版:* 1. 可以归还连接* 2. 如果是原本在连接池中的连接,就归还;如果是新创建的连接用完后就销毁** 如果是原本在连接池中的连接,调用close()方法不销毁,而是将其归还回连接池* 如果是新创建的连接,调用close()方法,就销毁(执行原本的close)** 要在不修改类的源码的基础之上,改变类的方法* 1. 继承(在这里没法用)* 2. 动态代理(代理模式)* 3. 装饰者模式* 1. 装饰者和被装饰者要实现相同的接口* 2. 将被装饰者的对象传入装饰者中* 3. 不需要修改的方法,直接调用被装饰者的方法;需要修改的方法,由装饰者重写*/
public class MyDataSource3 implements DataSource{private LinkedList<Connection> connectionPool = new LinkedList<>();public MyDataSource3() {//初始化往connectionPool中存放5个连接for (int i = 0; i < 5; i++) {try {//创建一个装饰后的连接Connection conn = new WrapperConnection(DriverManager.getConnection("jdbc:mysql:///day20?characterEncoding=utf8","root","123"),connectionPool);//将连接添加到connectionPool中connectionPool.add(conn);} catch (SQLException e) {e.printStackTrace();}}}@Overridepublic Connection getConnection() throws SQLException {//如果容器中有连接,则从容器中获取,如果容器中没有连接,就直接新创建连接Connection conn = null;if (connectionPool.size() > 0){//从容器中获取连接conn = connectionPool.removeFirst();}else {//则新创建连接conn = DriverManager.getConnection("jdbc:mysql:///day20?characterEncoding=utf8", "root", "123");}return conn;}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}
}
3.小结
- 创建一个MyConnection实现Connection
- 在MyConnection得到被增强的connection对象
- 改写MyConnection里面的close()方法的逻辑为归还
- MyConnection里面的其它方法 调用被增强的connection对象之前的逻辑
- 在MyDataSource03的getConnection()方法里面 返回了myConnection
自定义连接池扩展版本-使用动态代理
使用动态代理创建Connection对象的代理对象,增强Connection的close方法
/*** 包名:com.itheima.customer.datasource** @author Leevi* 日期2020-07-06 10:01* 自定义连接池的第四版:* 1. 可以归还连接* 2. 如果是原本在连接池中的连接,就归还;如果是新创建的连接用完后就销毁** 使用动态代理技术,增强connection的close方法*/
public class MyDataSource4 implements DataSource{private LinkedList<Connection> connectionPool = new LinkedList<>();public MyDataSource4() {//初始化往connectionPool中存放5个连接for (int i = 0; i < 5; i++) {try {Connection connection = DriverManager.getConnection("jdbc:mysql:///day20?characterEncoding=utf8", "root", "123");ClassLoader classLoader = connection.getClass().getClassLoader();//创建动态代理对象Connection connectionProxy = (Connection) Proxy.newProxyInstance(classLoader, new Class[]{Connection.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//判断执行方法是否是close方法,如果是,则增强,如果不是则执行被代理者原本的方法if (method.getName().equals("close")) {//增强close//将当前这个连接对象,添加到容器中connectionPool.addLast((Connection) proxy);return null;}//不需要增强的方法,就执行原本的方法return method.invoke(connection,args);}});//将连接添加到connectionPool中connectionPool.add(connectionProxy);} catch (Exception e) {e.printStackTrace();}}}@Overridepublic Connection getConnection() throws SQLException {//如果容器中有连接,则从容器中获取,如果容器中没有连接,就直接新创建连接Connection conn = null;if (connectionPool.size() > 0){//从容器中获取连接conn = connectionPool.removeFirst();}else {//则新创建连接conn = DriverManager.getConnection("jdbc:mysql:///day20?characterEncoding=utf8", "root", "123");}return conn;}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}
}
第二章-第三方连接池
知识点-常用连接池
1.目标
- 常用连接池
2.分析
通过前面的学习,我们已经能够使用所学的基础知识构建自定义的连接池了。其目的是锻炼大家的基本功,帮助大家更好的理解连接池的原理, 但现实是残酷的,我们所定义的 连接池 和第三方的连接池相比,还是显得渺小. 工作里面都会用第三方连接池.
3.讲解
常见的第三方连接池如下:
- C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。C3P0是异步操作的,所以一些操作时间过长的JDBC通过其它的辅助线程完成。目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能
- 阿里巴巴-德鲁伊druid连接池:Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求。
- DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。dbcp没有自动回收空闲连接的功能。
4.小结
我们工作里面用的比较多的是:
- C3P0
- druid
- 光连接池
知识点-C3P0
1.目标
- 掌握C3P0的使用
2.路径
- c3p0介绍
- c3p0的使用(硬编码)
- c3p0的使用(配置文件)
- 编写C3P0Util工具类
3.讲解
3.1 c3p0介绍
- C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时还需要添加配置文件c3p0-config.xml.
- 使用C3P0需要添加c3p0-0.9.1.2.jar
3.2c3p0的使用
3.2.1通过硬编码来编写【了解】
步骤
- 拷贝jar
- 创建C3P0连接池对象
- 从C3P0连接池对象里面获得connection
实现:
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/web10");
cpds.setUser("root");
cpds.setPassword("123456");
Connection connection = cpds.getConnection();
3.2.2 通过配置文件来编写【重点】
步骤:
- 拷贝jar
- 拷贝配置文件(c3p0-config.xml)到src目录【名字不要改】
- 创建C3P0连接池对象【自动的读取】
- 从池子里面获得连接
实现:
- 编写配置文件c3p0-config.xml,放在src目录下(注:文件名一定不要改)
<c3p0-config><default-config><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/web11</property><property name="user">root</property><property name="password">123</property><property name="initialPoolSize">5</property></default-config>
</c3p0-config>
- 编写Java代码 (会自动读取resources目录下的c3p0-config.xml,所以不需要我们解析配置文件)
DataSource ds = new ComboPooledDataSource();
3.3使用c3p0改写工具类【重点】
编写C3P0Util工具类,提供DataSource对象,保证整个项目只有一个DataSource对象
/*** 包名:com.itheima.utils** @author Leevi* 日期2020-07-06 11:43* 这个工具类就负责,提供C3P0连接池对象*/
public class C3P0Util {private static DataSource dataSource;static {dataSource = new ComboPooledDataSource();}/*** 获取连接池* @return*/public static DataSource getDataSource(){return dataSource;}
}
4.小结
-
C3P0 配置文件方式使用
- 拷贝jar
- 拷贝配置文件到resources【配置文件的名字不要改】
- 创建C3P0连接池对象
-
C3P0工具类
- 保证DataSource连接池只有一个【static】
知识点-DRUID
1.目标
- 掌握DRUID连接池的使用
2.路径
- DRUID的介绍
- DRUID的使用(硬编码方式)
- DRUID的使用(配置文件方式)
- DRUID抽取成工具类
3.讲解
3.1DRUID介绍
Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是国内目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过很久生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票
Druid的下载地址:
DRUID连接池使用的jar包:druid-1.0.9.jar
3.2DRUID的使用
3.2.1通过硬编码方式【了解】
步骤:
- 导入DRUID jar 包
- 创建Druid连接池对象, 配置4个基本参数
- 从Druid连接池对象获得Connection
实现:
//1. 创建DataSource
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");//设置驱动
dataSource.setUrl("jdbc:mysql://localhost:3306/web17");//设置数据库路径
dataSource.setUsername("root");//设置用户名
dataSource.setPassword("123");//设置密码dataSource.setInitialSize(5);//设置初始化连接的数量//2. 从数据源里面获得Connection
Connection connection = dataSource.getConnection();
3.2.2 通过配置文件方式【重点】
步骤:
- 导入DRUID jar 包
- 拷贝配置文件到src目录
- 根据配置文件创建Druid连接池对象
- 从Druid连接池对象获得Connection
实现:
- 创建druid.properties, 放在src目录下
url=jdbc:mysql:///day20
username=root
password=123
driverClassName=com.mysql.jdbc.Driver
- 编写Java代码
//0 根据druid.properties创建配置文件对象
Properties properties = new Properties();
// 关联druid.properties文件
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(is);
//1. 创建DataSource
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);//2. 从数据源(连接池)获得连接对象
Connection connection = dataSource.getConnection();
3.3 Druid工具类
/*** 包名:com.itheima.utils** @author Leevi* 日期2020-07-06 11:45*/
public class DruidUtil {private static DataSource dataSource;static {try {//1. 创建Properties对象Properties properties = new Properties();//2. 将配置文件转换成字节输入流InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");//3. 使用properties对象加载isproperties.load(is);//druid底层是使用的工厂设计模式,去加载配置文件,创建DruidDataSource对象dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}public static DataSource getDataSource(){return dataSource;}
}
4.小结
- Druid配置文件使用
- 拷贝jar
- 拷贝配置文件到src
- 读取配置文件成properties对象
- 使用工厂根据properties创建DataSource
- 从DataSource获得Connection
第三章-DBUtils
知识点-DBUtils的介绍
1.目标
- 知道什么是DBUtils
2.路径
- DBUtils的概述
- DBUtils的常用API介绍
3.讲解
3.1 DBUtils的概述
DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能
3.2 DBUtils的常用API介绍
-
创建QueryRunner对象的API
QueryRunner(DataSource ds) ,提供数据源(连接池),DBUtils底层自动维护连接connection
-
执行增删改的SQL语句的API
int update(String sql, Object… params),执行增删改的SQL语句, params参数就是可变参数,参数个数取决于语句中问号的个数
-
执行查询的SQL语句的API
query(String sql, ResultSetHandler rsh, Object… params),其中ResultSetHandler是一个接口,表示结果集处理者
4.小结
- DBUtils: Apache开发的一个数据库工具包, 用来简化JDBC操作数据库的步骤
知识点-JavaBean(POJO)
1.目标
- 理解JavaBean的字段和属性
2.讲解
-
JavaBean说白了就是一个类, 用来封装数据用的
-
JavaBean要求
- 私有字段
- 提供公共的get/set方法
- 无参构造
- 实现Serializable
- 字段和属性
- 字段: 全局/成员变量 eg:
private String username
- 属性: 去掉get或者set首字母变小写 eg:
setUsername-去掉set->Username-首字母变小写->username
一般情况下,我们通过IDEA直接生成的set/get 习惯把字段和属性搞成一样而言
3.小结
- JavaBean用来封装数据用的
- 字段和属性
- 字段: 全局/成员变量 eg:
private String username
- 属性: 去掉get或者set首字母变小写 eg:
setUsername-去掉set->Username-首字母变小写->username
- 字段: 全局/成员变量 eg:
知识点-使用DBUtils完成增删改
1.目标
- 掌握使用DBUtils完成增删改
2.步骤
- 拷贝jar包
- 创建QueryRunner()对象,传入dataSource
- 调用update()方法
3.实现
/*** 包名:com.itheima** @author Leevi* 日期2020-07-06 11:50* DBUtils是一个JDBC的简单的框架(它存在的价值,仅仅是简化这一阶段的JDBC的代码)* 1. 引入jar包:dbutils的jar、mysql驱动的jar* 2. 创建QueryRunner对象,并且往它的构造函数中传入一个DataSource对象* 3. 调用queryRunner对象的方法执行SQL语句* 1. update(sql)执行增删改的SQL语句* 2. query(sql)执行查询的SQL语句*/
public class TestDBUtils {@Testpublic void test01() throws SQLException {//目标:执行添加数据的SQL语句String sql = "insert into user values (null,?,?,?)";//1. 创建QueryRunner对象QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());//2. 使用QueryRunner对象调用update(sql,...)执行增删改的SQL语句queryRunner.update(sql,"aobama","666666","圣枪游侠");}@Testpublic void test02() throws SQLException {//目标:执行修改数据的SQL语句String sql = "update user set username=?,password=?,nickname=? where id=?";QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());queryRunner.update(sql,"threezhang","654321","小张",1);}
}
4.小结
- 创建QueryRuner()对象, 传入DataSource
- 调用update(String sql, Object…params)
知识点-使用DBUtils完成查询
1.目标
- 掌握使用DBUtils完成查询
2.步骤
- 拷贝jar包
- 创建QueryRunner()对象 传入DataSource
- 调用query(sql, resultSetHandler,params)方法
3.实现
3.1ResultSetHandler结果集处理类介绍
3.2代码实现
3.2.1 查询一条数据封装到JavaBean对象中(使用BeanHandler)
@Test
public void test04() throws SQLException {//1. 查询一条数据,封装到POJO对象中String sql = "select * from user where id=?";QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());//执行SQL语句U user = queryRunner.query(sql, new BeanHandler<>(U.class), 3);System.out.println(user);
}
3.2.2 查询多条数据封装到List中(使用BeanListHandler)
@Test
public void test05() throws SQLException {//1. 查询多行数据,封装到List<POJO>对象中String sql = "select * from user where id>?";QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());List<U> uList = queryRunner.query(sql, new BeanListHandler<>(U.class), 1);for (U u : uList) {System.out.println(u);}
}
3.2.3 查询一条数据,封装到Map对象中(使用MapHandler)
@Test
public void test06() throws SQLException {//1. 查询一条数据,封装到Map中: 结果集的字段名就是map的key,结果集的字段值就是map的valueString sql = "select * from user where id=?";QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());Map<String, Object> map = queryRunner.query(sql, new MapHandler(), 3);System.out.println(map);
}
3.2.4 查询多条数据,封装到List<Map>
对象中(使用MapListHandler)
@Test
public void test07() throws SQLException {//1. 查询多条数据,封装到List<Map>中String sql = "select * from user where id>?";QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());List<Map<String, Object>> mapList = queryRunner.query(sql, new MapListHandler(), 1);for (Map<String, Object> map : mapList) {System.out.println(map);}
}
3.2.5 查询单个数据(使用ScalarHandler())
@Test
public void test03() throws SQLException {//1. 查询单个数据:比如查询用户的总数String sql = "select count(*) from user";QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());//执行查询,调用query()方法long count = (long) queryRunner.query(sql,new ScalarHandler());System.out.println(count);
}
4. 小结
-
步骤
- 创建QueryRunner() 对象传入DataSource
- 调用query(sql,ResultSetHandler, params…)
-
ResultSetHandler
- BeanHandler() 查询一条记录封装到JavaBean对象
- BeanListHandler() 查询多条记录封装到List list
- MapHandler() 查询一条记录封装到Map对象
- MapListHandler() 查询多条记录封装到List
- ScalarHandler() 封装单个记录的 eg:统计数量
-
注意实现
封装到JavaBean条件, 查询出来的数据的列名必须和JavaBean属性一致
第四章-自定义DBUtils
知识点-元数据
1.目标
- 能够说出什么是数据库元数据
2.分析
我们要自定义DBUtils, 就需要知道列名, 参数个数等, 这些可以通过数据库的元数据库进行获得.元数据在建立框架和架构方面是特别重要的知识,我们可以使用数据库的元数据来创建自定义JDBC工具包, 模仿DBUtils.
3.讲解
3.1什么是元数据
元数据(MetaData),即定义数据的数据。打个比方,就好像我们要想搜索一首歌(歌本身是数据),而我们可以通过歌名,作者,专辑等信息来搜索,那么这些歌名,作者,专辑等等就是这首歌的元数据。因此数据库的元数据就是一些注明数据库信息的数据。
歌曲:凉凉
作词:刘畅
演唱: 杨宗纬 张碧晨
时长: 3分28秒
…
简单来说: 数据库的元数据就是 数据库、表、列的定义信息。
① 由PreparedStatement对象的getParameterMetaData ()方法获取的是ParameterMetaData对象。
② 由ResultSet对象的getMetaData()方法获取的是ResultSetMetaData对象。
3.2ParameterMetaData
3.2.1概述
ParameterMetaData是由preparedStatement对象通过getParameterMetaData方法获取而来,ParameterMetaData
可用于获取有关PreparedStatement
对象和其预编译sql语句
中的一些信息. eg:参数个数,获取指定位置占位符的SQL类型
获得ParameterMetaData:
`ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData ()`
3.2.2ParameterMetaData相关的API
- int getParameterCount(); 获得参数个数
- int getParameterType(int param) 获取指定参数的SQL类型。 (注:MySQL不支持获取参数类型)
3.2.3实例代码
//3. 获得参数的元数据
ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
// 获得sql语句里面参数的个数
int parameterCount = parameterMetaData.getParameterCount();
System.out.println("parameterCount="+parameterCount);
3.3ResultSetMetaData
3.3.1概述
ResultSetMetaData是由ResultSet对象通过getMetaData方法获取而来,ResultSetMetaData
可用于获取有关ResultSet
对象中列的类型和属性的信息。
获得ResultSetMetaData:
`ResultSetMetaData resultSetMetaData = resultSet.getMetaData()`
3.3.2resultSetMetaData 相关的API
- getColumnCount(); 获取结果集中列项目的个数
- getColumnName(int column); 获得数据指定列的列名
- getColumnTypeName();获取指定列的SQL类型
- getColumnClassName();获取指定列SQL类型对应于Java的类型
3.2.3实例代码
@Test
public void test03() throws SQLException {DataSource dataSource = DruidUtil.getDataSource();Connection connection = dataSource.getConnection();String sql = "select * from user";PreparedStatement pstm = connection.prepareStatement(sql);//获取结果集元数据ResultSetMetaData resultSetMetaData = pstm.getMetaData();int columnCount = resultSetMetaData.getColumnCount();System.out.println(columnCount);for (int i = 1; i <= columnCount; i++) {//获取每列的列名String columnName = resultSetMetaData.getColumnName(i);System.out.println(columnName);}
}
4.小结
- 元数据: 描述数据的数据. mysql元数据: 用来定义数据库, 表 ,列信息的 eg: 参数的个数, 列的个数, 列的类型…
- mysql元数据:
- ParameterMetaData
- ResultSetMetaData
案例-自定义DBUtils增删改
1.需求
- 模仿DBUtils, 完成增删改的功能
2.分析
- 创建MyQueryRunner类, 定义dataSource, 提供有参构造方法
- 定义int update(String sql, Object…params)方法
//1.从dataSource里面获得connection
//2.根据sql语句创建预编译sql语句对象
//3.获得参数元数据对象, 获得参数的个数
//4.遍历, 从params取出值, 依次给参数? 赋值
//5.执行
//6.释放资源
3.实现
public class MyQueryRunner {private DataSource dataSource;public MyQueryRunner(DataSource dataSource) {this.dataSource = dataSource;}/*** 执行增删改的SQL语句的方法* @param sql* @param params* @return* @throws SQLException*/public int update(String sql,Object... params) throws SQLException {//1. 获取连接对象Connection connection = dataSource.getConnection();//2. 预编译SQL语句PreparedStatement pstm = connection.prepareStatement(sql);//3. 设置参数//3.1 获取参数的个数ParameterMetaData parameterMetaData = pstm.getParameterMetaData();int parameterCount = parameterMetaData.getParameterCount();for (int i = 1; i <= parameterCount; i++) {pstm.setObject(i,params[i-1]);}//4. 执行SQL语句int num = pstm.executeUpdate();//5. 关闭资源pstm.close();connection.close();return num;}
}
4.小结
- 先模拟DBUtils写出架子
- update()
- 封装了PrepareStatement
- 用到了参数元数据
案例-自定义DBUtils查询多条数据封装到List<POJO>
/*** 包名:com.itheima.framework** @author Leevi* 日期2020-07-06 15:03*/
public class MyQueryRunner {private DataSource dataSource;public MyQueryRunner(DataSource dataSource) {this.dataSource = dataSource;}/*** 执行增删改的SQL语句的方法* @param sql* @param params* @return* @throws SQLException*/public int update(String sql,Object... params) throws SQLException {//1. 获取连接对象Connection connection = dataSource.getConnection();//2. 预编译SQL语句PreparedStatement pstm = connection.prepareStatement(sql);//3. 设置参数//3.1 获取参数的个数ParameterMetaData parameterMetaData = pstm.getParameterMetaData();int parameterCount = parameterMetaData.getParameterCount();for (int i = 1; i <= parameterCount; i++) {pstm.setObject(i,params[i-1]);}//4. 执行SQL语句int num = pstm.executeUpdate();//5. 关闭资源pstm.close();connection.close();return num;}/*** 查询到多条数据封装到List<T>* @param sql* @param clazz* @param params* @param <T>* @return* @throws Exception*/public <T> List<T> queryList(String sql, Class<T> clazz, Object... params) throws Exception {//1. 获取连接对象Connection connection = dataSource.getConnection();//2. 预编译SQL语句PreparedStatement pstm = connection.prepareStatement(sql);//3. 设置参数//3.1 获取参数的个数ParameterMetaData parameterMetaData = pstm.getParameterMetaData();int parameterCount = parameterMetaData.getParameterCount();for (int i = 1; i <= parameterCount; i++) {pstm.setObject(i,params[i-1]);}//4. 执行SQL语句ResultSet rst = pstm.executeQuery();//获取结果集元数据ResultSetMetaData rstMetaData = rst.getMetaData();//获取结果集的列数int columnCount = rstMetaData.getColumnCount();//1. 获取POJO中的所有方法Method[] methods = clazz.getMethods();//声明一个集合存储tList<T> tList = new ArrayList<>();while (rst.next()) {//创建出封装数据的POJO对象T t = clazz.newInstance();for (int i = 1; i <= columnCount; i++) {//获取每列的名字(字段名)String columnName = rstMetaData.getColumnName(i);//1. 获取每一行数据的各个字段的数据Object columnValue = rst.getObject(columnName);//2. 调用对象的对应的set方法,往对象中设置数据//遍历出POJO的每一个方法for (Method method : methods) {//获取方法名String methodName = method.getName();//匹配方法名if (methodName.equalsIgnoreCase("set"+columnName)) {//说明匹配到了对应的set方法,那么就调用这个set方法,设置columnValuemethod.invoke(t,columnValue);}}}tList.add(t);}//关闭资源rst.close();pstm.close();connection.close();return tList;}
}
本文标签: 连接池
版权声明:本文标题:连接池 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/biancheng/1704024556a616462.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论