Druid 阿里巴巴德鲁伊连接池

分享 123456789987654321 ⋅ 于 2021-01-15 09:07:34 ⋅ 2976 阅读

JDBC操作的工具类!

import java.sql.*;
import java.util.ResourceBundle;

/*
JDBC操作的工具类!
/
public class JDBCUtils {

private static String driverClass;
private static String url;
private static String username;
private static String password;

// 静态代码块,读取配置文件,获得连接数据的四大参数!
static{
    ResourceBundle bundle = ResourceBundle.getBundle("db");
    // 获得数据
    driverClass = bundle.getString("driverClass");
    url = bundle.getString("url");
    username = bundle.getString("username");
    password = bundle.getString("password");
}

/*
*   获得连接
* */
public static Connection getConnection(){
    Connection conn = null;
    try {
        // 注册驱动
        Class.forName(driverClass);
        // 获得连接对象
        conn = DriverManager.getConnection(url, username, password);

    } catch (Exception e) {
        e.printStackTrace();
    }

    // 返回
    return conn;
}

/*
*   释放资源
* */
public static void release(ResultSet rs, Statement stmt,Connection conn){
    if(rs!=null){
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if(stmt!=null){
        try {
            stmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if(conn!=null){
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

}

德鲁伊连接池工具类!

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 DruidUtils {

private static DataSource dataSource;

// 静态代码块读取配置文件
static{
    // 读取配置文件,获得流
    InputStream is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
    // 创建Properties对象
    Properties prop = new Properties();
    // 关联流对象
    try {
        prop.load(is);
        // 获得数据源对象
        dataSource = DruidDataSourceFactory.createDataSource(prop);
    }catch (Exception e) {
        e.printStackTrace();
    }
}

/*
*   从连接池中获得连接对象
* */
public static Connection getConnection(){
    Connection conn = null;
    try {
        conn = dataSource.getConnection();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return conn;
}

/*
*   释放资源
*
* */
public static void close(ResultSet rs, Statement stmt,Connection conn){
    if(rs!=null) {
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if(stmt!=null) {
        try {
            stmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if(conn!=null){
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

}
德鲁伊连接池的基本使用

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.hainiu.jdbc.utils.DruidUtils;
import org.junit.Test;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/*
德鲁伊连接池的基本使用
/
public class DruidTest {

// 德鲁伊工具类测试!
@Test
public void test2() throws SQLException {
    // 获得连接对象
    Connection conn = DruidUtils.getConnection();
    // 编写SQL语句
    String sql = "insert into user values(null,?,?)";
    // 获得预编译对象
    PreparedStatement pstmt = conn.prepareStatement(sql);
    // 为占位符赋值
    pstmt.setString(1,"武哥");
    pstmt.setString(2,"www");

    // 执行sql语句
    int rows = pstmt.executeUpdate();
    if(rows>0){
        System.out.println("添加用户信息成功!");
    }

    // 释放资源
    DruidUtils.close(null,pstmt,conn);
}

// 德鲁伊连接池基本使用
@Test
public  void test1() throws Exception {

    // 读取配置文件
    InputStream is = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
    //System.out.println(is);
    // 工具类
    Properties prop = new Properties();
    // 让其与流关联
    prop.load(is);

    // 获得DataSource对象!
    DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

    // 获得连接对象
    Connection conn = dataSource.getConnection();

    // 编写SQL语句
    String sql = "select * from user";

    // 获得预编译对象
    PreparedStatement pstmt = conn.prepareStatement(sql);

    // 执行SQL语句
    ResultSet rs = pstmt.executeQuery();
    while(rs.next()){
        System.out.println(rs.getInt(1)+", " + rs.getString(2)+"," + rs.getString(3));
    }

    // 释放资源(归还!)
    conn.close();
}

}


JDBC
学习目标
JDBC的概念
客户端操作MySQL数据库的方式

  1. 使用第三方客户端来访问MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL
    Manager for MySQL
  2. 使用MySQL自带的命令行方式
  3. 通过Java来访问MySQL数据库,今天要学习的内容
    什么是JDBC:Java DataBase Connectivity (Java数据库连接) JDBC是Java访问数据库的标准规范
    JDBC的作用:JDBC是用于执行SQL语句的Java API(Java语言通过JDBC可以操作数据库)
    JDBC的由来
  4. 直接写代码操作数据库
    直接写代码操作数据库存在的问题:
  5. 不知道MySQL数据库的操作方式,解析方式
  6. 代码繁琐,写起来麻烦
  7. MySQL和Oracle等其他数据库的操作方式和解析方式不同,每个数据库都要写一套代码
  8. MySQL和Oracle等其他数据库相互切换麻烦
  9. JDBC规范定义接口,具体的实现由各大数据库厂商来实现 JDBC是Java访问数据库的标准规范。真
    正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通
    信格式编写好自己数据库的驱动。所以我们只需要会调用JDBC接口中的方法即可。数据库驱动由
    数据库厂商提供。

JDBC的好处:

  1. 我们只需要会调用JDBC接口中的方法即可,使用简单
  2. 使用同一套Java代码,进行少量的修改就可以访问其他JDBC支持的数据库
    JDBC会用到的包:
  3. java.sql:JDBC访问数据库的基础包,在JavaSE中的包。如:java.sql.Connection
  4. javax.sql: JDBC访问数据库的扩展包
  5. 数据库的驱动,各大数据库厂商来实现。如:MySQL的驱动:com.mysql.jdbc.Driver
    JDBC四个核心对象 这几个类都是在java.sql包中
  6. DriverManager: 用于注册驱动
  7. Connection: 表示与数据库创建的连接
  8. Statement: 执行SQL语句的对象
  9. ResultSet: 结果集或一张虚拟表
    第3章 JDBC的快速入门
    jdbc的主要目的是对数据库的增删改查,那么让我们来看一下JDBC操作实现的具体效果
    1.导入驱动Jar包(参考4.1) 2.注册驱动 3.获取连接
    4.获得语句执行者 5.执行语句 6.释放资源
    public static void main(String[] args) throws Exception {
    //1.注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2.获得连接
    String url="jdbc:mysql://localhost:3306/ee59_day05";
    Connection connection = DriverManager.getConnection(url, "root", "root");

//3.创建语句执行者
Statement statement = connection.createStatement();
//4.执行语句
int count = statement.executeUpdate(" insert into tab_user values(null ,
'梦梦' , '男' , 22) ");
//5. 打印返回值
System.out.println(count);
//6.释放资源
statement.close();
connection.close();
}
第4章 JDBC获取连接
Connection 表示Java程序与数据库之间的连接,只有拿到Connection才能操作数据库。
JDBC获取连接步骤 1.导入驱动Jar包 2.注册驱动 3.获取连接
4.1 导入驱动Jar包

4.2 注册驱动
我们Java程序需要通过数据库驱动才能连接到数据库,因此需要注册驱动。 MySQL的驱动的入口类
是:com.mysql.jdbc.Driver
4.2.1 API介绍
java.sql.DriverManager 类用于注册驱动。提供如下方法注册驱动
static void registerDriver(Driver driver)
向 DriverManager 注册给定驱动程序。
4.2.2 使用步骤
1.DriverManager.registerDriver(驱动对象); 传入对应参数即可
4.2.3 案例代码

public class Demo01 {
public static void main(String[] args) throws Exception {
// 注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
}
}
通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册
注意:使用DriverManager.registerDriver(new com.mysql.jdbc.Driver()); ,存在两方面
不足

  1. 硬编码,后期不易于程序扩展和维护
  2. 驱动被注册两次
    使用Class.forName("com.mysql.jdbc.Driver"); 加载驱动,这样驱动只会注册一次
    public class Demo01 {
    public static void main(String[] args) throws Exception {
    Class.forName("com.mysql.jdbc.Driver"); // 后期可以
    将"com.mysql.jdbc.Driver"字符串写在文件中.
    }
    }
    演示:Class.forName("包名.类名"); 会走这个类的静态代码块

通常开发我们使用Class.forName() 加载驱动。Class.forName("com.mysql.jdbc.Driver"); 会走
Driver类的静态代码块。在静态代码块中注册一次驱动。
总结:注册MySQL驱动使用Class.forName("com.mysql.jdbc.Driver");
4.3 获取连接
4.3.1 API介绍
java.sql.DriverManager 类中有如下方法获取数据库连接
static Connection getConnection(String url, String user, String password)
连接到给定数据库 URL ,并返回连接。
4.3.2 参数说明

  1. String url :连接数据库的URL,用于说明连接数据库的位置
  2. String user :数据库的账号
  3. String password :数据库的密码
    连接数据库的URL地址格式:协议名:子协议://服务器名或IP地址:端口号/数据库名?参数=参数值
    MySQL写法:
    jdbc:mysql://localhost:3306/day24 如果是本地服务器,端口号是默认的3306,则可以简写:
    jdbc:mysql:///day24
    4.3.3 注意事项
    如果数据出现乱码需要加上参数: ?characterEncoding=utf8,表示让数据库以UTF-8编码来处理
    数据。 如: jdbc:mysql://localhost:3306/day24?characterEncoding=utf8
    4.3.4 使用步骤
    1.DriverManager.getConnection(url, user, password); 传入对应参数即可
    4.3.5 案例代码

public class Demo01 {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");

    // 连接到MySQL
    // url: 连接数据库的URL
    // user: 数据库的账号
    // password: 数据库的密码
    Connection conn = 

DriverManager.getConnection("jdbc:mysql://localhost:3306/day24", "root",
"root");
System.out.println(conn);
}
}
4.3.6 案例效果
1.连接成功
2.连接失败
第5章 JDBC实现对单表数据增、删、改、查
目标
对数据库数据进行增删改查操作
5.1 准备数据
-- 创建分类表
CREATE TABLE category (
cid INT PRIMARY KEY AUTO_INCREMENT,
cname VARCHAR(100)
);
-- 初始化数据
INSERT INTO category (cname) VALUES('家电');
INSERT INTO category (cname) VALUES('服饰');
INSERT INTO category (cname) VALUES('化妆品');
5.2 JDBC实现对单表数据增、删、改
目标
通过api进行操作增删改数据库

5.2.1 API介绍
5.2.1.1 获取Statement对象API介绍
在java.sql.Connection 接口中有如下方法获取到Statement 对象
Statement createStatement()
创建一个 Statement 对象来将 SQL 语句发送到数据库
5.2.1.2 Statement的API介绍
boolean execute(String sql)
此方法可以执行任意sql语句。返回boolean值,表示是否返回ResultSet结果集。仅当执行select
语句,且有返回结果时返回true, 其它语句都返回false;
int executeUpdate(String sql)
根据执行的DML(INSERT、UPDATE、DELETE)语句,返回受影响的行数
ResultSet executeQuery(String sql)
根据查询语句返回结果集,只能执行SELECT语句
注意:在MySQL中,只要不是查询就是修改。 executeUpdate:用于执行增删改
executeQuery:用于执行查询
5.2.2 使用步骤

  1. 注册驱动
  2. 获取连接
  3. 获取Statement对象
  4. 使用Statement对象执行SQL语句
  5. 释放资源
    5.2.3 案例代码
    public class Demo03 {
    public static void main(String[] args) throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection("jdbc:mysql:///day24",
    "root", "root");
    System.out.println(conn);
    // String sql = "SELECT * FROM category;";
    // 从连接中拿到一个Statement对象
    Statement stmt = conn.createStatement();
    // 1.插入记录
    String sql = "INSERT INTO category (cname) VALUES ('手机');";
    int i = stmt.executeUpdate(sql);
    System.out.println("影响的行数:" + i);
    // 2.修改记录
    sql = "UPDATE category SET cname='汽车' WHERE cid=4;";

    i = stmt.executeUpdate(sql);
    System.out.println("影响的行数:" + i);
    // 3.删除记录
    sql = "DELETE FROM category WHERE cid=1;";
    i = stmt.executeUpdate(sql);
    System.out.println("影响的行数:" + i);
    
    // 释放资源
    stmt.close();
    conn.close();

    }
    }
    5.2.4 案例效果
    5.3 JDBC实现对单表数据查询
    目标
    通过查询的API操作数据库
    5.3.1 原理
    ResultSet 用于保存执行查询SQL语句的结果。 我们不能一次性取出所有的数据,需要一行一行的取
    出。
    ResultSet的原理:

  6. ResultSet内部有一个指针,刚开始记录开始位置
  7. 调用next方法, ResultSet内部指针会移动到下一行数据

  8. 我们可以通过ResultSet得到一行数据 getXxx得到某列数据
    ResultSet获取数据的API 其实ResultSet获取数据的API是有规律的get后面加数据类型。我们统称
    getXXX()
    使用JDBC查询数据库中的数据的步骤
  9. 注册驱动
  10. 获取连接
  11. 获取到Statement
  12. 使用Statement执行SQL
  13. ResultSet处理结果
  14. 关闭资源
    案例代码
    public class Demo04 {
    public static void main(String[] args) throws Exception {
    Class.forName("com.mysql.jdbc.Driver");

    Connection conn = DriverManager.getConnection("jdbc:mysql:///day24", 

    "root", "root");
    Statement stmt = conn.createStatement();

    String sql = "SELECT * FROM category;";
    ResultSet rs = stmt.executeQuery(sql);
    
    // 内部有一个指针,只能取指针指向的那条记录
    while (rs.next()) { // 指针移动一行,有数据才返回true
        // 取出数据
        int cid = rs.getInt("cid");
        String cname = rs.getString("cname");
    
        System.out.println(cid + " == " + cname);
    }
    
    // 关闭资源
    rs.close();
    stmt.close();
    conn.close();

    }
    }
    注意:

  15. 如果光标在第一行之前,使用rs.getXXX()获取列值,报错:Before start of result set
  16. 如果光标在最后一行之后,使用rs.getXXX()获取列值,报错:After end of result set
    案例效果
    小结
    不管是增删改查的哪个操作,其实基本的操作都是一样的,如下
  17. 注册驱动
    Class.forName("com.mysql.jdbc.Driver");
  18. 获取连接
    Connection conn = DriverManager.getConnection("jdbc:mysql:///day24", "root", "root");
  19. 获取Statement对象
    Statement stmt = conn.createStatement();
  20. 使用Statement对象执行SQL语句
    ResultSet rs = stmt.executeQuery(sql); 或者 stmt.executeUpdate(sql);
  21. 处理结果
    rs.next() rs.get类型(列的名称)
  22. 释放资源

而麻烦的是对于查询,我们需要额外的处理ResultSet的结果集,我们需要针对不同的了类型操作
不同的api
第6章 JDBC事务
目标
使用java代码,完成转账操作
6.1 准备数据
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('张三', 1000), ('李四', 1000);
6.2 API介绍
Connection 接口中与事务有关的方法

  1. void setAutoCommit(boolean autoCommit) throws SQLException;
    false:开启事务, ture:关闭事务

  2. void commit() throws SQLException;
    提交事务
  3. void rollback() throws SQLException;
    回滚事务
    6.3 使用步骤
  4. 注册驱动
  5. 获取连接
  6. 获取到Statement
  7. 开启事务
  8. 使用Statement执行SQL
  9. 提交或回滚事务
  10. 关闭资源
    6.4 案例代码
    public class Demo05 {
    public static void main(String[] args) {
    Connection conn = null;
    try {
    // 拿到连接

        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection("jdbc:mysql:///day24", "root", 

    "root");

        // 开启事务
        conn.setAutoCommit(false);
    
        Statement pstmt = conn.createStatement();
    
        // 张三减500
        String sql = "UPDATE account SET balance = balance - 500 WHERE 

    id=1;";
    pstmt.executeUpdate(sql);
    // 模拟异常
    // int i = 10 / 0;

        // 李四加500
        sql = "UPDATE account SET balance = balance + 500 WHERE id=2;";
        pstmt.executeUpdate(sql);
    
        pstmt.close();
        // 成功,提交事务
        System.out.println("成功,提交事务");
        conn.commit();
    } catch (Exception e) {
        // 失败,回滚事务
        try {
            System.out.println("出了异常,回滚事务");
            conn.rollback();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    } finally {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    }
    }
    6.5 案例效果
    小结
    操作事务基本的原理跟之前操作cmd一样,都是开启事务,提交事务,回滚事务
    try{

  11. 代码第一行开启事务 connection.setAutocommit( false )
  12. 代码最后一行提交事务 connection.commit()

}catch(Exception e ){

  1. 发生异常回滚事务 connection.rollback()
    }
    第7章 JDBC获取连接与关闭连接工具类实现
    通过上面案例需求我们会发现每次去执行SQL语句都需要注册驱动,获取连接,得到Statement,以及
    释放资源。发现很多重复的劳动,我们可以将重复的代码定义到某个类的方法中。直接调用方法,可以
    简化代码。 那么我们接下来定义一个JDBCUtil 类。把注册驱动,获取连接,得到Statement,以及释
    放资源的代码放到这个类的方法中。以后直接调用方法即可。
    7.1 编写JDBC工具类步骤
  2. 将固定字符串定义为常量
  3. 在静态代码块中注册驱动(只注册一次)
  4. 提供一个获取连接的方法static Connection getConneciton();
  5. 定义关闭资源的方法close(Connection conn, Statement stmt, ResultSet rs)
  6. 重载关闭方法close(Connection conn, Statement stmt)
    7.2 案例代码
    JDBCUtils.java
    public class JDBCUtils {
    // 1.将固定字符串定义为常量
    private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
    private static final String URL = "jdbc:mysql:///day24";
    private static final String USER = "root";
    private static final String PASSWORD = "root";

    // 2.在静态代码块中注册驱动(只注册一次)
    // 当这个类加载到内存的时候就走这个静态代码块,再去触发Driver类中的静态代码块,主动注册
    static {
    try {
    Class.forName(DRIVER_CLASS);
    } catch (ClassNotFoundException e) {}
    }

    // 3.提供一个获取连接的方法static Connection getConneciton();
    // 我们面向JDBC编程
    public static Connection getConnection() throws SQLException {

    Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);

    /
    也可以将四个参数抽取为一个jdbc.properties文件通过Properties对象获取每个值
    InputStream is =
    JDBCUtils.class.getResourceAsStream("/jdbc.properties");
    Properties pp = new Properties();
    pp.load(is);
    /
    return conn;
    }

    // 5.重载关闭方法close(Connection conn, Statement stmt)
    public static void close(Connection conn, Statement stmt) {
    close(conn, stmt, null);
    }

    // 4.定义关闭资源的方法close(Connection conn, Statement stmt, ResultSet rs)
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
    if (rs != null) {
    try {
    rs.close();
    } catch (SQLException e) {}
    }

    if (stmt != null) {
        try {
            stmt.close();
        } catch (SQLException e) {}
    }
    
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) {}
    }

    }
    }
    Demo06.java
    public class Demo06 {
    public static void main(String[] args) throws Exception {
    createTable();
    // addEmployee();
    // updateEmployee();
    // deleteEmployee();
    }

    // 删除员工
    public static void deleteEmployee() throws Exception {
    Connection conn = JDBCUtils.getConnection();
    Statement stmt = conn.createStatement();

    // 删除id为3的员工
    String sql = "DELETE FROM employee WHERE id=3;";
    
    int i = stmt.executeUpdate(sql);
    System.out.println("影响的行数: " + i);

// stmt.close();
// conn.close();
// JDBCUtils.close(conn, stmt, null);
JDBCUtils.close(conn, stmt);
}

// 修改员工
public static void updateEmployee() throws Exception {

    Connection conn = JDBCUtils.getConnection();
    Statement stmt = conn.createStatement();

    // 将id为3的员工姓名改成田七,地址改成天津
    String sql = "UPDATE employee SET address='天津', name='田七' WHERE 

id=3;";

    int i = stmt.executeUpdate(sql);
    System.out.println("影响的行数: " + i);

// stmt.close();
// conn.close();
// JDBCUtils.close(conn, stmt, null);
JDBCUtils.close(conn, stmt);
}

// 定义添加员工
public static void addEmployee() throws Exception {
    Connection conn = JDBCUtils.getConnection();
    Statement stmt = conn.createStatement();

    // 添加4个员工
    String sql = "INSERT INTO employee VALUES (NULL, '张三4', 20, '北京'),"
            + " (NULL, '李四4', 21, '南京'),"
            + " (NULL, '王五4', 18, '东京'),"
            + " (NULL, '赵六4', 17, '西安');";

    int i = stmt.executeUpdate(sql);
    System.out.println("影响的行数: " + i);

// stmt.close();
// conn.close();
// JDBCUtils.close(conn, stmt, null);
JDBCUtils.close(conn, stmt);
}
// 创建表
public static void createTable() throws Exception {
Connection conn = JDBCUtils.getConnection();
Statement stmt = conn.createStatement();

    String sql = "CREATE TABLE IF NOT EXISTS employee ("
            + " id INT PRIMARY KEY AUTO_INCREMENT,"
            + " name VARCHAR(20) UNIQUE NOT NULL,"
            + " age INT,"
            + " address VARCHAR(50)"
            + ");";

    int i = stmt.executeUpdate(sql);
    System.out.println("ok");

// stmt.close();
// conn.close();
// JDBCUtils.close(conn, stmt, null);
JDBCUtils.close(conn, stmt);
}
}

7.3 案例效果
第8章 JDBC实现登录案例
8.1 案例目标
模拟用户输入账号和密码登录网站
8.2 案例效果

  1. 输入正确的账号,密码,显示登录成功
  2. 输入错误的账号,密码,显示登录失败
    8.3 案例分析
  3. 使用数据库保存用户的账号和密码
  4. 让用户输入账号和密码
  5. 使用SQL根据用户的账号和密码去数据库查询数据
  6. 如果查询到数据,说明登录成功
  7. 如果查询不到数据,说明登录失败

8.4 实现步骤

  1. 创建一个用户表保存用户的账号和密码,并添加一些数据,SQL语句如下:
    CREATE TABLE USER (
    id INT AUTO_INCREMENT PRIMARY KEY,
    NAME VARCHAR(50),
    PASSWORD VARCHAR(50)
    );
    INSERT INTO USER (NAME, PASSWORD) VALUES('admin', '123'), ('test', '123'),
    ('gm', '123');
  2. 编写代码让用户输入账号和密码
    public class Demo07 {
    public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入账号: ");
    String name = sc.nextLine();
    System.out.println("请输入密码: ");
    String password = sc.nextLine();
    }
  3. 使用SQL根据用户的账号和密码去数据库查询数据
    public class Demo07 {
    public static void main(String[] args) throws Exception {
    // 让用户输入账号和密码
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入账号: ");
    String name = sc.nextLine();
    System.out.println("请输入密码: ");
    String password = sc.nextLine();
    // 使用SQL根据用户的账号和密码去数据库查询数据
    Connection conn = JDBCUtils.getConnection();
    Statement stmt = conn.createStatement();
    String sql = "SELECT * FROM user WHERE name='" + name + "' AND
    password='" + password + "';";
    }
    }
  4. 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败
    public class Demo07 {
    public static void main(String[] args) throws Exception {
    // 让用户输入账号和密码
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入账号: ");
    String name = sc.nextLine();
    System.out.println("请输入密码: ");
    String password = sc.nextLine();
    // 使用SQL根据用户的账号和密码去数据库查询数据
    Connection conn = JDBCUtils.getConnection();

    Statement stmt = conn.createStatement();
    String sql = "SELECT * FROM user WHERE name='" + name + "' AND
    password='" + password + "';";
    // 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败
    ResultSet rs = stmt.executeQuery(sql);
    if (rs.next()) {
    //能进来查询到了数据.
    String name2 = rs.getString("name");
    System.out.println("欢迎您," + name2);
    } else {
    //查询不到数据,说明登录失败
    System.out.println("账号或密码错误...");
    }
    JDBCUtils.close(conn, stmt, rs);
    }
    }
    小结
    我们实现登陆案例其实一共就分为三步

  5. 获得用户输入的用户名和密码
  6. 拿着用户名和密码操作数据库
  7. 判断是否有查询到数据
    第9章 PreparedSatement预编译对象
    目标
    使用preparedStatement预编译对象解决SQL注入问题
    9.1 SQL注入问题
    在我们JDBC实现登录案例中,当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了
    请输入用户名:
    hehe
    请输入密码:
    a' or '1'='1
    问题分析:
    // 代码中的SQL语句
    "SELECT FROM user WHERE name='" + name + "' AND password='" + password + "';";
    // 将用户输入的账号密码拼接后
    "SELECT
    FROM user WHERE name='hehe' AND password='a' or '1'='1';"
    我们让用户输入的密码和SQL语句进行字符串拼接。用户输入的内容作为了SQL语句语法的一部分,改
    变了原有SQL真正的意义,以上问题称为SQL注入。

要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。需要使用
PreparedSatement类解决SQL注入。
9.2 PreparedSatement的执行原理
继承结构:
我们写的SQL语句让数据库执行,
数据库不是直接执行SQL语句字符串。和Java一样,数据库需要执行编译后的SQL语句(类似Java编译
后的字节码文件)。

  1. Satement 对象每执行一条SQL语句都会先将这条SQL语句发送给数据库编译,数据库再执行。
    Statement stmt = conn.createStatement();
    stmt.executeUpdate("INSERT INTO users VALUES (1, '张三', '123456');");
    stmt.executeUpdate("INSERT INTO users VALUES (2, '李四', '666666');");
    上面2条SQL语句我们可以看到大部分内容是相同的,只是数据略有不一样。数据库每次执行都编译一
    次。如果有1万条类似的SQL语句,数据库需要编译1万次,执行1万次,显然效率就低了。
  2. prepareStatement() 会先将SQL语句发送给数据库预编译。PreparedStatement 会引用着预编
    译后的结果。可以多次传入不同的参数给PreparedStatement 对象并执行。相当于调用方法多次
    传入不同的参数。
    String sql = "INSERT INTO users VALUES (?, ?, ?);";
    // 会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。
    PreparedStatement pstmt = conn.prepareStatement(sql);
    // 设置参数
    pstmt.setString(1, 1);
    pstmt.setInt(2, "张三");
    pstmt.setString(3, "123456");
    pstmt.executeUpdate();
    // 再次设置参数
    pstmt.setString(1, 2);
    pstmt.setInt(2, "李四");
    pstmt.setString(3, "66666");
    pstmt.executeUpdate();
    上面预编译好一条SQL,2次传入了不同的参数并执行。如果有1万条类似的插入数据的语句。数据库只
    需要预编译一次,传入1万次不同的参数并执行。减少了SQL语句的编译次数,提高了执行效率。

示意图
9.3 PreparedSatement的好处

  1. prepareStatement() 会先将SQL语句发送给数据库预编译。PreparedStatement 会引用着预编
    译后的结果。可以多次传入不同的参数给PreparedStatement 对象并执行。减少SQL编译次数,
    提高效率。
  2. 安全性更高,没有SQL注入的隐患。
  3. 提高了程序的可读性
    9.4 PreparedSatement的基本使用
    9.4.1 API介绍
    9.4.1.1 获取PreparedSatement的API介绍
    在java.sql.Connection 有获取PreparedSatement 对象的方法
    PreparedStatement prepareStatement(String sql)
    会先将SQL语句发送给数据库预编译。PreparedStatement对象会引用着预编译后的结果。
    9.4.1.2 PreparedSatement的API介绍
    在java.sql.PreparedStatement 中有设置SQL语句参数,和执行参数化的SQL语句的方法
  4. void setDouble(int parameterIndex, double x)
    将指定参数设置为给定 Java double 值。
  5. void setFloat(int parameterIndex, float x)
    将指定参数设置为给定 Java REAL 值。
  6. void setInt(int parameterIndex, int x)
    将指定参数设置为给定 Java int 值。
  7. void setLong(int parameterIndex, long x)
    将指定参数设置为给定 Java long 值。

  8. void setObject(int parameterIndex, Object x)
    使用给定对象设置指定参数的值。
  9. void setString(int parameterIndex, String x)
    将指定参数设置为给定 Java String 值。

  10. ResultSet executeQuery()
    在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的ResultSet对象。
  11. int executeUpdate()
    在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 SQL 数据操作语言(Data
    Manipulation Language,DML)语句,比如 INSERT、UPDATE 或 DELETE 语句;或者是无返回
    内容的 SQL 语句,比如 DDL 语句。
    9.4.2 PreparedSatement使用步骤

  12. 编写SQL语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND
    password=?;";
  13. 获得PreparedStatement对象
  14. 设置实际参数
  15. 执行参数化SQL语句
  16. 关闭资源
    9.4.3 案例代码
    public class Demo08 {
    public static void main(String[] args) throws Exception {
    // 获取连接
    Connection conn = JDBCUtils.getConnection();

    // 编写SQL语句,未知内容使用?占位
    String sql = "SELECT * FROM user WHERE name=? AND password=?;";
    
    // prepareStatement()会先将SQL语句发送给数据库预编译。
    PreparedStatement pstmt = conn.prepareStatement(sql);
    
    // 指定?的值
    // parameterIndex: 第几个?,从1开始算
    // x: 具体的值
    pstmt.setString(1, "admin");
    pstmt.setString(2, "123"); // 正确的密码
    // pstmt.setString(2, "6666"); // 错误的密码
    
    ResultSet rs = pstmt.executeQuery();
    
    if (rs.next()) {
        String name = rs.getString("name");
        System.out.println("name:" + name);
    } else {
        System.out.println("没有找到数据...");
    }
    
    JDBCUtils.close(conn, pstmt, rs);

    }
    }
    9.4.4 案例效果

  17. 输入正确的账号密码:
  18. 输入错误的密码:
    9.5 PreparedSatement实现增删查改
    9.5.1 添加数据
    向Employee表添加3条记录
    // 添加数据: 向Employee表添加3条记录
    public static void addEmployee() throws Exception {
    Connection conn = JDBCUtils.getConnection();
    String sql = "INSERT INTO employee VALUES (NULL, ?, ?, ?);";
    // prepareStatement()会先将SQL语句发送给数据库预编译。
    PreparedStatement pstmt = conn.prepareStatement(sql);
    // 设置参数
    pstmt.setString(1, "刘德华");
    pstmt.setInt(2, 57);
    pstmt.setString(3, "香港");
    int i = pstmt.executeUpdate();
    System.out.println("影响的行数:" + i);
    // 再次设置参数

    pstmt.setString(1, "张学友");
    pstmt.setInt(2, 55);
    pstmt.setString(3, "澳门");
    i = pstmt.executeUpdate();
    System.out.println("影响的行数:" + i);
    // 再次设置参数
    pstmt.setString(1, "黎明");
    pstmt.setInt(2, 52);
    pstmt.setString(3, "香港");
    i = pstmt.executeUpdate();
    System.out.println("影响的行数:" + i);
    JDBCUtils.close(conn, pstmt);
    }
    效果:
    9.5.2 修改数据
    将id为2的学生地址改成台湾
    // 修改数据: 将id为2的学生地址改成台湾
    public static void updateEmployee() throws Exception {
    Connection conn = JDBCUtils.getConnection();
    String sql = "UPDATE employee SET address=? WHERE id=?;";
    PreparedStatement pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, "台湾");
    pstmt.setInt(2, 2);
    int i = pstmt.executeUpdate();
    System.out.println("影响的行数:" + i);
    JDBCUtils.close(conn, pstmt);
    }
    效果:
    9.5.3 删除数据
    删除id为2的员工

// 删除数据: 删除id为2的员工
public static void deleteEmployee() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql = "DELETE FROM employee WHERE id=?;";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 2);
int i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
JDBCUtils.close(conn, pstmt);
}
效果:
9.5.4 查询数据
查询id小于8的员工信息,并保存到员工类中
public class Employee {
private int id;
private String name;
private int age;
private String address;
public Employee() {
}

public Employee(int id, String name, int age, String address) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.address = address;
}
public int getId() {
    return id;
}
public void setId(int id) {
    this.id = id;
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}
public String getAddress() {
    return address;
}
public void setAddress(String address) {
    this.address = address;
}
@Override
public String toString() {
    return "Employee2 [id=" + id + ", name=" + name + ", age=" + age + ", 

address=" + address + "]";
}
}
// 查询数据: 查询id小于8的员工信息,并保存到员工类中
public static void queryEmployee() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql = "SELECT * FROM employee WHERE id<?;";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 26);
ResultSet rs = pstmt.executeQuery();
// 创建集合存放多个Employee2对象
ArrayList list = new ArrayList<>();
while (rs.next()) {
// 移动到下一行有数据,取出这行数据
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String address = rs.getString("address");
// 创建Employee2对象
Employee e = new Employee(id, name, age, address);
// 将创建好的员工添加到集合中
list.add(e);
}
// 输出对象
for (Employee e : list) {
System.out.println(e);
}

JDBCUtils.close(conn, pstmt, rs);

}

效果:
9.6 案例:PreparedStatement改造登录案例
9.6.1 案例需求
模拟用户输入账号和密码登录网站,防止SQL注入
9.6.2 案例效果

  1. 输入正确的账号,密码,显示登录成功
  2. 输入错误的账号,密码,显示登录失败
  3. 输入"a' or '1'='1" 作为密码,解决SQL注入:
    9.6.3 案例分析
  4. 使用数据库保存用户的账号和密码
  5. 让用户输入账号和密码
  6. 编写SQL语句,账号和密码部分使用?占位
  7. 使用PreparedSatement给?设置参数
  8. 使用PreparedSatement执行预编译的SQL语句
  9. 如果查询到数据,说明登录成功
  10. 如果查询不到数据,说明登录失败
    9.6.4 实现步骤

  11. 编写代码让用户输入账号和密码
    public class Demo07 {
    public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入账号: ");
    String name = sc.nextLine();
    System.out.println("请输入密码: ");
    String password = sc.nextLine();
    }
  12. 编写SQL语句,账号和密码部分使用?占位,使用PreparedSatement给?设置参数,使用
    PreparedSatement执行预编译的SQL语句
    public class Demo11 {
    public static void main(String[] args) throws Exception {
    // 让用户输入账号和密码
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入账号: ");
    String name = sc.nextLine();
    System.out.println("请输入密码: ");
    String password = sc.nextLine();
    // 获取连接
    Connection conn = JDBCUtils.getConnection();
    // 编写SQL语句,账号和密码使用?占位
    String sql = "SELECT * FROM user WHERE name=? AND password=?;";
    // 获取到PreparedStatement对象
    PreparedStatement pstmt = conn.prepareStatement(sql);
    // 设置参数
    pstmt.setString(1, name);
    pstmt.setString(2, password);
    // pstmt.setString(2, "a' or '1'='1");
    }
    }
  13. 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败
    public class Demo11 {
    public static void main(String[] args) throws Exception {
    // 让用户输入账号和密码
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入账号: ");
    String name = sc.nextLine();
    System.out.println("请输入密码: ");
    String password = sc.nextLine();
    // 获取连接
    Connection conn = JDBCUtils.getConnection();
    // 编写SQL语句,账号和密码使用?占位
    String sql = "SELECT * FROM user WHERE name=? AND password=?;";
    // 获取到PreparedStatement对象
    PreparedStatement pstmt = conn.prepareStatement(sql);
    // 设置参数
    pstmt.setString(1, name);
    pstmt.setString(2, password);

// pstmt.setString(2, "a' or '1'='1");
// 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
// 能进来查询到了数据.
String name2 = rs.getString("name");
System.out.println("欢迎您," + name2);
} else {
// 查询不到数据,说明登录失败
System.out.println("账号或密码错误...");
}
JDBCUtils.close(conn, pstmt, rs);
}
}
第10章 连接池
10.1 连接池概念
我们现实生活中每日三餐。我们并不会吃一餐饭就将碗丢掉,而是吃完饭后将碗放到碗柜中,下一餐接
着使用。目的是重复利用碗,我们的数据库连接也可以重复使用,可以减少数据库连接的创建次数。提
高数据库连接对象的使用率。
连接池的概念: 连接池是创建和管理数据库连接的缓冲池技术。连接池就是一个容器,连接池中保存了
一些数据库连接,这些连接是可以重复使用的。
10.2 没有连接池的现状

  1. 之前JDBC访问数据库的步骤: 创建数据库连接 →运行SQL语句→关闭连接 每次数据库访问执行这
    样重复的动作
  2. 每次创建数据库连接的问题
    获取数据库连接需要消耗比较多的资源,而每次操作都要重新获取新的连接对象,执行一次
    操作就把连接关闭,而数据库创建连接通常需要消耗相对较多的资源,创建时间也较长。这
    样数据库连接对象的使用率低。

假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资
源,并且极易造成数据库服务器内存溢出
10.3 连接池解决现状问题的原理

  1. 程序一开始就创建一定数量的连接,放在一个容器中,这个容器称为连接池(相当于碗柜/容器)。
  2. 使用的时候直接从连接池中取一个已经创建好的连接对象。
  3. 关闭的时候不是真正关闭连接,而是将连接对象再次放回到连接池中。
    10.4 数据库连接池相关API
    Java为数据库连接池提供了公共的接口: javax.sql.DataSource ,各个厂商需要让自己的连接池实现
    这个接口。这样应用程序可以方便的切换不同厂商的连接池。
    在javax.sql.DataSource 接口中有一个方法:Connection getConnection() : 从连接池获取一个连

    归还连接的时候直接调用连接的close() 方法即可.
    第11章 DRUID连接池
    11.1 DRUID简介
    Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、
    性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL
    的执行情况。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考
    验。Druid地址:https://github.com/alibaba/druid DRUID连接池使用的jar包:druid-1.0.9.jar
    11.2 DRUID常用的配置参数
    常用的配置参数:

参数
说明
jdbcUrl
username
password
连接数据库的url:mysql : jdbc:mysql://localhost:3306/druid2
数据库的用户名
数据库的密码
驱动类名。根据url自动识别,这一项可配可不配,如果不配置druid会根据
url自动识别dbType,然后选择相应的driverClassName(建议配置下)
driverClassName
initialSize
初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一
次getConnection时
maxActive
maxIdle
minIdle
最大连接池数量
已经不再使用,配置了也没效果
最小连接池数量
maxWait
获取连接时最大等待时间,单位毫秒。
11.3 DRUID连接池基本使用
API介绍
com.alibaba.druid.pool.DruidDataSourceFactory 类有创建连接池的方法
public static DataSource createDataSource(Properties properties)
创建一个连接池,连接池的参数使用properties中的数据
我们可以看到DRUID连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用
properties文件来保存对应的参数。 DRUID连接池的配置文件名称随便,建议放到src目录下面方便加
载。 druid.properties 文件内容:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/day25
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
maxIdle=6
minIdle=3
使用步骤

  1. 在src目录下创建一个properties文件,并设置对应参数
  2. 加载properties文件的内容到Properties对象中
  3. 创建DRUID连接池,使用配置文件中的参数
  4. 从DRUID连接池中取出连接
  5. 执行SQL语句
  6. 关闭资源

案例代码

  1. 在src目录下新建一个DRUID配置文件,命名为:druid.properties,内容如下
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/day25
    username=root
    password=root
    initialSize=5
    maxActive=10
    maxWait=3000
    maxIdle=6
    minIdle=3
    java代码
    public class Demo02 {
    public static void main(String[] args) throws Exception {
    // 加载配置文件中的配置参数
    InputStream is = Demo03.class.getResourceAsStream("/druid.properties");
    Properties pp = new Properties();
    pp.load(is);

    // 创建连接池,使用配置文件中的参数
    DataSource ds = DruidDataSourceFactory.createDataSource(pp);
    
    // 从连接池中取出连接
    Connection conn = ds.getConnection();
    
    // 使用jdbc执行SQL 语句省略
    
    conn.close(); // 将连接还回连接池中

    }
    }
    案例效果

  2. 正常获取连接池中的连接

  3. 获取连接池中的连接超时
  4. 使用DRUID连接池中的连接操作数据库
    总结
    DRUID连接池根据Properties对象中的数据作为连接池参数去创建连接池,我们自己定义properties类
    型的配置文件,名称自己取,也可以放到其他路径,建议放到src目录下方便加载。 不管是C3P0连接
    池,还是DRUID连接池,配置大致都可以分为2种: 1.连接数据库的参数 , 2.连接池的参数 ,这2种配置
    大致参数作用都相同,只是参数名称可能不一样。
    11.4 连接池工具类
    我们每次操作数据库都需要创建连接池,获取连接,关闭资源,都是重复的代码。我们可以将创建连接
    池和获取连接池的代码放到一个工具类中,简化代码。
    连接池工具类步骤:

  5. 声明静态数据源成员变量
  6. 创建连接池对象
  7. 定义公有的得到数据源的方法
  8. 定义得到连接对象的方法
  9. 定义关闭资源的方法
    案例代码 DataSourceUtils.java
    public class DataSourceUtils {
    // 1. 声明静态数据源成员变量
    private static DataSource ds;
    // 2. 创建连接池对象
    static {
    // 加载配置文件中的数据
    InputStream is =
    DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");
    Properties pp = new Properties();
    try {
    pp.load(is);
    // 创建连接池,使用配置文件中的参数
    ds = DruidDataSourceFactory.createDataSource(pp);
    } catch (IOException e) {
    e.printStackTrace();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    // 3. 定义公有的得到数据源的方法
    public static DataSource getDataSource() {
    return ds;
    }
    // 4. 定义得到连接对象的方法
    public static Connection getConnection() throws SQLException {
    return ds.getConnection();
    }
    // 5.定义关闭资源的方法
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
    if (rs != null) {
    try {
    rs.close();
    } catch (SQLException e) {}
    }
    if (stmt != null) {
    try {
    stmt.close();
    } catch (SQLException e) {}
    }
    if (conn != null) {
    try {
    conn.close();
    } catch (SQLException e) {}

    }

    }
    // 6.重载关闭方法
    public static void close(Connection conn, Statement stmt) {
    close(conn, stmt, null);
    }
    }
    测试类代码
    public class Demo03 {
    public static void main(String[] args) throws Exception {
    // 拿到连接
    Connection conn = DataSourceUtils.getConnection();
    // 执行sql语句
    String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";
    PreparedStatement pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, "李四");
    pstmt.setInt(2, 30);
    pstmt.setDouble(3, 50);
    int i = pstmt.executeUpdate();
    System.out.println("影响的函数: " + i);
    // 关闭资源
    DataSourceUtils.close(conn, pstmt);
    }
    }
    总结
    使用Jdbc工具类后可以简化代码,我们只需要写SQL去执行。

版权声明:原创作品,允许转载,转载时务必以超链接的形式表明出处和作者信息。否则将追究法律责任。来自海汼部落-123456789987654321,http://hainiubl.com/topics/75386
点赞
成为第一个点赞的人吧 :bowtie:
回复数量: 0
    暂无评论~~
    • 请注意单词拼写,以及中英文排版,参考此页
    • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
    • 支持表情,可用Emoji的自动补全, 在输入的时候只需要 ":" 就可以自动提示了 :metal: :point_right: 表情列表 :star: :sparkles:
    • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif,教程
    • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
    Ctrl+Enter