JDBC遇到的问题

1 JDBC简介

  • 概念:JDBC(Java DataBase Connectivity) :Java数据库连接技术:具体讲就是通过Java连接广泛的数据库,并对表中数据执行增、删、改、查等操作的技术。
  • 可以通过 Navicat、SQLyog等图形化客户端发送SQL操作数据库。本质上,JDBC的作用和图形化客户端的作用相同,都是发送SQL操作数据库。差别在图形化界面的操作是图形化、傻瓜化的,而JDBC则需要通过编码(这时候不要思考JDBC代码怎么写,也不要觉得它有多难)完成图形操作时的效果。 总结:JDBC本质上也是一种发送SQL操作数据库的client技术,只不过需要通过Java编码完成。
  • 作用:通过JDBC技术与数据库进行交互,使用Java语言发送SQL语句到数据库中,可以实现对数据的增、删、改、查等功能,可以更高效、安全的管理数据。
    JDBC是数据库与Java代码的桥梁(链接)、

2 JDBC技术相关接口

  • 作用:JDBC要通过Java代码操作数据库,JDBC中定义了操作数据库的各种接口和类型:
接口作用
Driver驱动接口,定义建立链接的方式
DriverManager工具类,用于管理驱动,可以获取数据库的链接
Connection表示Java与数据库建立的连接对象(接口)
PreparedStatement发送SQL语句的工具
ResultSet结果集,用于获取查询语句的结果
- JDBC是规范(接口)不是实现(类) JDBC 是一种规范,由Sum公司它提供了一套完整的接口,由数据库厂商根据特点予以实现,因此只要学会接口的使用就可以轻松的用 JDBC编写适用于各种数据库的程序。 - Mysql提供的JDBC实现 Mysql提供的JDBC实现称为Mysql Connector,不同的数据库版本需要使用不同的Connector。 实际开发时根据数据库版本、JDK版本、选择不同的Connector。
Connector版本MySQL版本JDK版本
8.05.6, 5.7, 8.0JDK 8.0 或更高
5.15.6, 5.7, 8.0JDK 5.0 或更高
下载地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java 为什么JDBC只有接口,没有提供实现?

不同数据库的底层技术不同,不少数据库是闭源的,源代码不公开的。Sun公司无力为所有数据库提供具体实现,只能提供接口而由数据库厂商提供具体实现,Sun公司只是制定JDBC标准,各个厂商准守标准提供具体的实现。JDBC和数据库实现的关系就好比List接口和ArrayList、LinkedList之间的关系。

面向JDBC接口规范编程,写出的JDBC代码可以在不同的数据库间轻松的迁移。

3 JDBC的编程步骤

  • JDBC操作数据库的步骤和 Navicat 操作数据库步骤就大同小异。
  • JDBC开发六步
  1. 加载驱动
  2. 获取链接
  3. 准备SQL以及发送SQL的工具
  4. 执行SQL
  5. 处理结果集
  6. 释放资源

4 第一个JDBC程序

1、搭建开发环境
  • 在项目中引入数据库驱动jar包(jar文件:针对class文件的压缩格式包含了多个带包的class文件,类似于普通文件打包的zip、rar)。
  • eclipse 将mysql-connector-java.jar添加到项目环境中
  1. 右键选中项目,新建一个folder名为lib,将jar包复制到lib目录中。
  2. 右键选中jar包,build path–>add to build path。
  • idea 将mysql-connector-java.jar添加到项目环境中。
  1. 右键选中项目,新建一个Directory名为lib,将jar包复制到lib文件夹中。
  2. 右键选中jar包,Add as Library --> OK。 如图所示:
2 JDBC访问数据库
  • 编码:Applications no longer need to explicitly load JDBC drivers using Class.forName().
    应用程序不再需要使用Class.forName()显式加载JDBC驱动程序。

Existing programs which currently load JDBC drivers using Class.forName() will continue to work without modification
当前使用Class.forName()加载JDBC驱动程序的,将继续工作而不进行修改。

演示的代码如下:

package com.txw.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
@SuppressWarnings("all")   // 注解警告信息
public class JdbcTesrt {
   
    public static void main(String[] args) throws Exception {
   
        // 1.连接驱动类
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2.获取连接
        // 数据库用户名
        String username = "root";
        // 数据库密码
        String password = "123456";
         /*
            url参数用来确定连接的数据库信息: 数据库机器ip 端口号port 数据库名db_name 连接的参数,比如编解码集、时区...
            url格式:jdbc:mysql://ip:port/db_name?k=v参数 ,只需要了解url的组成,不需要记忆
        */
        String url = "jdbc:mysql://192.168.64.128:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai";
        Connection conn = DriverManager.getConnection(url,username,password);
        // url错误创建链接时会出现异常
        // 参数错误不会导致运行时异常
        System.out.println( conn );
        // 3.准备发送SQL
        String sql = "insert into t_person values(null,'Adair',25,'T','1383838381','硅谷');";
        PreparedStatement pstm = conn.prepareStatement(sql);
        // 4.发送执行SQL
        int update = pstm.executeUpdate();
        System.out.println( update );
        // 5.(如果是查询语句,需要处理结果集)
        // 6.关闭资源
        pstm.close();
        conn.close();
    }
}
  • 参数解释:1. useUnicode = true&characterEncoding=UTF-8:mysql编码默认为GBK,此参数的作用就是允许插入unicode字符集中的数据,使用UTF-8编码方式进行编码。
  1. useSSL=false: SSL是高版本Mysql提供的数据加密、安全保障的新协议,为了向下兼容所以设置为false关闭此协议。
  2. serverTimezone=Asia/Shanghai:为了确保日期类型数据能够正确存储,需要指定时区为上海时区(上海时区与北京一致),默认为美国时区。
3 结果集的处理
  • 需求:查询person表中的数据。select * from person;
  • 演示的代码如下:
package com.txw.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@SuppressWarnings("all")   // 注解警告信息
public class JdbcTest01 {
   
    public static void main(String[] args) throws Exception {
   
        // 1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2 创建和数据库之间的连接
        String username = "root";
        String password = "123456";
        String url = "jdbc:mysql://192.168.64.128:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai";
        Connection conn = DriverManager.getConnection(url,username,password);
        // 3.准备发送SQL
        String sql = "select * from t_person";
        PreparedStatement pstm = conn.prepareStatement(sql);
        // 4.执行SQL,接收结果集
        ResultSet rs = pstm.executeQuery();
        // 5 处理结果集
        while(rs.next()){
   
     /*
        rs.getXxx(列顺序从1开始) 或者 rs.getXxx("列名") 获取指定列的数据,Xxx为数据类型
        实战中多使用列名,可读性强
      */
            /*
            int personId2 = rs.getInt(1);
            String personName2 = rs.getString(2);
            int age2 = rs.getInt(3);
            String sex2 = rs.getString(4);
            String mobile2 = rs.getString(5);
            String address2 = rs.getString(6);
            System.out.println("personId="+personId2+",personName="+personName2
                    +",age="+age2+",sex="+sex2+",mobile="+mobile2+",address="+address2);
            */
            int personId1 = rs.getInt("person_id");
            String personName1 = rs.getString("person_name");
            int age1 = rs.getInt("age");
            String sex1 = rs.getString("sex");
            String mobile1 = rs.getString("mobile");
            String address1 = rs.getString("address");
            System.out.println("personId="+personId1+",personName="+personName1
                    +",age="+age1+",sex="+sex1+",mobile="+mobile1+",address="+address1);
        }
        // 6.释放资源
        rs.close();
        pstm.close();
        conn.close();
    }
}
  • PreparedStatement常用方法
方法声明作用
int executeUpdate(String sql)可执行增,删,改,返回执行受到影响的行数
ResultSet executeQuery(String sql)执行SQL查询,并返回ResultSet对象
boolean execute(String sql)可执行任何SQL语句,返回一个布尔值,表示是否返回ResultSet 。(只有执行查询才为true)
- ResultSet常用方法
方法声明作用
boolean next()游标下移,判断该行是否有结果
xx getXx(int index)获取该行结果中某个字段的数据,index为编号,index从1开始
xx getXx(String name)获取改行结果中某个字段的数据,name为字段名
xx为数据类型, 如图所示:

5 数据绑定

  • 作用:数据绑定:将用户输入的数据,绑定到要执行的SQL语句中。 JDBC执行的SQL中的数据要根据用户的输入发生变化,比如 登录功能背后的查询sql要根据用户名不同,执行不同的条件。这就需要将用户输入的数据绑定到执行的SQL中。
  • 准备工作:建表t_user 插入测试数据.create table user(
    user_id int primary key auto_increment,
    username varchar(20) unique not null,
    password varchar(50) not null
    );

– 添加测试数据
insert into user values(null,‘Adair’,‘123456’);

1 字符串拼接SQL
  • 步骤:1. 在需要数据的地方,使用变量名替换。
  1. 在变量名前添加"+,变量后添加+"。
  2. 注意:如果拼接的是字符串,那么需要在"+之前添加’ 在+"后添加’。 演示:根据用户输入的id进行查询。
package com.txw.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner;
public class JdbcTest02 {
   
    public static void main(String[] args) throws Exception{
   
        // 用户输入数据
        Scanner sc = new Scanner(System.in);
        String id = sc.nextLine();
        // 1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2.创建链接
        String dbUsername = "root";
        String dbPassword = "123456";
        String url = "jdbc:mysql://192.168.64.128:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai";
        Connection conn = DriverManager.getConnection(url,dbUsername,dbPassword);
        // 3.准备执行SQL
        String sql = "select * from t_user where user_id = "+id+";";
        PreparedStatement pstm = conn.prepareStatement( sql );
        // 4.执行SQL获取结果集
        ResultSet rs = pstm.executeQuery();
        // 5.处理结果集
        while( rs.next() ){
   
            Integer userId = rs.getInt("user_id");
            String username = rs.getString("username");
            String password = rs.getString("password");
            System.out.println(userId + "\t" + username + "\t" + password);
        }
        // 6.关闭资源
        pstm.close();
        conn.close();
    }
}

如图所示:

2、SQL注入问题
  • 使用字符串拼接SQL出现的问题。在进行查询操作时 恶意填入or 1=1 这类字符,使将要执行的sql变为无条件查询语句,泄漏数据或无法正确验证。

演示:

Scanner sc = new Scanner(System.in);
String id = sc.nextLine();//输入 1 or 1=1

String sql = "select * from user where user_id = "+id+";";
//拼接后:select * from t_user where user_id =1 or 1=1
3、?占位符
  • ?占位符是JDBC的一种特殊语法,专用于参数绑定,可以有效避免SQL注入问题。 使用步骤:1. 在需要使用数据的地方,使用?代替(占位)。
  1. 发送sql前,通过pstm.setXXX(index,数据)方法给?赋值。
    index:占位符编号,从1开始,表示第几个占位符。
    xxx 为数据类型,需要根据数据库字段类型选择对应的赋值方法。 例:在数据库中插入用户输入的数据。
package com.txw.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Scanner;
public class JdbcTest04 {
   
    public static void main(String[] args) throws Exception {
   
        // 用户输入数据
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.next();
        System.out.println("请输入年龄:");
        int age = sc.nextInt();
        System.out.println("请输入性别:(男/女)");
        String sex = sc.next();
        System.out.println("请输入手机号:");
        String mobile = sc.next();
        System.out.println("请输入地址:");
        String address = sc.next();
        // 1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2.创建和数据库之间的连接
        String username = "root";
        String password = "123456";
        String url = "jdbc:mysql://192.168.64.128:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai";
        Connection conn = DriverManager.getConnection(url,username,password);
        // 3.准备执行SQL,数据使用?占位
        String sql = "insert into t_person values(null,?,?,?,?,?);";
        PreparedStatement pstm = conn.prepareStatement(sql);
        // 为?占位符赋值
        pstm.setString(1,name);
        pstm.setInt(2,age);
        pstm.setString(3,sex);
        pstm.setString(4,mobile);
        pstm.setString(5,address);
        // 4.执行SQL
        pstm.executeUpdate();
        //5.关闭资源
        pstm.close();
        conn.close();
    }
}

如图所示: img 演示:解决SQL注入问题。

package com.txw.jdbc;

import java.sql.*;
import java.util.Scanner;
public class JdbcTest05 {
   
    public static void main(String[] args) {
   
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
   
            // 用户输入数据
            Scanner sc = new Scanner(System.in);
            String id = sc.nextLine();
            // 1.加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2.创建和数据库之间的连接
            String username = "root";
            String password = "123456";
            String url = "jdbc:mysql://192.168.64.128:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai";
            conn = DriverManager.getConnection(url,username,password);
            // 3.准备执行SQL,数据使用?占位
            String sql = "select * from t_user where user_id = ? ";
            pstm = conn.prepareStatement( sql );
            // 为?占位符赋值
            pstm.setString(1,id);
            // 4.执行SQL
            ResultSet rs = pstm.executeQuery();
            // 5.处理结果集
            while( rs.next() ){
   
                Integer userId = rs.getInt("user_id");
                username = rs.getString("username");
                password = rs.getString("password");
                System.out.println(userId + "\t" + username + "\t" + password);
            }
        }catch (Exception e ){
   
            e.printStackTrace();
        }finally {
   
            // 6.关闭资源
            try {
   
                pstm.close();
            } catch (SQLException throwables) {
   
                throwables.printStackTrace();
            }
            try {
   
                conn.close();
            } catch (SQLException throwables) {
   
                throwables.printStackTrace();
            }
        }
    }
}

6 JDBC异常处理

  1. 定位问题(哪一行出错)1. 添加打印语句,检查程序执行流程。
  2. 根据异常信息定位(找到异常信息中关于自己写的代码部分)。
  3. 单元测试更容易定位问题以某个小功能为单位进行测试,例如将用户输入的数据插入到数据库。
  4. 完成用户输入的数据,测试运行。
  5. 完成数据库Connection对象的创建,测试运行。
  6. 准备执行SQL语句,测试运行。
  7. 为?占位符赋值并执行SQL,测试运行。
  8. 根据异常日志分析 分析异常最高效的办法是阅读异常日志,根据异常日志的信息,可以快速定位问题的位置、问题的原因设置是问题的解决方案。 演示:Exception in thread “main” java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382) // 数字指的是代码行号
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at com.baizhi.test.ResultSetTest.main(ResultSetTest.java:8) 调试步骤:1. 先看异常类型:
    ClassNotFoundException: com.mysql.cj.jdbc.Driver,说明是驱动类文件没有找到。
  9. 再看异常位置:
    自上而下看出现数字的行,找到自己写的代码。比如当前案例下:自己的代码 ResultSetTest.java第8行有问题,那么错误就在这里发生的。
  10. 解决方案:
    com.mysql.cj.jdbc.Driver类没找到,这行代码是加载驱动类的,就一定要先找到对应的类。
  11. 错误原因:
    1.包名或类名可能写错了。
    2.如果包名与类名完全正确,检查驱动jar包没有导入当前项目中。 说明:阅读异常日志挑错是最有效的,但是对于初学者而言难度过高。刚开始不用急于求成,异常信息也非常有规范,在学习过程中慢慢提升异常阅读能力,最终掌握阅读异常信息的能力。
  • 快速掌握JDBC中的常见异常处理方案 JDBC的异常其实还是非常固定的,就那么几种:类加载问题、数据库用户名密码错误、sql语法错误、绑定数据错误、获取数据错误… 有1个办法可以快速的掌握JDBC中的异常: 站在上帝视角,人为的造错,观察记录异常信息,日后出现时可以快速调错。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值