DataBase | 01 JDBC

JDBC

1. JDBC基本概念

1.1 JDBC的全名

Java DataBase Connectivity 即Java数据库连接

1.2 JDBC的本质

由官方定义的一套操作所有关系数据库的规则 (Java中即接口)

使得用户可以使用统一的Java代码可以操作所有的关系型数据库

实现类由数据库厂商针对各自的数据库产品编写打包提供给用户 叫做数据库驱动

编写代码时使用JDBC接口编程,运行时执行的是指定驱动jar包中的实现类方法

  • 没有JDBC时的数据库访问

    直接访问数据库,对于不同的数据库使用不同的方式访问

在这里插入图片描述

  • 有JDBC时的数据库访问

    通过调用JDBC访问数据库,访问时不用考虑访问的具体实现,由数据库厂商提供具体的驱动实现

https://gitee.com/nothingYHN/picgo/raw/master/img//20201028172958.png

1.3 JDBC的地位

JDBC是Java访问数据库的基石,之后的许多技术框架都是对JDBC的封装

1.4 JDBC的体系结构

JDBC接口的两个层次:

  • 面向应用的接口:抽象接口,供程序开发者使用
  • 面向数据库的接口:供数据库厂商编写相应的驱动

2. JDBC基本使用步骤

JDBC八条

  1. 导入所使用数据库的驱动jar包
  2. 注册驱动
  3. 获取数据库连接对象
  4. 定义SQL语句
  5. 获取执行SQL语句的对象
  6. 执行SQL,接收返回结果
  7. 处理结果
  8. 释放资源

Tips:释放资源的顺序,先申请的最后释放,后申请的最先释放

示例

//JDBC的基本使用步骤
//使用Statement执行
/**
 * 增删改操作
 */ 
public class JdbcDemo1 {
    public static void main(String[] args){
        Connection conn = null;
        Statement stmt = null;
        try{
            //注册驱动,使用MySQL数据库
            Class.forName("com.mysql.cj.jdbc.Driver");
            //创建连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///testdb","root","1234");
            //定义SQL语句
            String sql = "update test set bonus=1500 where id=2;";
            //获取执行语句的对象
            stmt = conn.createStatement();
            //执行SQL语句,接收返回结果
            int count = stmt.executeUpdate(sql);
            //处理结果
            System.out.println(count);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //释放资源,先申请的后释放,后申请的先释放
            if(stmt != null){
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

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

/**
 * 查询操作
 */
public class JdbcDemo2 {
    public static void main(String[] args){
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        List<User> users = null;
        try{
            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //创建连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///testdb","root","1234");
            //定义SQL语句
            String sql = "select * from test;";
            //获取执行语句的对象
            stmt = conn.createStatement();
            //执行SQL语句,接收返回结果
            rs = stmt.executeQuery(sql);
            //处理结果
            users = new ArrayList<User>();
            while(rs.next()){
                User user = new User();
                user.setId(rs.getInt(1));
                user.setName(rs.getString("name"));
                user.setBonus(rs.getInt("bonus"));
                users.add(user);
            }
            for(User user : users){
                System.out.println(user);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //释放资源
            if(rs != null){
                try{
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

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

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

//使用PreparedStatement执行
public class JdbcDemo3 {
    public static void main(String[] args){
        Connection conn = null;
        PreparedStatement stmt = null;
        try{
            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //创建连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///testdb","root","1234");
            //定义SQL语句
            String sql = "update test set bonus=? where id=?;";
            //获取执行语句的对象
            stmt = conn.prepareStatement(sql);
            //给可变参数赋值
            stmt.setInt(1,1000);
            stmt.setInt(2,2);
            //执行SQL语句,接收返回结果
            int count = stmt.executeUpdate();
            //处理结果
            System.out.println(count);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //释放资源
            if(stmt != null){
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

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

3. JDBC中常用接口和类详解

以下所有的类和接口在Java.sql包中

3.1 Class DriveManager

  • Class DriveManager:驱动管理对象

实现功能:

  1. 注册驱动,指明程序所使用的数据库驱动

    方法:

    在这里插入图片描述

  2. 获取数据库连接

    方法:

    在这里插入图片描述
    参数url数据库路径的写法:jdbc:mysql:// ( IP地址 ) : ( 端口号 ) / ( 数据库名称 )

Tips 3.1:注册驱动代码

使用MySQL数据库时代码为:Class.forName(“com.mysql.cj.jdbc.Driver”)

问题:这其中并没有使用到registerDriver方法,如何注入驱动

关系:com.mysql.cj.jdbc.Driver类中包含了一个静态代码块其中执行了registerDriver方法

3.2 Interface Connection

  • Interface Connection:数据库连接对象(Java和数据库之间的桥梁)

实现功能:

  1. 获取执行SQL的对象

    方法:

    使用Statement对象

    在这里插入图片描述

    使用PreparedStatement对象

    在这里插入图片描述

  2. 管理事务,进行事务控制

    开启事务:将自动提交改为手动提交、
    在这里插入图片描述

    提交事务:事务正常完成,提交结果

    在这里插入图片描述

    回滚事务:事务异常结束,回滚操作

    在这里插入图片描述

3.3 Interface Statement

  • Interface Statement:执行静态SQL语句的对象

    静态SQL语句:SQL语句的参数都是给定的

实现功能:

  1. 执行静态SQL语句

    方法:

    执行任意的SQL语句
    在这里插入图片描述

    执行DML(insert、update、delete)语句或DDL(create、alter、drop)语句
    在这里插入图片描述

    返回值表示语句影响的行数,可以判断是否执行成功

    • DML语句大于0表示执行成功,数值表示语句影响的行数
    • DDL语句等于0表示执行成功
    • 小于0表示语句执行失败

    执行DQL(select)语句
    在这里插入图片描述

Tips 3.2:SQL注入问题

在拼接SQL时有SQL特殊关键字( OR、’ ’ 等等 )参与字符串拼接,会造成数据库的安全性问题

  • SQL注入问题代码
public class JdbcDemo6 {
    //客户端方法
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("用户名:");
        String username = sc.nextLine();
        System.out.println("密码:");
        String password = sc.nextLine();
        boolean flag = login(username,password);
        if(flag){
            System.out.println("登陆成功");
        }else{
            System.out.println("登陆失败");
        }
    }

    //登录方法
    public static boolean login(String username,String password){
        //当用户名或密码为空时返回False
        if(username == null || password == null){
            return false;
        }
        //连接数据库查找用户名和密码是否有匹配
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql:///testdb","root","1234");
            String sql = "select * from login_test where username='"+username+
                "'and password='"+password+"';";
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            //当结果集有数据就返回True,表示有匹配
            return rs.next();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException throwables){
                    throwables.printStackTrace();
                }
            }
            if(stmt != null){
                try{
                    stmt.close();
                }catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(conn != null){
                try{
                    stmt.close();
                }catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return false;
    }
}

测试

  • 数据库信息
    在这里插入图片描述

  • 用户名为"aaa",密码为"1234",登陆成功
    在这里插入图片描述

  • 用户名为"aaa",密码为"2345",登陆失败
    在这里插入图片描述

  • 用户名为任意值,密码为"a’ or ‘a’ = 'a",登陆成功
    在这里插入图片描述

问题:

​ 当密码为"a’ or ‘a’ = 'a" 时

​ 实际的SQL语句为select * from account where username=‘example’ and password=‘a’ or ‘a’ = ‘a’

​ 这时可以获取到数据库中所有的记录,由于返回值不为空所以可以登录成功

​ 这就是SQL注入问题,会对于数据库的安全性产生影响

解决方式:使用下面的对象PreparedStatement执行预编译SQL语句

3.4 Interface PreparedStatement

  • Interface PreparedStatement:执行预编译SQL语句的对象 常用来解决SQL注入问题

    预编译SQL语句:SQL语句中参数可变,需要参数的位置在语句中使用占位符"?"

实现功能:

  1. 给预编译SQL语句中的参数赋值

    方法:

    形如:setType( parameterIndex ,x )
    在这里插入图片描述
    参数parameterIndex表示SQL语句中占位符的位置,计数从1开始

    参数x表示对应占位符的值,Type表示参数x的数据类型

  2. 执行预编译SQL语句

    方法:
    在这里插入图片描述
    在这里插入图片描述

Tips 3.3:Statement and PreparesdStatement

注:以增删改操作为例,只关注执行语句的部分

//Statement
//定义SQL语句
String sql = "update test set bonus=1500 where id=2;";
//获取执行语句的对象
stmt = conn.createStatement();
//执行SQL语句,接收返回结果
int count = stmt.executeUpdate(sql);

//PreparesdStatement
//定义SQL语句
String sql = "update test set bonus=? where id=?;";
//获取执行语句的对象
stmt = conn.prepareStatement(sql);
//给可变参数赋值
stmt.setInt(1,1500);
stmt.setInt(2,2);
//执行SQL语句,接收返回结果
int count = stmt.executeUpdate();

对比:

  • 两个对象针对于不同的SQL语句

    静态语句使用Statement执行

    预编译语句使用PreparedStatement执行,在执行前要使用setType给可变参数赋值

  • 使用Statement对象时,在执行方法时注入SQL语句

    使用PreparedStatement对象时,在使用连接创建对象时注入SQL语句,执行时无参数

3.5 Interface ResultSet

  • Interface ResultSet:结果集对象

实现功能:

  1. 封装查询结果

    方法:

    游标向下移动一行

    Tips:游标最开始指向表头行
    在这里插入图片描述

    返回值判断当前行是否为最后一行末尾即是否还有数据,True表示有数据

    获取数据

    形如:getType( int columnIndex )或getType( String columnLabel )
    在这里插入图片描述
    在这里插入图片描述

    Type指定获取数据的类型

    参数为int类型时表示按列的编号获取数据(列数从1开始)

    参数为String类型时表示按列的名称获取数据

  2. 遍历结果集

    使用while循环遍历结果集 while(rs.next( ))

4. JDBC工具类抽取

JdbcUtil–抽取实现注册驱动、获取连接、释放资源功能的重复代码,降低代码重复度

  • 工具类代码

Tips 4.1:几点注意

  • 见注释
//1.工具类的方法多使用静态方法,方便进行调用
public class JdbcUtil {
    private static Properties prop;
    private static String url;
    private static String username;
    private static String password;

    /**
     * 4.使用静态代码块读配置文件、注册驱动
     * 配置文件只需要读取一次即可获得属性值,驱动也只需要注册一次
     * 这两步操作在初始化时直接执行一次即可
     */
    static{
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            prop = new Properties();
            //3.获取src下的资源文件路径
            //获取类加载器:用于加载类的字节码文件进内存的工具
            ClassLoader classLoader = JdbcUtil.class.getClassLoader();
            //获取对应名称的资源文件,返回值文件的绝对路径URL
            URL path = classLoader.getResource("jdbcConfig.properties");
            //获取字符串路径 
            String stringPath = path.getPath();
            //读取配置文件prop.load方法
            prop.load(new FileReader(stringPath));

            url = prop.getProperty("url");
            username = prop.getProperty("username");
            password = prop.getProperty("password");

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 2.使用配置文件.properties 可以使方法更加通用
     * 属性可以在配置文件中定义,运行时从配置文件中读取属性值
     * 对于不同的用户使用时只需要修改配置文件中的值不用更改源码 
     */
    public static Connection getConnection() throws SQLException {
            return DriverManager.getConnection(url,username,password);
    }
	
    //close方法重载来适应不同的关闭场景
    public static void close(ResultSet rs, Statement stmt,Connection conn){
        if(rs != null){
            try{
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

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

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

    public static void close(Statement stmt,Connection conn){
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

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

    public static void close(PreparedStatement stmt, Connection conn){
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

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

配置文件 jdbcConfig.properties

url=jdbc:mysql:///testdb
username=root
password=1234
  • 使用工具类

可以看出代码简化的效果十分显著

public class JdbcDemo4 {
    public static void main(String[] args){
        Connection conn = null;
        Statement stmt = null;
        try{
            //创建连接对象
            conn = JdbcUtil.getConnection();
            //执行的SQL语句
            String sql = "update test set bonus=1500 where id=2;";
            //获取执行语句的对象
            stmt = conn.createStatement();
            //执行SQL语句
            int count = stmt.executeUpdate(sql);
            System.out.println(count);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //释放资源
            JdbcUtil.close(stmt,conn);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值