乐趣区

关于mysql:通俗易懂的JDBC教程绝对适合零基础

数据库实现了数据的长久化,但咱们最终要在程序里解决数据啊,那 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 数据库接口。

如有问题能够观看视频:

https://www.bilibili.com/vide…

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

调用形式有三种:

Statement 语句、PreparedStatement 预处理语句、CallableStatement 存储过程,举荐应用第二种 PreparedStatement,避免 SQL 注入,其也是预编译性能高。

应用步骤

  • 导入 jar 包(丰盛的工具类)
  • 获取和数据库的连贯(用户名、明码)
  • 通过程序执行 SQL
  • 通过程序处理后果

idea 创立我的项目并导入 jar 包

  • 创立 stage2 Java 工程
  • 创立 lib 目录,拷贝驱动 objbc6-11.1.0.7.0 到 lib 目录下
  • 我的项目援用这个内部 jar 包

入门案例

package cn.tedu.jdbc;
 
import java.sql.*;
 
// 测试 jdbc
// 需要:查问 cgb2104 库里的 students 表里的所有数据
public class Test1 {public static void main(String[] args) throws Exception {
        //1, 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2, 获取和数据库的连贯
//String url= "jdbc:mysql://localhost:3306/cgb2104?characterEncoding=utf8";// 指定要连贯哪个数据库
String url= "jdbc:mysql:///cgb2104?characterEncoding=utf8";// 指定要连贯哪个数据库
        String user= "root" ; // 应用的用户名
        String pwd= "root" ; // 应用的明码
        Connection conn = DriverManager.getConnection(url, user, pwd);
        //3, 获取传输器, 执行 SQL
        Statement st = conn.createStatement();
        //4, 执行 SQL
        ResultSet rs = st.executeQuery("select * from students");
        //5, 解析后果集
        while(rs.next() ){//next()判断后果集中是否有数据
            for (int i = 1; i <= 5 ; i++) {
                // 获取每列的值并打印
                System.out.println(rs.getString(i) );
            }
        }
        //6, 开释资源
        rs.close(); // 敞开后果集
        st.close();// 敞开传输器
        conn.close();// 敞开连贯}
}

SQL 注入

/* 本人筹备 user2 表(id/name/password), 筹备数据
    CREATE TABLE `user2` (`id` int(11)  PRIMARY KEY  auto_increment,
          `name` varchar(10) default NULL,
          `password` varchar(10) default NULL
    ) ;
 */
// 需要:利用 jdbc, 依据用户名和明码查问 cgb2104 库里的 user 表
//SQL 注入攻打问题
private static void login() {
    try{Class.forName("com.mysql.jdbc.Driver");
        String url="jdbc:mysql:///cgb2104?characterEncoding=utf8";
        Connection conn = DriverManager.getConnection(url, "root", "root");
        Statement st = conn.createStatement();
// String sql ="select * from user2 where name='jack'and password='123456'";// 写死了
 
        String user = new Scanner(System.in).nextLine();// 用户输出 jack'#
        String pwd = new Scanner(System.in).nextLine();
        //SQL 注入攻打问题:实质上是因为 SQL 语句中呈现了特殊符号 #, 扭转了 SQL 语义
String sql ="select * from user2 where name='"+user+"'and password='"+pwd+"'";
        ResultSet rs = st.executeQuery(sql);// 执行查问的 SQL, 返回后果集
        if(rs.next()){System.out.println("登录胜利~");
        }else{System.out.println("登录失败~");
        }
        st.close();
        conn.close();}catch(Exception e){e.printStackTrace();// 有异样, 间接打印异样信息
        //System.out.println("执行失败。。。");// 上线
    }
 
}

SQL 注入的解决方案

// 解决 SQL 注入攻打的计划
private static void login2() {
    try{Class.forName("com.mysql.jdbc.Driver");
        String url="jdbc:mysql:///cgb2104?characterEncoding=utf8";
        Connection conn = DriverManager.getConnection(url, "root", "root");
//            Statement st = conn.createStatement(); 不行, 不平安, 会被 SQL 攻打
 
        String user = new Scanner(System.in).nextLine();// 用户输出 jack'#
        String pwd = new Scanner(System.in).nextLine();
        //? 叫占位符 ,SQL 的骨架
String sql ="select * from user2 where name=? and password=?";
        // 先把 SQL 骨架发给数据库执行
        PreparedStatement ps = conn.prepareStatement(sql);
        // 给 SQL 里的? 设置参数
        ps.setString(1,user);// 给第一个? 设置值是 user
        ps.setString(2,pwd);// 给第二个? 设置值是 pwd
        
        ResultSet rs = ps.executeQuery();// 执行拼接好的 SQL, 返回后果集
 
        if(rs.next()){System.out.println("登录胜利~");
        }else{System.out.println("登录失败~");
        }
        ps.close();
        conn.close();}catch(Exception e){e.printStackTrace();// 有异样, 间接打印异样信息
        //System.out.println("执行失败。。。");// 上线
    }
}

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

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
谬误起因:

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

退出移动版