JDBC-02

1. 连接池

1.1 连接池概述

1.1.1 什么是连接池

连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。

1.1.2 为什么要学习连接池

连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。

从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)

1.2 自定义连接池

1.2.1 自定义连接池的步骤

① 编写一个类实现DataSource接口
② 重写getConnection方法
③ 初始化多个连接在内存中
④ 编写归还连接的方法

1.2.2 自定义连接池的代码实现

public class MyDataSource implements DataSource {
    //将一些连接存入到内存中,可以定义一个集合,用于存储连接对象。
    private List<Connection> connList = new ArrayList<Connection>();

    //在初始化的时候提供一些连接
    public MyDataSource(){
        //初始化连接:
        for(int i=1;i<=3;i++){
            //向集合中存入连接
            connList.add(JDBCUtils.getConnection());
        }
    }
    //从连接池中获得连接的方法
    @Override
    public Connection getConnection() throws SQLException {
        Connection conn = connList.remove(0); //为了防止同一连接被多次获取,所以使用remove方法将其暂时移出连接集合
        return conn;
    }
    //编写一个归还连接的方法:
    public void addBack(Connection conn){
        connList.add(conn);
    }

1.2.3 从连接池中获取连接代码实现

    public static void dataSource(){
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        MyDataSource dataSource = null;
        try{
            //从连接池中获得连接:
            dataSource = new MyDataSource();
            conn = dataSource.getConnection();
            //编写SQL:
            String sql = "select * from emp";
            //预编译SQL:
            pstmt = conn.prepareStatement(sql);
            //执行sql:
            rs = pstmt.executeQuery();
            while (rs.next()){
                System.out.println("name:" + rs.getString("name")+" salary:" +rs.getInt("salary"));
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
//            JDBCUtils.release(pstmt,conn,rs);
            //此时就不能简单的通过close简单的销毁连接了
            dataSource.addBack(conn);
            if(rs!= null){
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(pstmt!=null){
                try {
                    pstmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }

1.3 自定义连接池的问题

1.3.1 使用接口的实现类完成的构造

MyDataSource dataSource = new MyDataSource();
这种方法不方便程序的扩展。

1.3.2 额外提供了方法归还连接

dataSource.addBack(conn);
这种方法增加使用连接池的用户的难度。

1.3.3 自定义连接池的问题解决

如果不提供自定义的方法就可以解决这个问题,但是连接要如何归还到连接池呢?

原来在Connection中是有一个close方法的,close方法完成了连接的销毁。能不能做一个事情,将原有的连接的close方法改为归还。

  • 现在要做的事情就是将原有的close方法的逻辑改为归还。(增强一个类中的方法)。
  • 如何增强一个类中的方法
    ① 采用继承的方式。(这种方式是最简单的,但是是由使用条件的:必须能够控制这个类的构造!)
    ② 采用装饰者模式。(要求增强的类和被增强的类实现相同的接口,在增强的类中获得被增强的类的引用)
    ③ 动态代理的方式。

装饰者模式:

interface Waiter{
	public void server();
}

public class Waitress implements Waiter{
	public void server(){
		System.out.println("服务中");
	}
}

public class WaitressWrapper implements Waiter{
	private Waiter waiter;
	public WaitressWrapper(Waiter waiter){
		this.waiter = waiter;
	}	
	public void server(){
		System.out.println("微笑");
		waiter.server();
	}
}

1.3.4 解决代码实现

使用装饰者模式增强Connection中的close方法

思路及具体操作流程:
由于要增强close方法,所以创建一个MyConnectionWrapper类来实现Connection接口,但是实现Connection接口需要重写Connection中所有方法(三四十个),这任务量太大了,所以我们再创建一个ConnectionWrapper来作为模板类实现Connection方法,这个ConnectionWrapper内重写了所有Connection中的所有方法,然后再用MyConnectionWrapper继承ConnectionWrapper即可。

PS:这里👴没有搞到那个模板类

继承ConnectionWrapper之后,就可以再MyConnectionWrapper中单独对close方法进行重写:

public class MyConnectionWrapper extends ConnectionWrapper {
    private  Connection conn;
    private List<Connection> connList;
    public MyConnectionWrapper(Connection conn,List<Connection> connList) {
        super();
        this.conn = conn;
        this.connList = connList;
    }

    @Override
    public void close() throws SQLException {
        connList.add(conn);
    }
}

之后再去修改 连接池类(MyDataSource)中的获得连接方法即可:

    @Override
    public Connection getConnection() throws SQLException {
        Connection conn = connList.remove(0); //为了防止同一连接被多次获取,所以使用remove方法将其暂时移出连接集合
        MyConnectionWrapper connWrapper = new MyConnectionWrapper(conn,connList);
        return connWrapper;
    }

2.开源连接池的使用

2.1 Druid

2.1.1 Druid的概述

Druid为阿里旗下开源连接池产品,使用非常简单,可以与Spring框架进行快速整合。

2.1.2 手动配置完成连接池的使用

    public static void demo01(){
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try{
            //使用连接池获得连接:
//            conn = JDBCUtils.getConnection();
            DruidDataSource dataSource = new DruidDataSource();
            //手动设置数据库连接的参数:
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql:///emp");
            dataSource.setUsername("root");
            dataSource.setPassword("266531");
            conn = dataSource.getConnection();

            String sql = "select * from emp";
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            while (rs.next()){
                System.out.println("name:" + rs.getString("name")+" salary:" +rs.getInt("salary"));
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCUtils.release(pstmt,conn,rs);
        }
    }

2.1.3 使用配置方式完成连接池的使用

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///emp
username=root
password=266531

其中配置文件里不仅可以写路径设置,用户名密码,还可以有
初始化连接:
initialSize=10

最大连接数量:
maxActive=50

最大空闲连接
maxIdle=20

最小空闲连接
minIdle=5

超时等待时间以毫秒为单位
maxWait=60000
    public static void demo02(){
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        DataSource dataSource = null;
        try{
            Properties prop = new Properties();
            prop.load(new FileInputStream("jdbc03_druid/src/druid.properties"));
            dataSource = DruidDataSourceFactory.createDataSource(prop);
            conn = dataSource.getConnection();
            String sql = "select * from emp";
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            while (rs.next()){
                System.out.println("name:" + rs.getString("name")+" salary:" +rs.getInt("salary"));
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCUtils.release(pstmt,conn,rs);
        }
    }

2.2 C3P0

2.2.1 C3P0的概述

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。

2.2.2 手动配置完成连接池的使用

注意,使用c3p0需要导入两个包,不然会报错。
在这里插入图片描述

    public static void main(String[] args) {
        //手动设置参数方式:
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            //从连接池中获得连接
            ComboPooledDataSource ds = new ComboPooledDataSource();
            //设置连接参数:
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql:///emp");
            ds.setUser("root");
            ds.setPassword("266531");
            conn = ds.getConnection();

            String sql = "select * from emp";
            pstmt = conn.prepareStatement(sql);

            rs = pstmt.executeQuery();

            while (rs.next()) {
                System.out.println("name:" + rs.getString("name") + " salary:" + rs.getInt("salary"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(pstmt, conn, rs);
        }
    }

2.2.3 使用配置方式完成连接池的使用

c3p0的连接池配置文件可以使用properties,但最好还是使用xml配置文件
配置文件一定要命名为c3p0-config

<c3p0-config>
  <default-config>     //这个是默认配置
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql:///emp</property>
    <property name="user">root</property>
    <property name="password">266531</property>
    
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">10</property>
    <property name="checkoutTimeout">3000</property>
  </default-config>

  <named-config name="otherc3p0">  //这个是其他配置,具体怎么选择在于创建ComboPooledDataSource对象时传递的参数是什么
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql:///emp</property>
    <property name="user">root</property>
    <property name="password">266531</property>

    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">10</property>
    <property name="checkoutTimeout">3000</property>
  </named-config>
</c3p0-config>
    public static void demo04(){
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            //从连接池中获得连接
            ComboPooledDataSource ds = new ComboPooledDataSource();  
            //在括号中传递对应参数可以选择xml中的不同配置,比如传递otherc3p0,就会使用下面的那个配置
            //不给参数或者给错参数则会使用默认配置
            conn = ds.getConnection();
            String sql = "select * from emp";
            pstmt = conn.prepareStatement(sql);

            rs = pstmt.executeQuery();

            while (rs.next()) {
                System.out.println("name:" + rs.getString("name") + " salary:" + rs.getInt("salary"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(pstmt, conn, rs);
        }
    }

2.3 C3p0

在 以上虽然成功使用了第三方数据库,但是每使用一次就要创建一个 ComboPooledDataSource 容器对象,这对资源无疑是巨大浪费,所以我们要优化工具类

    //创建一个连接池:但是这个连接池只创建一次即可。
    private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();

    public static Connection getConnection() throws Exception{
        return dataSource.getConnection();
    }

    //获得连接池:
    public static DataSource getDataSource(){
        return dataSource;
    }

释放资源方法的改进后面再进行。

3.DBUtils

3.1 DBUtils的概述

3.1.1 什么是DBUtils

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

是对JDBC的简单封装,而且没有影响性能。

3.1.2 为什么要学习DBUtils

因为JDBC手写比较麻烦,而且有很多代码是类似的。比如获得连接,预编译SQL,释放资源等。那么可以把这些代码抽出来放到工具类中。将类似的代码进行抽取。大大简化JDBC的编程。

3.2 DBUtils的API

3.2.1 DBUtils的API概述

3.2.1.1 QueryRunner对象

是DBUtils的一个核心运行类

构造方法
在这里插入图片描述
在这里插入图片描述

方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在一般情况下,如果执行CRUD的操作:

构造:
QueryRunner(DataSource ds);
方法:
int update(String sql,Object... args);
T query(String sql,ResultSetHandler rsh,Object.. args);

如果有事务管理的话使用另一套完成CRUD的操作

构造:
QueryRunner();
方法:
int update(Connection conn,String sql,Object... args);
T query(Connection conn,String sql,ResultSetHandler rsh,Object... args);

在这里插入图片描述

3.2.1.2 DbUtils

在这里插入图片描述
在这里插入图片描述
Quietly意思是把异常也处理了。

3.3 DBUtils的使用

3.3.1 DBUtils的增删改操作

① DBUtils的添加操作

    public static void demo01() throws Exception {
        //创建核心类:QueryRunner:    
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        qr.update("insert into remainder values(?,?)", "Spike", 5000000);
    }

② DBUtils的修改操作

    public static void demo02() throws Exception{
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        qr.update("update remainder set money=? where name=?",5000,"Spike");
    }

③ DBUtils的删除操作

    public static void demo03() throws Exception{
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        qr.update("delete from remainder where name = ?","Spike");
    }

3.3.2 DBUtils的查询操作

查询代码实现

    //查询一条记录
    public static void demo04() throws Exception{
        //创建核心类:
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        //执行查询
        Account emp = qr.query("select * from remainder where name=?", new ResultSetHandler<Account>() {

            @Override
            public Account handle(ResultSet rs) throws SQLException {
                Account account = new Account();
                while (rs.next()) {
                    account.setName(rs.getString("name"));
                    account.setMoney(rs.getDouble("money"));
                }
                return account;
            }
        }, "Rarity");
        System.out.println(emp);
    }
    //查询多条记录
    public static void demo05() throws Exception{
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        List<Account> accounts = qr.query("select * from remainder", new ResultSetHandler<List<Account>>() {
            @Override
            public List<Account> handle(ResultSet rs) throws SQLException {
                List<Account> accountList = new ArrayList<Account>();
                Account account = new Account();
                while (rs.next()) {
                    account.setName(rs.getString("name"));
                    account.setMoney(rs.getDouble("money"));
                    accountList.add(account);
                }
                return accountList;
            }
        });
        System.out.println(accounts);
    }

3.3.3 DBUtils的使用之ResultSetHandler的实现类

3.3.3.1 ArrayHandler

一条记录封装到一个数组当中。这个数组应该是Object[]。

    public static void demo01() throws Exception{
        //ArrayHandler:将一条记录封装到一个Object数组中
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        Object[] objects = qr.query("select * from remainder where name=?", new ArrayHandler(), "Rarity");
        System.out.println(Arrays.toString(objects));
    }
3.3.3.2 ArrayListHandler

多条记录封装到一个装有Object[]的List集合中。

    public static void demo02() throws Exception{
        //ArrayListHandler:将多条记录封装到一个装有Object数组的List集合中
        //一条记录封装到Object[]数组中,多条记录就是多个Object[],那么多个Object数组就将其装入List集合中即可。
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        List<Object[]> remainders = qr.query("select * from remainder", new ArrayListHandler());
        for(Object[] objects:remainders){
            System.out.println(Arrays.toString(objects));
        }
    }
3.3.3.3 BeanHandler

将一条记录封装到一个JavaBean中。

    public static void demo01() throws Exception{
        //BeanHandler:将一条记录封装到一个JavaBean中
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        Account account = qr.query("select * from remainder where name = ?", new BeanHandler<Account>(Account.class), "Rarity");
        System.out.println(account);
    }
3.3.3.4 BeanListHandler

将多条记录封装到一个装有JavaBean的List集合中。

    public static void demo02() throws Exception {
        //BeanListHandler:将多条记录封装到一个装有JavaBean的List集合中
        //一条记录就是一个JavaBean的对象,将多个JavaBean对象装到一个List集合中
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        List<Account> accountList = qr.query("select * from remainder", new BeanListHandler<Account>(Account.class));
        for(Account account : accountList){
            System.out.println(account);
        }
    }
3.3.3.5 MapHandler

将一条记录封装到一个Map集合中,Map的Key是列名,value是表中列的记录值。

    public static void demo01() throws Exception {
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        Map<String, Object> objectMap = qr.query("select * from remainder where name = ?", new MapHandler(), "Rarity");
        System.out.println(objectMap);
    }
3.3.3.6 MapListHandler

将多条记录封装到一个装有Map的List集合中。

    public static void demo02() throws Exception{
        QueryRunner qr = new QueryRunner(JDBCUtils2.getDataSource());
        //为什么是Map<String,Object> 因为列名一定是String,但是value则有int,有String,有date,所以使用object
        List<Map<String, Object>> mapList = qr.query("select * from remainder", new MapListHandler());
        for(Map<String,Object> m: mapList){
            System.out.println(m);
        }
    }
3.3.3.7 ColumnListHandler

将数据中的某列的值封装到List集合中

3.3.3.8 ScalarHandler

将单个值封装。

3.3.3.9 KeyedHandler

将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值