jdbc mysql长连接池,数据库连接池之自定义连接池(mysql)

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

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

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

为什么要使用数据库连接池:

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

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

​废话不多说,我们开始:

1 项目结构:

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

168b198b1f3b9f1e21b42e9e401865c6.png

2 省略的过程

项目的创建

jdbc驱动jar包的导入

数据库中用户表(user)的创建和添加数据(可见上一篇,传送门)

实体类(User)的创建(可见上一篇,传送门)

配置文件(db.properties)的创建(可见上一篇,传送门)

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

实现下面目录中类的封装:857aeb4eef119f8a4f06ceb2093519a8.png

3.1 数据库连接池的初始化

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

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 通过数据库连接池获取数据库连接

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增删改查的封装

实现下面目录中类的封装:35d7d0d68c27019fe6caad374f949f5d.png

要配合数据库连接池使用,所以这里在初始化连接和关闭连接时做了一点点的修改

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 泛型

* @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)

封装接口:32812ac80e88a7a01a625a1acab4bc4a.png

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)

实现接口:df909b7b16bcc014cf457877f0a4f8c6.png

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 测试数据库连接池封装类

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 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的.

感谢您的阅读,希望您有收获,祝您生活愉快,开心快乐每一天~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值