Druid-ThreadLocal-QueryRunner
数据库连接池
一般情况下我们链接数据库是需要则创建,利用完则销毁,也要遵循JDBC的四步骤:注册驱动,建立通道,配置小车,销毁。
当然,这种一条一条地创建链接是十分耗费资源的,而且效率也不高。数据库连接池的概念应运而生。
我们预先设置好一部分链接,放在一个池中,并且将这些连接标记为空闲状态。如果要使用连接就从池中获取一个连接使用,用完之后再次放回到池中。
- 连接池自己应该有自动初始化的功能,自动增长,自动缩减
- 自动增长:当池中的空闲连接使用完后,自动创建新的空闲连接到连接池中
- 自动缩减:当池中的空闲连接剩余过多时,自动关闭部分链接
Druid
数据库连接池有很多种:C3P0,DBCP,Druid。其中Druid是目前最为流行的连接池。
Druid连接池是阿里巴巴开源平台上一个数据库连接池实现,结合了C3P0,DBCP,PROXOOL等DB池的优点,同时加入了日志监控,能很好地监控DB池连接状况和SQL执行情况。下载Duird的jar包:druid-1.1.5.jar
配置文件+IO流创建连接池
1 | //database.properties |
- 使用IO流来加载该配置文件,其中注意
Dbutils.class
是当前类名。这个流是写在类Dbutils
中的。
1 | Properties properties = new Properties(); |
- 将已经加载了IO流的
properties
导入连接池
1 | private static DruidDataSource ds; |
- 至此,一个Druid连接池创建好了。其中需要特别注意的是,使用流创建连接池时配置文件中的key必须要和官方配置参数完全相同。
官方配置参数 | 缺省值 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候 可以通过名字来区分开来。如果没有配置,将会生成一个名字, 格式是:”DataSource-“ + System.identityHashCode(this) |
|
url | 连接数据库的url,不同数据库不一样。例如MySQL数据库: url=jdbc:mysql://localhost:3306/数据库名称?useUnicode=true&characterEncoding=utf8 &useSSL=false&serverTimezone=Asia/Shanghai |
|
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中可以使用ConfigFilter | |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。 |
处理完异常后,创建连接池的完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class DbUtils {
private static DruidDataSource ds;
static{
Properties properties = new Properties();
InputStream inputStream = DbUtils.class.getResourceAsStream("/database.properties");
try {
properties.load(inputStream);
ds = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}ThreadLocal<>存储链接
ThreadLocal用法
ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。
ThreadLocal存储Connection
1 | private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>(); |
创建ThreadLocal<>时将其声明为private static final
,可以有效防止ThreadLocal的弱引用问题。至此,每一个线程将会有一个属于自己的链接Connection,如果没有的话就去连接池中取一个然后绑定到当前线程中。线程使用完链接后就remove()解除绑定,同时连接池中close()关闭该链接。
以getConnection()为例
1 | public static Connection getConection(){ |
1 | Connection connection = Dbutils.getConnection(); |
相比于传统方法获取Connection
1 | public static Connection connection = null; |
可以很明显的看出,连接池技术很好的代替了以上一整块代码,免去了注册驱动以及一条条建立通道的麻烦,最重要的是,可复用性和隐蔽性碾压了传统方法,所有的原始数据如username,password等都不会在代码中展现出来,全部都隐蔽在配置文件中。
ThreadLocal<>的优越性体现在与连接池的高度契合上,它使得一次作业绑定唯一一个Connection,否则一次作业中可以通过ds.getConnection()
一直获取Connection,这显得十分无意义且浪费连接池资源。
QueryRunner+ResultSetHandler
使用前需添加jar包:commons-dbutils.jar+mysql-connector-java.jar
QreryRunner类(org.apache.commons.dbutils.QueryRunner) 是Dbutils的核心类之一,它显著的简化了SQL查询,并与ResultSetHandler协同工作将使编码量大为减少。它包含以下几个方法:
**query(Connection conn, String sql, ResultSetHandler rsh,Object[] params)**:执行选择查询,在查询中,对象阵列的值被用来作为查询的置换参数。
**update(Connection conn, String sql, Object[] params)**:被用来执行插入、更新或删除(DML)操作。
其中ResultSetHandler接口(org.apache.commons.dbutils.ResultSethandler)执行处理一个结果集对象,将数据转变并处理为任何一种形式,供其他应用使用。实现类如下:
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个对象数组,再存放到List中。
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。//重点
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。//重点
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
1 | private QueryRunner queryRunner = new QueryRunner(); |
Connection conn
:线程绑定的通道,用来传输SQL语句,返回结果
String sql
:如果是查询语句就使用.query()
,如果是DML语句就使用.update()
。
ReslutSetHandler rsh
:因为JDBC中查询语句需要返回ResultSet
,而ResultSet
本质上是表格。虽然我们可以人为遍历表格然后一个个装入Admin生成一个对象实例,ReslutSetHandler rsh
代替我们做了这件事。
Object[] params
:可变数组 ,预加载的SQL语句中有多少?
这里就对应了多少参数。相当于preparedStatement.setString(1,username);
。总之,QueryRunner技术代替了准备小车和执行语句[ps.executeUpdate()、ps.executeQuery()]的作用,进一步的,搭配ResultSetHandler还可以将返回结果进行处理,用过都说好!
- QueryRunner.query()
这条语句牵涉到了数据库,Admin类。如果要用到BeanHandler就必须要考虑数据库key值与Admin类属性的匹配。比如说数据库中表头有username VARCHAR(20)
,Admin类的属性就得是String username;
而不是String UserName;
。如果返回的ResultSet是多行多列,也就是有多个实体信息,就需要用到BeanListHandler了。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!