目录
一. 数据库连接池
二. Spring JDBC:JDBC Template
内容
一. 数据库连接池
-
概念:
容器。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。 -
背景:
数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。 -
影响因素:
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数制约。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素:- 最小连接数
是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。 - 最大连接数
是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。 - 最小连接数与最大连接数差距
最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。
- 最小连接数
-
原理
连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。 -
优点
- 节约资源
- 用户访问高效
-
实现
- 标准接口:DataSource javax.sql包
- 方法:
- 获取连接:getConnection();
- 归还连接:close(); 如果连接对象Connection是从连接池获取的,那么调用close()方法,则不会再关闭连接了。而是归还连接到数据库连接池中。
- 方法:
- 一般我们不会自己实现它,由数据库厂商来实现
- C3P0:数据库连接池技术,比较老。
- Druid:数据库连接池实现技术,由阿里巴巴提供。高效。号称最好的连接池技术实现之一。
- 标准接口:DataSource javax.sql包
-
C3P0 使用
- 导入c3p0 jar包(2个)
* 两个c3p0-0.9.5.2.jar 和 mchange-commons-java-0.2.11.jar - 导入JDBC驱动jar包
- mysql-connector-java-8.0.15.jar
- 定义配置文件:
- 名称:c3p0.properties 或者 c3p0-config.xml
- 路径:直接将文件放在 src 目录下即可
- 创建核心对象,数据库连接池对象
- 类:
ComboPooledDataSource
- 类:
- 获取连接
- getConnection();
- 导入c3p0 jar包(2个)
-
Druid 使用
- 步骤:
- 导入Druid jar包
- druid-1.1.9.jar
- 导入JDBC驱动jar包
- mysql-connector-java-8.0.15.jar
- 定义配置文件
- 格式:.properties
- 命名:任意名称
- 路径:可以放在任意目录下
- 加载配置文件
- 获取数据库连接池对象
- 通过工厂类来获取
DruidDataSourceFactory
- 通过工厂类来获取
- 获取连接
- getConnection();
- 导入Druid jar包
- 定义工具类
- 定义一个类JDBCUtils
- 提供静态代码块加载配置文件,初始化连接池对象
- 提供方法:
- 获取连接方法:通过数据库连接池获取连接
- 释放资源
- 获取连接池的方法
- 步骤:
二. Spring JDBC:JDBC Template
Spring框架对JDBC的简单封装。提供了JDBCTemplate
对象简化JDBC的开发。
使用步骤:
-
导入jar包
-
创建
JDBCTemplate
对象。该对象的创建依赖于数据源DataSource
-
调用
JDBCTemplate
对象的方法,来完成CRUD的操作- update(); 执行DML语句,增、删、改
- queryForMap(); 查询结果,将结果集封装为Map集合。将列名作为key,将值作为value,把这条记录封装为一个Map集合
- 注意:查询的结果集长度只能是1。
- queryForList(); 查询结果,将结果集封装为List集合
- 注意:将每条记录封装为一个Map集合,再把Map集合装载到List集合中。
- query(); 查询结果,将结果封装为JavaBean对象
- 参数 RowMapper rowMapper:
- 一般我们使用 BeanPropertyRowMapper 实现类,可以实现数据到JavaBean的自动封装
- 例如:new BeanPropertyRowMapper<类型>(类型.class)
- 参数 RowMapper rowMapper:
- queryForObject(); 查询结果,将结果封装为对象。一般执行聚合函数。
- 参数 Class requiredType
- 传递的参数是:该方法返回值结果的类型
- 例如:Long total = template.queryForObject(sql, Long.class);
- 参数 Class requiredType
-
练习
- 修改employee表中id=1的员工salary=10000;
- 添加一条记录
- 删除一条记录
- 查询id=1的记录,将其封装为Map集合
- 查询所有记录,将其封装为List集合
- 查询所有记录,将其封装为Employee对象的List集合
- 查询总的记录数
代码实现:
// 1. 获取 JdbcTemplate
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 1. 修改employee表中id=1的员工salary=10000;
*/
@Test
public void test1() {
// 2. 定义sql
String sql = "UPDATE employee SET salary = 10000 WHERE pk_id = ?";
// 3. 执行sql
int count = template.update(sql, 1);
System.out.println("count = " + count);
}
/*
* 2. 添加一条记录
* */
@Test
public void test2() {
// 2. 定义sql
String sql = "INSERT INTO employee VALUES(null,?,?,?,?,?)";
// 3. 执行sql
int count = template.update(sql, "宙斯","男",12000,"1980-01-01",3);
System.out.println("count = " + count);
}
/*
* 3. 删除一条记录
* */
@Test
public void test3() {
// 定义sql
String sql = "DELETE FROM employee WHERE pk_id = ?";
// 执行sql
int count = template.update(sql, 9);
System.out.println("count = " + count);
}
/*
* 4. 查询id=1的记录,将其封装为Map集合
* 注意:queryForMap 查询的结果集长度只能是1
* */
@Test
public void test4() {
String sql = "SELECT * FROM employee WHERE pk_id = ?";
Map<String, Object> map = template.queryForMap(sql, 1);
System.out.println(map);
// {pk_id=1, name=小张, gender=男, salary=10000.0, join_date=1998-02-24, department_id=1}
}
/*
* 5. 查询所有记录,将其封装为List集合
* */
@Test
public void test5() {
String sql = "SELECT * FROM employee";
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> map : list) {
System.out.println(map);
}
}
/*
* 6. 查询所有记录,将其封装为Employee对象的List集合
* */
@Test
public void test6() {
String sql = "SELECT * FROM employee";
// 匿名内部类
// Employee 属性如果有基本数据类型,数据库中存在null值,赋值给基本数据类型是会报错。所以最好都定义为引用数据类型。
List<Employee> list = template.query(sql, new RowMapper<Employee>() {
@Override
public Employee mapRow(ResultSet resultSet, int i) throws SQLException {
Employee emp = new Employee();
int pk_id = resultSet.getInt("pk_id");
String name = resultSet.getString("name");
String gender = resultSet.getString("gender");
double salary = resultSet.getDouble("salary");
Date join_date = resultSet.getDate("join_date");
int department_id = resultSet.getInt("department_id");
emp.setPk_id(pk_id);
emp.setName(name);
emp.setGender(gender);
emp.setSalary(salary);
emp.setJoin_date(join_date);
emp.setDepartment_id(department_id);
return emp;
}
});
for (Employee emp : list) {
System.out.println(emp);
}
}
/*
* 6. 查询所有记录,将其封装为Employee对象的List集合
* */
@Test
public void test7() {
String sql = "SELECT * FROM employee";
// 匿名内部类
List<Employee> list = template.query(sql, new BeanPropertyRowMapper<Employee>(Employee.class));
for (Employee emp : list) {
System.out.println(emp);
}
}
/*
* 7. 查询总的记录数
* */
@Test
public void test8() {
String sql = "SELECT COUNT(pk_id) FROM employee";
// 第二个参数是封装的返回值结果的类型
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
}