1 PreparedStatement
1.1 登录
问题分析
SELECT * FROM USER WHERE username = 'tom ' #' and password = '123456'
使用了参数与sql字符串拼接,传递给数据库后编译解析执行sql,改变了sql原有的意义,这个问题我们称为sql注入

1.2 解决方案
使用 PreparedStatement

使用参数传递与sql与解析进行分离,先编译解析sql后参数传递

select * from user where username = ? and password =?
通过 Connection获取 API

public PreparedStatement prepareStatement(String sql);
设置参数

public void setXxx(int 第几个问号,Object 实参);
int
double
String
Object
例如:
setString(1,"tom");
setString(2,"123")
执行sql

// 执行sql
public boolean execute();
// 执行dml
public int executeUpdate();
// 执行dql
public ResultSet executeQuery();
1.3 修改登录案例
代码片段

public static void main(String[] args) throws Exception {

// 在控制台输入用户名和密码 scanner
// 1.1 创建scanner对象
Scanner scanner = new Scanner(System.in);
// 1.2 控制台录入
System.out.println("欢迎来到德莱联盟...");
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
// 根据用户名和密码查询数据库 jdbc
// 2.1 获取连接
Connection connection = JDBCUtilsPlus.getConnection();
// 2.2 编写sql
//String sql ="select * from user where username = 'admin' and password = '123'";
//String sql = "select * from user where username = '" + username + "' and password = '" + password + "'";
String sql = "SELECT * from USER WHERE  username= ? and password = ? ";
System.out.println(sql);
// 2.3 创建语句执行者
//Statement statement = connection.createStatement();
// 创建预编译语句执行者
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置参数
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
// 2.4 执行sql返回结果
ResultSet resultSet = preparedStatement.executeQuery();
// 根据返回结果判断用户是否登录成功 if else
// 2.5 处理结果
if (resultSet.next()) {
    String u = resultSet.getString("username");
    System.out.println("登录成功,欢迎:" + u);
} else {
    System.out.println("用户名密码错误....");
}
// 2.7 释放资源
JDBCUtilsPlus.closeResource(connection, preparedStatement, resultSet);

}
1.4 PreparedStatement实现增删改查操作
步骤分析

// 获取连接

// 编写sql(占位符 ?)

// 创建预编译语句执行者(sql)

// 设置参数

// 执行sql并返回结果

// 处理结果

// 释放资源

新增

// 新增
@Test
public void test01() throws Exception {

// 获取连接
Connection connection = JDBCUtilsPlus.getConnection();
// 编写sql(占位符 ?)
String sql = "insert into user(username,password) values(?,?);";
// 创建预编译语句执行者(sql)
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置参数
preparedStatement.setString(1, "小泽老师");
preparedStatement.setString(2, "123");
// 执行sql并返回结果
int i = preparedStatement.executeUpdate();
// 处理结果
if (i>0) {
    System.out.println("添加成功");
}else{
    System.out.println("添加失败");
}
// 释放资源
JDBCUtilsPlus.closeResource(connection, preparedStatement);

}
修改

// 修改
@Test
public void test02() throws Exception {

// 获取连接
Connection connection = JDBCUtilsPlus.getConnection();
// 编写sql(占位符 ?)
String sql = "update user set password = ? where id = ?";
// 创建预编译语句执行者(sql)
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置参数
preparedStatement.setString(1, "321");
preparedStatement.setInt(2, 5);
// 执行sql并返回结果
int i = preparedStatement.executeUpdate();
// 处理结果
if (i>0) {
    System.out.println("修改成功");
}else{
    System.out.println("修改失败");
}
// 释放资源
JDBCUtilsPlus.closeResource(connection, preparedStatement);

}
删除

// 删除 如果我想连续删除2个人呢?
@Test
public void test() throws Exception {

// 获取连接
Connection connection = JDBCUtilsPlus.getConnection();
// 编写sql
String sql = "delete from user where id = ?";
// 创建预编译语句执行者
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置参数
preparedStatement.setInt(1, 5);
// 执行sql并返回结果
int i = preparedStatement.executeUpdate();
System.out.println(i);
// 再次设置参数
preparedStatement.setInt(1, 6);
// 再次执行sql 并返回结果
int i1 = preparedStatement.executeUpdate();
System.out.println(i1);
// 处理结果

// 释放资源
JDBCUtilsPlus.closeResource(connection, preparedStatement);

}
1.5 PreparedStatement特性

防止sql注入,提高安全

减少编译次数(编译1次),提高性能

参数与sql赋值分离,提高代码可读型

2 连接池
2.1 什么是连接池
大白话存放连接的池子

这二天使用JDBC操作数据库创建连接,使用完毕后销毁连接,这些操作非常浪费时间和消耗资源,我们使用连接池进行优化,使用连接时从池中获得,使用完毕后归还到池中即可,提高连接的复用性

2.2 自定义连接池
DataSource规范

java对数据库连接池的一套规范(接口),实现类由连接池厂商提供,方便后期切换连接池

步骤分析

提供获取连接的方法

提供归还连接池的方法

2.3 C3P0
开源连接池,目前hibernate等框架推荐使用

配置文件
步骤分析

1 导入jar包

2 定义一个配置文件

要求:名称必须为:c3p0-config.xml 位置必须在:src目录下

3 编写代码

// 创建连接池

// 获取连接

// 归还连接

代码片段

// 配置文件
@Test
public void test() throws Exception {

// 创建连接池
// 此无参构造方法 会去src目录下 找 c3p0-config.xml 默认配置 <default-config>
// ComboPooledDataSource dataSource = new ComboPooledDataSource();
//  会去src目录下 找 c3p0-config.xml  <named-config name="abc">
ComboPooledDataSource dataSource = new ComboPooledDataSource("abc");
// 获取连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
// 归还连接
connection.close();// 归还连接池 对原有的 close方法增强

}
2.4 druid

德鲁伊开源连接池,阿里巴巴(java贡献者),号称目前最好的连接池,支持日志监控功能

配置文件【掌握】
步骤分析

1 导入jar包

2 定义配置文件

推荐:名称为 druid.properties 位置在 src目录下

3 编写代码

// 创建连接池

// 获取连接

// 归还连接

代码片段

// 配置文件
@Test
public void test() throws Exception {

//  创建连接池
InputStream is = Demo01.class.getClassLoader().getResourceAsStream("druid.properties");
Properties prop = new Properties();
prop.load(is);
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
// 获取连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
// 归还连接
connection.close(); // 进行增强 归还连接池

}
2.5 连接池工具类
连接池是一个重量级对象,创建和销毁是==非常==消耗时间和浪费资源,一般情况下一个项目只有一个连接池,我们把连接池创建抽取到一个工具类,静态

步骤分析

提供获取连接池的方法

提供获取连接的方法

提供释放资源的方法

代码片段

public class DataSourceUtils {

// 声明为静态变量
private static DataSource dataSource;

// 静态代码块 初始化连接池
static {
    try {
        InputStream is = DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        Properties prop = new Properties();
        prop.load(is);
        dataSource = DruidDataSourceFactory.createDataSource(prop);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

// 提供获取连接池的方法
public static DataSource getDataSource() {
    return dataSource;
}

//提供获取连接的方法
public static Connection getConnection() throws SQLException {
    return dataSource.getConnection();
}

}
3 动态代理

3.1 什么是动态代理
刚才咱们使用第三方连接池,Connection.close();是归还连接池,而不是销毁对象,它(连接池厂商)其实对close 方法进行了增强 —– 动态代理

3.2 方法增强
继承
针对于目标类,编写子类,重写父类的方法进行增强

动态代理
针对于目标类,在程序运行期间,JVM创建一个增强类【代理类】对象,对目标类的方法进行增强

常见动态代理技术

JDK、 CGLIB、 JAVA ASSIST

JDK动态代理:需要目标是接口的实现,针对接口创建增强类【代理】对象

3.3 动态案例
需求

买了辆二手QQ车,这辆车有行驶和刹车二个功能,这辆15年的老爷车慢悠悠的跑,相对行驶方法进行增强

步骤分析

1 创建 Car 接口 行驶和刹车

2 创建 QQ车类 实现 Car 接口

慢悠悠的跑

刹车了。。。。

3 需要对QQ车类 行驶方法进行增强

4 学习JDK动态代理

jdk提供了一个工具类 生产 增强类【代理类】对象

Proxy

// 生产代理对象
public static Object newProxyInstance(ClassLoader loader,Class [] interfaces,InvocationHanlder h);
loader:使用目标类的加载器
interfaces:使用目标类的接口数组
h:调用处理接口,实现方法增强的逻辑
InvocationHanlder接口

// 实现方法增强的逻辑
public Object invoke(Object proxy: ,Method method,Object [] args);
proxy: newProxyInstance创建代理对象,一般我们不用
method:当前执行的方法
args:方法实际传递的参数
代码实现

@Test
public void test01() throws Exception {

// 创建qq车
QQ qq = new QQ();
/*        qq.run();
    qq.stop();*/
Class[] classes = new Class[]{Car.class};
// 使用JDK动态代理 对 run方法进行增强  Proxy 代理类对象
Car proxyQQ = (Car) Proxy.newProxyInstance(QQ.class.getClassLoader(), classes, new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 编写我们增强的业务逻辑
        //System.out.println("我是增强的处理方法");
        System.out.println(method.getName());
        if ("run".equalsIgnoreCase(method.getName())) {
            // 增强功能
            System.out.println("5秒百米加速,666");
            return null;
        } else {
            // 调用原有的方法
            // 对象实例
            // 参数列表
            return method.invoke(qq, args);
        }

    }
});

proxyQQ.run();
proxyQQ.stop();

}

@Test
public void test02() throws Exception {

// QQ车
QQ qq = new QQ();
// 对qq车 行驶方法进行增强
Car proxyQQ = (Car) Proxy.newProxyInstance(QQ.class.getClassLoader(), QQ.class.getInterfaces(), (proxy, method, args) -> {
    // 方法增强的业务逻辑
    if ("run".equalsIgnoreCase(method.getName())) {
        // 增强
        System.out.println("五秒百米加速,666");
        return null;
    } else {
        // 执行原来的
        return method.invoke(qq, args);
    }

});
proxyQQ.run();
proxyQQ.stop();

}
总结:

动态代理,就在程序运行期间,对方法进行增强

— 让jvm虚拟机创建了一个代理类对象【增强类对象】,指定方法拓展增强

3.4 升级自定义连接池
使用Connection.close方法 进行增强 (归还连接池)

Last modification:July 29th, 2019 at 05:41 pm