MySql的学习_JDBC(5)

JDBC概述

JDBC是 Java 访问数据库的标准规范(接口)
在这里插入图片描述
JDBC就是一套操作所有关系型数据库的规则(接口),而数据库厂商需要实现这套接口,提供数据库驱动jar包, 我们可以使用这套接口编程,真正执行的代码是对应驱动包中的实现类。

开发准备

数据准备

-- 创建 jdbc_user表 
CREATE TABLE jdbc_user ( 
   id INT PRIMARY KEY AUTO_INCREMENT , 
   username VARCHAR(50), 
   PASSWORD VARCHAR(50), 
   birthday DATE 
);

-- 添加数据 
INSERT INTO jdbc_user (username, PASSWORD,birthday) 
VALUES
('admin1', '123','1991/12/24'), 
('admin2','123','1995/12/24'), 
('test1', '123','1998/12/24'), 
('test2', '123','2000/12/24');

在这里插入图片描述

IDEA中建立仓库

首先在自己的D盘下创建一个名为MyJar的文件专门放包
这里把JDBC中的MySQL驱动包也放进去了
在这里插入图片描述
点击这里的+,直接关联Myjar文件,那么这个MyJar文件就可以用来当作是我们放包的仓库了
在这里插入图片描述

API使用: 1.注册驱动

JDBC规范定义的驱动接口: java.sql.Driver
MySql驱动包提供了实现类: com.mysql.jdbc.Driver

public class JDBCdemo01 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //1.注册驱动
        // forName 方法执行将类进行初始化
        Class.forName("com.mysql.jdbc.Driver");
    }
}

其中Class.forName(数据库驱动实现类)加载和注册数据库驱动,数据库驱动由数据库厂商MySql提供"com.mysql.jdbc.Driver"
这里Class类的forName方法 ,将类初始化了
Driver类的源码如下:

// Driver类是MySql提供的数据库驱动类, 实现了JDBC的Driver接口 java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    
    
    // 空参构造
   public Driver() throws SQLException {
    
    
   }
   //静态代码块,Class类的 forName()方法将Driver类 加载到内存, static代码块会自动执行
   //静态代码块是随着类的加载而加载
 static {
    
    
       try {
    
    
		     /*
		     DriverManager 驱动管理类
		     registerDriver(new Driver) 注册驱动的方法 registerDriver就是用来注册驱动的
		     注册数据库驱动
		     */
       		DriverManager.registerDriver(new Driver());
       } catch (SQLException var1) {
    
    
           throw new RuntimeException("Can't register driver!"); 
       } 
   } 
}

从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接用。 Class.forName 这句话可以省略。

API使用: 2.获得连接

  • Connection 接口,代表一个连接对象
  • 使用 DriverManager类的静态方法,getConnection可以获取数据库的连接
    在这里插入图片描述
    其中:
  • user:登录用户名
  • password:登录密码
  • url:访问mysql数据库的地址(要按格式)
    mysql url标准格式:
    jdbc:mysql://localhost:3306/db4
  • localhost:IP地址
  • 3306:端口号
  • db4:数据库名称
  • jdbc:mysql:这是协议,不变的
    在这里插入图片描述
    打印连接对象看看
public class JDBCdemo01 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        //2.获取连接 connection连接对象
        String url = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
        Connection con = DriverManager.getConnection(url,"root","hmyhmy");
        //打印连接对象
        System.out.println(con);
    }
}

在这里插入图片描述

API 使用: 3.获取语句执行平台

通过Connection 的 createStatement方法 获取sql语句执行对象
在这里插入图片描述

Statement : 代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
在这里插入图片描述

executeUpdate的使用

增删改用executeUpdate

public class JDBCdemo01 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        //2.获取连接 connection连接对象
        String url = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
        Connection con = DriverManager.getConnection(url,"root","hmyhmy");
        //打印连接对象
        //System.out.println(con);

        //3.获取语句执行平台 statement
        Statement statement = con.createStatement();

        //3.1通过statement对象的executeUpdate方法 创建一张表
        String sql = "create table test(id int,name varchar(20),age int);";
        int i = statement.executeUpdate(sql);//返回值是int类型,表示受影响的行数

        System.out.println(i);
        //关闭流
        statement.close();
        con.close();
    }
}

结果:
在这里插入图片描述
test表已创建成功
在这里插入图片描述

API 使用: 4.处理结果集

excuteQuery操作执行查询操作

ResultSet接口

ResultSet接口的作用是封装数据库查询的结果集,对结果进行遍历,取出每一条记录
方法:

  • next(): 判断是否有下一条数据,返回 boolean 类型,如果还有下一条记录,返回 true,否则返回 false
  • xxx getXxx(String or int):
    在这里插入图片描述
  • int getInt()表示获取的这列数据是int类型
    int getInt("列名")

    int getInt(1)表示第一列
  • String getString()表示获取的这列数据是String类型
    String getString("列名")

    String getString(1)表示第一列
public class JDBCdemo02 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //1.获取连接
        String url = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
        Connection con = DriverManager.getConnection(url,"root","hmyhmy");

        //2.获取语句执行平台对象
        Statement statement = con.createStatement();

        //3.执行查询操作 使用executeQuery
        String sql = "select * from jdbc_user;";
        //ResultSet 是结果集对象
        ResultSet resultset = statement.executeQuery(sql);

        //处理结果集对象 ResultSet
        boolean next=resultset.next();//判断是否有下一条数据
        System.out.println(next);

        //获取id
        int id = resultset.getInt("id");//通过列名方式
        System.out.println("通过列名方式获取第一列id:"+id);

        int id2 = resultset.getInt(1);
        System.out.println("通过列号方式获取第一列id:"+id2);

        //通过while循环 遍历获取resultset中数据
        while(resultset.next()){
    
    
            //id
            int id3 = resultset.getInt("id");
            System.out.print("当前id是:"+id3+"     ");
            //username
            String name = resultset.getString("username");
            System.out.print("当前username是"+name+"     ");
            //PASSWORD
            String password = resultset.getString("PASSWORD");
            System.out.print("当前PASSWORD是:"+password+"     ");
            //birthday
            Date birthday = resultset.getDate("birthday");
            System.out.println("当前birthday是:"+birthday);
        }

        //4.关闭流
        resultset.close();
        statement.close();
        con.close();
    }
}

在这里插入图片描述
在这里插入图片描述
这里就已经让指针向下移动了一格,所以结果从id为2开始

但是注意

必须要让next先行动,否则xxx getxxx()返回不了数据

API 使用: 5.释放资源

在这里插入图片描述

  • 需要释放的对象:ResultSet 结果集(只有查询会使用到),Statement 语句,Connection 连接
  • 释放原则:先开的后关,后开的先关。ResultSet ==> Statement ==> Connection
  • 放在哪个代码块中:finally 块
public class JDBCdemo03 {
    
    
    public static void main(String[] args) {
    
    
        String url = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
        Connection con =null;
        Statement statement =null;
        ResultSet resultset = null;
        
        try {
    
    
            //获取连接
            con = DriverManager.getConnection(url,"root","hmyhmy");
            //2.获取语句执行平台对象
            statement = con.createStatement();

            //3.执行查询操作 使用executeQuery
            String sql = "select * from jdbc_user;";
            resultset = statement.executeQuery(sql);
            
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        } finally {
    
    
            //finally 中代码始终会执行
            try {
    
    
                resultset.close();
                statement.close();
                con.close();
            } catch (SQLException throwables) {
    
    
                throwables.printStackTrace();
            }
            
        }
        
    }
}

步骤总结

  1. 获取连接
  2. 获取Statement对象
  3. 处理结果集(只在查询时处理)
  4. 释放资源

JDBC实现增删改查

编写JDBC工具类

  • 如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用。
  • “获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。
    JDBC工具类编写方式如下:
public class JDBCUtils {
    
    
    //1.将连接信息定义为 字符串常量
    public static final String DRIVERNAME = "com.mysql.jdbc.Driver";//对应注册驱动
    public static final String  URL = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";//对应获取连接
    public static final String  USER = "root";
    public static final String PASSWORD = "hmyhmy";

    //2.编写静态代码块
    static {
    
    
        //随着类的加载而加载
        try {
    
    
            //注册驱动
            Class.forName(DRIVERNAME);
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        }
    }

    //3.获取连接的 静态方法
    //返回值也是个Connection对象
    public static Connection getConnection(){
    
    
        try {
    
    
            //获取连接对象 并返回
            Connection connection = DriverManager.getConnection(URL,USER,PASSWORD);
            return connection;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return null;
        }

    }

    //4.关闭资源的方法
    public static void close(Connection con, Statement statement){
    
    
        if(con != null && statement != null){
    
    
            try {
    
    
                statement.close();
                con.close();
            } catch (SQLException throwables) {
    
    
                throwables.printStackTrace();
            }
        }

    }
    //重载一下,处理执行了查找操作的情况
    public static void close(Connection con, Statement statement, ResultSet resultSet){
    
    
        if(con != null && statement != null){
    
    
            try {
    
    
                statement.close();
                con.close();
                resultSet.close();
            } catch (SQLException throwables) {
    
    
                throwables.printStackTrace();
            }
        }

    }
}

这样一个JDBCUtils工具类就写好了

DML(增删改查)操作

插入记录

//插入数据
    @Test //添加junit架包 可让当前方法像main方法那样允许
    public void testInsert() throws SQLException {
    
    
        //1.通过JDBCUtils工具类 获取连接
        Connection connection = JDBCUtils.getConnection();//这是个静态方法

        //2.获取statement对象
        Statement statement = connection.createStatement();

        //2.1编写SQL
        String sql = "insert into jdbc_user values(null,'admin3','123','2020/11/11')";//这里张百万用单引号
        //2.2执行SQL
        int i = statement.executeUpdate(sql);//返回受影响的行数
        System.out.println(i);

        //3.关闭流
        JDBCUtils.close(connection,statement);
    }

返回值
在这里插入图片描述
在这里插入图片描述

更新操作

//更新操作 根据id修改username
    @Test
    public void testUpdate() throws SQLException {
    
    
        Connection con = JDBCUtils.getConnection();

        Statement statement = con.createStatement();

        String sql = "update jdbc_user set username='aaaaa' where id = 1";
        statement.executeUpdate(sql);
        JDBCUtils.close(con,statement);
    }

在这里插入图片描述

删除操作


//删除操作 删除id为1和2的数据
    @Test
    public void testDelete() throws SQLException {
    
    
        Connection con = JDBCUtils.getConnection();

        Statement statement = con.createStatement();

        String sql = "delete from jdbc_user where id in(1,2)";
        statement.executeUpdate(sql);
        JDBCUtils.close(con,statement);
    }

在这里插入图片描述

DQL操作

需求:查询username = 'admin3’的一条数据

public class TestDQL {
    
    
    public static void main(String[] args) throws SQLException {
    
    
        //1.通过JDBCUtils工具类 获取连接
        Connection connection = JDBCUtils.getConnection();//这是个静态方法

        //2.获取statement对象
        Statement statement = connection.createStatement();

        //2.1编写SQL
        String sql = "select * from jdbc_user where username = 'admin3'";
        ResultSet resultSet = statement.executeQuery(sql);

        //3.处理结果集
        while(resultSet.next()) {
    
    
            //通过列名的方式获取
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("PASSWORD");
            Date date = resultSet.getDate("birthday");
            System.out.println(id + " : " + username + " : " + password + " : " + date);
        }
        //4.释放资源
        JDBCUtils.close(connection,statement,resultSet);
    }
}

在这里插入图片描述

SQL注入问题

问题演示

例子数据如下
在这里插入图片描述

在这里插入图片描述
因为关键字OR的存在,使得SQL注入问题的语句为真,不知道正确用户名和密码也能完成登录操作

sql注入案例:用户登陆

需求:用户在控制台上输入用户名和密码, 然后使用 Statement 字符串拼接的方式 实现用户的登录

public class TestLogin01 {
    
    
    public static void main(String[] args) throws SQLException {
    
    
        //用户登录案例
        //1.通过JDBCUtils工具类 获取连接
        Connection connection = JDBCUtils.getConnection();

        //2.获取statement对象
        Statement statement = connection.createStatement();

        //3.获取用户输入的用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.nextLine();

        System.out.println("请输入密码:");
        String password = sc.nextLine();

        //4.拼接SQL语句
        String sql = "select * from jdbc_user where username = '" + name + "' and PASSWORD='"+password+"'";
        System.out.println(sql);

        //5.执行查询 获取结果集对象
        ResultSet resultSet = statement.executeQuery(sql);

        //6.处理结果集
        if(resultSet.next()){
    
    
            System.out.println("登录成功!");
            System.out.println("欢迎您:"+name);
        } else {
    
    
            System.out.println("登录失败");
        }

        //7.关闭流
        JDBCUtils.close(connection,statement,resultSet);
    }
}

在这里插入图片描述

非法操作:使用错误信息拼接也能使登录成功

在这里插入图片描述
用户输入的内容作为了 SQL 语句语法的一部分,改变了 原有SQL 真正的意义,以上问题称为 SQL 注入
这个问题的主要原因是使用了拼接SQL的操作

解决方法

解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进 行简单的字符串拼接

使用预处理对象解决SQL注入

使用预处理对象 PreparedStatement 是Statement接口的子接口
好处:

  1. 使用预处理对象 有预编译功能,提高SQL的执行效率
  2. 使用预处理对象 通过占位符的方式 设置参数 可以有效防止SQL注入
    在这里插入图片描述
    PreparedStatement 使用 ?占位符的方式来设置参数
    PreparedStatement继承于Statement
public class TestLogin02 {
    
    
   //SQL注入中用户会改变SQL原有的意思
   public static void main(String[] args) throws SQLException {
    
    
       //1.通过JDBCUtils工具类 获取连接
       Connection connection = JDBCUtils.getConnection();

       //2.获取PreparedStatement预处理对象
       //使用?占位符的方式来设置参数
       String sql = "select * from jdbc_user where username = ? and PASSWORD = ?";
       PreparedStatement ps = connection.prepareStatement(sql);

       //3.获取用户输入的用户名和密码
       Scanner sc = new Scanner(System.in);
       System.out.println("请输入用户名:");
       String name = sc.nextLine();

       System.out.println("请输入密码:");
       String password = sc.nextLine();

       //4.设置参数 使用setXXX(占位符的位置(从1开始,int类型),要设置的值)的方法设置占位符参数
       ps.setString(1,name);//设置第一个?值为name
       ps.setString(2,password);

       //5.执行查询
       ResultSet resultSet = ps.executeQuery();

       //6.处理结果集
       if(resultSet.next()){
    
    
           System.out.println("登录成功!");
           System.out.println("欢迎您:"+name);
       } else {
    
    
           System.out.println("登录失败");
       }
       //7.关闭流
       JDBCUtils.close(connection,ps,resultSet);
   }
}

这时候再想使用SQL注入非法登录就不行了
在这里插入图片描述

PreparedStatement的执行原理

在这里插入图片描述

  1. Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句
  2. PrepareStatement是预编译的SQL语句对象,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值
  3. PrepareStatement可以减少编译次数提高数据库性能

JDBC控制事务

数据准备

-- 创建账户表 
CREATE TABLE account( 
	-- 主键 
	id INT PRIMARY KEY AUTO_INCREMENT, 
	-- 姓名 
	NAME VARCHAR(10), 
	-- 转账金额 
	money DOUBLE 
);

-- 添加两个用户 
INSERT INTO account (NAME, money) VALUES ('tom', 1000), ('jack', 1000);

在这里插入图片描述

事务相关API

在这里插入图片描述

开发例子:使用JDBC操作事务

执行两次修改操作:tom-500,jack+500

public class TestJDBCConnection {
    
    

    public static void main(String[] args) {
    
    
        Connection con = null;
        PreparedStatement ps = null;
        try {
    
    
            //1.获取连接
            con = JDBCUtils.getConnection();

            //2.开启事务
            con.setAutoCommit(false);//手动提交事务

            //3.获取预处理对象 执行SQL (执行两次修改操作:tom-500,jack+500)
            //3.1tom账户-500
            ps = con.prepareStatement("update account set money = money - ? where name = ?");
            ps.setDouble(1,500.0);
            ps.setString(2,"tom");
            ps.executeUpdate();

            //3.2jack账户+500
            ps = con.prepareStatement("update account set money = money + ? where name = ?");
            ps.setDouble(1,500.0);
            ps.setString(2,"jack");
            ps.executeUpdate();

            //4.提交事务(正常情况)
            con.commit();
            System.out.println("转账成功");




        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
            //5.出现异常就回滚事务
            try {
    
    
                con.rollback();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        } finally {
    
    
            //6.释放资源
            JDBCUtils.close(con,ps);
        }
    }
}

若转账成功:
在这里插入图片描述
在这里插入图片描述

回滚

在3.1和3.2中加入语句

//模拟tom转账后出现异常
System.out.println(1/0);

在这里插入图片描述
在这里插入图片描述
执行了回滚操作,数据不发生变化

猜你喜欢

转载自blog.csdn.net/Maybe_do_it/article/details/120842377