LAGOU任务五 JDBC

1 JDBC概述
1.1 客户端操作数据库的方式
(1)方式1:使用第三方客户端来访问MySQL:SQLyog
在这里插入图片描述
(2)方式2:使用命令行
在这里插入图片描述

(3)我们今天要学习的是通过java程序来访问MySQL数据库
1.2 什么是JDBC
JDBC(Java Data Base Connectivity)是java访问数据库的标准规范,是一种用于执行SQL语句的java API,可以为多种关系数据库提供统一访问,它由一组用java语言编写的类和接口组成,是java访问数据库的标准规范。
1.3 JDBC原理
JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库,每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生产厂商提供。
在这里插入图片描述
总结:
JDBC就是有sun公司定义的一套操作所有关系型数据库的规则(接口),而数据库厂商需要实现这套接口,提供数据库驱动jar包,我们可以使用这套接口编程,真正执行的代码是对应驱动包中的实现类。
2 JDBC开发
2.1 数据准备

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

USE db4;

-- 添加数据
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');

2.2 MySql驱动包
1 将MySQL驱动包添加到jra包库文件夹中,Myjar文件夹,用于存放当前项目需要的所有jar包
在这里插入图片描述
2 在idea中配置jar包库的位置
在这里插入图片描述
3 创建一个新的项目jdbc_task01,配置jar包库
在这里插入图片描述
在这里插入图片描述
2.3 API使用:1 注册驱动
JDBC规范定义驱动接口:java.sql.Driver
MySql驱动包提供了实现类:com.mysql.jdbc.Driver

加载注册驱动的方式描述
Class.forName(数据库驱动实现类)加载和注册数据库驱动,数据库驱动由数据库厂商MySql提供"com.mysql.jdbc.Driver"

(1)代码示例

package com.lagou.demo01;

public class JDBCDemo01 {

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

}

(2)为什么这样可以注册驱动?
我们知道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) 注册驱动的方法 
			注册数据库驱动 
			*/
			DriverManager.registerDriver(new Driver()); 
		} catch (SQLException var1) { 
		throw new RuntimeException("Can't register driver!"); 
		} 
	} 
}

注意:从JDBC3开始,目前已经普遍使用的版本,可以不注册驱动而直接使用,ClassforName这句话可以省略
2.4 API使用:2 获得连接
Connection接口:代表一个连接对象,具体的实现类由数据库的厂商实现
使用DriverManager类的静态方法getConnection可以获取数据库的连接

获取连接的静态方法说明
Connection getConnection(String url,String user,String password)通过连接字符串和用户名,密码来获取数据库连接对象

(1)getConnection方法 3个 连接参数说明

连接参数说明
user登录用户名
password登录密码
urlmysql URL的格式 jdbc:mysql://localhost:3306/db4

(2)对URL的详细说明

jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8

JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔
第一部分是协议jdbc,这是固定的;
第二部分是子协议,就是数据库名称,连接mysql数据库,第二部分当然是mysql了;
第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别由数据库服务器的ip地址(localhost)、端口号(3306),以及要使用的数据库名称组成。
在这里插入图片描述

package com.lagou.demo01;

import java.sql.Connection;
import java.sql.DriverManager;

public class JDBCDemo01 {

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

        //2.获取连接url,用户名,密码
        String url = "jdbc:mysql://localhost:3306/db4";
        Connection con = DriverManager.getConnection(url,"root","520555");

        //com.mysql.jdbc.JDBC4Connection@2e3fc542
        System.out.println(con);
    }

}

2.5 API使用:3 获取语句执行平台
通过Connection的createStatement方法 获取sql语句执行对象

Connection接口中的方法说明
Statement createStatement()创建SQL语句执行对象

Statement:代表一条语句对象,用于发送SQL语句给服务器,用于执行静态SQL语句并返回它所生成结果的对象。

Statement类 常用方法说明
int executeUpdate(String sql);执行insert update delete语句,返回int类型,代表受影响的行数
ResultSet executeQuery(String sql);执行select语句,返回ResultSet结果集对象

代码示例:

public class JDBCDemo01 {

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

        //2.获取连接url,用户名,密码
        String url = "jdbc:mysql://localhost:3306/db4";
        Connection con = DriverManager.getConnection(url,"root","520555");

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

        //4.执行创建表操作
        String sql = "create table test01(id int,name varchar(20),age int)";

        //5.增删改操作 使用executeUpdate,增加一张表
        int i = statement.executeUpdate(sql);  //返回值是int类型 表示受影响的行数

        //6.返回值是受影响的函数
        System.out.println(i);

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

2.6 API使用:4 处理结果集
只有在进行查询操作时,才会处理结果集
代码示例:

public class JDBCDemo02 {

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

        //2.获取连接
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db4","root","123456");

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

        //4.执行查询操作 使用executeQuery()
        String sql = "SELECT * FROM jdbc_user;";

        //resultSet 是结果集对象
        ResultSet resultSet = statement.executeQuery(sql);

        //处理结果集对象 resultSet
     }
}

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

ResultSet接口方法说明
boolean next()(1)游标向下一行(2)返回boolean类型,如果还有下一条记录,返回true,否则返回false
xxx getXxx(String or int)(1)通过列名,参数是String类型,返回不同的类型(2)通过列号,参数是整数,从1开始,返回不同的类型

在这里插入图片描述
代码示例:

public class JDBCDemo02 {

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

        //2.获取连接
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db4","root","520555");

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

        //4.执行查询操作 使用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);
//
//        int id01 = resultSet.getInt(1);
//        System.out.println("通过列号的方式获取:"+id01);

        //通过while循环遍历获取resultSet中的数据
        while(resultSet.next())
        {
            //获取id
            int id = resultSet.getInt("id");

            //获取姓名
            String username = resultSet.getString("username");

            //获取密码
            String password = resultSet.getString("password");

            //获取生日
            Date birthday = resultSet.getDate("birthday");

            System.out.println(id+":"+username+":"+password+":"+birthday);
        }


        //5.关闭流操作
        resultSet.close();
        statement.close();
    }
}

2.7 API使用:5 释放资源
(1)需要释放的对象:ResultSet结果集,Statement语句,Connection连接
(2)释放原则:先开的后关,后开的先关。ResultSet==>Statement==>Connection
(3)放在哪个代码块中:finally块
与IO流一样,使用后的东西都需要关闭!关闭的顺序是先开后关,先得到的后关闭,后得到的先关闭
在这里插入图片描述
代码示例:

public class JDBCDemo03 {

    public static void main(String[] args) {
        ResultSet resultSet = null;
        Statement statement = null;
        Connection connection = null;
        try {
            //1.注册驱动 省略
            //2.获取连接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db4","root","520555");

            //3.获取语句执行对象
            statement = connection.createStatement();

            //4.执行SQL
            String sql = "select * from jdbc_user";
            resultSet = statement.executeQuery(sql);

            //5.处理结果集对象

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //finally中的代码始终会执行
            try {
                resultSet.close();
                statement.close();
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

2.8 步骤总结
1 获取驱动
2 获取连接
3 获取Statement对象
4 处理结果集(只在查询时处理)
5 释放资源

3 JDBC实现增删改查
3.1 JDBC工具类
什么时候自己创建工具类?
=》如果一个功能经常要用到,我们建议把这个功能做出一个工具类,可以在不同的地方重用。
=》"获取数据库连接"操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。
工具类包含的内容
(1)可以把几个字符串定义成常量:用户名,密码,URL,驱动类
(2)得到数据库的连接:getConnection()
(3)关闭所有打开的资源
代码示例:

/**
 * 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 = "520555";

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

    //3.获取连接的静态方法
    public static Connection getConnection(){
        try {
            //获取连接对象
            Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);

            //返回连接对象
            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

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

    public static void close(Connection con, Statement st, ResultSet rs){
        if (rs!=null)
        {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        close(con,st);
    }

}

3.2 DML操作
3.2.1 插入记录
解决插入中文乱码问题(一劳永逸)

jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
charaterRncoding=UTF-8 指定字符的编码、解码格式

增删改操作代码演示:

public class TestDML {

    /**
     * 插入数据
     */
    @Test
    public void testInsert() throws SQLException {

        //1.通过JDBCUtils工具类,获取连接
        Connection con = JDBCUtils.getConnection();


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

        //2.1编写SQL
        String sql = "insert into jdbc_user values(null,'张百万','123','2020/11/11')";

        //2.2执行SQL
        int i = statement.executeUpdate(sql);
        System.out.println(i);

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

    /*
    * 更新操作,根据id修改用户名
    * */
    @Test
    public void testUpdate() throws SQLException {
        Connection connection = JDBCUtils.getConnection();

        Statement statement = connection.createStatement();

        String sql = "update jdbc_user set username = '刘能' where id = 1";

        statement.executeUpdate(sql);

        JDBCUtils.close(connection,statement);
    }

    /**
     * 删除操作
     * 删除id为1和2的数据
     * @throws SQLException
     */
    @Test
    public void testDelete() throws SQLException {

        Connection connection = JDBCUtils.getConnection();

        Statement statement = connection.createStatement();

        String sql = "delete from jdbc_user where id in(1,2)";

        statement.executeUpdate(sql);

        JDBCUtils.close(connection,statement);

    }
}

3.3 DQL操作
3.3.1 查询姓名为张百万的一条记录

public class TestDQL {

    //查询姓名为张百万的一条记录
    public static void main(String[] args) throws SQLException {

        //1.获取连接
        Connection connection = JDBCUtils.getConnection();

        //2.创建statement对象
        Statement statement = connection.createStatement();

        //3.编写SQL
        String sql = "select * from jdbc_user where username = '张百万'";
        ResultSet resultSet = statement.executeQuery(sql);

        //4.处理结果集
        while (resultSet.next()){
            //通过列名的方式获取
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            Date birthday = resultSet.getDate("birthday");

            System.out.println(id+":"+username+":"+password+":"+birthday);
        }
        
        //5.释放资源
        JDBCUtils.close(connection,statement,resultSet);
        
    }
}

4 SQL注入问题
4.1 Sql注入问题演示
(1)向jdbc_user表中 插入两条数据

# 插入两条数据
INSERT INTO jdbc_user VALUES(NULL,'jack','123456','2020/2/24');
INSERT INTO jdbc_user VALUES(NULL,'tom','123456','2020/2/24');

(2)SQL注入演示

# SQL注入演示
-- 填写一个错误的密码
SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1';

如果这是一个登陆操作,那么用户就登陆成功了,显然这不是我们想要看到的结果
4.2 sql注入案例:用户登陆
需求:用户在控制台上输入用户名和密码,然后使用Statement字符串拼接的方式实现用户的登录。
步骤:
(1)得到用户从控制台上输入的用户名和密码来查询数据库
(2)写一个登录的方法
(a)通过工具类得到连接
(b)创建语句对象,使用拼接字符串的方式生成SQL语句
(c)查询数据库,如果有记录则表示登录成功,否则登录失败
(d)释放资源

sql注入方式:123

代码示例:

public class TestLogin01 {

    /**
     * 用户登录案例
     * 使用Statement字符串拼接的方式完成查询
     * @param args
     */
    public static void main(String[] args) throws SQLException {

        //1.获取连接
        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 pass = sc.nextLine();
        System.out.println(pass);

        //4.拼接Sql,执行查询
        String sql = "select * from jdbc_user " + "where username = " + "'"+name+"'"+"and password = "+"'"+pass+"'";

        System.out.println(sql);
        ResultSet resultSet = statement.executeQuery(sql);

        //5.处理结果集,判断结果集是否为空
        if (resultSet.next())
        {
            System.out.println("登录成功!欢迎您:"+name);
        }else{
            System.out.println("登陆失败!");
        }

        //释放资源
        JDBCUtils.close(connection,statement,resultSet);
    }

}

4.3 问题分析
(1)什么是SQL注入?
我们让用户输入的密码和SQL语句进行字符串拼接,用户输入的内容作为了 SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。
(2)如何实现的注入
根据用户输入的数据,拼接处的字符串

select * from jdbc_user where username = 'abc' and password = 'abc' or '1'='1' 
name='abc' and password='abc' 为假 '1'='1' 真 
相当于 select * from user where true=true; 查询了所有记录

在这里插入图片描述
(3)如何解决
要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接
5 预处理对象
5.1 PreparedStatement 接口介绍
PreparedStatement 是 statement接口的子接口,继承于父接口中所有的方法,它是一个预编译的SQL语句对象
预编译:是指SQL语句被预编译,并存储在PreparedStatement对象中,然后可以使用此对象多次高效的执行该语句
5.2 PreparedStatement 特点
因为有预先编译的功能,提高SQL的执行效率
可以有效地防止SQL注入的问题,安全性更高
5.3 获取PreparedStatement对象
通过Connection创建PreparedStatement对象

Connection接口中的方法说明
PreparedStatement prepareStatement(String sql)指定预编译的SQL语句。 SQL语句中使用占位符 创建一个语句对象

5.4 PreparedStatement接口常用方法

常用方法说明
int executeUpdate();执行insert update delete语句
ResultSet executeQuery();执行select语句,返回结果集对象ResultSet

5.5 使用PreparedStatement的步骤
(1)编写SQL语句,未知内容使用 占位符

"SELECT * FROM jdbc_user WHERE username = ? AND password = ?"

(2)获得PreparedStatement对象
(3)设置实际参数:setXxx(占位符的位置,真实的值)
(4)执行参数化SQL语句
(5)关闭资源

setXxx重载方法说明
void setDouble(int parameterindex,double x)将指定参数设置为给定java double值
void setInt(int parameterIndex,int x)将指定参数设置为给的java int值
void setString(int parameterIndex,String x)将指定参数设置为给定java String值
void setObject(int parameterIndex,Object x)使用给定对象设置指定参数的值

5.6 使用PreparedStatement完成登录案例
使用PreparedStatement预处理对象,可以有效的避免SQL注入
在这里插入图片描述
步骤:
1 获取数据库连接对象
2 编写SQL使用 占位符方式
3 获取预处理对象(预编译对象会将sql发送给数据库 进行预编译)
4 提示用户输入用户名&密码
5 设置实际参数:setXxx(占位符的位置,真实的值)
6 执行查询获取结果集
7 判断是否查询到数据
8 关闭资源

public class TestLogin02 {

    /**
     * 使用预编译对象 PreparedStatement 完成登录案例
     * @param args
     */
    public static void main(String[] args) throws SQLException {

        //1.获取连接
        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 pass = sc.nextLine();

        //4 设置占位符参数 使用setXxx(占位符的位置(整数),要设置的值)的方法设置占位符的参数
        ps.setString(1,name);  //设置第一个问号值为name
        ps.setString(2,pass);

        //5.执行查询 处理结果集
        ResultSet resultSet = ps.executeQuery();

        if (resultSet.next())
        {
            System.out.println("登录成功!欢迎您:"+name);
        }else{
            System.out.println("登陆失败!");
        }

        //6.释放资源
        JDBCUtils.close(connection,ps,resultSet);
    }
}

5.7 PreparedStatement的执行原理
分别使用Statement对象和PreparedStatement对象进行插入操作
代码示例:

public class TestPS {

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

        Connection con = JDBCUtils.getConnection();

        Statement statement = con.createStatement();

        statement.executeUpdate("insert into jdbc_user values (null ,'张三','123456','2000/12/26')");
        statement.executeUpdate("insert into jdbc_user values (null ,'李四','654321','1900/12/26')");

        PreparedStatement ps = con.prepareStatement("insert into jdbc_user values (?,?,?,?)");

        ps.setString(1,null);
        ps.setString(2,"小斌");
        ps.setString(3,"666");
        ps.setString(4,"1992/12/11");

        ps.executeUpdate();

        ps.setString(1,null);
        ps.setString(2,"小李");
        ps.setString(3,"2333");
        ps.setString(4,"1897/09/08");

        ps.executeUpdate();
    }
}

在这里插入图片描述
5.8 Statement和PreparedStatement的区别?
1 Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句。
2 PrepareStatement是预编译的SQL语句对象,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值。
3 PrepareStatement可以减少编译次数提高数据库性能。
6 JDBC控制事务
之前我们是使用MySQL的命令来操作事务,接下来我们使用JDBC来操作银行转账的事务
6.1 数据准备

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

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

6.2 事务相关API
我们使用Connection中的方法实现事务管理

方法说明
void setAutoCommit(boolean autoCommit)参数是true或false,如果设置为false,表示关闭自动提交,相当于开启事务
void commit()提交事务
void rollback()回滚事务

6.3 开发步骤
1 获取连接
2 开启事务
3 获取到PreparedStatement,执行两次更新操作
4 正常情况下提交事务
5 出现异常回滚事务
6 最后关闭资源
6.4 代码示例

public class TestJDBCTransaction {

    public static void main(String[] args) {

        Connection con = null;

        PreparedStatement ps = null;

        try {

            con = JDBCUtils.getConnection();

            con.setAutoCommit(false);

            ps = con.prepareStatement("update account set money = money - ? where name = ? ");

            ps.setDouble(1,500.0);
            ps.setString(2,"tom");

            ps.executeUpdate();

            System.out.println(1/0);

            ps = con.prepareStatement("update account set money = money + ? where name = ? ");

            ps.setDouble(1,500.0);
            ps.setString(2,"jack");
            ps.executeUpdate();

            con.commit();
            System.out.println("转账成功!");

        } catch (SQLException e) {
            e.printStackTrace();
            try {
                con.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            JDBCUtils.close(con,ps);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值