D19-JDBC连接池&动态代理

1.PreparedStatement预编译对象

1.1 SQL注入

登录案例: 可以在不输入密码的情况下登录成功!

String sql = "select * from user where username = '" 
	+ username + "' and password = '" + password + "' ";

自控制台输入:

欢迎来到……
请输出用户名:
admin '#
请输入用户密码:
[空着]
select * from user where username = 'admin '#' and password = '' 
登陆成功!

原因: 将用户输入的实际参数与sql字符串进行拼接,发送给数据库(编译,执行),改变了数据库原有的含义, 这个现象叫做SQL注入。

1.2解决方案

不用实际数据直接与数据库进行拼接,用占位符(?)解决

String sql="select * from user where username = ? and password = ?;";
//创建预编译语句
public PreparedStatement prepareStatement(sql);
//设置实际参数
public void setXXX(int 问号位置,Object 实际参数)//执行方法
public boolean exctue();  //dml返回false,dql返回ture
//执行dml
public int executeUpdate(); //影响行数
//执行dql
public ResultSet executeQuery();//结果集

1.3修复用户登录

代码:

public static void main(String[] args) throws SQLException {
        //1. 在控制台输入用户名和密码
        Scanner scanner = new Scanner(System.in);
        //提示
        System.out.println("欢迎来到……");
        System.out.println("请输出用户名:");
        String username = scanner.nextLine();
        System.out.println("请输入用户密码:");
        String password = scanner.nextLine();

        //2.根据输入的用户名和密码查询数据库 jdbc
        //2.1 获取连接
        Connection connection = JDBCTools01.getConnection();
        //2.2 编写sql代码
        //String sql = "select * from user where username = '" + username + "' and password = '" + password + "' ";
        String sql = "select * from `user` where username = ?  and password=?";
        System.out.println(sql);
        //创建预编译语句执行者
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //设置值
        preparedStatement.setString(1, username);
        preparedStatement.setString(2, password);
        // System.out.println(sql);
        //2.3 创建执行者
        // statement = connection.createStatement();
        //2.4 发送sql语句并接收返回结果
        ResultSet resultSet = preparedStatement.executeQuery();
        //2.5处理事件
        if (resultSet.next()) {
            System.out.println("登陆成功!");
        } else {
            System.out.println("登录失败!");
        }

        //2.6关闭资源
        JDBCTools01.closeResorce(resultSet, preparedStatement, connection);
    }

1.4 优点

  • 防止sql注入,提高了安全性
  • 减少编译次数,提高效率
  • 参数与sql分离,提高了程序的可读性。

1.5 实现CURD

步骤分析:

  1. 连接数据库
  2. 编写sql语句(使用占位符)
  3. 创建预编译语句(PreparedStatement),发送sql语句
  4. 设置实际参数
  5. 执行sql语句并返回结果
  6. 处理结果
  7. 关闭资源
    代码(一部分)
//新增(主要修改部分)
//2.编写sql语句
        String sql = "insert into user(username,password) values(?,?)";
        // 3.创建预编译语句执行执行者,发送给数据库编译sql语句
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //4.设置实际参数
        preparedStatement.setString(1, "keke");
        preparedStatement.setString(2, "123");


查询
    @Test
    public void test04() throws Exception{
        Connection connection = JDBCTools01.getConnection();
        String sql = "select * from user where id=?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,"1");
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            System.out.println(id+" "+username+" "+ password);
        }
        JDBCTools01.closeResorce(resultSet,preparedStatement,connection);
    }

PS: 以上代码都需要一个 “JDBCTools01”的工具类D18有

2. 连接池

2.1 概述

2.1.1 每次创建数据库连接问题

  • 每次获取数据库连接需要消耗资源和时间(创建连接,运行sql语句,关闭连接),为了减轻服务器的压力,提高效率,我们使用连接池 保证连接的复用性。

2.1.2连接池能够解决的问题的原理

  • 程序从一开始就创建一定数量的连接,放在一个容器中,叫连接池
  • 使用的时候从连接池 取出一个对象。
  • 关闭资源的时候,并不是真正的关闭,而是将对象放回连接池中,以便下次使用

2.2 API

公共的接口: javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这
样应用程序可以方便的切换不同厂商的连接池。
接口中有一个方法: Connection getConnection(): 从连接池获取一个连接
归还连接的时候直接调用连接的
close()
方法即可.

2.3 c3p0

  • 是java的开源连接池,目前【hibernate(JDBC封装的框架)、spring】默认推荐使用的连接池

2.3.1 常用API

public ComboPooledDataSource( ) :无参构造使用默认配置(使用xml中default‐config标签中对应的参数) 创建连接池
public ComboPooledDataSource():有参构造使用命名配置
public Connection getConnection() throws SQLException
从连接池中取出一个连接

2.3.2常用配置参数解释

  1. 初始连接数initialPoolSize()
  2. 最大连接数maxPoolSize()
  3. 最大等待时间checkoutTimeout()
  4. 最大空闲回收时间maxIdleTime()

在这里插入图片描述
a) 硬编码(了解)

 @Test
    public void test01() throws Exception {
        //创建连接池
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        //设置参数
        //2.1数据库的基本四项
        comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/student2");
        comboPooledDataSource.setUser("root");
        comboPooledDataSource.setPassword("root");
        //2.2设置参数
        comboPooledDataSource.setInitialPoolSize(5);//初始连接数5
        comboPooledDataSource.setMaxPoolSize(20);//最大连接数20
        comboPooledDataSource.setAcquireIncrement(5);//一次创建五个连接
        comboPooledDataSource.setMaxIdleTime(30000);//最大空闲销毁时间,单位毫秒
        comboPooledDataSource.setCheckoutTimeout(3000);//连接等待时间

        //获取连接
        Connection connection = comboPooledDataSource.getConnection();
        System.out.println(connection);
        //归还资源
        connection.close();
        //之前咱们使用close方法销毁连接,而现在变成了归还连接池,其实是连接池厂商对此方法进行了增强(动态代理)
    }

配置文件

步骤:

  1. 导入jar包:c3p0-0.9.5.2.jar 和 mchange-commons-java-0.2.12.jar
  2. 定义配置文件

要求:
文件名必须是 c3p0-config.xml
位置必须是在src根目录下

  1. 编写代码

创建连接池
获取连接
归还连接池


 @Test
    public void test01() throws Exception{
        //创建连接池对象
        //默认去src根目录下去加载c3p0-config.xml中的<named-config name="test">标签信息
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        //获取连接
        Connection connection = comboPooledDataSource.getConnection();
        //归还连接池
        connection.close();
    }

2.4 druid

(德鲁伊)是阿里巴巴开源的连接池,号称目前最好的连接池没有之一,并支持日志监控功能。

2.4.1 配置文件

步骤:
1.导入 jar包

druid-1.0.9.jar

  1. 定义配置文件

建议:
文件名为: druid.properties
位置在src根目录下

  1. 编写代码
  1. 创建连接池对象,提供一个工具类
  2. 获取连接
  3. 归还连接池

代码片段:

 @Test
    public void test01() throws Exception{
        //获取druid.properties
        InputStream is = Demo02.class.getClassLoader().getResourceAsStream("druid.properties");
        //创建配置文件对象
        Properties properties = new Properties();
        properties.load(is);
        //创建连接池对象,提供一个工具类
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

        //2.获取连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        //支持日志的监控功能,初体验dataSource toString
        System.out.println(dataSource);
        //归还连接池
        connection.close();
        System.out.println(dataSource);
    }

2.5 连接池工具类(web阶段都在使用)

步骤分析:

  1. 提供获取连接池的方法
  2. 提供连接的方法
  3. 提供关闭资源的方法(归还)

代码片段:

package com.itheima05_pooltools;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class Demo01DateSourceUtils {
    private static DataSource dataSource;
    //保证连接池初始化一次
    static {
        try {
            //获取配置文件流
            InputStream is = Demo01DateSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");

            //创建配置文件对象
            Properties properties = new Properties();
            properties.load(is);
            //使用工厂创建连接池对象
            DataSource dataSource1 = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //提供获取连接池的方法
    public static DataSource getDataSource(){
        return  dataSource;
    }
    //提供获取连接的方法
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    //归还连接池
    public static void  closeResource(ResultSet resultSet, Statement statement,Connection connection){
        closeResultSet(resultSet);
        closeStatement(statement);
        closeConnection(connection);
    }
    public static void  closeResource( Statement statement,Connection connection){
        closeStatement(statement);
        closeConnection(connection);
    }

    private static void closeConnection(Connection connection) {
        if (connection!=null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static void closeStatement(Statement statement) {
        if (statement!=null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    private static void closeResultSet(ResultSet resultSet) {
        if (resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

3 动态代理

jdbc中调用connection.close()是关闭连接,但上面c3p0、druid最后调用close()方法时是归还连接池(动态代理)。

3.1 动态代理

  • 在程序运行期间(反射),有jvm虚拟机来创建代理对象,针对目标方法进行增强

JDK动态代理:

  • 目标类是接口的实现,JDK根据接口规范来创建代理对象。
    步骤分析:
  1. 创建接口,
  2. 并创建实现接口的对象
  3. 使用JDK技术实现代理:Proxy工具类

代码:

package com.itheima06_proxy;

import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Demo01 {
    @Test
   public void test01() throws Exception{
    //1.创建目标对象
        JiJi jiJi = new JiJi();
        //使用proxy工具类创建代理对象
        /*
        参数一:目标对象的类加载器
        参数二:目标对象的接口数组
        参数三:调用处理器接口,--实现增强逻辑
         */
       Sing  songze = (Sing) Proxy.newProxyInstance(JiJi.class.getClassLoader(), new Class[]{Sing.class}, new InvocationHandler() {
           /*
           实现增强逻辑的invoke
           参数一:proxy代理对象的本身
           参数二:methed当前调用的方法
           参数三:args当前方法传递的参数列表
            */
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               //真正干活的是目标对象
               Object obj = method.invoke(jiJi, args);
               return obj;
           }
       });
       //调用大礼对象方法实现增强
        songze.sing("单身情歌");
        songze.eat();
    }
    
    //版本2
    @Test
    public void test02() throws Exception{
        //创建目标对象
        JiJi jiJi=new JiJi();
        //使用proxy代理对象
        Sing songze=(Sing) Proxy.newProxyInstance(JiJi.class.getClassLoader(),JiJi.class.getInterfaces(),((proxy, method, args) ->{
            if ("sing".equals(method.getName())){
                System.out.println("收费998");
            }
            return method.invoke(jiJi,args);
        } ));
        songze.eat();
    }
}
  • 创建Sing接口,有eat()和sing(String name)两个抽象方法
  • 创建JiJi类,实现Sing接口,并重写里面的抽象方法。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值