avatar

目录
数据库连接池之自定义连接池(mysql)

上一篇博文是”基于mysql的JDBC的增删改查的封装“:点击可查看

今天本仙在昨天JDBC封装增删改查的基础上实现自定义的数据库连接池:

为什么要使用数据库连接池:
用户每次请求都需要向数据库获得连接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、宕机.

数据库连接池:顾名思义就是一个存放着数据库连接的游泳池,这些数据库连接初始化后都在这个池子里面遨游,当有请求要获取连接时,拿出去一个使用,用完后再放回这个池子里面,方便循环使用.数据库连接小鱼生生不息,有效的节省了资源,提高了处理效率,这个解释通俗易懂吧~
​废话不多说,我们开始:

1 项目结构:

昨天有个’屎’嘲笑我的项目路径竟然包括中文,本仙受挫了,哼,但是他所说的对,于是乎决定全改成英文,本仙一定会好好学习英语的
项目目录结构

2 省略的过程

  1. 项目的创建
  2. jdbc驱动jar包的导入
  3. 数据库中用户表(user)的创建和添加数据(可见上一篇,传送门)
  4. 实体类(User)的创建(可见上一篇,传送门)
  5. 配置文件(db.properties)的创建(可见上一篇,传送门)

3 自定义数据库连接池的实现(DB_pool.java)

实现下面目录中类的封装:DB_pool.java

3.1 数据库连接池的初始化

Java为连接池实现提供了一个规范(接口),规范的写法,我们需要实现DataSource接口(当然这个接口中的方法我们不用每个都实现,没有实现的方法我就不贴了)!

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class DB_pool implements DataSource {
public static final int CONNCOUNT = 5;
//创建连接的集合
public static List 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 通过数据库连接池获取数据库连接
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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轮之后数据库连接池就没有连接了,那数据库连接池就是去了它存在的意义)

java
1
2
3
4
5
6
7
8
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增删改查的封装
实现下面目录中类的封装:DB_Tools.java
要配合数据库连接池使用,所以这里在初始化连接和关闭连接时做了一点点的修改

4.1 初始化数据库连接池(修改)
java
1
2
3
4
5
6
7
8
9
public class DB_Tools {
//全静态变量
private static DB_pool db_pool;

//完成了数据库连接池的初始化
static {
db_pool = new DB_pool();
}
}
4.2 获取数据库连接(修改)
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class DB_Tools {
/**
* 通过连接池来获取数据库连接
*
* @return 返回一个连接的对象
*/
public static Connection getDBConnection() {
try {
return db_pool.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
4.3 释放资源(修改)
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 增删改查的方法(一点点修改)
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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 泛型
* @return 返回单个实体对象
*/
public static T getSingleResult(String sql, Object[] objects, Class 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 泛型
* @return 返回多个对象的集合
*/
public static List getComplexResult(String sql, Object[] objects, Class tClass) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
List tList = new ArrayList();

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 泛型
* @return 返回一个泛型对象Object
* @throws Exception
*/
public static T getObject(ResultSet resultSet, Class 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)

封装接口:DbPoolsDao.java

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public interface DbPoolsDao {
/**
* 通过主键查询单条用户信息
*
* @param id 主键id
* @return
*/
public User findSingleUserByPrimaryKey(int id);

/**
* 查询全部的用户信息
*
* @return
*/
public List 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)

实现接口:DbPoolDaoImpl.java

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 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 测试数据库连接池封装类
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 测试实现接口
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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 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的.
感谢您的阅读,希望您有收获,祝您生活愉快,开心快乐每一天~

文章作者: XiaoMing
文章链接: https://xiaoming0929.github.io/2020/04/02/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0%E4%B9%8B%E8%87%AA%E5%AE%9A%E4%B9%89%E8%BF%9E%E6%8E%A5%E6%B1%A0-mysql/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 XiaoMing's Blog
打赏
  • 微信
    微信
  • 支付寶
    支付寶