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

5 篇文章 0 订阅
5 篇文章 0 订阅

数据库连接池之自定义连接池(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接口(当然这个接口中的方法我们不用每个都实现,没有实现的方法我就不贴了)!

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

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)

封装接口: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)

实现接口: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的.
感谢您的阅读,希望您有收获,祝您生活愉快,开心快乐每一天~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值