数据库连接池之自定义连接池(mysql)
上一篇博文是"基于mysql的JDBC的增删改查的封装":点击可查看
今天本仙在昨天JDBC封装增删改查的基础上实现自定义的数据库连接池:
为什么要使用数据库连接池:
用户每次请求都需要向数据库获得连接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、宕机.
数据库连接池:顾名思义就是一个存放着数据库连接的游泳池,这些数据库连接初始化后都在这个池子里面遨游,当有请求要获取连接时,拿出去一个使用,用完后再放回这个池子里面,方便循环使用.数据库连接小鱼生生不息,有效的节省了资源,提高了处理效率,这个解释通俗易懂吧~
废话不多说,我们开始:
1 项目结构:
昨天有个’屎’嘲笑我的项目路径竟然包括中文,本仙受挫了,哼,但是他所说的对,于是乎决定全改成英文,本仙一定会好好学习英语的
2 省略的过程
- 项目的创建
- jdbc驱动jar包的导入
- 数据库中用户表(user)的创建和添加数据(可见上一篇,传送门)
- 实体类(User)的创建(可见上一篇,传送门)
- 配置文件(db.properties)的创建(可见上一篇,传送门)
3 自定义数据库连接池的实现(DB_pool.java)
实现下面目录中类的封装:
3.1 数据库连接池的初始化
Java为连接池实现提供了一个规范(接口),规范的写法,我们需要实现DataSource接口(当然这个接口中的方法我们不用每个都实现,没有实现的方法我就不贴了)!
public class DB_pool implements DataSource {
public static final int CONNCOUNT = 5;
//创建连接的集合
public static List<Connection> connections;
private static String driverClass;
private static String url;
private static String user;
private static String password;
//初始化数据库连接池
static {
//使用安全集合才存储连接
connections = Collections.synchronizedList(new LinkedList<>());
try {
//获取配置文件
InputStream inputStream = DB_Tools.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(inputStream);
//从配置文件中获取数据信息
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
//注册驱动
Class.forName(driverClass);
System.out.println("注册驱动成功");
//初始化Connection对象
for (int i = 0; i < CONNCOUNT; i++) {
connections.add(DriverManager.getConnection(url, user, password));
}
System.out.println("数据库连接池初始化完成");
} catch (Exception e) {
e.printStackTrace();
System.out.println("初始化失败");
}
}
}
3.2 通过数据库连接池获取数据库连接
public class DB_pool implements DataSource {
//获取连接,即从连接池中取出一个连接来使用
@Override
public Connection getConnection() throws SQLException {
synchronized (connections) {
if (connections.size() > 0) {
Connection connection = connections.remove(0);
//测试一下我们拿到的是哪个连接
System.out.println("使用了Connection:"+connection.toString());
return connection;
}
}
return null;
}
}
3.3 断开数据库连接
数据库连接使用完毕之后,并不是将这个连接给close()掉,而是重新放入数据库连接池中(不然你在数据库连接池中拿一个连接,用完了就关闭不放回去…N轮之后数据库连接池就没有连接了,那数据库连接池就是去了它存在的意义)
public class DB_pool implements DataSource {
//自定义断开连接方法,其实实现的是将使用完的connection放回连接池
public void releaseConnection(Connection connection){
//测试一下,是将哪个链接返回了数据库
System.out.println("归还了Connection:"+connection.toString());
connections.add(connection);
}
}
4 JDBC增删改查封装类(DB_Tools.java)的修改
传送门:上一篇博客:简易JDBC增删改查的封装
实现下面目录中类的封装:
要配合数据库连接池使用,所以这里在初始化连接和关闭连接时做了一点点的修改
4.1 初始化数据库连接池(修改)
public class DB_Tools {
//全静态变量
private static DB_pool db_pool;
//完成了数据库连接池的初始化
static {
db_pool = new DB_pool();
}
}
4.2 获取数据库连接(修改)
public class DB_Tools {
/**
* 通过连接池来获取数据库连接
*
* @return 返回一个连接的对象
*/
public static Connection getDBConnection() {
try {
return db_pool.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
4.3 释放资源(修改)
public class DB_Tools {
/**
* 3.释放资源close()
*
* @param connection 连接
* @param preparedStatement 执行命令
* @param resultSet 结果集
*/
public static void allClose(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
if (resultSet != null) {
resultSet.close();
}
if (connection != null) {
db_pool.releaseConnection(connection);
}
System.out.println("执行结束");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
4.4 增删改查的方法(一点点修改)
public class DB_Tools {
/**
* 4.执行增删改
*
* @param sql sql操作语句
* @param objects sql语句中的参数,可以为null
* @return int >0操作(增删改)成功 <0操作失败
*/
public static int excumentDBUpdate(String sql, Object[] objects) {
int updateResult = 0;
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
//1.获取连接
connection = getDBConnection();
//2.获取增删改查的执行命令
preparedStatement = connection.prepareStatement(sql);
//3.向sql语句中?填充数据
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
preparedStatement.setObject(i + 1, objects[i]);
}
}
//4.执行增删改查
updateResult = preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//5.关闭连接,释放资源
allClose(connection, preparedStatement, null);
}
return updateResult;
}
/**
* 5.查询单条数据(反射加内省实现)
*
* @param sql sql操作语句
* @param objects sql语句中的参数,可以为null
* @param tClass
* @param <T> 泛型
* @return 返回单个实体对象
*/
public static <T> T getSingleResult(String sql, Object[] objects, Class<T> tClass) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
T object = null;
try {
//1.获取连接
connection = getDBConnection();
//2.创建命令对象
preparedStatement = connection.prepareStatement(sql);
//3.向sql语句中添加数据(objects),如果objects不为空的话
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
preparedStatement.setObject(i + 1, objects[i]);
}
}
//4.执行查询语句
resultSet = preparedStatement.executeQuery();
//5.通过反射创建tClass类的对象
//6.从结果集中获取数据
if (resultSet.next()) {
object = getObject(resultSet, tClass);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//7.关闭连接释放资源
allClose(connection, preparedStatement, resultSet);
}
//8.返回object对象
return object;
}
/**
* 5.2查询多条数据(反射+内省实现)
*
* @param sql sql操作语句
* @param objects sql语句中的参数,可以为null
* @param tClass
* @param <T> 泛型
* @return 返回多个对象的集合
*/
public static <T> List<T> getComplexResult(String sql, Object[] objects, Class<T> tClass) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
List<T> tList = new ArrayList<T>();
try {
//1.获取连接
connection = getDBConnection();
//2.创建命令对象
preparedStatement = connection.prepareStatement(sql);
//3.向sql语句中添加数据(objects),如果objects不为空的话
if (objects != null) {
for (int i = 0; i < objects.length; i++) {
preparedStatement.setObject(i + 1, objects[i]);
}
}
//4.执行查询语句
resultSet = preparedStatement.executeQuery();
//5.从结果集中获取数据
while (resultSet.next()) {
T object = getObject(resultSet, tClass);
tList.add(object);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//7.关闭连接释放资源
allClose(connection, preparedStatement, resultSet);
}
//8.返回tList集合
return tList;
}
/**
* 通过反射+内省将结果集中数据拿出来
* @param resultSet 结果集啊
* @param tClass 传递过来的操作类
* @param <T> 泛型
* @return 返回一个泛型对象Object
* @throws Exception
*/
public static <T> T getObject(ResultSet resultSet, Class<T> tClass) throws Exception {
T object = tClass.newInstance();
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {
//获取列名
String colName = resultSetMetaData.getColumnName(i + 1);
//通过内省获取包含列名指定方法(一般是get和set)
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(colName, tClass);
if (propertyDescriptor != null) {
//获取到方法后我们再获取set方法来对private修饰的相关属性进行赋值
Method method = propertyDescriptor.getWriteMethod();
//然后我们执行这个方法并将属性添加到object对象中
method.invoke(object, resultSet.getObject(colName));
}
}
return object;
}
}
通过上面我们就分别封装好了自定义的数据库连接池和JDBC操作类,在这里写一个接口封装几个方法使用数据库连接池和JDBC封装的方法来实现:
5 封装接口(DbPoolsDao.java)
封装接口:
public interface DbPoolsDao {
/**
* 通过主键查询单条用户信息
*
* @param id 主键id
* @return
*/
public User findSingleUserByPrimaryKey(int id);
/**
* 查询全部的用户信息
*
* @return
*/
public List<User> findUserAllUser();
/**
* 添加用户信息
*
* @param user
*/
public void addUserInfo(User user);
/**
* 更新用户信息
*
* @param user
*/
public void updateUserInfoByPrimaryKey(User user);
/**
* 通过主键删除用户信息
*
* @param id
*/
public void deleteUserInfoByPrimaryKey(int id);
}
6 实现上面的接口(DbPoolDaoImpl.java)
实现接口:
public class DbPoolDaoImpl implements DbPoolsDao {
@Override
public User findSingleUserByPrimaryKey(int id) {
String sql = "select * from user where id = ?";
Object[] objects = {id};
return DB_Tools.getSingleResult(sql, objects, User.class);
}
@Override
public List<User> findUserAllUser() {
String sql = "select * from user";
return DB_Tools.getComplexResult(sql, null, User.class);
}
@Override
public void addUserInfo(User user) {
String sql = "insert into user values(?,?,?,?,?)";
Object[] objects = {user.getId(),user.getUsername(),user.getPassword(),user.getAge(),user.getGendar()};
DB_Tools.excumentDBUpdate(sql, objects);
}
@Override
public void updateUserInfoByPrimaryKey(User user) {
String sql = "update user set username=?,password=?,age=?,gendar=? where id=?";
Object[] objects = {user.getUsername(),user.getPassword(),user.getAge(),user.getGendar(),user.getId()};
DB_Tools.excumentDBUpdate(sql, objects);
}
@Override
public void deleteUserInfoByPrimaryKey(int id) {
String sql = "delete from user where id=?";
Object[] objects = {id};
DB_Tools.excumentDBUpdate(sql, objects);
}
}
7 测试
上面写了那么多,到底结果如何呢,这里本仙分别用单元测试(简易版),测试数据库连接池的封装类(DB_pool)和接口实现(DbPoolDaoImpl)看看写的代码成功与否**走你┏ (゜ω゜)=☞**
7.1 测试数据库连接池封装类
public class DB_poolTest {
@Test
public void testPool() {
for (int i = 0; i < 100; i++) {
Connection connection = DB_Tools.getDBConnection();
DB_Tools.allClose(connection, null, null);
System.out.println(connection.toString() + "\n--------------------------------------------------");
//JDBC4Connection:connection.getClass()从数据库获取的原生连接
}
}
}
//运行结果:
/*
注册驱动成功
数据库连接池初始化完成
使用了Connection:com.mysql.jdbc.JDBC4Connection@4ca8195f
归还了Connection:com.mysql.jdbc.JDBC4Connection@4ca8195f
执行结束
com.mysql.jdbc.JDBC4Connection@4ca8195f
--------------------------------------------------
使用了Connection:com.mysql.jdbc.JDBC4Connection@65e579dc
归还了Connection:com.mysql.jdbc.JDBC4Connection@65e579dc
执行结束
com.mysql.jdbc.JDBC4Connection@65e579dc
--------------------------------------------------
使用了Connection:com.mysql.jdbc.JDBC4Connection@61baa894
归还了Connection:com.mysql.jdbc.JDBC4Connection@61baa894
执行结束
com.mysql.jdbc.JDBC4Connection@61baa894
--------------------------------------------------
使用了Connection:com.mysql.jdbc.JDBC4Connection@b065c63
归还了Connection:com.mysql.jdbc.JDBC4Connection@b065c63
执行结束
com.mysql.jdbc.JDBC4Connection@b065c63
--------------------------------------------------
使用了Connection:com.mysql.jdbc.JDBC4Connection@768debd
归还了Connection:com.mysql.jdbc.JDBC4Connection@768debd
执行结束
com.mysql.jdbc.JDBC4Connection@768debd
.....(这里往后省略)
*/
7.2 测试实现接口
public class Db_PoolDaoTest {
private static DbPoolsDao dbPoolsDao;
@BeforeClass
public static void beforeTest() {
dbPoolsDao = new DbPoolDaoImpl();
}
@AfterClass
public static void afterTest() {
dbPoolsDao = null;
}
@Test
public void findSingleUserByPrimaryKeyTest() {
User user = dbPoolsDao.findSingleUserByPrimaryKey(100);
System.out.println(user);
}
@Test
public void findUserAllUserTest() {
List<User> users = dbPoolsDao.findUserAllUser();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void addUserInfoTest() {
dbPoolsDao.addUserInfo(new User(105, "明明", "mingming", 20, "male"));
System.out.println("添加成功");
}
@Test
public void updateUserInfoByPrimaryKeyTest() {
dbPoolsDao.updateUserInfoByPrimaryKey(new User(105, "明明", "helloworld", 18, "male"));
System.out.println("修改成功");
}
@Test
public void deleteUserInfoByPrimaryKeyTest() {
dbPoolsDao.deleteUserInfoByPrimaryKey(105);
System.out.println("删除成功");
}
}
方法太多,测试结果我就不一个一个贴上来了,不过每个方法小编都是测试过的!如果哦除了问题,那肯定是你代码里有小臭虫了,本仙这里是通过的,若你再测试过程中遇到了问题,可以留言欧,我超级喜欢给别人找Bug的.
感谢您的阅读,希望您有收获,祝您生活愉快,开心快乐每一天~