一前言
上一节针对之前服务层存在的两个问题,对服务层进行了优化
1,提取DatabaeHelper类,便于多Service复用数据库相关操作的方法
2,使用DbUtils解决数据库查询时大量重复代码问题
目前仍存在需优化的问题:
每次执行数据库操作时,都需要新创建一个Connection对象,操作完成后关闭
如何能将这部分操作从Service中移除,是Connection对于开发者完全透明
本节将对此部分进行优化
二,优化服务层-Connection的线程隔离
1,问题分析
目前Service中的每个数据库操作都需要先创建一个Connection连接对象,再销毁
public List<Customer> getCustomerList() {
// 优化点:创建Connection连接
Connection conn = DatabaseHelper.getConnection();
try{
String sql = "select * from customer";
return DatabaseHelper.queryEntityList(Customer.class, sql);
}finally {
// 优化点:关闭Connection连接
DatabaseHelper.closeConnection(conn);
}
}
如果能够将这部分重复的操作在Service类中移除,放入DatabaseHelper中会变得非常方便
为了做到这一点,需要确保每一个线程中只有一个Connection对象
这里我们使用ThreadLocal存放本地线程变量,ThreadLocal作为隔离线程的容器
将当前线程中的Connection放入ThreadLocal中存起来,确保不会出现线程安全问题
2,调整DatabaseHelper,引入ThreadLocal保存Connection对象
// 隔离线程:用于保存当前线程的Connection对象
private static final ThreadLocal<Connection> CONNECTION_HOLDER = new ThreadLocal<Connection>();
/**
* 获取数据库连接
*/
public static Connection getConnection() {
// 每次获取Connection连接,先查看是否存在
Connection conn = CONNECTION_HOLDER.get();
if(conn == null){
try {
LOGGER.info("创建Connection");
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (SQLException e) {
LOGGER.error("get connection failure", e);
throw new RuntimeException(e);
} finally {
// 将Connection保存到ThreadLocal中
CONNECTION_HOLDER.set(conn);
}
}
return conn;
}
/**
* 关闭数据库连接
*/
public static void closeConnection(){
// 获取当前线程Connection对象
Connection conn = CONNECTION_HOLDER.get();
if(conn != null){
try {
conn.close();
LOGGER.info("销毁Connection");
} catch (SQLException e) {
LOGGER.error("close connection failure", e);
throw new RuntimeException(e);
} finally {
// 销毁当前线程的Connection对象
CONNECTION_HOLDER.remove();
}
}
}
每次获取Connection时,先在ThreadLocal中查看是否存在
若不存在,创建一个新的Connection并将其放入ThreadLocal中
当Connection使用完毕,移除ThreadLocal中持有的Connection对象
3,优化Service查询操作
有了上边对线程中Connection进行隔离的方法,我们就可以简化Service中数据库操作了
/**
* 获取客户列表
*/
public List<Customer> getCustomerList() {
String sql = "select * from customer";
return DatabaseHelper.queryEntityList(Customer.class, sql);
}
可以看到,在"查询客户列表"方法中,去除了创建/获取连接和关闭连接的操作
这意味着,所有的Service方法都不需要再在开头和结尾做这件事情了
三,测试
现在Service中的查询方法不再需要获取和关闭Connection对象了
数据库查询方法和获取,销毁Connection操作的方法都在DatabaseHelper中
所以可以在查询方法中进行数据库Connection对象的创建和销毁
/**
* 查询实体列表
*/
public static <T> List<T> queryEntityList(Class<T> entityClass,
String sql, Object... params) {
List<T> entityList;
try {
Connection conn = getConnection();
entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler<T>(entityClass), params);
} catch (SQLException e) {
LOGGER.error("query entity list failure", e);
throw new RuntimeException(e);
}finally {
closeConnection();
}
return entityList;
}
测试结果:
四,结尾:
这一节将每个线程的Connection对象保存到ThreadLocal中,实现线程安全
将需要在每一个Service方法中获取和关闭数据库Connection对象的操作进行了提取,
将这些操作提取到了DatabaseHelper中,优化并实现了代码的重用,使Service更加简单
经过这些优化后依然存在一些问题,例如:
每次连接数据库时,都要调用getConnection方法,操作完毕后调用closeConnection方法
虽然这件事已经被DatabaseHelper封装了,但是目前对Service的操作会频繁创建Connection
数据库的连接数是有限的,频繁创建数据库连接会对数据库造成大量的系统开销
因此,需要一种方案,实现对数据库的"池化",也就是"数据库连接池"来解决这个问题