day21-连接池和DBUtils

今日内容

  • 连接池

    • 自定义连接池---------------了解即可(熟练装饰者设计模式) 难点
    • 使用第三方连接池---------重点\掌握
      • C3P0
      • DRUID
  • DBUtils---------重点\掌握
  • 元数据-------了解

学习目标

  • 能够理解连接池解决现状问题的原理
  • 能够使用C3P0连接池
  • 能够使用DRUID连接池
  • 能够编写C3P0连接池工具类
  • 能够使用DBUtils完成CRUD
  • 能够理解元数据
  • 能够自定义DBUtils

第一章-自定义连接池

知识点-连接池概念

1.目标

  • 能够理解连接池解决现状问题的原理

2.讲解

2.1为什么要使用连接池

​ Connection对象在JDBC使用的时候就会去创建一个对象,使用结束以后就会将这个对象给销毁了(close).每次创建和销毁对象都是耗时操作.需要使用连接池对其进行优化.

​ 程序初始化的时候,初始化多个连接,将多个连接放入到池(集合)中.每次获取的时候,都可以直接从连接池中进行获取.使用结束以后,将连接归还到池中.

2.2.生活里面的连接池例子

  • 老方式:

    ​ 下了地铁需要骑车, 跑去生产一个, 然后骑完之后,直接把车销毁了.

  • 连接池方式 摩拜单车:

    ​ 骑之前, 有一个公司生产了很多的自行车, 下了地铁需要骑车, 直接扫码使用就好了, 然后骑完之后, 还回去

2.3连接池原理【重点】

  1. 程序一开始就创建一定数量的连接,放在一个容器(集合)中,这个容器称为连接池。
  2. 使用的时候直接从连接池中取一个已经创建好的连接对象, 使用完成之后 归还到池子
  3. 如果池子里面的连接使用完了, 还有程序需要使用连接, 先等待一段时间(eg: 3s), 如果在这段时间之内有连接归还, 就拿去使用; 如果还没有连接归还, 新创建一个, 但是新创建的这一个不会归还了(销毁)
  4. 集合选择LinkedList
    • 增删比较快
    • LinkedList里面的removeFirst()和addLast()方法和连接池的原理吻合

3.小结

  1. 使用连接池的目的: 可以让连接得到复用, 避免浪费

自定义连接池-初级版本

1.目标

​ 根据连接池的原理, 使用LinkedList自定义连接池

2.分析

  1. 创建一个类MyDataSource, 定义一个集合LinkedList
  2. 程序初始化的时候, 创建5个连接 存到LinkedList
  3. 定义getConnection() 从LinkedList取出Connection返回
  4. 定义addBack()方法归还Connection到LinkedList

3.实现

public class MyDataSource1 {// 1.定义一个LinkedList集合,用来存储初始化的连接private static LinkedList<Connection> list = new LinkedList<>();// 2.初始化5个连接,并存储到集合中static {for (int i = 0; i < 5; i++) {try {// 获得连接Connection connection = JDBCUtils.getConnection();// 添加到集合中list.add(connection);} catch (Exception e) {e.printStackTrace();}}}// 3.定义getConnection方法,用来获得连接public Connection getConnection(){Connection connection = list.removeFirst();return connection;}// 4.定义addBack方法,用来归还连接public void addBack(Connection connection){list.addLast(connection);}// 5.定义getCount方法,返回连接池中连接数量public static int getCount(){return list.size();}
}// 测试
public class CRUDTest1 {// 查询记录@Testpublic void select() throws Exception {// 1.创建连接池对象MyDataSource1 dataSource = new MyDataSource1();System.out.println("获得连接之前,连接池中连接的数量:"+MyDataSource1.getCount());// 5// 2.获得连接Connection connection = dataSource.getAbc();// 2.书写sql语句,预编译sql语句,得到预编译对象String sql = "select * from user where id = ?";PreparedStatement ps = connection.prepareStatement(sql);// 3.设置参数ps.setInt(1,3);// 4.执行sql语句ResultSet resultSet = ps.executeQuery();// 封装,处理数据User user = null;while (resultSet.next()){user = new User();user.setId(resultSet.getInt("id"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setNickname(resultSet.getString("nickname"));}System.out.println("获得连接之后,连接池中连接的数量:"+MyDataSource1.getCount());// 4System.out.println(user);// 5.释放资源// 归还连接dataSource.addBack(connection);JDBCUtils.release(resultSet,ps,null);System.out.println("归还连接之后,连接池中连接的数量:"+MyDataSource1.getCount());// 5}
}

4.小结

  1. 创建一个类MyDataSource, 定义一个集合LinkedList
  2. 程序初始化(静态代码块)里面 创建5个连接存到LinkedList
  3. 定义提供Connection的方法
  4. 定义归还Connection的方法

自定义连接池-进阶版本

1.目标

​ 实现datasource完成自定义连接池

2.分析

​ 在初级版本版本中, 我们定义的方法是getAbc(). 因为是自定义的.如果改用李四的自定义的连接池,李四定义的方法是getCon(), 那么我们的源码就需要修改, 这样不方便维护. 所以sun公司定义了一个接口DataSource,让自定义连接池有了规范

3.讲解

3.1datasource接口概述

​ Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商(用户)需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

3.2代码实现

public class MyDataSource2 implements DataSource {// 1.定义一个LinkedList集合,用来存储初始化的连接private static LinkedList<Connection> list = new LinkedList<>();// 2.初始化5个连接,并存储到集合中static {for (int i = 0; i < 5; i++) {try {// 获得连接Connection connection = JDBCUtils.getConnection();// 添加到集合中list.add(connection);} catch (Exception e) {e.printStackTrace();}}}/*// 3.定义getAbc方法,用来获得连接public Connection getAbc(){Connection connection = list.removeFirst();return connection;}*/// 4.定义addBack方法,用来归还连接public void addBack(Connection connection){list.addLast(connection);}// 5.定义getCount方法,返回连接池中连接数量public static int getCount(){return list.size();}@Overridepublic Connection getConnection() throws SQLException {Connection connection = list.removeFirst();return connection;}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}
}// 测试
public class CRUDTest2 {// 查询记录@Testpublic void select() throws Exception {// 1.创建连接池对象DataSource dataSource = new MyDataSource2();System.out.println("获得连接之前,连接池中连接的数量:"+MyDataSource2.getCount());// 5// 2.获得连接Connection connection = dataSource.getConnection();// 2.书写sql语句,预编译sql语句,得到预编译对象String sql = "select * from user where id = ?";PreparedStatement ps = connection.prepareStatement(sql);// 3.设置参数ps.setInt(1,3);// 4.执行sql语句ResultSet resultSet = ps.executeQuery();// 封装,处理数据User user = null;while (resultSet.next()){user = new User();user.setId(resultSet.getInt("id"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setNickname(resultSet.getString("nickname"));}System.out.println("获得连接之后,连接池中连接的数量:"+MyDataSource2.getCount());// 4System.out.println(user);// 5.释放资源// 归还连接// 解决办法: 1.向下转型  2.增强Connection的close方法(默认是销毁连接,增强后变成归还连接)((MyDataSource2)dataSource).addBack(connection);JDBCUtils.release(resultSet,ps,null);System.out.println("归还连接之后,连接池中连接的数量:"+MyDataSource2.getCount());// 5}
}

4.小结

4.1编写连接池遇到的问题

  • 实现DataSource接口后,addBack()不能调用了.
  • 能不能不引入新的api,直接调用之前的connection.close(),但是这个close不是关闭,是归还

4.2解决办法

  • 继承

    • 条件:可以控制父类, 最起码知道父类的名字
  • 装饰者模式

    • 作用:改写已存在的类的某个方法或某些方法
    • 条件:
      • 增强类和被增强类实现的是同一个接口
      • 增强类里面要拿到被增强类的引用
  • 动态代理

自定义连接池-终极版本

1.目标

使用装饰者模式改写connection的close()方法, 让connection归还

2.讲解

2.1装饰者模式

2.1.1概述
  • 什么是装饰者模式

    ​ 装饰者模式,是 23种常用的面向对象软件的设计模式之一. 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。

    ​ 装饰者的作用:改写已存在的类的某个方法或某些方法, 增强方法的逻辑

  • 使用装饰者模式需要满足的条件

    • 增强类和被增强类实现的是同一个接口
    • 增强类里面要拿到被增强类的引用
2.2.2装饰者模式的使用【重点】

实现步骤:

  1. 增强类和被增强类需要实现同一个接口
  2. 增强类里面需要得到被增强类的引用,
  3. 对于不需要改写的方法,调用被增强类原有的方法。
  4. 对于需要改写的方法写自己的代码
  • 接口:
public interface Star {public void sing();public void dance();
}
  • 被增强的类:
public class LiuDeHua implements Star {@Overridepublic void sing() {System.out.println("刘德华唱忘情水...");}@Overridepublic void dance() {System.out.println("刘德华跳霹雳舞...");}
}
  • 增强的类:
public class LiuDeHuaWrapper implements Star {// 增强类中获得被增强类的引用LiuDeHua ldh;public LiuDeHuaWrapper(LiuDeHua ldh) {this.ldh = ldh;}@Overridepublic void sing() {System.out.println("刘德华唱冰雨...");ldh.sing();System.out.println("刘德华唱练习...");}@Overridepublic void dance() {// 不增强ldh.dance();}
}
  • 测试

    public class Test {public static void main(String[] args) {// 没有使用装饰者LiuDeHua ldh = new LiuDeHua();//ldh.sing();//ldh.dance();// 使用装饰者LiuDeHuaWrapper ldhw = new LiuDeHuaWrapper(ldh);ldhw.sing();ldhw.dance();}
    }
    

2.2自定义连接池终极版本

2.2.1分析

​ 增强connection的close()方法, 其它的方法逻辑不改

  1. 创建MyConnection实现Connection
  2. 在MyConnection里面需要得到被增强的connection对象(通过构造方法传进去)
  3. 改写close()的逻辑, 变成归还
  4. 其它方法的逻辑, 还是调用被增强connection对象之前的逻辑
2.2.2实现
  • MyConnection
public class MyConnection implements Connection {// 获得被增强的连接对象的引用Connection con;// 获得连接池LinkedList<Connection> list;public MyConnection(Connection con, LinkedList<Connection> list) {this.con = con;this.list = list;}@Overridepublic void close() throws SQLException {// 归还连接list.addLast(con);}@Overridepublic Statement createStatement() throws SQLException {return con.createStatement();}// 必须写@Overridepublic PreparedStatement prepareStatement(String sql) throws SQLException {return con.prepareStatement(sql);}// 剩余的重写方法,使用被增强的连接对象调用原有方法...// ...
}
  • MyDataSource03
public class MyDataSource3 implements DataSource {// 1.定义一个LinkedList集合,用来存储初始化的连接private static LinkedList<Connection> list = new LinkedList<>();// 2.初始化5个连接,并存储到集合中static {for (int i = 0; i < 5; i++) {try {// 获得连接Connection connection = JDBCUtils.getConnection();// 添加到集合中list.add(connection);} catch (Exception e) {e.printStackTrace();}}}/*// 3.定义getAbc方法,用来获得连接public Connection getAbc(){Connection connection = list.removeFirst();return connection;}*//*// 4.定义addBack方法,用来归还连接public void addBack(Connection connection){list.addLast(connection);}*/// 5.定义getCount方法,返回连接池中连接数量public static int getCount(){return list.size();}@Overridepublic Connection getConnection() throws SQLException {Connection connection = list.removeFirst();// 返回的是被增强的连接对象// 增强MyConnection myConnection = new MyConnection(connection,list);return myConnection;}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}
}
  • 测试类

    public class CRUDTest3 {// 查询记录@Testpublic void select() throws Exception {// 1.创建连接池对象DataSource dataSource = new MyDataSource3();System.out.println("获得连接之前,连接池中连接的数量:"+MyDataSource3.getCount());// 5// 2.获得连接Connection connection = dataSource.getConnection();// 返回的是增强的连接对象 MyConnection// 2.书写sql语句,预编译sql语句,得到预编译对象String sql = "select * from user where id = ?";PreparedStatement ps = connection.prepareStatement(sql);// 3.设置参数ps.setInt(1,3);// 4.执行sql语句ResultSet resultSet = ps.executeQuery();// 封装,处理数据User user = null;while (resultSet.next()){user = new User();user.setId(resultSet.getInt("id"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setNickname(resultSet.getString("nickname"));}System.out.println("获得连接之后,连接池中连接的数量:"+MyDataSource3.getCount());// 4System.out.println(user);// 5.释放资源// 归还连接//connection.close();// 归还连接JDBCUtils.release(resultSet,ps,connection);System.out.println("归还连接之后,连接池中连接的数量:"+MyDataSource3.getCount());// 5}
    }
    

3.小结

  1. 创建一个MyConnection实现Connection
  2. 在MyConnection得到被增强的connection对象
  3. 改写MyConnection里面的close()方法的逻辑为归还
  4. MyConnection里面的其它方法 调用被增强的connection对象之前的逻辑
  5. 在MyDataSource03的getConnection()方法里面 返回了myConnection(增强的连接对象)

第二章-第三方连接池

知识点-常用连接池

1.目标

  • 常用连接池

2.分析

​ 通过前面的学习,我们已经能够使用所学的基础知识构建自定义的连接池了。其目的是锻炼大家的基本功,帮助大家更好的理解连接池的原理, 但现实是残酷的,我们所定义的 连接池 和第三方的连接池相比,还是显得渺小. 工作里面都会用第三方连接池.

3.讲解

常见的第三方连接池如下:

  • C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。C3P0是异步操作的,所以一些操作时间过长的JDBC通过其它的辅助线程完成。目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能
  • 阿里巴巴-德鲁伊druid连接池:Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求。
  • DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。dbcp没有自动回收空闲连接的功能。

4.小结

我们工作里面用的比较多的是:

  • C3P0
  • druid
  • 光连接池

知识点-C3P0

1.目标

  • 掌握C3P0的使用

2.路径

  1. c3p0介绍
  2. c3p0的使用(硬编码)
  3. c3p0的使用(配置文件)—工作中使用
  4. 使用c3p0改写工具类

3.讲解

3.1 c3p0介绍

  • C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时还需要添加配置文件c3p0-config.xml.
  • 使用C3P0需要添加c3p0-0.9.1.2.jar

3.2c3p0的使用

3.2.1通过硬编码来编写【了解】

步骤

  1. 拷贝jar
  2. 创建C3P0连接池对象
  3. 从C3P0连接池对象里面获得connection

实现:

public class Test1_通过硬编码来编写 {public static void main(String[] args) throws Exception {// 1.拷贝c3p0jar包到模块下,并添加到classpath路径中// 2.创建连接池对象ComboPooledDataSource dataSource = new ComboPooledDataSource();// 连接池设置参数dataSource.setDriverClass("com.mysql.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/day21_1");dataSource.setUser("root");dataSource.setPassword("root");// 设置初始化连接数量dataSource.setInitialPoolSize(5);// 3.获得连接Connection connection = dataSource.getConnection();// 4.书写sql语句,预编译sql语句,得到预编译对象String sql = "select * from user where id = ?";PreparedStatement ps = connection.prepareStatement(sql);// 5.设置参数ps.setInt(1, 3);// 6.执行sql语句ResultSet resultSet = ps.executeQuery();// 封装,处理数据User user = null;while (resultSet.next()) {user = new User();user.setId(resultSet.getInt("id"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setNickname(resultSet.getString("nickname"));}System.out.println(user);// 7.释放资源---连接归还JDBCUtils.release(resultSet,ps,connection);}
}
3.2.2 通过配置文件来编写【重点】

步骤:

  1. 拷贝jar
  2. 拷贝配置文件(c3p0-config.xml)到src目录【名字不要改
  3. 创建C3P0连接池对象【自动的读取】
  4. 从池子里面获得连接

实现:

  • 编写配置文件c3p0-config.xml,放在src目录下(注:文件名一定不要改)
<c3p0-config><default-config><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property><property name="user">root</property><property name="password">1998</property><property name="initialPoolSize">5</property></default-config>
</c3p0-config>
  • 编写Java代码 (会自动读取src目录下的c3p0-config.xml,所以不需要我们解析配置文件)
public class Test2_通过配置文件来编写 {public static void main(String[] args) throws Exception{// 1.拷贝c3p0jar包到模块下,并添加到classpath路径中// 2.创建连接池对象ComboPooledDataSource dataSource = new ComboPooledDataSource();// 3.获得连接Connection connection = dataSource.getConnection();// 4.书写sql语句,预编译sql语句,得到预编译对象String sql = "select * from user where id = ?";PreparedStatement ps = connection.prepareStatement(sql);// 5.设置参数ps.setInt(1, 3);// 6.执行sql语句ResultSet resultSet = ps.executeQuery();// 封装,处理数据User user = null;while (resultSet.next()) {user = new User();user.setId(resultSet.getInt("id"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setNickname(resultSet.getString("nickname"));}System.out.println(user);// 7.释放资源---连接归还JDBCUtils.release(resultSet,ps,connection);}
}

3.3使用c3p0改写工具类【重点】

​ 我们之前写的工具类(JdbcUtils)每次都会创建一个新的连接, 使用完成之后, 都给销毁了; 所以现在我们要使用c3p0来改写工具类. 也就意味着,我们从此告别了JdbcUtils. 后面会使用c3p0写的工具类

思路:

  1. 创建C3P0Utils这个类
  2. 定义DataSource, 保证DataSource全局只有一个
  3. 定义getConnection()方法从DataSource获得连接
  4. 定义closeAll()方法 释放资源
package Utils;import com.mchange.v2.c3p0.ComboPooledDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;/*** @Author:pengzhilin* @Date: 2020/10/8 11:33*/
public class C3P0Utils {// 创建c3p0连接池对象private static final ComboPooledDataSource DATA_SOURCE = new ComboPooledDataSource();/*** 获得连接的方法* @return  连接* @throws Exception*/public static Connection getConnection() throws Exception{Connection connection = DATA_SOURCE.getConnection();return connection;}/*** 获得连接池的方法* @return*/public static DataSource getDataSource(){return DATA_SOURCE;}/*** 释放资源** @param resultSet* @param statement* @param connection*/public static void release(ResultSet resultSet, Statement statement, Connection connection) {if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}

4.小结

  1. C3P0 配置文件方式使用

    • 拷贝jar
    • 拷贝配置文件到src【配置文件的名字不要改】
    • 创建C3P0连接池对象
  2. C3P0工具类

    • 保证DataSource连接池只有一个【static】
    • 提供connection
    • 释放资源

    代替昨天写的JdbcUtils

知识点-DRUID

1.目标

  • 掌握DRUID连接池的使用

2.路径

  1. DRUID的介绍
  2. DRUID的使用(硬编码方式)
  3. DRUID的使用(配置文件方式)
  4. DRUID抽取成工具类(作业)

3.讲解

3.1DRUID介绍

​ Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是国内目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票。

Druid的下载地址:https://github.com/alibaba/druid

DRUID连接池使用的jar包:druid-1.0.9.jar

链接:https://pan.baidu.com/s/1jVwdssB3bkJfzqhBV73brw
提取码:3dqz

3.2DRUID的使用

3.2.1通过硬编码方式【了解】

步骤:

  1. 导入DRUID jar 包
  2. 创建Druid连接池对象, 配置4个基本参数
  3. 从Druid连接池对象获得Connection

实现:

public class Test1_硬编码方式使用 {@Testpublic void select() throws Exception{// 1.拷贝jar包,添加classpath路径// 2.创建Druid连接池对象DruidDataSource dataSource = new DruidDataSource();// 设置参数dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/test");dataSource.setUsername("root");dataSource.setPassword("1998");dataSource.setInitialSize(5);// 3.获得连接DruidPooledConnection connection = dataSource.getConnection();
78s// 4.书写sql语句,预编译sql语句,得到预编译对象String sql = "select * from user where id = ?";PreparedStatement ps = connection.prepareStatement(sql);// 5.设置参数ps.setInt(1, 3);// 6.执行sql语句ResultSet resultSet = ps.executeQuery();// 封装,处理数据User user = null;while (resultSet.next()) {user = new User();user.setId(resultSet.getInt("id"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setNickname(resultSet.getString("nickname"));}System.out.println(user);// 7.释放资源---连接归还JDBCUtils.release(resultSet,ps,connection);}
}
3.2.2 通过配置文件方式【重点】

步骤:

  1. 导入DRUID jar 包
  2. 拷贝配置文件到src目录
  3. 根据配置文件创建Druid连接池对象
  4. 从Druid连接池对象获得Connection

实现:

  • 创建druid.properties, 放在src目录下
# 数据库连接参数
url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8
username=root
password=
driverClassName=com.mysql.jdbc.Driver
# 连接池的参数
initialSize=10
maxActive=10
maxWait=2000
  • 编写Java代码
public class Test2_配置文件方式使用 {@Testpublic void select() throws Exception{// 1.拷贝jar包,添加classpath路径// 2.创建Druid连接池对象Properties prop = new Properties();// 加载配置文件InputStream is = Test2_通过配置文件来编写.class.getClassLoader().getResourceAsStream("druid.properties");prop.load(is);// 创建Druid连接池对象DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);// 3.获得连接Connection connection = dataSource.getConnection();// 4.书写sql语句,预编译sql语句,得到预编译对象String sql = "select * from user where id = ?";PreparedStatement ps = connection.prepareStatement(sql);// 5.设置参数ps.setInt(1, 3);// 6.执行sql语句ResultSet resultSet = ps.executeQuery();// 封装,处理数据User user = null;while (resultSet.next()) {user = new User();user.setId(resultSet.getInt("id"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setNickname(resultSet.getString("nickname"));}System.out.println(user);// 7.释放资源---连接归还JDBCUtils.release(resultSet,ps,connection);}
}

3.3作业(必做)

  • 把druid连接池也抽取一个工具类(参照C3P0Util)

**DruidUtil **

public class DruidUtil {private static DataSource dataSource;static {try {//1. 创建Properties对象Properties properties = new Properties();//2. 将配置文件转换成字节输入流InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");//3. 使用properties对象加载isproperties.load(is);//druid底层是使用的工厂设计模式,去加载配置文件,创建DruidDataSource对象dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}public static DataSource getDataSource(){return dataSource;}
}
//获取connection连接public static Connection getConnection() throws SQLException {return dataSource.getConnection();}

关于DruidUtil获取唯一connection对象可以查看笔记el^jstl

4.小结

  1. Druid配置文件使用

    • 拷贝jar
    • 拷贝配置文件到src
    • 读取配置文件成properties对象
    • 使用工厂根据properties创建DataSource
    • 从DataSource获得Connection

第三章-DBUtils

知识点–DBUtils的介绍

1.目标

  • 知道什么是DBUtils

2.路径

  1. DBUtils的概述
  2. DBUtils的常用API介绍

3.讲解

3.1 DBUtils的概述

​ DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能

3.2 DBUtils的常用API介绍

  1. 创建QueryRunner对象的API

    public QueryRunner(DataSource ds) ,提供数据源(连接池),DBUtils底层自动维护连接connection

  2. QueryRunner执行增删改的SQL语句的API

    int update(String sql, Object... params),执行增删改的SQL语句, params参数就是可变参数,参数个数取决于语句中问号的个数

    eg: qr.update(“insert into user values(null,?,?,?)”,“zs”,“123456”,“张三”);

  3. 执行查询的SQL语句的API

    query(String sql, ResultSetHandler<T> rsh, Object... params),其中ResultSetHandler是一个接口,表示结果集处理者

4.小结

  1. DBUtils: Apache开发的一个数据库工具包, 用来简化JDBC操作数据库的步骤

知识点-JavaBean

1.目标

  • 理解JavaBean的字段和属性

2.讲解

  1. JavaBean说白了就是一个类, 用来封装数据用的

  2. JavaBean要求

    • 私有字段
    • 提供公共的get/set方法
    • 无参构造
    • 建议满参构造
    • 实现Serializable
  3. 字段和属性

    • 字段: 全局/成员变量 eg: private String username
    • 属性: 去掉get或者set首字母变小写 eg: setUsername-去掉set->Username-首字母变小写->username

    一般情况下,我们通过IDEA直接生成的set/get 习惯把字段和属性搞成一样而言

3.小结

  1. JavaBean用来封装数据用的
  2. 字段和属性
    • 字段: 全局/成员变量 eg: private String username
    • 属性: 去掉get或者set首字母变小写 eg: setUsername-去掉set->Username-首字母变小写->username

知识点-使用DBUtils完成增删改

1.目标

  • 掌握使用DBUtils完成增删改

2.步骤

  1. 拷贝jar包
  2. 创建QueryRunner()对象,传入dataSource
  3. 调用update()方法

3.实现

public class UpdateTest {// 插入记录@Testpublic void insert() throws Exception {// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用update方法String sql = "insert into user values(null,?,?,?)";int rows = qr.update(sql, "tianqi", "123456", "田七");System.out.println("受影响的行数:" + rows);}// 删除记录  删除id为4的记录@Testpublic void delete() throws Exception {// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用update方法String sql = "delete from user where id = ?";int rows = qr.update(sql, 4);System.out.println("受影响的行数:" + rows);}// 修改记录  修改id为5的记录@Testpublic void update() throws Exception{// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用update方法String sql = "update user set password = ? where id = ?";int rows = qr.update(sql,"abcd",3);System.out.println("受影响的行数:" + rows);}
}

4.小结

  1. 创建QueryRuner()对象, 传入DataSource
  2. 调用update(String sql, Object…params)

知识点-使用DBUtils完成查询

jar包链接
链接:https://pan.baidu.com/s/1zxw9Gnfz5iLrvqtngOZojQ
提取码:fe0t

1.目标

  • 掌握使用DBUtils完成查询

2.步骤

  1. 拷贝jar包
  2. 创建QueryRunner()对象 传入DataSource
  3. 调用query(sql, resultSetHandler,params)方法

3.实现

3.1ResultSetHandler结果集处理类介绍

public queryRunner(DataSource ds);
query(String sql, ResultSetHandler<T> rsh, Object... params) 执行查询语句参数ResultSetHandler是一个接口,表示结果集处理者(对查询结果的封装):ResultSetHandler接口的实现类:ArrayHandler:适合查询结果是一条记录的,会把这条记录的数据封装到一个Object数组中ArrayListHandler:适合查询结果是多条记录的,会把每条记录的数据封装到一个Object数组中,然后把这些数组添加到List集合中BeanHandler:适合查询结果是一条记录的,会把这条记录的数据封装到一个javaBean对象中BeanListHandler:适合查询结果是多条记录的,会把每条记录的数据封装到一个javaBean对象中,然后把这些javaBean对象添加到List集合中ColumnListHandler:适合查询结果是单列多行的,会把该列的所有数据存储到List集合中KeyedHandle:适合查询结果是多条记录的,会把每条记录的数据封装到一个Map集合中,然后把这些Map集合添加到另一个Map集合中MapHandler:适合查询结果是一条记录的,会把这条记录的数据封装到一个Map集合中MapListHandler:适合查询结果是多条记录的,会把每条记录的数据封装到一个Map集合中,然后把这些Map集合添加到List集合中ScalarHandler:适合查询结果是单个值的,会把这个值封装成一个对象

3.2代码实现

3.2.1 查询一条数据封装到JavaBean对象中(使用BeanHandler)
 @Testpublic void select1() throws Exception{// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用query(sql,resultSetHandler,args)方法String sql = "select * from user where id = ?";User user = qr.query(sql, new BeanHandler<User>(User.class), 3);System.out.println(user);}
3.2.2 查询多条数据封装到List中(使用BeanListHandler)
  @Testpublic void select2() throws Exception{// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用query方法String sql = "select * from user";List<User> list = qr.query(sql, new BeanListHandler<User>(User.class));for (User user : list) {System.out.println(user);}}
3.2.3 查询一条数据,封装到Map对象中(使用MapHandler)
  // 查询一条数据,封装到Map对象中(使用MapHandler)// 列名作为key,列中的值作为value@Testpublic void select3() throws Exception {// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用query方法String sql = "select * from user where id = ?";Map<String, Object> map = qr.query(sql, new MapHandler(), 3);for (String key : map.keySet()) {System.out.println(key + ":" + map.get(key));}}
3.2.4 查询多条数据,封装到List<Map>对象中(使用MapListHandler)
  // 查询多条数据,封装到List<Map>对象中(使用MapListHandler)@Testpublic void select4() throws Exception{// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用query方法String sql = "select * from user";List<Map<String, Object>> list = qr.query(sql, new MapListHandler());for (Map<String, Object> map : list) {// map--->每条记录for (String key : map.keySet()) {System.out.println(key + ":" + map.get(key));}System.out.println("=================================================");}}
3.2.5 查询单个数据(使用ScalarHandler())
    // 查询单个数据(使用ScalarHandler())@Testpublic void select5() throws Exception{// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用query方法String sql = "select count(*) from user";Long count = (Long) qr.query(sql, new ScalarHandler());System.out.println("记录数:"+count);}
3.2.6 查询单列多个值(使用ArrayListHandler)
    // 查询单列多个值(ArrayListHandler)// 单列多值: 每列的数据封装成一个数组,再把所有的数组存储到List集合中// 多条记录: 每条记录的数据封装成一个数组,再把所有的数组存储到List集合中@Testpublic void select6() throws Exception{// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用query方法String sql = "select username from user";List<Object[]> list = qr.query(sql, new ArrayListHandler());for (Object[] arr : list) {System.out.println(Arrays.toString(arr));}}
3.2.7 查询一条记录(使用ArrayHandler)
     // 查询一条记录(ArrayHandler)// 一条记录封装成一个数组,数组中的元素就是每列的值@Testpublic void select7() throws Exception{// 1.创建QueryRunner对象,传入连接池QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());// 2.调用query方法String sql = "select * from user where id = ?";Object[] arr = qr.query(sql, new ArrayHandler(), 3);System.out.println(Arrays.toString(arr));}

4. 小结

  1. 步骤

    • 创建QueryRunner() 对象传入DataSource
    • 调用query(sql,ResultSetHandler, params…)
  2. ResultSetHandler

    • BeanHandler() 查询一条记录封装到JavaBean对象
    • BeanListHandler() 查询多条记录封装到List list
    • ScalarHandler() 封装单个记录的 eg:统计数量
    • ColumnListHandler() 封装单列多行记录
    • MapHandler() 查询一条记录封装到Map对象
    • MapListHandler() 查询多条记录封装到List list
    • ArrayHandler() 查询一条记录封装到数组中

    原理: 使用了反射+内省

  3. 注意实现

    封装到JavaBean条件, 查询出来的数据的列名必须和JavaBean属性一致

第四章-自定义DBUtils

知识点-元数据

1.目标

  • 能够说出什么是数据库元数据

2.分析

​ 我们要自定义DBUtils, 就需要知道列名, 参数个数等, 这些可以通过数据库的元数据库进行获得.元数据在建立框架和架构方面是特别重要的知识,我们可以使用数据库的元数据来创建自定义JDBC工具包, 模仿DBUtils.

3.讲解

3.1什么是元数据

​ 元数据(MetaData),即定义数据的数据。打个比方,就好像我们要想搜索一首歌(歌本身是数据),而我们可以通过歌名,作者,专辑等信息来搜索,那么这些歌名,作者,专辑等等就是这首歌的元数据。因此数据库的元数据就是一些注明数据库信息的数据。

歌曲:凉凉

作词:刘畅

演唱: 杨宗纬 张碧晨

时长: 3分28秒

​ 简单来说: 数据库的元数据就是 数据库、表、列的定义信息。

​ ① 由PreparedStatement对象的getParameterMetaData ()方法获取的是ParameterMetaData对象。

② 由ResultSet对象的getMetaData()方法获取的是ResultSetMetaData对象。

3.2 ParameterMetaData

3.2.1概述

​ ParameterMetaData是由preparedStatement对象通过getParameterMetaData方法获取而来,ParameterMetaData可用于获取有关PreparedStatement对象和其预编译sql语句 中的一些信息. eg:参数个数,获取指定位置占位符的SQL类型

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S9mzFk9S-1633700225396)(img/tu_10.png)]

​ 获得ParameterMetaData:

`ParameterMetaData parameterMetaData =  preparedStatement.getParameterMetaData ()`
3.2.2ParameterMetaData相关的API
  • int getParameterCount(); 获得参数个数
  • int getParameterType(int param) 获取指定参数的SQL类型。 (注:MySQL不支持获取参数类型)
3.2.3实例代码
public class Test1_参数元数据 {public static void main(String[] args) throws Exception{/*ParameterMetaData类:概述: 是一个参数元数据类,可以用来获取参数的元数据使用:1.获取参数元数据类对象PreparedStatement对象调用getParameterMetaData()方法2.获取参数的元数据ParameterMetaData相关的API- int getParameterCount(); 获得参数个数- int getParameterType(int param) 获取指定参数的SQL类型。 (注:MySQL不支持获取参数类型)*/// 1.获取连接Connection connection = C3P0Utils.getConnection();// 2.预编译sql语句String sql = "select * from user where username = ? and password = ?";PreparedStatement ps = connection.prepareStatement(sql);// 3.通过PreparedStatement对象获取参数元数据对象ParameterMetaData pmd = ps.getParameterMetaData();// 参数元数据对象// 4.获取参数个数元数据   参数个数就是参数的元数据int count = pmd.getParameterCount();System.out.println("sql语句的参数个数:"+count);// 2// 5.获取参数类型元数据  参数类型也是参数的元数据//int parameterType = pmd.getParameterType(1);//System.out.println(parameterType);//String typeName = pmd.getParameterTypeName(1);//System.out.println(typeName);}
}

3.3 ResultSetMetaData

3.3.1概述

​ ResultSetMetaData是由ResultSet对象通过getMetaData方法获取而来,ResultSetMetaData可用于获取有关ResultSet对象中列的类型和属性的信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RgZkHYca-1633700225400)(img/tu_9-1573632938379.png)]

​ 获得ResultSetMetaData:

`ResultSetMetaData resultSetMetaData =  resultSet.getMetaData()`
3.3.2resultSetMetaData 相关的API
  • getColumnCount(); 获取结果集中列项目的个数
  • getColumnName(int column); 获得数据指定列的列名
  • getColumnTypeName();获取指定列的SQL类型
  • getColumnClassName();获取指定列SQL类型对应于Java的类型
3.2.3实例代码
     public class Test2_结果集元数据 {public static void main(String[] args) throws Exception {/*ResultSetMetaData类:概述:是一个结果集元数据类,可以用来获取结果集的元数据使用:1.获取结果集元数据类的对象ResultSet的对象调用getMetaData()方法2.获取结果集的元数据ResultSetMetaData 相关的API- getColumnCount(); 获取结果集中列项目的个数- getColumnName(int column); 获得数据指定列的列名- getColumnTypeName();获取指定列的SQL类型- getColumnClassName();获取指定列SQL类型对应于Java的类型*/// 1.获取连接Connection connection = C3P0Utils.getConnection();// 2.预编译sql语句String sql = "select * from user where username = ? and password = ?";PreparedStatement ps = connection.prepareStatement(sql);// 3.设置参数ps.setString(1, "zs");ps.setString(2, "123456");// 4.执行sql语句ResultSet resultSet = ps.executeQuery();// 5.获取结果集的元数据对象ResultSetMetaData rsmd = resultSet.getMetaData();// 1.获取列的数量int columnCount = rsmd.getColumnCount();System.out.println("列的个数:" + columnCount);//4// 2.获取列的名字for (int i = 1; i <= columnCount; i++) {System.out.println(rsmd.getColumnName(i));}System.out.println("==================================");// 3.获取列的MySQL类型for (int i = 1; i <= columnCount; i++) {System.out.println(rsmd.getColumnTypeName(i));}System.out.println("==================================");// 4.获取列在Mysql中的类型对应于java中的类型for (int i = 1; i <= columnCount; i++) {System.out.println(rsmd.getColumnClassName(i));}}
}

4.小结

  1. 元数据: 描述数据的数据. mysql元数据: 用来定义数据库, 表 ,列信息的 eg: 参数的个数, 列的个数, 列的类型…
  2. mysql元数据:
    • ParameterMetaData
    • ResultSetMetaData

案例-自定义DBUtils增删改

1.需求

  • 模仿DBUtils, 完成增删改的功能

2.分析

  1. 创建MyQueryRunner类, 定义dataSource, 提供有参构造方法
  2. 定义int update(String sql, Object…params)方法
//0.非空判断
//1.从dataSource里面获得connection
//2.根据sql语句创建预编译sql语句对象
//3.获得参数元数据对象, 获得参数的个数
//4.遍历, 从params取出值, 依次给参数? 赋值
//5.执行
//6.释放资源

3.实现

public class MyQueryRunner {private DataSource dataSource;public MyQueryRunner(DataSource dataSource) {this.dataSource = dataSource;}public MyQueryRunner() {}/*** 增删改的方法* @param sql* @param args* @return rows*/public int update(String sql,Object... args) throws Exception{// 非空判断// 连接池为空if (dataSource == null){throw new RuntimeException("连接池不能为null");}if (sql == null){throw  new RuntimeException("sql语句不能为空");}// 1.获得连接Connection connection = dataSource.getConnection();// 2.预编译sql语句PreparedStatement ps = connection.prepareStatement(sql);// 3.设置参数// 3.1 获得参数元数据类的对象ParameterMetaData pmd = ps.getParameterMetaData();// 3.2 根据参数元数据类的对象,获取参数个数(元数据)int count = pmd.getParameterCount();// 3.3 给参数赋值for (int i = 0; i < count; i++) {ps.setObject(i+1,args[i]);}// 4.执行sql语句int rows = ps.executeUpdate();// 5.释放资源C3P0Utils.release(null,ps,connection);// 6.返回影响的行数return  rows;}
}

4.小结

  1. 先模拟DBUtils写出架子
  2. update()
    • 封装了PrepareStatement操作
    • 用到了参数元数据

总结

必须练习:1.通过配置文件使用C3P0连接池---必须2.通过配置文件使用DRUID连接池---必须3.编写C3P0工具类   4.编写DRUID工具类5.DBUtils的增删查改----必须\重点掌握--开发常用- 能够理解连接池解决现状问题的原理解决的问题:连接可以得到重复利用    原理:1. 程序一开始就创建一定数量的连接,放在一个容器(集合)中,这个容器称为连接池。2. 使用的时候直接从连接池中取一个已经创建好的连接对象, 使用完成之后 归还到池子3. 如果池子里面的连接使用完了, 还有程序需要使用连接, 先等待一段时间(eg: 3s), 如果在这段时间之内有连接归还, 就拿去使用; 如果还没有连接归还, 新创建一个, 但是新创建的这一个不会归还了(销毁)- 能够使用C3P0连接池1.导入jar包---c3p\驱动包2.创建连接池 ---->配置文件(配置文件一定要放在src目录下,并且名字必须为c3p0-config.xml)3.根据连接池获得连接4.创建预编译sql语句对象5.设置参数6.执行sql语句,处理结果7.释放资源- 能够使用DRUID连接池0.导入jar包---druid\驱动包1.创建Properties对象,加载配置文件中的数据---->配置文件(配置文件一定要放在src目录下)2.创建连接池 3.根据连接池获得连接4.创建预编译sql语句对象5.设置参数6.执行sql语句,处理结果7.释放资源
- 能够编写C3P0连接池工具类 1.创建静态的连接池常量2.提供一个用来获取连接池的静态方法3.提供一个用来获取连接的静态方法4.提供一个用来获取释放资源的静态方法- 能够使用DBUtils完成CRUD创建QueryRunner对象:public QueryRunner(DataSource datasource);增删改: int update(String sql,Object... args);查询: 返回值 query(String sql,ResultSetHandler rsh,Object... args)ResultSetHandler接口的实现类:BeanHandler:适合查询结果是一条记录的,会把这条记录的数据封装到一个javaBean对象中BeanListHandler:适合查询结果是多条记录的,会把每条记录的数据封装到一个javaBean对象中,然后把这些javaBean对象添加到List集合中ColumnListHandler:适合查询结果是单列多行的,会把该列的所有数据存储到List集合中ScalarHandler:适合查询结果是单个值的,会把这个值封装成一个对象- 能够理解元数据定义数据的数据ParameterMetaData类:概述:表示参数元数据对象,可以用来获取sql语句参数的元数据获取参数元数据对象: 使用预编译sql语句对象调用方法获得public ParameterMetaData  getParameterMetaData ();根据参数元数据对象获取参数的元数据:使用ParameterMetaData类的方法- int getParameterCount(); 获得参数个数        ResultSetMetaData类:概述:表示结果集的元数据对象,用来获取结果集的元数据使用:获取结果集元数据对象: 使用ResultSet结果集的方法public ResultSetMetaData getMetaData();根据结果集元数据对象获取结果集的元数据:使用ResultSetMetaData的方法- getColumnCount(); 获取结果集中列项目的个数- getColumnName(int column); 获得数据指定列的列名- getColumnTypeName();获取指定列的SQL类型- getColumnClassName();获取指定列SQL类型对应于Java的类型
- 能够自定义DBUtils封装jdbc操作--见案例

连接池的原理以及分析相关推荐

  1. python连接池原理_python redis之连接池的原理

    python redis之连接池的原理 什么是连接池 通常情况下, 当我们需要做redis操作时, 会创建一个连接, 并基于这个连接进行redis操作, 操作完成后, 释放连接, 一般情况下, 这是没 ...

  2. jdbc连接池工作原理_JDBC连接实际上如何工作?

    jdbc连接池工作原理 The JDBC Connection interface is a part of java.sql package. The java.sql.Connection int ...

  3. mysql连接池的工作原理_连接池工作原理

    连接池工作原理 连接池技术的核心思想是连接复用,通过建立一个数据库连接池以及一套连接使用.分配和管理策略,使得该连接池中的连接可以得到高效.安全的复用,避免了数据库连接频繁建立.关闭的开销. 连接池的 ...

  4. DBCP数据源连接池实现原理分析

    前些天在调试公司系统的时候发现这样的一个问题:mysql数据库服务停止一段时间后再次重启后吗,tomcat服务无法请求数据库服务,调试了半天对这个问题进行定位解决,期间也搞了很多有关mysql数据库的 ...

  5. Mybatis连接池介绍与分类 Mybatis使用POOLED UNPOOLED配置连接池的原理分析

    一.连接池 1.概念:其实就是一个容器(集合),存放数据库连接的容器. 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象 ...

  6. Spring Boot下Druid连接池的使用配置分析

    引言: 在Spring Boot下默认提供了若干种可用的连接池,Druid来自于阿里系的一个开源连接池,在连接池之外,还提供了非常优秀的监控功能,这里讲解如何与Spring Boot实现集成. 1.  ...

  7. 线程池的原理和连接池的原理

    线程池的原理: 来看一下线程池究竟是怎么一回事?其实线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:先启动若干数量的线程,并让这些线程都处于睡 眠状态,当客户端有一个新请求时,就会唤 ...

  8. mysql odbc连接池_Java Mysql连接池配置和案例分析--超时异常和处理

    前言: 最近在开发服务的时候, 发现服务只要一段时间不用, 下次首次访问总是失败. 该问题影响虽不大, 但终究影响用户体验. 观察日志后发现, mysql连接因长时间空闲而被关闭, 使用时没有死链检测 ...

  9. mysql连接池的原理_数据库连接池的实现及原理

    对于一个简单的数据库应用,由于对于数据库的访问不是很频繁.这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销.但是对于一个复杂的数据库应用,情况 ...

最新文章

  1. 逻辑覆盖测试(四)判定/条件覆盖
  2. 转:MySQL 开发实践 8 问,你能 hold 住几个?
  3. BZOJ3992:[SDOI2015]序列统计——题解
  4. 2017年安防监控中的那些核心云计算技术
  5. 在iOS 中使用sleep方法 让程序暂停几秒钟
  6. mysql timeout知多少
  7. PHP闭包(Closure)初探
  8. 精简系统绝对不可删除的应用程序
  9. 算法训练营 重编码_编码训练营之后该做什么-以及如何获得成功
  10. [导入]使用SqlCommand对象执行存储过程
  11. Python 创建和使用类
  12. 使用系统调用pipe建立一条管道线_使用Unixbench对服务器综合性能打分及测试结果...
  13. 2场直播丨Oracle数据库SQL执行计划的取得和解析、一次特殊的 Oralce 硬解析性能问题的技术分享...
  14. mysql索引之联合索引
  15. cad计算机不显示内存不足怎么办,为什么CAD打开显示内存不足怎么办
  16. 真爱一个人,何妨赌一生
  17. 广东省工科赛 智能终端配送机器人:创作心路历程,踩过的坑和解决方案
  18. 九宫怎么排列和使用_风水知识:三元九运与九宫飞星排布方法!
  19. 小程序下载图片、文件、wx.downloadFile,废话少说直接上代码
  20. 《熟练掌握OpenCV----实用计算机视觉工程案例》第5章 车牌号码识别

热门文章

  1. JZOJ ???? dexterity
  2. eclipse打不开的常见错误
  3. 调用麦克风列阵进行录音
  4. ajax请求怎么实现跨域
  5. android_app开发微信支付集成
  6. 《电路学习第三天》 之 线性稳压电源的设计
  7. Makefile文件是什么
  8. 基于51单片机智能温度控制器温控系统(毕设课设)
  9. java xmap_转:使用XMAP完成JavaBean和XML之间转换
  10. 活着——活着就是对生命最好的尊重