JDBC

JDBC概述

客户端操作数据库的方式

  1. 方式一:使用第三方客户端来访问MySQL:SQLyog
  2. 方式二:使用命令窗口

什么是JDBC

JDBC(Java Data Base Connectivity)是Java访问数据库的标准规范,是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范。

JDBC原理

JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库的连接,从而不能操作数据库。每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
在这里插入图片描述总结:

JDBC就是一套操作关系型数据库的规则(接口)
数据库厂商需要实现这套接口,并且提供数据库驱动jar包
我们去使用这套接口,真正执行的是对应的驱动包中的实现类

JDBC开发

API使用:1.注册驱动

  • JDBC规范定义驱动接口:java.sql.Driver
  • Mysql驱动包提供了实现类:com.mysql.jdbc.Driver
加载注册驱动的方式描述
Class.forName(数据库驱动实现类)加载和注册数据库驱动,数据库驱动由数据库厂商MySQL提供
“com.mysql.jdbc.Driver”
//Driver类是由MySQL驱动包提供的一个实现类 它实现了java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
//静态代码块 随着类的加载而加载  只加载一次
    static {
        try {
        //DriverManager类就是驱动管理类  registerDriver()方法就是用来注册驱动
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

从JDBC3开始,目前已经普遍使用的版本:可以不用注册驱动而直接使用。

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
  1. 对URL的详细说明
jdbc:mysql://localhost:3306/db4 ? 参数名 = 参数值
jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8

JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。

  • 第一部分是协议jdbc,这是固定的
  • 第二部分是子协议,就是数据库名称,连接mysql数据库,第二部分当然就是mysql了
  • 第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别由数据库服务器的IP地址(localhost)、端口号(3306)以及要使用的数据库名称 组成

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结果集对象

代码示例:

package com;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class demo01 {

    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", "123456");

        //打印连接对象    com.mysql.jdbc.JDBC4Connection@2d6e8792
        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);

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


    }
}

API使用:4.处理结果集

  • 只有在进行查询操作的时候,才会处理结果集

ResultSet接口

  • 作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录
ResultSet接口方法说明
boolean next()1)游标向下一行
2)返回boolean类型,如果还有下一条记录,返回true,否则返回false
xxx getXxx(String or int)1)通过列名,参数是String类型,返回不同的类型
2)通过列号,参数是整数。从1开始,返回不同的类型

xxx getXxx(String or int) 重载方法 参数可以是String 或者 int

通过列号获取,参数是整数,根据获取列的数据类型,选择对应的方法 getInt(1),getString(2)…
通过列名获取,参数是String类型的字段名称,getInt(“id”) getString(“name”)…

package com;

import com.mysql.jdbc.Driver;

import java.sql.*;

public class JDBCDemo02 {

    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", "123456");

        //打印连接对象    com.mysql.jdbc.JDBC4Connection@2d6e8792
        System.out.println(con);

        //3.获取语句执行平台statement
        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 id = resultSet.getInt(1);
//        System.out.println("通过列号获取id" + id);

        //通过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();
        con.close();
    }
}

API使用:5.释放资源

  1. 需要释放的对象:ResultSet结果集,Statement语句,Connection连接
  2. 释放原则:先开的后关,后开的先关。ResultSet ==> Statement ==> Connection
  3. 放在哪个代码块中:finally块

与IO流一样,使用后的东西都需要关闭! 关闭的顺序是先开后关,先得到的后关闭,后得到的先关闭

在这里插入图片描述

步骤总结

  1. 注册驱动(可以省略)
  2. 获取连接
  3. 获取Statement对象
  4. 处理结果集(只在查询时处理)
  5. 释放资源

JDBC实现增删改查

JDBC工具类

  • 什么时候自己创建工具类
如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用
“获取数据库连接”操作,将在以后的增删改查所有功能中农都存在,可以封装工具类JDBCUtils,提供获取连接对象的方法,从而达到代码的重复利用

  • 工具类包含的内容
1.可以把几个字符串定义成常量:用户名,密码,URL,驱动类
2.得到数据库的连接:getConnection()
3.关闭所有打开的资源
package com.utils;

import java.sql.*;

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 = "123456" ;
    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 ;
        }
    }

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

}

DML操作

插入记录

解决插入中文乱码问题

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

package com.jdbc01;

import com.utils.JDBCUtils;
import org.junit.Test;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBC01 {
    /**
     * 插入数据
     */
    @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 = 'admin3' where id = '1'";

        statement.executeUpdate(sql);

        JDBCUtils.close(connection,statement);

    }

    /**
     * 删除操作
     * 删除id为1和2 的数据
     */
    @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);

    }
}

DQL 操作

查询姓名为张百万的一条记录

package com.jdbc01;

import com.utils.JDBCUtils;

import java.sql.*;

public class JDBC02 {

//    查询姓名为张百万的一条记录
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);
}
}

SQL注入问题

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');
  1. SQL注入演示
# SQL注入演示
----填写一个错误的密码
select * from jdbc_user where username='tom' and password='123' or '1' = '1' ;

如果这是一个登陆操作,那么用户就登录成功了。显然这不是我们想要看到的结果。

sql注入案例:用户登录

  • 需求
    用户在控制台上输入用户名和密码,然后使用Statement字符串拼接的方式实现用户的登录
  • 步骤
    1)得到用户从控制台上输入的用户名和密码来查询数据库
    2)写一个登录的方法
    a)通过工具类得到连接
    b)创建语句对象,使用拼接字符串的方式生成SQL语句
    c)查询数据库,如果有记录则表示登录成功,否则登录失败
    d)释放资源
sql注入方式:'123' or '1' = '1'

代码示例:

package com.JDBC03;

import com.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

public class TestLogin01 {

    /**
     * 用户登录案例
     * @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.next();

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

//        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("登录成功!欢迎您:" + name);
        }else {
            System.out.println("登录失败!");
        }

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

    }
}

问题分析

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 jdbc_user where true = true; 查询了所有记录

预处理对象

PreparedStatement 接口介绍

  • PreparedStatement 是Statement接口的子接口,继承于父接口中所有的方法。它是一个预编译的SQL语句对象。
  • 预编译:是指SQL语句被预编译,并存储在PreparedStatement对象中。然后可以使用此对象多次高效地执行该语句

PreparedStatement 特点

  • 因为有预先编译的功能,提高SQL的执行效率
  • 可以有效的方式SQL注入的问题,安全性更高

获取PreparedStatement 对象

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

PreparedStatement 接口常用方法

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

import com.utils.JDBCUtils;

import java.sql.*;
import java.util.Scanner;

public class TestLogin02 {
    /**
     * SQL注入:
     *    用户输入的用户名和密码,与我们编写的SQL进行了拼接,用户输入的内容成为了SQL语法的一部分,
     *    用户会利用这个漏洞,输入一些其他的字符串,改变SQL原有的意思
     *
     *    如何解决:
     *        要解决SQL注入 就不能让用户输入的数据和我们的SQL进行直接的拼接
     *
     *    预处理对象 PreparedStatement 它是Statement 接口的子接口
     *
     */

    public static void main(String[] args) throws SQLException {
        //        1.获取连接
        Connection connection = JDBCUtils.getConnection();

//        2.获取prepareStatement对象
//        使用?占位符的方式来设置参数
        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.next();

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

//        4.设置参数 使用setXXX(占位符的位置(整数),要设置的值)方法设置占位符的参数
        ps.setString(1,name);
        ps.setString(2,password) ;

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

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

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

    }
}

PreparedStatement 的执行原理

  • 分别使用Statement对象和PreparedStatement 对象进行插入操作

代码示例:

package com.JDBC03;

import com.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

public class TestPS {

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

        Connection connection = JDBCUtils.getConnection();

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

//        向数据库插入两条数据
        statement.executeUpdate("insert into jdbc_user values (null,'张三','123456','2000/12/16')");
        statement.executeUpdate("insert into jdbc_user values (null,'李四','654321','2000/12/16')");

//        获取预处理对象
        PreparedStatement ps = connection.prepareStatement("insert into jdbc_user values (?,?,?,?)");

//        先插入第一条数据
        ps.setObject(1,null);
        ps.setString(2,"小小");
        ps.setString(3,"123789");
        ps.setString(4,"1999/2/4");

//        执行插入
        ps.executeUpdate();
//        插入第二条数据
        ps.setObject(1,null);
        ps.setString(2,"小乐");
        ps.setString(3,"123789");
        ps.setString(4,"1999/5/4");

//        执行插入
        ps.executeUpdate();

//        释放资源
        statement.close();
        ps.close();
        connection.close();

    }
}

  • Statement对象每执行一条SQL就会发送给数据库 数据库要先编译再执行
  • 预处理对象会将SQL发送给数据库进行一个预编译,然后将预编译的SQL保存起来,这样就只需要编译一次了

JDBC控制事务

  • 之前我们是使用MySQL的命令来操作事务,接下来使用JDBC来操作银行转账的事务

数据准备

create table account(
id int PRIMARY KEY auto_increment,
name VARCHAR(20),
money DOUBLE
);

INSERT into account(name,money)
VALUES('tom',1000);
INSERT into account(name,money)
VALUES('jack',1000);

事务相关API

我们使用Connection中的方法实现事务管理

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

开发步骤

1.获取连接

2.开启事务

3.获取预处理对象 执行SQL(两次修改操作)

4.提交事务(正常情况)

5.出现异常就回滚事务

6.释放资源

代码示例

package com.JDBC04;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestJDBCTransaction {
//    使用JDBC操作事务
     public static void main(String[] args) {

//         1.获取连接
          Connection connection = null;
          PreparedStatement ps = null ;


//         2.开启事务
          try {
               connection = JDBCUtils.getConnection();
               connection.setAutoCommit(false);
//        3.获取预处理对象 执行SQL(两次修改操作)
//          3.1 tom 账户  -500
               ps = connection.prepareStatement("update account set money = money - ? where name = ?");
               ps.setDouble(1,500);
               ps.setString(2,"tom");
               ps.executeUpdate();
               
//               模拟tom转账后出现异常
//               System.out.println(1 / 0);
               
//          3.2 jack 账户  +500
               ps = connection.prepareStatement("update account set money = money + ? where name = ?");
               ps.setDouble(1,500);
               ps.setString(2,"jack");
               ps.executeUpdate();

//         4.提交事务(正常情况)
               connection.commit();
               System.out.println("转账成功!");
          } catch (SQLException e) {
               e.printStackTrace();
//         5.出现异常就回滚事务
               try {
                    connection.rollback();
               } catch (SQLException ex) {
                    ex.printStackTrace();
               }

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

     }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值