栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

数据库从入门到精通 04 JDBC

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

数据库从入门到精通 04 JDBC

文章目录
  • 1 JDBC
    • 1.1 概念
    • 1.2 使用步骤
    • 1.3 idea 创建项目并导入jar包cg
    • 1.4.1 入门案例-连接数据库
    • 1.4.2 查询tb_user表里的数据
    • 1.4.3 模拟用户登录
    • 1.4.4 模拟用户登录--更换传输器后的版本
    • 1.5 SQL注入
  • 2 JDBC常见问题
    • 2.1 Class.forName这句话有用没?
    • 2.2 驱动版本
    • 2.3 中文乱码
    • 2.4 SQL注入
    • 2.5 PreparedStatement 语句
    • 2.6 常见错误
    • 2.6.1 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
    • 2.6.2 Unknown database mydb;
    • 2.6.3 Access denied for user ‘root123’@‘localhost’ (using password: YES)
    • 2.6.4 Table ‘py-school-db.mydb’ doesn’t exist


1 JDBC 1.1 概念

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

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

所以,JDBC是java database connectivity的简称,是专门用来完成 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注入,其也是预编译性能高。

1.2 使用步骤
  1. 导入jar包(使用JDBC提供了丰富的工具类)

  2. 提供连接数据库的参数(用户名root 密码root 端口号3306)

  3. 在java程序中,发起SQL语句操作数据库

  4. 如果数据库有查到的结果,返回给java程序

1.3 idea 创建项目并导入jar包cg
  • 创建project: File - New - Project - 选择java - next - next - 输入工程名称 - Finish
  • 导入jar包:找到文件mysql-connector-java-5.1.32.jar 复制,粘贴到Project里
  • 在IDEA里,选中jar包,右键编译(add as library…),ok
  • 检查是否编译成功:看到IDEA里的jar包可以被点开了

最后编写java代码

package cn.tedu.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

//JDBC的入门案例:
//JDBC是java连接数据库的一个标准,本质上就是一堆的工具类
public class Test1 {
    public static void main(String[] args) throws Exception {
        //1,注册驱动
        Class.forName("com.mysql.jdbc.Driver");//全路径
        //2,获取数据库的连接(用户名/密码)
        //jdbc连接mysql数据库的协议//本机:端口号/数据库的名字
        String url="jdbc:mysql://localhost:3306/cgb2108" ;
        Connection c = DriverManager.getConnection(
                url,"root","root");
        //3,获取传输器
        Statement s = c.createStatement();
        //4,执行SQL,并返回结果集
        String sql="select * from dept" ;//查询dept表的所有数据
        ResultSet r = s.executeQuery(sql);//executeQuery执行查询的SQL,executeUpdate执行增删改查的SQL
        //5,处理数据库返回的结果
        while(r.next()){//next()判断resultset中有数据吗
            //getXxx()获取resultset中的数据
            int a = r.getInt(1);//获取第1列的 整数值
            String b = r.getString(2);//获取第2列的 字符串值
            String c1 = r.getString(3);//获取第3列的 字符串值
            System.out.println(a+b+c1);
        }
        //6,释放资源
        r.close();//释放结果集
        s.close();//释放传输器
        c.close();//释放连接器
    }
}
1.4.1 入门案例-连接数据库
package jdbc;

import com.mysql.jdbc.Driver;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;


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/cgb2108"; //固定写法:jdbc:mysql://localhost:端口号/数据库名称
        Connection c = DriverManager.getConnection(url,"root","root");
        //3.获取传输器
        Statement s = c.createStatement();
        //4.执行sql语句,并返回结果给结果集保存
        String sql="select * from dept";//查询dept表的所有数据
        ResultSet r = s.executeQuery(sql);//executeQuery()用来执行查询的语句,executeLargeUpdate()用来执行增删改,ResultSet结果集,用来保存查询后的结果
        //5.处理数据库返回的结果
        while(r.next()){//判断ResultSet中有没有数据,有返回true
            int a = r.getInt(1);//获取ResultSet第1列整数类型的值
            String b = r.getString(2);//获取ResultSet第2列整数类型的值
            String c1 = r.getString(3);//获取ResultSet第3列整数类型的值
            System.out.println(a+" "+b+" "+c1);
        }
        //6.释放/关闭资源
        r.close();//释放结果集
        s.close();//释放传输器
        c.close();//释放连接器
    }
}
1.4.2 查询tb_user表里的数据
package jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Test2 {
    public static void main(String[] args) throws Exception {
        method();
    }

    //查询tb_user表里的数据
    private static void method() throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取数据库返回的结果
        String url="jdbc:mysql://localhost:3306/cgb2108";
        Connection c = DriverManager.getConnection(url,"root","root");
        //3.获取传输器
        Statement s = c.createStatement();
        //4.执行SQL
        String sql = "select * from tb_user";
        ResultSet r = s.executeQuery(sql);
        //5.解析结果集
        while (r.next()){
            int a = r.getInt("id");
            String b = r.getString("name");
            String c1 = r.getString("password");
            System.out.println(a+" "+b+" "+c1);
        }
        //6.释放资源
        r.close();
        s.close();
        c.close();
    }
}
1.4.3 模拟用户登录
package cn.tedu.jdbc;

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

//需求:利用JDBC,查询tb_user表里的数据

public class Test3 {
    public static void main(String[] args) throws Exception {
//        method();//查询tb_user表里的数据
//        method2();//模拟用户登录
        method3();//解决SQL攻击问题
    }
    //解决SQL攻击问题
    private static void method3() throws Exception {
        //1,注册驱动 2,获取连接
        Connection c = JDBCUtils.getConnection();
        //3,执行SQL
        String a = new Scanner(System.in).nextLine();//用户名
        String b = new Scanner(System.in).nextLine();//密码
        //如果动态的拼接字符串时,数据在中间的位置  "+a+"
//        String sql="select * from tb_user where name='jack' and password='321'" ;
//        String sql="select * from tb_user where name='"+a+"' and password='"+b+"'" ;
        //SQL骨架:用?代替了参数的位置,?叫占位符,好处:简洁(避免了SQL拼接参数)
        String sql="select * from tb_user where name=? and password=?" ;
        //4,获取传输器
//        Statement s = c.createStatement();
        PreparedStatement s = c.prepareStatement(sql);
        //设置SQL参数--setXxx()设置不同类型的参数
        s.setString(1,a);//?的索引,要给?设置的值
        s.setString(2,b);//?的索引,要给?设置的值
        //TODO 当用户名输入jack'#时还会发生SQL攻击吗???
        ResultSet r = s.executeQuery();
        //5,解析结果集
        if(r.next()){//查到数据了吗?查到了就登录成功
            System.out.println("登录成功~");
        }else{
            System.out.println("用户名或者密码输入错误,登录失败~");
        }
        //6,关闭资源
        r.close();
        s.close();
        c.close();
    }

    //查询tb_user表里的数据
    private static void method() throws Exception{
        //调用工具类的方法
        Connection c = JDBCUtils.getConnection();
        //3,获取传输器
        Statement s = c.createStatement();
        //4,执行SQL
        ResultSet r = s.executeQuery("select * from tb_user");
        //5,解析结果集
        while(r.next()){//判断r有数据
            //获取r的数据
            int a = r.getInt("id");//获取表里的id字段的值
            String b = r.getString("name");//获取表里的name字段的值
            String c1 = r.getString("password");//获取表里的password字段的值
            System.out.println(a+b+c1);
        }
        //6,释放资源
        r.close();//释放结果集
        s.close();//释放传输器
        c.close();//释放连接器
    }
    
    private static void method2() throws Exception {
        //1,注册驱动 2,获取连接
        Connection c = JDBCUtils.getConnection();
        //3,获取传输器
        Statement s = c.createStatement();
        //4,执行SQL
        String a = new Scanner(System.in).nextLine();//用户名
        String b = new Scanner(System.in).nextLine();//密码
        //如果动态的拼接字符串时,数据在中间的位置  "+a+"
//        String sql="select * from tb_user where name='jack' and password='321'" ;
String sql="select * from tb_user where name='"+a+"' and password='"+b+"'" ;
        ResultSet r = s.executeQuery(sql);
        //5,解析结果集
        if(r.next()){//查到数据了吗?查到了就登录成功
            System.out.println("登录成功~");
        }else{
            System.out.println("用户名或者密码输入错误,登录失败~");
        }
        //6,关闭资源
        r.close();
        s.close();
        c.close();
    }
}
1.4.4 模拟用户登录–更换传输器后的版本
package jdbc;

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

public class Test4 {
    public static void main(String[] args) throws Exception {
        method();
    }
    //查询tb_user表里的数据
    private static void method() throws Exception {
        //1.获取数据库的连接(注册驱动+获取连接)
        Connection c = JDBCUtils.getConnection();
        //2.执行SQL
        System.out.println("请输入用户名");
        String a = new Scanner(System.in).nextLine();//用户名
        System.out.println("请输入密码");
        String b = new Scanner(System.in).nextLine();//密码
        //SQL骨架:用?代替了参数的位置,?叫占位符;好处:简洁(避免了SQL拼接参数)
        String sql = "select * from tb_user where name=? and password=? ";
        //3.获取传输器
        
        //Statement s = c.createStatement();
        PreparedStatement s = c.prepareStatement(sql);
        //4.设置SQL参数----setxxx()方法
        s.setString(1,a);//括号中需要俩个参数,第一个是位置,第二个是值
        s.setString(2,b);//?的索引,要给?设置的值
        ResultSet r = s.executeQuery();
        //5.解析结果集
        if(r.next()) {//查到数据了吗,查到了就登录成功
            System.out.println("登录成功");
        }else{
            System.out.println("用户名或密码输入错误,登录失败");
        }
        //6.释放资源
        r.close();
        s.close();
        c.close();
    }
}
1.5 SQL注入
package jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

//JDBC练习
public class Test5 {
    public static void main(String[] args) throws Exception {
        method();//查询部门表的<100数据
        metod2();//向dept表里插入数据
    }

    private static void metod2() throws Exception {
        Connection c = JDBCUtils.getConnection();
        //插入数据时要看表里有几个字段需要设置值,就设置几个问号
        String sql = "insert into dept values(?,?,?)";
        PreparedStatement p = c.prepareStatement(sql);
        //设置SQL参数
        p.setObject(1,666);
        p.setObject(2,"软件测试部");
        p.setObject(3,"大山西");
        
        //执行SQL
        int i = p.executeUpdate();
        //T0D0 会返回结果集吗?返回了的是啥?
        System.out.println("影响的行数是:"+i);
        //关闭资源
        p.close();
        c.close();


    }

    //查询部门表的所有数据
    private static void method() throws Exception {
        //利用工具类获取数据库的连接
        Connection c = JDBCUtils.getConnection();
        //获取传输数据
        String sql = "select * from dept where depno < ?";
        PreparedStatement s = c.prepareStatement(sql);
        //设置参数
        s.setInt(1,100);
        ResultSet r = s.executeQuery();
        //处理结果集
        while (r.next()){//next()判断有数据吗
            //获取数据
            String str = r.getString("dname");
            //获取dname列的值,并打印
            System.out.println(str);

        }
        //释放资源
        r.close();
        s.close();
        c.close();
    }
}
2 JDBC常见问题 2.1 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文件中找启动类。

2.2 驱动版本

不同版本的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
2.3 中文乱码

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

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

2.4 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 ''

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

2.5 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语句。

2.6 常见错误 2.6.1 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

错误原因:

  • jar没有导入,没有builder path

  • Class.forName(“com.mysql.jdbc.Driver”); 字符串拼写错误

2.6.2 Unknown database mydb;

错误原因:

  • 数据库名称拼写错误
2.6.3 Access denied for user ‘root123’@‘localhost’ (using password: YES)

错误原因:

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

2.6.4 Table ‘py-school-db.mydb’ doesn’t exist

错误原因:

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/318680.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号