时间:2021-07-01 10:21:17 帮助过:13人阅读
我们已经完成前三步了,现在问题来了。我们调用Conncetion.close()方法,是把数据库的物理连接关掉,而不是返回给LinkedList的
解决思路:
分析第一个思路:
分析第二个思路:
分析第三个思路代码实现:
    @Override
    public Connection getConnection() throws SQLException {
        if (list.size() > 0) {
            final Connection connection = list.removeFirst();
            //看看池的大小
            System.out.println(list.size());
            //返回一个动态代理对象
            return (Connection) Proxy.newProxyInstance(Demo1.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //如果不是调用close方法,就按照正常的来调用
                    if (!method.getName().equals("close")) {
                        method.invoke(connection, args);
                    } else {
                        //进到这里来,说明调用的是close方法
                        list.add(connection);
                        //再看看池的大小
                        System.out.println(list.size());
                    }
                    return null;
                }
            });
        }
        return null;
    }
我们上面已经能够简单编写一个线程池了。下面我们来使用一下开源数据库连接池
使用DBCP数据源的步骤:
    private static DataSource dataSource = null;
    static {
        try {
            //读取配置文件
            InputStream inputStream = Demo3.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            //获取工厂对象
            BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory();
            dataSource = basicDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    //这里释放资源不是把数据库的物理连接释放了,是把连接归还给连接池【连接池的Connection内部自己做好了】
    public static void release(Connection conn, Statement st, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
C3P0数据源的性能更胜一筹,并且它可以使用XML配置文件配置信息!
步骤:
    private static ComboPooledDataSource comboPooledDataSource = null;
    static {
        //如果我什么都不指定,就是使用XML默认的配置,这里我指定的是oracle的
        comboPooledDataSource = new ComboPooledDataSource("oracle");
    }
    public static Connection getConnection() throws SQLException {
        return comboPooledDataSource.getConnection();
    }
Tomcat服务器也给我们提供了连接池,内部其实就是DBCP
步骤:
context.xml文件的配置:
<Context>
  <Resource name="jdbc/EmployeeDB"
            auth="Container"
            type="javax.sql.DataSource"
            
            username="root"
            password="root"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/zhongfucheng"
            maxActive="8"
            maxIdle="4"/>
</Context>
        try {
            //初始化JNDI容器
            Context initCtx = new InitialContext();
            //获取到JNDI容器
            Context envCtx = (Context) initCtx.lookup("java:comp/env");
            //扫描以jdbc/EmployeeDB名字绑定在JNDI容器下的连接池
            DataSource ds = (DataSource)
                    envCtx.lookup("jdbc/EmployeeDB");
            Connection conn = ds.getConnection();
            System.out.println(conn);
        } 
dbutils它是对JDBC的简单封装,极大简化jdbc编码的工作量
提供了关闭连接,装载JDBC驱动,回滚提交事务等方法的工具类【比较少使用,因为我们学了连接池,就应该使用连接池连接数据库】
该类简化了SQL查询,配合ResultSetHandler使用,可以完成大部分的数据库操作,重载了许多的查询,更新,批处理方法。大大减少了代码量
该接口规范了对ResultSet的操作,要对结果集进行什么操作,传入ResultSetHandler接口的实现类即可。
使用DbUtils框架对数据库的CRUD
/*
* 使用DbUtils框架对数据库的CRUD
* 批处理
*
* */
public class Test {
    @org.junit.Test
    public void add() throws SQLException {
        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "INSERT INTO student (id,name) VALUES(?,?)";
        //我们发现query()方法有的需要传入Connection对象,有的不需要传入
        //区别:你传入Connection对象是需要你来销毁该Connection,你不传入,由程序帮你把Connection放回到连接池中
        queryRunner.update(sql, new Object[]{"100", "zhongfucheng"});
    }
    @org.junit.Test
    public void query()throws SQLException {
        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "SELECT * FROM student";
        List list = (List) queryRunner.query(sql, new BeanListHandler(Student.class));
        System.out.println(list.size());
    }
    @org.junit.Test
    public void delete() throws SQLException {
        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "DELETE FROM student WHERE id=‘100‘";
        queryRunner.update(sql);
    }
    @org.junit.Test
    public void update() throws SQLException {
        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "UPDATE student SET name=? WHERE id=?";
        queryRunner.update(sql, new Object[]{"zhongfuchengaaa", 1});
    }
    @org.junit.Test
    public void batch() throws SQLException {
        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "INSERT INTO student (name,id) VALUES(?,?)";
        Object[][] objects = new Object[10][];
        for (int i = 0; i < 10; i++) {
            objects[i] = new Object[]{"aaa", i + 300};
        }
        queryRunner.batch(sql, objects);
    }
}
分页技术是非常常见的,在搜索引擎下搜索页面,不可能把全部数据都显示在一个页面里边。所以我们用到了分页技术。
    /*
      Oracle分页语法:
        @lineSize---每页显示数据行数
        @currentPage----当前所在页
    
    */
    SELECT *FROM (
        SELECT 列名,列名,ROWNUM rn
        FROM 表名
        WHERE ROWNUM<=(currentPage*lineSize)) temp
    
    WHERE temp.rn>(currentPage-1)*lineSize;
Oracle分页原理简单解释:
    /*
      Oracle分页:
        Oracle的分页依赖于ROWNUM这个伪列,ROWNUM主要作用就是产生行号。
    
      分页原理:
        1:子查询查出前n行数据,ROWNUM产生前N行的行号
        2:使用子查询产生ROWNUM的行号,通过外部的筛选出想要的数据
    
      例子:
        我现在规定每页显示5行数据【lineSize=5】,我要查询第2页的数据【currentPage=2】
        注:【对照着语法来看】
    
      实现:
        1:子查询查出前10条数据【ROWNUM<=10】
        2:外部筛选出后面5条数据【ROWNUM>5】
        3:这样我们就取到了后面5条的数据
    */
    /*
      Mysql分页语法:
      @start---偏移量,不设置就是从0开始【也就是(currentPage-1)*lineSize】
      @length---长度,取多少行数据
    
    */
    SELECT *
    FROM 表名
    LIMIT [START], length;
    
    /*
      例子:
        我现在规定每页显示5行数据,我要查询第2页的数据
    
      分析:
        1:第2页的数据其实就是从第6条数据开始,取5条
    
      实现:
        1:start为5【偏移量从0开始】
        2:length为5
*/
总结:
下面是常见的分页图片

配合图片,看下我们的需求是什么:
分析:
通过上面分析,我们会发现需要用到4个变量
        //每页显示3条数据
        int lineSize = 3;
        //总记录数
        int totalRecord = getTotalRecord();
        //假设用户指定的是第2页
        int currentPage = 2;
        //一共有多少页
        int pageCount = getPageCount(totalRecord, lineSize);
        //使用什么数据库进行分页,记得要在JdbcUtils中改配置
        List<Person> list = getPageData2(currentPage, lineSize);
        for (Person person : list) {
            System.out.println(person);
        }
    }
    //使用JDBC连接Mysql数据库实现分页
    public static List<Person> getPageData(int currentPage, int lineSize) throws SQLException {
        //从哪个位置开始取数据
        int start = (currentPage - 1) * lineSize;
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "SELECT name,address  FROM person LIMIT ?,?";
        List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{start, lineSize});
        return persons;
    }
    //使用JDBC连接Oracle数据库实现分页
    public static List<Person> getPageData2(int currentPage, int lineSize) throws SQLException {
        //从哪个位置开始取数据
        int start = (currentPage - 1) * lineSize;
        //读取前N条数据
        int end = currentPage * lineSize;
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "SELECT " +
                "  name, " +
                "  address " +
                "FROM ( " +
                "  SELECT " +
                "    name, " +
                "    address , " +
                "    ROWNUM rn " +
                "  FROM person " +
                "  WHERE ROWNUM <= ? " +
                ")temp WHERE temp.rn>?";
        List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{end, start});
        return persons;
    }
    public static int getPageCount(int totalRecord, int lineSize) {
        //简单算法
        //return (totalRecord - 1) / lineSize + 1;
        //此算法比较好理解,把数据代代进去就知道了。
        return totalRecord % lineSize == 0 ? (totalRecord / lineSize) : (totalRecord / lineSize) + 1;
    }
    public static int  getTotalRecord() throws SQLException {
        //使用DbUtils框架查询数据库表中有多少条数据
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "SELECT COUNT(*) FROM person";
        Object o = queryRunner.query(sql, new ScalarHandler());
        String ss = o.toString();
        int  s = Integer.parseInt(ss);
        return s;
    }
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章的同学,可以关注微信公众号:Java3y。
JDBC【数据库连接池、DbUtils框架、分页】
标签:into idle exce ntp 调用 处理 lease bean jdbc