盒子
盒子
文章目录
  1. JDBC概念和数据库驱动程序
  2. JDBC原理
  3. 准备数据
  4. JDBC的开发步骤
  5. 执行select语句获取结果集
  6. SQL注入攻击
  7. SQL注入攻击用户登录案例
  8. PreparedStatement接口预编译SQL语句
  9. PreparedStatement接口预编译SQL语句执行修改
  10. PreparedStatement接口预编译SQL语句执行查询
  11. JDBC的工具类和测试

JDBC基础

JDBC概念和数据库驱动程序

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

  • JDBC提供了一种基准,据此可以构建更高级的工具和接口,是数据库开发人员能够编写数据库应用程序。

  • JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。

  • 可以理解为,jdbc提供的接口API规定了我们怎么与数据库进行交互,但是每个数据库(比如mysql,oracle,sqlserver..)都是不一样的,具体怎么操作还是得数据库厂商提供给我们的类库。该类库实现了jdbc的接口,但是每个不同的数据库的实现方式不同罢了。

  • 举个栗子,一个任务,是让我们从A地到达B地,这个任务可以表示jdbc的规定,他规定我们从A地到B地,然后三个人,分别代表小明(mysql数据库厂商),小红(oracle数据库厂商),小绿(sqlserver数据库厂商),他们的任务就是从A地去B地,但是他们的方式不同,小明选择走过去,小红选择坐公交,小绿选择打的。最终都完成了任务,但是他们因为人与人之间有差异而有不同的方法去,这个每个人的方法就是驱动,我们只要选择谁的驱动,便能操作谁。

  • 我们这里使用的是mysql的驱动 mysql-connector-java-5.1.39-bin.jar

JDBC原理

  1. Java提供访问数据库规范称为JDBC,而生产厂商提供规范的实现类称为驱动。

  2. JDBC就是一套API,就是Sun公司定义的类或者是接口。

    • MySQL驱动:驱动也是类库,实现了Sun规定的接口,重写接口方法
    • Oracle驱动: 驱动程序也是类库,实现Sun规定接口,重写接口方法
    • 然而不管是哪一种驱动,都必须实现jdbc的接口重写其中的方法。数据库不同,当然底层实现原理不同,比如jdbc接口中的连接数据库方法,不同的数据库重写他的方式肯定不同,但是我们没必要学习他们,我们只要有某个数据库的驱动,直接调用接口中的连接方法就行,他会自动调用被重写后的,所以我们学习的话,如果学习各个驱动的话,学不完的,所以只要学习jdbc的接口和类库中的方法就行了。

    JDBC原理

准备数据

  • 数据库中准备数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
create database jdbcdb charset = utf8mb4;

use jdbcdb;

create table sort(
sid int primary key auto_increment,
sname varchar(100),
sprice double,
sdesc varchar(5000)
);

insert into sort(sname,sprice,sdesc) values('家具',8900,'家具价格上调,原材料涨价'),
('家电',2000,'优惠促销'),
('儿童玩具',300,'赚家长钱'),
('生鲜',500.99,'新不新鲜谁知道'),
('服装',24000,'换季销售'),
('洗涤',50,'洗衣液促销');

select * from sort;//插入数据成功

JDBC的开发步骤

  1. 注册驱动

    • 告知JVM使用的是哪一个数据库的驱动
  2. 获得连接

    • 使用JDBC中的类,完成对MySQL数据库的连接
  3. 获得语句执行平台

    • 通过连接对象获取对SQL语句的执行者对象
  4. 执行sql语句

    • 使用执行者对象,向数据库执行SQL语句
    • 获取到数据库的执行后的结果
  5. 处理结果

  6. 释放资源 一堆close()

导入mysql数据库驱动程序jar包

点击此处下载mysql驱动

导入jar包

导入jar包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package jdbcTest;

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

import com.mysql.jdbc.Driver;

/*
JDBC操作数据库的步骤
1. 注册驱动
告知JVM使用的是哪一个数据库的驱动
2. 获得连接
使用JDBC中的类,完成对MySQL数据库的连接
3. 获得语句执行平台
通过连接对象获取对SQL语句的执行者对象
4. 执行sql语句
使用执行者对象,向数据库执行SQL语句
获取到数据库的执行后的结果
5. 处理结果
6. 释放资源 一堆close()
*/
public class JDBCDemo {
public static void main(String[] args) throws ClassNotFoundException {
//1.注册驱动
//使用java.sql.DriverManager类静态方法registerDriver(Driver driver)
//jdbc中的Driver是一个接口,传参的话这里应该是传MySQL驱动程序中的实现类Driver
//DriverManager.registerDriver(new Driver());

注册驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
		//以上是驱动类Driver源代码,我们发现,当我们new Driver()的时候,Driver类中的static静态代码块也会执行一次,而代码块中又进行了一次注册驱动,这样就等于注册2次驱动
//而Driver的源码文件建议我们使用反射来进行注册,使用反射的话,将我们的字节码文件加载到jvm中去会执行static代码块
Class.forName("com.mysql.jdbc.Driver");

//2.获得数据库连接 DriverManager类中静态方法
//static Connection getConnection(String url,String user,String password)
//返回值是Connection接口的实现类,在mysql驱动程序
//url:数据库地址(固定写法):jdbc:mysql://连接的主机IP:端口号//数据库名字
String url = "jdbc:mysql://localhost:3306/jdbcdb";
String username = "root";
String password = "root";
Connection con = DriverManager.getConnection(url,username,password);
//打印出来结果说明连接成功
//System.out.println(con);//com.mysql.jdbc.JDBC4Connection@277050dc

//3.获得语句执行平台,通过数据库连接对象,获取到SQL语句的执行者对象
//con对象调用方法 Statement createStatement() 获取Statement对象,将SQL语句发送到数据库
//返回值是Statement接口的实现类对象,在mysql驱动程序
Statement stat = con.createStatement();
//4.执行sql语句
//通过执行者对象调用方法执行SQL语句,获取结果
//int executeUpdate(String sql) 执行数据库中的SQL语句,insert delete update
//返回值int,操作成功数据表多少行
int row = stat.executeUpdate("insert into sort(sname,sprice,sdesc) values('汽车用品',50000,'疯狂涨价')");
System.out.println(row);

//6.释放资源 一堆close()
stat.close();
con.close();
}
}

执行select语句获取结果集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package jdbcTest;

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

/*
* JDBC技术,查询数据表,获取结果集
*/
public class JDBCDemo0 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
String url = "jdbc:mysql://localhost:3306/jdbcdb";
String username = "root";
String password = "root";
Connection con = DriverManager.getConnection(url,username,password);
//3.获得语句执行平台,通过数据库连接对象,获取到SQL语句的执行者对象
//con对象调用方法 Statement createStatement() 获取Statement对象,将SQL语句发送到数据库
//返回值是Statement接口的实现类对象,在mysql驱动程序
Statement stat = con.createStatement();
//拼写查询的SQL
String sql = "select * from sort";
//4.调用执行者对象方法,执行sql语句获取结果集
//ResultSet executeQuery(String sql) 执行SQL语句中的select查询
//返回值ResultSet接口的实现类对象,实现类在mysql驱动中
ResultSet rs = stat.executeQuery(sql);
//5.处理结果集
//ResultSet接口方法boolean next() 返回true,有结果集,返回false没有结果集
while(rs.next()){
//获取每列数据,使用是ResultSet接口的方法:getXXX,方法的参数中,建议写String列名
System.out.println(rs.getInt("sid")+ " "+rs.getString("sname")+" "+ rs.getDouble("sprice")+" "+rs.getString("sdesc"));
}
rs.close();
stat.close();
con.close();
}
}

SQL注入攻击

  • SQL注入是什么

用户在页面输入账号密码,提交后,后台拿到账号密码和数据表中比对,比对成功则登录成功。

SQL注入的登录案例

  • 数据准备
1
2
3
4
5
6
7
8
9
10
11
use jdbcdb;

create table users(
id int primary key auto_increment,
username varchar(100),
password varchar(100)
);

insert into uses(username,password) values('a','1'),('b','2');

select * from users;
  • 登录查询
    select * from users where username='a' and password='1';
    以上语句可以查询到结果

但是SQL注入攻击是什么呢?
select * from users where username='a' and password='1' or 1=1;
这里我们添加了一个or 1=1,1=1永远为true,or是或,只要满足一个条件,所以,这里前面的username和password是什么已经不重要了,永远都能查到。
这就是注入攻击,通过SQL语句注入攻击。

SQL注入攻击用户登录案例

  1. 以下这个案例,我们sql语句后面加了or 1=1后发现,无论我们输入错的还是对的用户名密码,都能登录成功(查找到数据库中数据)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package jdbcTest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

/*
Java程序实现用户登录,用户名和密码,数据库检查
演示被被人注入攻击
*/
public class JDBCDemo2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbcdb";
String username = "root";
String password = "root";
Connection con = DriverManager.getConnection(url,username,password);
Statement stat = con.createStatement();

Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String user = sc.next();
System.out.println("请输入密码:");
String pass = sc.next();

//执行SQL语句,数据表,查询用户名和密码,如果存在登录成功,不存在登录失败
String sql = "select * from users where username='"+user+"' and password='"+pass+"' or 1=1";
ResultSet rs = stat.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getString("username")+" "+ rs.getString("password"));
}
rs.close();
stat.close();
con.close();
}
}
  1. 根据上面那个案例,我们可以知道,我们就可以通过在输入密码的时候,加点别的,就能利用这一点登录到别人的账号,比如以下案例

注入攻击

PreparedStatement接口预编译SQL语句

  • 预编译的Statement的使用是为了提高数据库性能(通过Statement向数据库发送一个 sql语句,数据库再进行编译,编译完成后将编译结果存到数据库端的缓存中,下次有相同的sql语句不会编译了直接使用,提高数据库的性能)和防止注入攻击。

  • 看下面的案例,用了预编译后,我们再随便输入就不行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package jdbcTest;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;

/*
Java程序实现用户登录,用户名和密码,数据库检查
防止注入攻击
Statement接口的实现类,作用是执行SQL语句,返回结果集
Statement接口有一个子接口PreparedStatement (SQL预编译存储,多次高效的执行SQL)
PreparedStatement的实现类在数据库的驱动中,如何获取该实现类呢
通过Connection数据库连接对象的方法:
PreparedStatement prepareStatement(String sql)
*/
public class JDBCDemo3 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbcdb";
String username = "root";
String password = "root";
Connection con = DriverManager.getConnection(url,username,password);
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String user = sc.nextLine();
System.out.println("请输入密码:");
String pass = sc.nextLine();

//执行SQL语句,数据表,查询用户名和密码,如果存在登录成功,不存在登录失败
String sql = "select * from users where username=? and password=?";
//调用Connection接口的方法prepareStatement,获取PreparedStatement接口的实现类
//方法中参数是SQL语句,SQL语句中的参数全部采用问号占位符
PreparedStatement pst = con.prepareStatement(sql);
//调用pst对象set方法,设置问号占位符上的参数
pst.setObject(1,user);//第一个参数1,表示第一个占位符,第二个参数是给该占位符一个值
pst.setObject(2, pass);//同上,这里使用setObject也可以用setXXX其他的,和获取结果集的getXXX一样
//调用方法,执行SQL,获取结果集
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("username")+" "+rs.getString("password"));
}
rs.close();
pst.close();
con.close();
}
}

PreparedStatement接口预编译SQL语句执行修改

  • 执行以下代码,进入数据库中查看表中数据,发现修改成功。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package jdbcTest;

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

/*
* 使用PreparedStatement接口,实现数据表的更新操作
*/
public class JDBCDemo4 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbcdb";
String username = "root";
String password = "root";
Connection con = DriverManager.getConnection(url,username,password);

//拼写修改的SQL语句,参数采用?占位
String sql ="update sort set sname = ?,sprice=? where sid = ?";
//调用数据库连接对象con的方法prepareStatement获取SQL语句的预编译对象
PreparedStatement pst = con.prepareStatement(sql);
//调用pst的方法setXXX设置?占位符
pst.setObject(1, "预编译修改");
pst.setObject(2,40000);
pst.setObject(3, 5);
//调用pst方法执行SQL语句
pst.executeUpdate();

pst.close();
con.close();
}
}

PreparedStatement接口预编译SQL语句执行查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package jdbcTest;

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

/*
* PreparedStatement接口实现数据表的查询操作
*/
public class JDBCDemo5 {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbcdb";
String username = "root";
String password = "root";
Connection con = DriverManager.getConnection(url,username,password);

String sql ="select * from sort";
PreparedStatement pst = con.prepareStatement(sql);
//调用pst对象的方法,执行查询语句,select
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("sid")+" "+ rs.getString("sname")+" "+rs.getString("sprice")+" "+rs.getString("sdesc"));
}
rs.close();
pst.close();
con.close();
}
}

JDBC的工具类和测试

  • 因为jdbc连接数据库操作,前几步骤都是一样的,为了避免写重复的代码,我们封装一个工具类。通过该类直接调用方法获取数据库连接对象。

  • 封装工具类JDBCUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package jdbcTest;

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

/*
* 定义JDBC的工具类
* 定义方法,直接返回数据库的连接对象
*
* 写关闭资源方法
*/
public class JDBCUtils {
//私有构造方法,工具类直接调用静态方法,不让创建对象
private JDBCUtils(){}
//定义一个静态变量,该变量是数据库连接对象
//这里我们用的单例饿汉模式创建该变量
private static Connection con;

static{
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbcdb";
String username = "root";
String password = "root";
con = DriverManager.getConnection(url,username,password);
} catch (Exception e) {
// 如果连接数据库都出现了异常,就没必要往下运行了,所以这里我们创建一个异常
throw new RuntimeException(e+"数据库连接失败");
}

}
/*
定义静态方法,返回数据库的连接对象
*/
public static Connection getConnection(){
return con;
}
//关闭资源的静态方法(无查询结果集的)
public static void close(Connection con,Statement stat){
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//关闭资源的静态方法(有查询结果集的)
public static void close(Connection con,Statement stat,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
  • 测试类

    • 查询成功,工具类可用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package jdbcTest;

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

public class TestJDBCUtils {
public static void main(String[] args) throws SQLException {
Connection con = JDBCUtils.getConnection();
PreparedStatement pst = con.prepareStatement("select sname from sort");
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("sname"));
}
JDBCUtils.close(con, pst, rs);
}
}
联系我
扫一扫,添加JzhBetter
  • 微信扫一扫
  • qq扫一扫