Day6: JDBC

本文详细介绍了JDBC操作MySQL数据库的步骤,包括加载驱动、建立连接、执行SQL语句和释放资源。讨论了JDBC代码封装、SQL注入问题、PreparedStatement的使用以提高安全性与效率,还涉及了JDBC事务处理和数据库连接池的概念,如Druid等。

JDBC:是SUN公司为了简化开发人员对数据库的统一操作,提供的java操作数据库的规范。

一. JDBC的测试代码

编写java代码的步骤:

  • step1: 加载驱动;
  • step2: 连接数据库(DriverManager.getConnection),设置用户信息和url;
  • step3: 获得执行sql的对象(statement);
  • step4: 获得返回的结果集
  • step5: 释放连接
package lesson01;

import java.sql.*;

// 第一个jdbc程序
public class JdbcFirst {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");  // 固定写法
        
        // 2. 用户信息和url
        // useUnicode=true&characterEncoding=utf8&&useSSL=true
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=true";
        String username = "root";
        String password = "123456";   // 密码改成自己的
        
        // 3. 连接成功,数据库对象(connection代表数据库)
        Connection connection = DriverManager.getConnection(url,username,password);
        
        // 4. 执行SQL的对象
        Statement statement = connection.createStatement();
        
        // 5. 用执行SQL的对象 去 执行SQL,可能存在结果,查看返回的结果
        String sql = "SELECT * FROM `users`";
        ResultSet resultSet = statement.executeQuery(sql); //返回的结果集,封装了所有的查询结果

        while(resultSet.next())
        {
            System.out.println("id=" + resultSet.getObject("id"));
            System.out.println("name=" + resultSet.getObject("NAME"));
            System.out.println("password=" + resultSet.getObject("PASSWORD"));
            System.out.println("email=" + resultSet.getObject("email"));
            System.out.println("birthday=" + resultSet.getObject("birthday"));
        }
        // 6. 释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

二. JDBC代码解析

DriverManager:加载驱动 以及 连接数据库

//DriverManager.registerDriver(new Driver()); 原来的写法
Class.forName("com.mysql.cj.jdbc.Driver");//固定写法(查看Driver底层)

// connection代表数据库对象
Connection connection = DriverManager.getConnection(url,username,password);
// connection可以完成数据库的操作,例如:
//事务自动提交
//事务提交
//事务回滚
connection.commit();
connection.rollback();
connection.setAutoCommit();

URL和用户名

String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "123456";   // 密码改成自己的

//mysql --3306
//协议://主机地址:端口号/数据库名?参数1&参数2&参数3
// oralce -- 1521
//jdbc:oracle:thin:@localhost:1521:sid

Statement 执行SQL的对象
Jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。

只需要记住两个常用的语句statement.executeQuery()statement.executeUpdate()

  • statement.executeUpdate():用于数据库的增、删、改,操作成功返回一个正数,代表受影响的行数。
  • statement.executeQuery():查询操作,返回查询的结果集。
// 创建statement 执行SQL的对象
Statement statement = connection.createStatement();

// 编写sql语句
String sql = "SELECT * FROM `users`";
// 执行
ResultSet resultSet = statement.executeQuery(sql); //返回的结果集,封装了所有的查询结果

statement.executeUpdate();//插入,删除,更新,都用这个,返回一个受影响的行数;
statement.execute(); //执行任意的SQL
statement.executeQuery();//查询操作返回resultSet

ResultSet:查询的结果集
封装了所有的查询结果,再利用result.getObject(“字段名”)来获得具体查询数据。

resultSet.getObject();//在不知道列属性的情况下使用
//如果知道列的属性使用指定的类型
resultSet.getString();
resultSet.getInt();
resultSet.getBoolean();
....

resultSet.beforeFirst();//移动到最前面
resultSet.afterLast();//移动到最后面
resultSet.next();//移动到下一个数据
resultSet.previous();//移动到前一行
resultSet.absolute(row);//移动到指定行

释放资源:数据库很占用资源,一定要释放。

// 6. 释放连接
resultSet.close();
statement.close();
connection.close();

三. JDBC代码的封装

3.1 提取工具类

我们的JDBC 程序步骤,大部分都是一样的:
1、加载驱动;
2、连接数据库DriverManager;

3、获得执行SQL的对象Statement;
4、编写sql语句执行数据库操作,获得返回的结果集;
5、释放资源。

因此可以将1,2和5步进行封装,方便直接使用,每次编写只需要进行第3、4步的操作即可。

配置属性文件:主要包含数据库连接时的参数,在src文件夹下创建.properties属性文件。

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=true&serverTimezone=Asia/Shanghai
username=root
password=199784

封装一个类:主要封装加载驱动数据库连接释放资源的操作。

public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static {
        try {

            //获得类的加载器,加载资源;
            //这里是通过类获取反射对象,然后获取反射对象的类加载器,调用类加载器的获取资源的方法。一步一步来的
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dp.properties");

            //把流读入到properties里面
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //驱动只能加载一次:封装第1步
            Class.forName(driver);

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

    //获取连接:封装第2步
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    //释放连接资源:封装第5步
    public static void release(Connection connection, Statement statement, ResultSet resultSet) {
        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();
            }
        }
    }
}

3.2 增删改

增删改用的是statement.executeUpdate语句,返回的是受影响的行数。因此这三种操作是一样的,唯一不同点在编写sql语句时。

增(添加 Insert操作):

public class TestInsert {
    public static void main(String[] args) {

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();    // 直接获取连接,第1,2步

            statement = connection.createStatement();  // 获得sql的执行对象,第3步

            // 编写sql语句
            String sql = "INSERT INTO `users` (`id`,`NAME`,`PASSWORD`,`email`,`birthday`)\n" +
                    "VALUES (4,'hurunqiao','123456','2674568@qq.com','2020-02-01')";

            int i = statement.executeUpdate(sql);   // 第4步,返回的是受影响的行

            if(i > 0)
            {
                System.out.println("插入成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            // 释放资源,第5步
            JdbcUtils.release(connection,statement,resultSet);
        }
    }
}

删除(Delete 操作):

public class TestDelete {
    public static void main(String[] args) {

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();    // 直接获取连接

            statement = connection.createStatement();  // 获得sql的执行对象

            // 编写sql语句
            String sql = "DELETE FROM users WHERE id=4";

            int i = statement.executeUpdate(sql);

            if(i > 0)
            {
                System.out.println("删除成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            // 释放资源
            JdbcUtils.release(connection,statement,resultSet);
        }
    }
}

修改(更新 Update 操作):

public class TestUpdate {
    public static void main(String[] args) {

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();    // 直接获取连接

            statement = connection.createStatement();  // 获得sql的执行对象

            // 编写sql语句
            String sql = "UPDATE `users` SET `NAME`='hurunqiao' ,`email`='7721789@qq.com' WHERE `id`='4'";

            int i = statement.executeUpdate(sql);

            if(i > 0)
            {
                System.out.println("更新成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            // 释放资源
            JdbcUtils.release(connection,statement,resultSet);
        }
    }
}

3.3 查询

查询操作使用的是statement.executeQuery()语句,返回的是查询出的结果集,需要使用ResultSet resultSet对象来接收,再利用resultSet.getObject(“列名”)函数来得到我们具体的查询结果。

剩下的内容和增删改是一个模板,仍然只需要编写对应的sql语句。

public class TestSelect {
    public static void main(String[] args) {

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();   // 获取连接

            statement = connection.createStatement();  // 获得sql的执行对象

            String sql = "select * from `users`";

            resultSet = statement.executeQuery(sql);   // 执行查询语句,返回的是结果集

            while (resultSet.next())
            {
                System.out.println(resultSet.getObject("NAME"));  // 查询具体的内容
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(connection,statement,resultSet);
        }
    }
}

四. SQL的注入问题

sql存在漏洞,会被攻击,导致数据泄露。

public class SQL注入 {
    public static void main(String[] args) {
        // 正常查询
        // login("hurunqiao","123456");

        // sql注入攻击:传入脏数据
        login(" 'or '1=1"," 'or '1=1");
    }

    public static void login(String username,String password)
    {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();   // 获取连接

            statement = connection.createStatement();  // 获得sql的执行对象

            // SELECT * FROM `users` WHERE `NAME`='hurunqiao' AND `PASSWORD`='123456'
            // sql注入后的数据
            // SELECT * FROM `users` WHERE `NAME`=' 'or '1=1' AND `PASSWORD`='123456'
            String sql = "select * from `users` WHERE `NAME`='"+username+"' AND `PASSWORD`='"+password+"'";

            resultSet = statement.executeQuery(sql);   // 执行查询语句,返回的是结果集

            while (resultSet.next())
            {
                System.out.println(resultSet.getObject("NAME"));
                System.out.println(resultSet.getObject("PASSWORD"));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(connection,statement,resultSet);
        }
    }
}

五. PreparedStatement对象

PreparedStatement 可以防止SQL注入问题,且效率更高!

5.1 增删改

要注意,使用PreparedStatement对象要在创建statement前预先编译编译sql语句,使用 ? 占位符来表示要传入的数据,之后再利用statement.set()来传入数据。而在执行语句statement.executeUpdate()statement.executeQuery()不需要再传入sql语句。

增(添加数据)

public class TestInsert {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement statement = null;

        try {
            connection = JdbcUtils.getConnection();  // 封装后的第1,2步

            // 区别
            // 使用?占位符代替参数,更加美观
            String sql = "INSERT INTO `users` (`id`,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";

            statement = connection.prepareStatement(sql);  // 预编译sql,先写sql,然后不执行(第3步)

            // 手动给参数赋值
            statement.setInt(1,5);   // 1代表第1个占位符(id)
            statement.setString(2,"xuziqi");   // 2代表第2个占位符(name)
            statement.setString(3,"123456");   // 3代表第3个占位符(password)
            statement.setString(4,"1235676@qq.com");   // 4代表第4个占位符(email)
            // 注意点: sql.Date   数据库   java.sql.Date()
            //        util.Date   java    new Date().getTime()
            statement.setDate(5,new java.sql.Date(new Date().getTime()));   // 5代表第5个占位符(birthday)

            // 执行(第4步)
            int i = statement.executeUpdate();   // 没有再传参

            if(i > 0)
            {
                System.out.println("插入成功!'");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //(第5步)
            JdbcUtils.release(connection,statement,null);
        }
    }
}

删(删除数据)

public class TestDelete {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement statement = null;

        try {
            connection = JdbcUtils.getConnection();

            // 区别
            // 使用?占位符代替参数,更加美观
            String sql = "delete from `users` where `id`=?";

            statement = connection.prepareStatement(sql);  // 预编译sql,先写sql,然后不执行

            // 手动给参数赋值
            statement.setInt(1,5);   // 传入要删除的id

            // 执行
            int i = statement.executeUpdate();   // 没有再传sql

            if(i > 0)
            {
                System.out.println("删除成功!'");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(connection,statement,null);
        }
    }
}

改(更新数据)

public class TestUpdate {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement statement = null;

        try {
            connection = JdbcUtils.getConnection();

            // 区别
            // 使用?占位符代替参数,更加美观
            String sql = "update `users` set `NAME`=? where id=?";

            statement = connection.prepareStatement(sql);  // 预编译sql,先写sql,然后不执行

            // 手动给参数赋值
            statement.setString(1,"kuangshen");
            statement.setInt(2,4);   // 传入要删除的id

            // 执行
            int i = statement.executeUpdate();   // 没有再传sql

            if(i > 0)
            {
                System.out.println("更新成功!'");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(connection,statement,null);
        }
    }
}

5.2 查询

查询仍然使用statement.executeQuery()语句,剩下的相同。

public class TestSelect {
    public static void main(String[] args) {

        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();

            String sql = "select * from users where `id`=?";

            statement = connection.prepareStatement(sql);  // 传递参数

            statement.setInt(1,1);   // 查询id为1的数据

            // 执行
            resultSet = statement.executeQuery();

            if(resultSet.next())
            {
                System.out.println(resultSet.getObject("NAME"));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(connection,statement,resultSet);
        }
    }
}

六. JDBC操作事务

在JDBC中依然可以进行事务的操作,它的步骤仍然和sql语句开启事务类似:

  • step1: 关闭数据库的自动提交功能,自动开启事务。在JDBC中只需要connection.setAutoCommit(false)这一个语句就可。
  • step2: 编写事务执行的sql语句。
  • step3: 业务执行完毕,提交事务connection.commit()
  • step4: 可以在catch语句中定义回滚语句connection.rollback(),如果失败就会回滚。

用事务模拟银行转账操作,在JDBC中实现。

public class TestTransaction1 {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();

            // 关闭数据库的自动提交功能,会自动开启事务
            connection.setAutoCommit(false);

            // 用事务模拟转账业务
            String sql1 = "update account set money=money-100 where name='C'";
            statement = connection.prepareStatement(sql1);
            statement.executeUpdate();

            String sql2 = "update account set money=money+100 where name='B'";
            statement = connection.prepareStatement(sql2);
            statement.executeUpdate();

            // 业务完毕,提交事务
            connection.commit();
            System.out.println("成功!");

        } catch (SQLException e) {
            try {
                connection.rollback();   // 如果失败就回滚
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            JdbcUtils.release(connection,statement,resultSet);
        }
    }
}

七. 数据库连接池

数据库连接–执行完毕–释放,从连接到释放是十分浪费系统资源的。

池化技术: 准备一些预先的资源,过来就连接预先准备好的。

参数的设置:

  • 最小连接数;
  • 最大连接数;
  • 等待超时;

编写连接池,只需要实现一个接口:DataSource

目前开源的数据源实现有,在使用了这些数据库连接池后,我们在项目开发中就不需要编写连接数据库的代码了。

  • DBCP;
  • C3P0;
  • Druid:阿里巴巴;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值