0%

JDBC笔记

JDBC概述

Java Database connectivity,Java与数据库连接的操作规范。不同的数据库都要遵循这个规范(接口)。

JDBC规范

掌握四个核心对象:

  • DriverManager:用于注册驱动。
  • Connection:表示与数据库创建的连接。
  • Statement:操作数据库SQL语句的对象。
  • ResultSet:结果集 或 一张虚拟表。

JDBC的准备工作

JDBC接口放在JDK中的 java.sql 和 javax.sql。

  1. 下载jdbc的jar包。
  2. 导入jar包到项目中。

实现JDBC操作

  1. 注册驱动
  2. 创建连接
  3. 得到执行SQL语句的Statement对象
  4. 执行SQL语句,并返回结果
  5. 处理结果
  6. 关闭资源

DriverManager

java.sql.DriverManager类:注册驱动,创建连接。

这个类在加载时就会注册驱动,所以在使用时,只需要使用Class.forName("com.mysql.jdbc.Driver");来加载一下这个类即可完成注册驱动。使用DriverManager.registerDriver(new Driver());会造成二次注册驱动。

Connection

有三种创建连接的方法:

  1. Connection conn = DriverManager.getConnection(url,user,password);

  2. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1?user=root&password=123456");

  3. String url = "jdbc:mysql://localhost:3306/test1";
    Properties info = new Properties();
    info.setProperty("user","root");
    info.setProperty("password","passwo2d@CQ");
    Connection conn = DriverManager.getConnection(url, info);
    <!--0-->
    

java的数据类型与数据库中类型的对应关系

java 数据库
byte tityint
short smallint
int int
long bigint
float float
double double
String char varchar
Date date

resultSet用游标记录了当前在哪一行。

next()将游标移动到下一行。previous()将游标移动到上一行。afterLast()将光标移动到末尾,位于最后一行之后。beforeFirst()将光标移动到开头,正好位于第一行之前。

正确关闭资源

因为程序中有可能抛出异常,而抛出异常会导致后面的资源无法关闭,所以将close方法放到finally中。再用一套try/catch包裹close来处理close的方法。

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
public static void main(String[] args){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
// 1. 注册驱动,使用mysql驱动
// DriverManager.registerDriver(new Driver()); 不建议使用,会导致注册两次驱动。
// Class.forName("com.mysql.jdbc.Driver");
// 2. 创建连接
String url = "jdbc:mysql://localhost:3306/test1?serverTimezone=GMT";
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "passwo2d@CQ");
conn = DriverManager.getConnection(url, info);

// 3. 得到执行SQL语句的Statement对象
stmt = conn.createStatement();

// 4. 执行SQL语句,并返回结果
rs = stmt.executeQuery("select * from user;");

// 5. 处理结果
while (rs.next()) {
// 获取5个字段的数据
System.out.print(rs.getObject(1) + " - ");// id
System.out.print(rs.getObject(2) + " - ");// name
System.out.print(rs.getObject(3) + " - ");// password
System.out.print(rs.getObject(4) + " - ");// email
System.out.print(rs.getObject(5) + " - ");// birthday
System.out.println();
}
}catch(Exception e){

}finally{
// 6. 关闭资源
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

抽取工具类(简化连接数据库和关闭资源操作)

创建一个同级工具类包,内放一个工具类

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
package com.Retur0.util;

import java.sql.*;

public class DBUtils {
private static String url = "jdbc:mysql://localhost:3306/test1?serverTimezone=GMT";
private static String user = "root";
private static String password = "passwo2d@CQ";
//private static String DriverClass = "com.mysql.jdbc.Driver";//驱动
// static{
// try {
// Class.forName(DriverClass);
// }catch(ClassNotFoundException e){
// e.printStackTrace();
// }
// }
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}

public static void close(Connection conn, ResultSet rs, Statement stmt){
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

之后连接和操作数据库的代码就简化如下:

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
package demo;

import com.Retur0.util.DBUtils;
import java.sql.*;

/**
* 抽取数据库操作(简化操作):
* 把资源连接封装到一个工具类
* 把关闭资源封装到一个工具类
*/
public class demo02 {
public static void main(String[] args){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
conn = DBUtils.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from user");
while (rs.next()) {
// 获取5个字段的数据
System.out.print(rs.getObject(1)+"\t");// id
System.out.print(rs.getObject(2)+"\t");// name
System.out.print(rs.getObject(3)+"\t");// password
System.out.print(rs.getObject(4)+"\t");// email
System.out.println(rs.getObject(5)+"\t");// birthday
}
}catch(Exception e){
e.printStackTrace();
}finally{
DBUtils.close(conn, rs, stmt);
}

}
}

插入数据

使用预处理的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
26
27
public static void main(String[] args){
Connection conn = null;
PreparedStatement ps = null;

try {
conn = DBUtils.getConnection();
// 创建预处理的Statement对象。?代表占位。
String sql = "insert into user (id,name,password,email,birthday) values (?,?,?,?,?)";
ps = conn.prepareStatement(sql);

// 设置参数
ps.setInt(1, 4);
ps.setString(2, "name03");
ps.setString(3, "123456");
ps.setString(4, "11111@qq.com");
// setDate的日期类型是java.sql.Date,不是java.util.Date
ps.setDate(5, new java.sql.Date(System.currentTimeMillis()));

int r = ps.executeUpdate();

System.out.println("受影响的行数:"+r);
}catch(Exception e){
e.printStackTrace();
}finally{
DBUtils.close( conn, null, ps);
}
}

更改数据

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 static void main(String[] args){
// 更改name02的密码和邮箱。
Connection conn = null;
PreparedStatement ps = null;
try {
conn = DBUtils.getConnection();
// 创建预处理的Statement对象。?代表占位。
String sql = "update user set password=?, email=? where name=?";
ps = conn.prepareStatement(sql);

// 设置参数
ps.setString(1, "000000");
ps.setString(2, "xxx@qq.com");
ps.setString(3, "name02");

int r = ps.executeUpdate();

System.out.println("受影响的行数:"+r);

}catch(Exception e){
e.printStackTrace();
}finally{
DBUtils.close( conn, null, ps);
}
}

删除数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args){
Connection conn = null;
PreparedStatement ps = null;
try {
conn = DBUtils.getConnection();
// 创建预处理的Statement对象。?代表占位。
String sql = "delete from user where id=?";
ps = conn.prepareStatement(sql);

// 设置参数
ps.setInt(1, 4);

int r = ps.executeUpdate();

System.out.println("受影响的行数:"+r);

}catch(Exception e){
e.printStackTrace();
}finally{
DBUtils.close( conn, null, ps);
}
}

查询数据

查询不使用预处理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
26
27
28
29
30
31
32
33
34
public static void main(String[] args){
// 更改name02的密码和邮箱。
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DBUtils.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from user");

List<User> list = new ArrayList<User>();
// 遍历结果集,把数据封装到模型中
while (rs.next()){
// 把每一条数据封装到一个类User类,这个类就是一个数据模型。
// 模型的包名可以是model/domain/po/pojo
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setEmail((rs.getString("email")));
user.setBirthday((rs.getDate("birthday")));

list.add(user);
}

// 打印
for(User u : list){
System.out.println(u.toString());
}
}catch(Exception e){
e.printStackTrace();
}finally{
DBUtils.close(conn, rs, stmt);
}
}

将数据封装成的模型起码要保存数据

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
public class User {
private int id;
private String name;
private String password;
private String email;
private Date birthday;

public int getId() {
return id;
}

public String getName() {
return name;
}

public String getPassword() {
return password;
}

public String getEmail() {
return email;
}

public Date getBirthday() {
return birthday;
}

public void setId(int id) {
this.id = id;
}

public void setName(String name) {
this.name = name;
}

public void setPassword(String password) {
this.password = password;
}

public void setEmail(String email) {
this.email = email;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

public String toString(){
return id + " " + name + " " + password + " " + email + " " + birthday;
}
}

SQL注入

如果把占位符换成字符串变量,用拼接的方法完成sql语句,会有注入的问题。

这个问题就是,如果输入的字符串利用了sql语句的规则成为了字符串的一部分,导致信息校验出现问题(比如查询出不存在的数据)。被注入了其它的sql语句进来。

解决办法就是使用预加载Statement和占位符。