JDBC的使用笔记

下午花了三四个小时,在慕课网上看了看关于JDBC的两个系列的视频,跟着做了做。因为之前也看过,所以一直都记得有这么一个视频存在,但是唯一不足的是没写上一些笔记。写个简易的学习笔记,加深一下对其中一些知识的理解,以后翻起来就不用去看视频了,看这个就好。还记录下自己在学习过程中所遇到的一些疑问以及自己所找到的答案。

视频链接如下:

JDBC之“对岸的女孩看过来”
JDBC之“对岸的女孩走过来”

JDBC是什么

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。从根本上说,JDBC是一种规范,它提供的接口,一套完整的,可移植的访问底层数据库的程序。具体实现由数据库厂商实现。

虽然在实践中,不会使用这个原始的工具,但是了解一下还是有一些好处的呢。

JDBC使用基本步骤

这里写图片描述
用自己的话描述复述如下:

  1. 注册驱动
  2. 获取连接
  3. 通过连接执行SQL

代码示例:

加载驱动与获取数据库连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package db;
import java.sql.*;
public class DBUtil {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/imooc";
private static final String NAME = "root";
private static final String PASSWORD = "root";
private static Connection conn;
static {
try {
// 加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 获得数据库连接
conn = DriverManager.getConnection(URL, NAME, PASSWORD);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
return conn;
}
}

执行SQL

1
2
3
4
5
6
7
8
9
public void deleteGoddess(int id) throws SQLException {
Connection conn = DBUtil.getConnection();
String sql = "" +
" delete from imooc_goddess" +
" where id = ?";
PreparedStatement ptmt = conn.prepareStatement(sql);
ptmt.setInt(1, id);
ptmt.execute();
}

基本的CURD

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package dao;

import db.DBUtil;
import model.Goddess;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class GoddessDao {

public void addGoddess(Goddess goddess) throws SQLException {
Connection conn = DBUtil.getConnection();
String sql = "" +
"insert into imooc_goddess" +
"(user_name, sex, age, birthday, email, mobile, "+
"create_user, create_date, update_user, update_date, isdel)" +
"values("+
"?, ?, ?, ?, ?, ?, ?, current_date(), ?, current_date(), ?)";
PreparedStatement ptmt = conn.prepareStatement(sql);
ptmt.setString(1, goddess.getUser_name());
ptmt.setInt(2, goddess.getSex());
ptmt.setInt(3, goddess.getAge());
ptmt.setDate(4, new Date(goddess.getBirthday().getTime()));
ptmt.setString(5, goddess.getEmail());
ptmt.setString(6, goddess.getMobile());
ptmt.setString(7, goddess.getCreate_user());
ptmt.setString(8, goddess.getUpdate_user());
ptmt.setInt(9, goddess.getIsdel());
ptmt.execute();
}

public void updateGoddess(Goddess goddess) throws SQLException {
Connection conn = DBUtil.getConnection();
String sql = "" +
" update imooc_goddess" +
" set user_name=?, sex=?, age=?, birthday=?, email=?, mobile=?, "+
" update_user=?, update_date=current_date(), isdel=?"+
" where id = ?";
PreparedStatement ptmt = conn.prepareStatement(sql);
ptmt.setString(1, goddess.getUser_name());
ptmt.setInt(2, goddess.getSex());
ptmt.setInt(3, goddess.getAge());
ptmt.setDate(4, new Date(goddess.getBirthday().getTime()));
ptmt.setString(5, goddess.getEmail());
ptmt.setString(6, goddess.getMobile());
ptmt.setString(7, goddess.getUpdate_user());
ptmt.setInt(8, goddess.getIsdel());
ptmt.setInt(9, goddess.getId());
ptmt.execute();
}

public void deleteGoddess(int id) throws SQLException {
Connection conn = DBUtil.getConnection();
String sql = "" +
" delete from imooc_goddess" +
" where id = ?";
PreparedStatement ptmt = conn.prepareStatement(sql);
ptmt.setInt(1, id);
ptmt.execute();
}

public List<Goddess> query() throws SQLException {
List<Goddess> gs = new ArrayList<>();
Connection conn = DBUtil.getConnection();
Statement stmt = conn.createStatement();
StringBuilder sb = new StringBuilder("select * from imooc_goddess");
ResultSet rs = stmt.executeQuery(sb.toString());

Goddess g = null;
while (rs.next()){
g = new Goddess();
g.setId(rs.getInt("id"));
g.setUser_name(rs.getString("user_name"));
g.setAge(rs.getInt("age"));
g.setSex(rs.getInt("sex"));
g.setBirthday(rs.getDate("birthday"));
g.setEmail(rs.getString("email"));
g.setMobile(rs.getString("mobile"));
g.setCreate_date(rs.getDate("create_date"));
g.setCreate_user(rs.getString("create_user"));
g.setUpdate_date(rs.getDate("update_date"));
g.setUpdate_user(rs.getString("update_user"));
g.setIsdel(rs.getInt("isdel"));
gs.add(g);
}
return gs;
}

public List<Goddess> query(List<Map<String, Object>> params) throws SQLException {
List<Goddess> gs = new ArrayList<>();
Connection conn = DBUtil.getConnection();
// 这里有点厉害
StringBuilder sb = new StringBuilder("select * from imooc_goddess where 1=1");
if (params != null && params.size() > 0){
for (int i = 0; i < params.size(); i++) {
Map<String, Object> map = params.get(i);
sb.append(" and " + map.get("name") + " " + map.get("rela") + " " + map.get("value"));
}
}
System.out.println(sb.toString());
PreparedStatement ptmt = conn.prepareStatement(sb.toString());
ResultSet rs = ptmt.executeQuery();

Goddess g = null;
while (rs.next()){
g = new Goddess();
g.setUser_name(rs.getString("user_name"));
g.setAge(rs.getInt("age"));
g.setSex(rs.getInt("sex"));
g.setBirthday(rs.getDate("birthday"));
g.setEmail(rs.getString("email"));
g.setMobile(rs.getString("mobile"));
g.setCreate_date(rs.getDate("create_date"));
g.setCreate_user(rs.getString("create_user"));
g.setUpdate_date(rs.getDate("update_date"));
g.setUpdate_user(rs.getString("update_user"));
g.setIsdel(rs.getInt("isdel"));
gs.add(g);
}
return gs;
}

public Goddess get(int id) throws SQLException {
Connection conn = DBUtil.getConnection();
String sql = "" +
" select * from imooc_goddess" +
" where id = ?";
PreparedStatement ptmt = conn.prepareStatement(sql);
ptmt.setInt(1, id);

ResultSet rs = ptmt.executeQuery();
Goddess g = null;
while (rs.next()){
g = new Goddess();
g.setUser_name(rs.getString("user_name"));
g.setAge(rs.getInt("age"));
g.setSex(rs.getInt("sex"));
g.setBirthday(rs.getDate("birthday"));
g.setEmail(rs.getString("email"));
g.setMobile(rs.getString("mobile"));
g.setCreate_date(rs.getDate("create_date"));
g.setCreate_user(rs.getString("create_user"));
g.setUpdate_date(rs.getDate("update_date"));
g.setUpdate_user(rs.getString("update_user"));
g.setIsdel(rs.getInt("isdel"));
}
return g;
}
}

MVC架构

其实这就是我为啥想看这个系列视频的理由咯。都知道M(Model)、V(View)、C(Controller),但是在代码中是如何体现呢?非常想知道这一点。
这里写图片描述

在“本系列视频·上”中,V是使用命令行的黑窗口,通过黑窗口来输入命令。命令给到控制层后,由控制层进行相应的操作;而控制层则是通过调用dao来直接与数据库交流。其中黑窗口(View层)可学习的价值并不是很高。

学习时遇到的疑问

为什么执行Class.forName("xxxxx")驱动就加载好了?

com.mysql.jdbc.Driver中,有一段静态代码,就是向DriverManager注册自身这个驱动。JDBC的规范吧,都需要这么写。

1
2
3
4
5
6
7
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}

Registers the given driver with the {@code DriverManager}.A newly-loaded driver class should call the method {@code registerDriver} to make itself known to the {@code DriverManager}. If the driver is currently registered, no action is taken.

1
2
3
4
5
6
7
8
9
10
11
public static synchronized void registerDriver(java.sql.Driver driver,DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}

后面的获取连接是通过DriverManager的getConnection方法,通过此方法会调用其同名不同参数方法getConnection(String url, java.util.Properties info, Class<?> caller),其中有段源代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 容器registeredDrivers则是保存着我们在上面代码中所注册的所有驱动的相关信息
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
//如果获取到的连接不为空,那么则通过其所需要的驱动,获取连接成功,并返回。
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}

因此,Class.forName(xxxx)在加载该类时触发了其中的static代码块,并在其中向DriverManager注册自身。然后通过DriverManager来遍历所有的驱动信息,并用之加载相应的url,如果成功获取到连接,表示驱动加载成功,并获取到了连接;否则,驱动加载失败或尚未注册驱动,连接获取失败。

PreparedStatement与Statement安全性的差异在哪里?

使用Statement可能会被SQL注入。
假设存在某个需求,对给定的用户id,返回该id所对应的所有信息。言外之意,只有返回相应id的信息才是正确的、可行的,如果获取得到了除了该id之外的信息,那么则将之称为信息泄露也不为过。然而,使用Statement就可能会被有心机的用户利用,获取到除了自身之外的其它用户信息,这想一想都是不允许也不应该发生的。代码演示如下:

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
public List<Goddess> query(String id) throws SQLException {
List<Goddess> gs = new ArrayList<>();
Connection conn = DBUtil.getConnection();
Statement stmt = conn.createStatement();
String sql = "select * from imooc_goddess where id = " + id;
ResultSet rs = stmt.executeQuery(sql);
System.out.println(sql);
Goddess g = null;
while (rs.next()){
g = new Goddess();
g.setId(rs.getInt("id"));
g.setUser_name(rs.getString("user_name"));
g.setAge(rs.getInt("age"));
gs.add(g);
}
return gs;
}

public static void main(String[] args) throws SQLException {
// 别有心机的用户名"1 or 1 = 1"
List<Goddess> list = new GoddessDao().query("1 or 1 = 1");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}

输出的结果如下

这里写图片描述

JDBC调用存储过程

存储过程是存储在数据库中的一个小代码,其标识符为Procedure,与开发语言中的函数类似。比如说在Pascal中,函数定义的标识符就是Procedure。在该函数中,可以不带参数,也可以带In类型参数、Out类型参数(顾名思义,传入、传出)。如何使用JDBC调用这些过程呢?

无参存储过程调用

过程
这里写图片描述

代码
这里写图片描述

IN类型参数存储过程调用

这里写图片描述

OUT类型参数存储过程调用

这里写图片描述

OUT类型参数的存储过程的定义如下:
这里写图片描述

JDBC使用事务

事务是作为单个逻辑工作单元执行的一些列操作,这些操作做为一个整体,要么都提交、要么都不执行。

这里写图片描述

JDBC中如何控制事务?

这里写图片描述

代码演示如下:

这里写图片描述

JDBC使用连接池

连接池有一点像线程池的意思,作用有点类似。常用的有dbcp和c3p0。

作者

遇寻

发布于

2018-05-07

更新于

2022-01-01

许可协议

评论