JDBC 基础操作 与 相关知识

目录

背景概述

使用步骤

准备工作

入门案例

SQL注入

SQL注入的解决方案

JDBC常见的问题

Class.forName这句话有用没?

驱动版本

中文乱码

SQL注入

PreparedStatement 语句

常见错误

模拟服务器解析数据


背景概述

我们学习了数据库,数据库实现了数据的持久化,但我们最终要在程序里处理数据啊,那java代码中怎么去访问数据库读写数据呢?

这就要用到sun公司设定的一套数据库标准了,这套标准就是JDBC(Java Database Connectivity)。但它只是规范,不做具体实现。于是数据库厂商又根据JDBC标准,实现自家的驱动Driver。如:mysql驱动com.mysql.cj.jdbc.Driver,Oracle的驱动oracle.jdbc.OracleDriver。有了这套解决方案,java就可以访问数据库中的数据了。

public interface Connection extends Wrapper, AutoCloseable {}

public interface Statement extends Wrapper, AutoCloseable {}

public interface PreparedStatement extends Statement {}

public interface CallableStatement extends PreparedStatement {}

public interface ResultSet extends Wrapper, AutoCloseable {}

Java中提倡面向接口开发,而最经典的接口设计莫过于JDBC数据库接口。

Connection链接、Statement语句、PreparedStatement预处理语句、CallableStatement存储过程、ResultSet结果集。

调用方式有三种:Statement语句、PreparedStatement预处理语句、CallableStatement存储过程推荐使用第二种PreparedStatement,防止SQL注入,其也是预编译性能高。

使用步骤

导入jar包(丰富的工具类)

获取和数据库的连接(用户名、密码)

通过程序执行SQL

通过程序处理结果

准备工作

idea 创建项目并导入jar包(准备工作,将来会有Maven来管理)

  • 创建cgb210602工程
  • 创建lib目录,拷贝驱动mysql-connector-java到lib目录下
  • 项目引用这个外部jar包(右键mysql-connector-java,点击Add as library)

 

入门案例

import java.sql.*;

//测试JDBC的入门案例
//查询表的所有数据
public class Test1 {
    public static void main(String[] args) throws Exception {
//        1.注册驱动 Driver
          Class.forName("com.mysql.jdbc.Driver");
//        2.获取连接(用户名 密码 端口号 库名)
        //连接哪里的哪个数据库
                   //   协议       IP地址    :端口号  库名
        String url = "jdbc:mysql://127.0.0.1:3306/cgb2106";
        Connection c = DriverManager.getConnection(url,"root","root");
//        3.获取传输器(将SQL翻译好,传输给数据库)
        Statement s = c.createStatement();
//        4.执行SQL(利用传输器,执行QL)  SELECT * FROM dept;
        ResultSet r = s.executeQuery("SELECT * FROM dept");
//        5.处理结果集
        while (r.next()){//next()  每次调用该方法,就挪动指针,判断下一行是否有数据返回true/false(指针初始化在第一行数据之前)
            //获取数据
            String a = r.getString(1);//获取第一列的数据
//            String b = r.getString("deptno");//获取指定字段所属列的数据
            //TODO
            String b = r.getString(2);//获取第二列的数据
            String e = r.getString(3);//获取第三列的数据
            System.out.println(a+","+b+","+e);
        }
//        6.释放资源
        r.close();//结果集
        s.close();//传输器
        c.close();//连接
    }
}

SQL注入

创建表并进行测试

USE cgb2106;
CREATE TABLE USER(
id INT AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(20) UNIQUE,
PASSWORD VARCHAR(10)
);
INSERT INTO USER  VALUES(NULL,'王一博','1234');
INSERT INTO USER  VALUES(NULL,'赵丽颖','1234');
INSERT INTO USER  VALUES(NULL,'易烊千玺','1234');
INSERT INTO USER  VALUES(NULL,'邓伦','1234');
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

public class Test2 {
    public static void main(String[] args) throws Exception {
        method();//查询用户所有的信息
        method2();//根据指定的用户名和密码查询用户信息,查到了就登录成功
    }
    //模拟用户登录
    private static void method2() throws Exception {
        //注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //获取连接
        String url = "jdbc:mysql://localhost:3306/cgb2106";
        Connection c = DriverManager.getConnection(url, "root", "root");
        //获取传输器
        Statement s = c.createStatement();
        String a = new Scanner(System.in).nextLine();
        String b = new Scanner(System.in).nextLine();
                                                //拼串  ”+???+“
        //问题:当用户输入了特殊的数据是,发生的现象叫SQL注入攻击
        //当用户输入了王一博'#时候,SQL中出现了特殊符号#,在SQL中表示注释
        //只需要输入用户名正确,不需要密码也能登录
        //SQL语义变了:select * from user where name='王一博'#' and password='"+b+"'"
        String sql = "select * from user where name='"+a+"' and password='"+b+"'";
        //执行SQL
        ResultSet r = s.executeQuery(sql);
        //处理结果集
        if(r.next()){
            System.out.println("恭喜您,登录成功");
        }else{
            System.out.println("用户名或者密码输入错误");
        }
        //关闭资源
        r.close();
        s.close();
        c.close();
    }

    private static void method() throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/cgb2106";
        Connection c = DriverManager.getConnection(url, "root", "root");
        Statement s = c.createStatement();
        ResultSet r = s.executeQuery("select * from user");
        while (r.next()){
            String id = r.getString(1);
            String name = r.getString("name");
            System.out.println(id+","+name);
        }
        r.close();
        s.close();
        c.close();
    }
}

SQL注入的解决方案

 private static void method2() throws Exception {
        //注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //获取连接
        String url = "jdbc:mysql://localhost:3306/cgb2106";
        Connection c = DriverManager.getConnection(url, "root", "root");

        String a = new Scanner(System.in).nextLine();
        String b = new Scanner(System.in).nextLine();
        //获取传输器
//        Statement s = c.createStatement();//缺点:不安全,低效
        String sql = "select * from user where name=? and password=?";
        //SQL骨架,?叫占位符
        PreparedStatement ps = c.prepareStatement(sql);
        //设置参数
        ps.setString(1,a);//给第一个?的位置设置值
        ps.setString(2,b);//给第二个?的位置设置值
        ResultSet r = ps.executeQuery();
        //处理结果集
        if(r.next()){
            System.out.println("恭喜您,登录成功");
        }else{
            System.out.println("用户名或者密码输入错误");
        }
        //关闭资源
        r.close();
        ps.close();
        c.close();
    }

JDBC常见的问题

Class.forName这句话有用没?

Class.forName可以指定class类路径进行动态创建对象实例,可JDBC这句话没有返回对象啊,那写这句有什么作用呢?看看java.sql.Driver.class的源码就找到真相了,原来它用了静态代码块创建对象。

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }

写了创建了,那不写呢?怎么不写也能执行呢?

Java提供了SPI机制,用户可以自行配置类,JDBC高版本驱动就都引入了这个支持。如果用户使用了Class.forName方式就自己指定了驱动,如果未写这句话,则Java自动去META-INF/services/java.sql.Driver文件中找启动类。

驱动版本

不同版本的mysql需要不同版本的驱动

Mysql5.0x mysql-connector-java-5.1.32.jar

Mysql8.0x mysql-connector-java-8.0.21.jar
  • Driver变成了: com.mysql.cj.jdbc.Driver,中间多了cj
  • url必须加时区参数: serverTimezone=Asia/Shanghai

中文乱码

url增加参数:characterEncoding=utf8防止中文乱码

String url ="jdbc:mysql://localhost:3306/mydb?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false";

SQL注入

String condition = "光头强";
String condition = "光头强' or 1=1 or '";
String condition = "光头强' or true or '";

String sql = "select * from teachers where tname='" + condition+"'";

利用sql中'单撇是字符串的结束符,or只要一个条件成立其它就不用再判断,而恶意造成sql查询失效,本应该只展示一条数据,结果全部展现。

注入后形成的SQL:

SELECT * FROM teachers WHERE tname='光头强' OR 1=1 OR ''

大家试想如果是一个财务表,本你只能看自己的信息,结果你看了所有人的信息。结果新员工比你工资高,你说气人不。

PreparedStatement 语句

SQL注入解决方案:

Statement对象换为PreparedStatement对象

sql = "select * from teachers where tname=?";			#参数使用问号
PreparedStatement stat = cn.prepareStatement(sql); 		#对象换掉
stat.setString(1, condition);					#对应参数类型,第几个问号
ResultSet rs = stat.executeQuery();			#去掉sql参数

PS后的结果:

SELECT * FROM teachers WHERE tname='光头强\' or 1=1 or \''

利用转义字符,屏蔽了SQL中的恶意字符。不仅解决了sql注入问题,使系统变的安全,PreparedStatement还有个极大的好处,它是预编译的语句,其主干部分mysql进行预编译后缓存,下次这部分就无需在解析,只把条件拼入,这样执行效率远高于statement每次都要编译sql语句。

常见错误

有以下几种:
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
错误原因:

1)jar没有导入,没有builder path/add as library

2)Class.forName("com.mysql.jdbc.Driver"); 字符串拼写错误

Unknown database mydb;
错误原因:

数据库名称拼写错误

Access denied for user ‘root123’@‘localhost’ (using password: YES)
错误原因:

数据库用户名或者密码错误

Table ‘py-school-db.mydb’ doesn’t exist
错误原因:

表不存在,也可能表名写错了

模拟服务器解析数据

public class TestUrl {
    public static void main(String[] args) {
//       http://127.0.0.1:8848/cgb2106/test04.html?user=1&age=123&sex=1&like=ppq&like=ps&edu=2;
//        1.把字符串按照?切割split("?"),得到数组a,两个字符串
//        2.重点解析a[1],得到user=1&age=123&sex=1&like=ppq&like=ps&edu=2
//        3.把字符串按照&切割split("&"),得到数组b
//        4.遍历数组,再按照=切割,又得到数组c
//        5.只获取c[1]就是用户从浏览器输入的数据
//        TODO 6.利用jdbc把数据入库/查库
        String url = "http://127.0.0.1:8848/cgb2106/test04.html?" +
                "user=1&age=123&sex=1&like=ppq&like=ps&edu=2";
        String[] b = url.split("\\?")[1].split("&");
        for(String str:b){
            String[] c = str.split("=");
            String data = c[1];
            System.out.println(data);
            //利用jdbc将数据入库
//            .......
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值