言成言成啊 | Kit Chen's Blog

Java DataBase Connectivity

发布于2020-05-23 16:56:49,更新于2021-02-28 07:00:19,标签:java  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

最近一直在看数学跟英语,好久没碰电脑了。这次尝试用idea,并且破解了永久版本,使用起来,跟eclipse相比,太好用啦!

今天晚上竟然刷了半个多小时的吴京的采访视频,我也是醉咯!不过吴京这人还挺有意思。

先附上java中jar包的maven仓库的国内镜像下载地址,关于JDBC也发现了一个比较好的教程网站

一、了解JDBC

概念:Java DataBase Connectivity即java数据库连接,java语言操作数据库

本质:官方(Sun)公司定义的一套操作所有关系型数据库的规则,即接口。然后各个数据库厂商去实现这套接口,提供数据库驱动jar包,开发者可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类

二、入门JDBC

下面这就是上课时我们所用到的代码,拿来就用。具体为什么要这么用,我也不知道,老师也说不用知道,都是这个套路。

google一下,发现网上的教程,比老师讲得清楚明了

步骤

  1. 导入驱动jar包
  2. 注册驱动
  3. 获取数据库连接对象(Connection)
  4. 定义sql
  5. 获取执行sql语句的对象(Statement)
  6. 执行sql,接收返回的结果
  7. 处理结果
  8. 释放资源

代码

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 class Demo01JDBC {
public static void main(String[] args) throws Exception {
//1.导入驱动jar包
//2.注册驱动
//如果mysql数据库是5+
// Class.forName("com.mysql.jdbc.Driver");
//如果mysql数据库是8+,并且,mysql5之后,是可以省略注册驱动这一代码的,jar包里面是带有自动注册的代码的
Class.forName("com.mysql.cj.jdbc.Driver");
//3.获取数据库的连接对象,如果中途报错timezone的话,可以设置统一时区,UTC时间跟北京时间相差8小时,将ssl设置为false是为了防止开发过程中出现的警告
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/beauty?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false","root","13838385638");
//4.定义sql语句
String sql="select * from memeber";
//5.获取执行sql的对象
Statement stmt=conn.createStatement();
//6.执行sql
ResultSet rs=stmt.executeQuery(sql);
//7.处理结果
while(rs.next()) {
String name=rs.getString("name");
String gender=rs.getString("gender");
System.out.println(name+"->"+gender);
}
//8.释放资源
stmt.close();
conn.close();
}
}

三、详解JDBC

3.1 DriverManager:驱动管理对象

功能

  1. 注册驱动
  2. 获取数据库连接

注册驱动

注册驱动:告诉程序使用哪一个数据库驱动jar,讲解

在DriverManager里面,有这样一个方法

1
2
static void	registerDriver (Driver driver)	
//Registers the given driver with the DriverManager.

Class.forName(“全类名”),在源代码阶段,将字节码文件加载进内存,并返回类对象,当然,这个是没有接收的。

通过查看源码,发现在com.mysql.cj.jdbc.Driver类中,调用了静态代码块

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

mysql5之后的版本可以省略注册驱动

也就是说,我们其实可以直接使用DriverManager.registerDriver(new Driver())来代替Class.forName("xxx");

获取数据库连接对象

在DriverManager里面有这样一个方法

1
2
3
4
5
6
7
8
9
10
/*
* @param url 指定连接的路径
* jdbc:mysql://ip地址(域名):端口号/数据库名称
* 如果连接的是本机的mysql服务器,并且mysql服务默认端口是3306,则url可以简写为jdbc:mysql:///数据库名称
* @param 用户名
* @param 密码
* @return 返回Connection对象
*/
public static Connection getConnection(String url,
String user, String password)

3.2 Connection:数据库连接对象

功能

  1. 获取执行sql的对象
  2. 管理事务

获取执行sql的对象

Connection中有两个这样的方法

1
2
3
4

Statement createStatement();

PreparedStatement prepareStatement(String sql);

管理事务

开启事务

1
2
//传入false,即开启了手动提交
void setAutoCommit(boolean autoCommit);

提交事务

1
void commit()

回滚事务

1
void rollback()

3.3 Statement:执行sql对象

执行sql

1
2
3
4
5
6
7
8
9
//可以执行任意的sql,但是不常用
//若返回true,则表示结果是一个结果集;若false,表示修改数或没有结果
boolean execute(String sql);
//执行DDL和DML语句,也就是对库和表进行操作的
//@return 影响的行数
int executeUpdate(String sql);
//执行DQL语句
//@return 结果集
ResultSet executeQuery(String 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
50
51
52
public class Demo02JDBC {
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.定义sql
String sql="insert into memeber(name,gender,age,grade,p_id) values('武当陈师行','男',40,100,1)";
//3.获取Connection对象
conn=DriverManager.getConnection("jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai", "root", "13838385638");
//4.获取执行sql的对象
stmt=conn.createStatement();
//5.执行sql
int count=stmt.executeUpdate(sql);
//6.打印结果
if(count>0) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
//7.释放资源
stmt.close();
conn.close();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//如果在执行getConnection的时候,就报错了,那么返回的对象就是空了
//避免空指针异常
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.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
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
public class Demo03JDBC {
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接对象
conn=DriverManager.getConnection("jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai", "root", "13838385638");
//3.定义sql
String sql="update memeber set grade=99 where id=2";
//4.获取执行sql的对象
stmt=conn.createStatement();
//5.执行sql
int count=stmt.executeUpdate(sql);
//6.输出结果
if(count>0) {
System.out.println("修改成功");
}else {
System.out.println("修改失败");
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.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
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
public class Demo04JDBC {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.定义删除的sql语句
String sql = "delete from memeber where name='武当陈师行'";
// 3.获取连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai", "root",
"13838385638");
// 4.获取执行sql的对象
stmt = conn.createStatement();
// 5.执行sql语句
int count = stmt.executeUpdate(sql);
// 6.运行结果
if (count > 0) {
System.out.println("删除成功");
} else {
System.out.println("删除失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 避免空指针异常
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

3.4 ResultSet:结果集

使用

  1. next():游标向下移动一行,并返回boolean类型值。向下移动,若有数据,则是true,否则为false
  2. getxxx(参数):获取数据。
    • xxx代表数据类型。
    • 参数可以接收int,表示列的编号,从1开始,不是0,特殊。
    • 参数可以接受String,表示列名称。

使用步骤:

  1. 游标向下移动一行
  2. 判断是否有数据
  3. 获取数据
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
public class Demo05JDBC {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.定义删除的sql语句
String sql = "select * from memeber";
// 3.获取连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai", "root",
"13838385638");
// 4.获取执行sql的对象
stmt = conn.createStatement();
// 5.执行sql语句
ResultSet rs = stmt.executeQuery(sql);
// 6.运行结果
// //6.1 让游标从标题栏移到第一行
// rs.next();
// //6.2 获取数据
// System.out.println(rs.getString("name")+" "+rs.getString("gender"));
while (rs.next()) {
System.out.println(rs.getString("name") + " " + rs.getString("gender"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 避免空指针异常
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

查询练习

定义一个方法,查询emp表的数据,将其封装为对象,装载集合,返回

初版本

Emp.java

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
/**
* 封装Emp表的JavaBean
* @author kit chen
*
*/
public class Emp {
private int id;
private String ename;
private int job_id;
private int mgr;
private Date joindate;
private double salary;
private double bonus;
private int dept_id;

public int getId() {
return id;
}

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

public String getEname() {
return ename;
}

public void setEname(String ename) {
this.ename = ename;
}

public int getJob_id() {
return job_id;
}

public void setJob_id(int job_id) {
this.job_id = job_id;
}

public int getMgr() {
return mgr;
}

public void setMgr(int mgr) {
this.mgr = mgr;
}

public Date getJoindate() {
return joindate;
}

public void setJoindate(Date joindate) {
this.joindate = joindate;
}

public double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}

public double getBonus() {
return bonus;
}

public void setBonus(double bonus) {
this.bonus = bonus;
}

public int getDept_id() {
return dept_id;
}

public void setDept_id(int dept_id) {
this.dept_id = dept_id;
}

@Override
public String toString() {
return "Emp {id=" + id + ", ename=" + ename + ", job_id=" + job_id + ", mgr=" + mgr + ", joindate=" + joindate
+ ", salary=" + salary + ", bonus=" + bonus + ", dept_id=" + dept_id + "}";
}
}

Demo06JDBC.java

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
public class Demo06JDBC {
public static void main(String[] args) {
List<Emp> list=new Demo06JDBC().findAll();
System.out.println(list);
}
/**
* 查询所有emp对象
*
* @return
*/
public List<Emp> findAll() {
List<Emp> list = null;
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获取连接对象
conn = DriverManager.getConnection("jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai", "root", "13838385638");
// 3.定义sql
String sql = "select * from emp";
// 4.获取执行sql对象
stmt = conn.createStatement();
// 5.执行sql
rs = stmt.executeQuery(sql);
// 6.遍历结果集,装载集合
Emp emp = null;
list=new ArrayList<Emp>();
while (rs.next()) {
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
// 这个date是java.sql.Date,他是继承自java.util.Date的。所以往emp中存的时候,是没问题的
Date joindate = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
// 创建对象
//在javaBean中,若参数在4个以上,便不推荐使用有参构造函数
emp = new Emp();
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
// 装载集合
list.add(emp);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//一定不要忘记释放资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

return list;
}
}

优化版本

目的:简化书写

步骤:

  1. 抽取注册驱动
  2. 抽取一个方法来获取连接对象
    • 需求:不传递参数,同时还要保证工具类的通用性
    • 解决:配置文件,在之前的博客也有提到使用方法
  3. 抽取一个方法来释放资源

util.JDBCUtils.java

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
public class JDBCUtils {
/**
* 只有静态变量才能被静态代码块所访问
*/
private static String url;
private static String user;
private static String password;
private static String driver;

/**
* 静态代码块,只需要读取一次拿到这些值
*/
static {
//读取资源文件,获取值
//1.创建Properties集合类
Properties pro = new Properties();
try {
//2.加载文件
// pro.load(new FileInputStream("src/demo45/jdbc.properties"));
//另一种加载方式
//获取src路径下的文件的方式-->ClassLoader类加载器
ClassLoader loader = JDBCUtils.class.getClassLoader();
URL res = loader.getResource("jdbc.properties");
String path = res.getPath();
pro.load(new FileInputStream(path));
//3.获取属性赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4.注册驱动
Class.forName(driver);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

/**
* 获取连接
*
* @return 连接对象
*/
public static Connection getConnection() throws SQLException {
Connection conn = null;
conn = DriverManager.getConnection(url, user, password);

return conn;
}

/**
* 释放资源
*
* @param stmt
* @param conn
*/
public static void close(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

/**
* 释放资源的重载形式
*
* @param rs
* @param stmt
* @param conn
*/
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

jdbc.JDBC.java

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
/**
*
* @author kit chen
*
*/
public class JDBC {
public static void main(String[] args) {
List<Emp> list=new JDBC().findAll();
System.out.println(list);
}
/**
* 查询数据,并返回
* @return
*/
public List<Emp> findAll() {
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
Emp emp=null;
List<Emp> list=null;
try {
conn=JDBCUtils.getConnection();
stmt=conn.createStatement();
String sql="select * from emp";
rs=stmt.executeQuery(sql);
list=new ArrayList<Emp>();
while(rs.next()) {
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
// 这个date是java.sql.Date,他是继承自java.util.Date的。所以往emp中存的时候,是没问题的
Date joindate = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
// 创建对象
//在javaBean中,若参数在4个以上,便不推荐使用有参构造函数
emp = new Emp();
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
// 装载集合
list.add(emp);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtils.close(rs, stmt, conn);
}
return list;
}
}

domain.Emp.java

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
/**
* 封装Emp表的JavaBean
* @author kit chen
*
*/
public class Emp {
private int id;
private String ename;
private int job_id;
private int mgr;
private Date joindate;
private double salary;
private double bonus;
private int dept_id;

public int getId() {
return id;
}

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

public String getEname() {
return ename;
}

public void setEname(String ename) {
this.ename = ename;
}

public int getJob_id() {
return job_id;
}

public void setJob_id(int job_id) {
this.job_id = job_id;
}

public int getMgr() {
return mgr;
}

public void setMgr(int mgr) {
this.mgr = mgr;
}

public Date getJoindate() {
return joindate;
}

public void setJoindate(Date joindate) {
this.joindate = joindate;
}

public double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}

public double getBonus() {
return bonus;
}

public void setBonus(double bonus) {
this.bonus = bonus;
}

public int getDept_id() {
return dept_id;
}

public void setDept_id(int dept_id) {
this.dept_id = dept_id;
}

@Override
public String toString() {
return "Emp {id=" + id + ", ename=" + ename + ", job_id=" + job_id + ", mgr=" + mgr + ", joindate=" + joindate
+ ", salary=" + salary + ", bonus=" + bonus + ", dept_id=" + dept_id + "}";
}
}

jdbc.properties

1
2
3
4
url=jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai
user=root
password=13838385638
driver=com.mysql.cj.jdbc.Driver

登录练习

需求:

  1. 通过键盘录入用户名和密码
  2. 判断用户是否登录成功
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
public class Demo07JDBC {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String user=input.nextLine();
String password=input.nextLine();
if(login(user,password)) {
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
input.close();
}
/**
* 登录方法
* @param user
* @param password
* @return
*/
public static boolean login(String user,String password) {
if(user==null||password==null) {
return false;
}
//连接数据库判断是否登录成功
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
//1.获取连接
conn=JDBCUtils.getConnection();
//2.定义sql
String sql="select * from user where user='"+user+"' and password='"+password+"'";
//3.获取执行sql的对象
stmt=conn.createStatement();
//4.执行查询
rs=stmt.executeQuery(sql);
//5.结果
return rs.next();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtils.close(rs, stmt, conn);
}

//若出现异常,返回false
return false;
}
}

但是,这样登录,会出现sql注入的问题。

假如,密码输入的是a’ or ‘a’ = ‘a,那么整个sql语句select * from user where user=’fjaljfl’ and password=’a’ or ‘a’ = ‘a’

不论怎么查,都会返回数据

3.5 PreparedStatement:执行sql的对象

  1. sql注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性的问题
  2. 解决sql注入问题:使用PreparedStatement对象来解决

静态sql:直接拼接而成的sql,这种会产生sql注入问题

预编译sql:参数使用作为占位符

JDBC的步骤也会相应改变

  1. 导入驱动jar包
  2. 注册驱动
  3. 获取数据库连接对象(Connection)
  4. 定义sql,参数使用占位符
    • select * from where user = ? and password = ?;
  5. 获取执行sql语句的对象(PreparedStatement)
  6. 给占位符赋值
    • 方法:setXXX(参数1,参数2)
    • 参数1:位置的编号。从1开始
    • 参数2:值
  7. 执行sql,接收返回的结果
  8. 处理结果
  9. 释放资源

改造上面的登录案例

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 Demo08JDBC {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String user=input.nextLine();
String password=input.nextLine();
if(login(user,password)) {
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
input.close();
}
/**
* 登录方法
* @param user
* @param password
* @return
*/
public static boolean login(String user,String password) {
if(user==null||password==null) {
return false;
}
//连接数据库判断是否登录成功
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
try {
//1.获取连接
conn=JDBCUtils.getConnection();
//2.定义sql
String sql="select * from user where user= ? and password= ?";
//3.获取执行sql的对象
pstmt=conn.prepareStatement(sql);
//4.赋值
pstmt.setString(1, user);
pstmt.setString(2, password);
//5.执行查询
rs=pstmt.executeQuery();
//6.结果
return rs.next();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCUtils.close(rs, pstmt, conn);
}

//若出现异常,返回false
return false;
}
}

实际开发中经常使用PreparedStatement来完成增删改查操作

  1. 防止sql注入
  2. 效率更高

四、JDBC管理事务

事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

操作

  1. 开启事务
  2. 提交事务
  3. 回滚事务

使用Connection对象来管理事务。

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
public class Demo09JDBC {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;

try {
// 1.获取连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
// 2.定义sql
// 2.1 张三-500
String sql1 = "update account set balance = balance-? where id=?";
// 2.2 李四+500
String sql2 = "update account set balance = balance+? where id=?";
//3.获取执行sql对象
pstmt1=conn.prepareStatement(sql1);
pstmt2=conn.prepareStatement(sql2);
//4.设置参数
pstmt1.setDouble(1, 500);
pstmt1.setInt(2, 1);
pstmt2.setDouble(1, 500);
pstmt2.setInt(2, 2);
//5.执行sql
pstmt1.executeUpdate();
//手动制造异常
int i=3/0;
pstmt2.executeUpdate();

//提交事务
conn.commit();
} catch (Exception e) {
// TODO: handle exception
//不管出现什么异常,事务都要回滚
try {
if(conn!=null) {
conn.rollback();
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.close(pstmt1, conn);
JDBCUtils.close(pstmt2, null);
}
}
}

五、数据库连接池

概念:本质是一个容器(集合),存放数据库连接的容器。

当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

好处

  1. 节约资源
  2. 用户访问过程变得高效

实现

  1. 标准接口:DataSource,javax.sql包下的官方提供的接口
    • 获取连接:getConnection()
    • 归还连接:如果连接对象Connection是从连接池中获取的,那么调用Connection.close()则不会关闭连接,而是归还连接。
  2. 一般是由数据库厂商来实现,推荐三种数据库连接池技术
    • C3P0:比较老
    • Druid:由阿里巴巴提供的,而且高效。号称全球最好?!
    • DBCP:apache上的java连接池项目,也是tomcat使用的连接池组件。

5.1 C3P0

在这之前,我要强烈吐槽eclipse!

我用的eclipse最新版2019.06,搞这个数据库连接池的时候,eclipse成功把我推向了idea。主要就是在新版jdk中,eclipse是读不到配置文件的,无论你放到哪里,他都读不到。但是如果把jdk降为1.8,就可以了。

同时,还有个坑,我如果一开始建的项目以jdk1.8为准,后期再换成jdk新版,哎,他又可以用了,玄学啊!如果一开始直接建以最新jdk的项目,他是不能导出jar包的,会无缘无故失败。但是换成1.8,再换成新版,又可以了,又是玄学啊!

而idea,不论是哪个版本jdk,都能完美运行,这就是收费跟不收费软件的区别吗?

发现这个问题的,还有一个小伙伴,他的博客

eclipse,我以后不会再用了。idea,他不香吗?

c3p0文档

c3p0配置文件

步骤

  1. 导入jar包 c3p0.jar跟mchange.jar
  2. 定义配置文件 c3p0.properties或者c3p0-config.xml,直接将文件放置到src下面即可
  3. 创建核心对象 数据库连接池对象 ComboPooledDataSource
  4. 获取连接 getConnection

实现

c3p0-config.xml

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
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置 -->
<default-config>
<!-- 连接参数 -->
<property name="user">root</property>
<property name="password">13838385638</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai</property>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<!-- 连接池参数 -->
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">5</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">5</property>
<!-- 超时时间 -->
<property name="checkoutTimeout">3000</property>
</default-config>
<!-- 命名的配置,可以通过方法调用实现 -->
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="user">root</property>
<property name="password">13838385638</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai</property>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<!-- 连接池参数 -->
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">5</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">5</property>
<!-- 超时时间 -->
<property name="checkoutTimeout">3000</property>
</named-config>
</c3p0-config>

当然,也可以用c3p0.properties

1
2
3
4
5
6
7
8
9
c3p0.driverClass=com.mysql.cj.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai
c3p0.user=root
c3p0.password=13838385638
c3p0.acquireIncrement=5
c3p0.initialPoolSize=20
c3p0.minPoolSize=10
c3p0.maxPoolSize=40
c3p0.checkoutTimeout=5000

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo01c3p0 {
public static void main(String[] args) throws SQLException {
//1.创建数据库连接池对象
//使用默认配置
// DataSource ds=new ComboPooledDataSource();
//使用指定配置
DataSource ds=new ComboPooledDataSource("otherc3p0");
//2.获取连接对象
Connection conn=ds.getConnection();
System.out.println(conn);
System.out.println();
}
}

5.2 Druid

步骤

  1. 导入jar包
  2. 定义配置文件.properties
    • properties格式
    • 任意名称,可以放在任意目录下
  3. 加载配置文件
  4. 获取数据库连接池对象:通过工厂类DruidDataSourceFactory,来获取
  5. 获取连接:getConnection

配置文件

1
2
3
4
5
6
7
8
# Druid配置文件
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai
username=root
password=13838385638
initialSize=5
maxActive=10
maxWait=3000

代码

1
2
3
4
5
6
7
8
9
10
11
12
public class Demo03Druid {
public static void main(String[] args) throws Exception {
//加载配置文件
Properties pro=new Properties();
pro.load(Demo03Druid.class.getClassLoader().getResourceAsStream("druid.properties"));
//获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//获取连接
Connection conn=ds.getConnection();
System.out.println(conn);
}
}

5.3 定义连接池工具类

步骤

  1. 定义类JDBCPoolUtils
  2. 提供静态代码块,来加载配置文件,并且初始化连接池对象
  3. 提供方法
    • 获取连接方法:getConnection
    • 释放资源:close()
    • 获取连接池的方法:getDataSource

实现

JDBCPoolUtils.java

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
/**
* Druid连接池的工具类
*/
public class JDBCPoolUtils {
//1.定义一个成员变量
private static DataSource ds;
//2.静态代码块初始化赋值
static {
//加载配置文件
Properties pro=new Properties();
try {
pro.load(JDBCPoolUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
//初始化DataSource
ds= DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* @return 获取到的连接对象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}

/**
* 释放资源
*/
// public static void close(Statement stmt, Connection conn){
// if(stmt!=null){
// try {
// stmt.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
// if(conn!=null){
// try {
// conn.close();//归还连接
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
// }
public static void close(ResultSet rs,Statement stmt, Connection conn){
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();
}
}
}

/**
*
* @return 获取连接池对象
*/
public static DataSource getDataSource(){
return ds;
}
}

DruidMain.java

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
/**
* 使用工具类JDBCPoolUtils
*/
public class DruidMain {
public static void main(String[] args) {
Connection conn = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
try {
conn = JDBCPoolUtils.getConnection();
// String sql = "select * from memeber";
// pstmt = conn.prepareStatement(sql);
// rs = pstmt.executeQuery();
// while (rs.next()) {
// System.out.println(rs.getString("name") + "-->"
// + rs.getString("gender"));
// }
String sql="update memeber set gender=? where name=?";
pstmt=conn.prepareStatement(sql);
pstmt.setString(1,"男");
pstmt.setString(2,"唐三");
int count=pstmt.executeUpdate();
System.out.println(count);
if(count>0){
System.out.println("成功");
}else{
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCPoolUtils.close(rs,pstmt,conn);
}
}
}

六、Spring JDBC

Spring框架对JDBC的简单封装。提供了JDBCTemplate来简化JDBC的开发

上学期课程设计的时候,我就觉得这部分比较麻烦,问老师,老师也支支吾吾说不明白。老师大概也就只要个功能,具体的实现优化,他才不管,我猜,他也不会。

报告管理信息系统,说实话,写得很幼稚。

步骤

  1. 导入jar包 core.jar jdbc.jar tx.jar beans.jar commons-logging.jar
  2. 创建JDBCTemplate对象。依赖于数据源DataSource
    • JDBCTemlate temp=new JDBCTemplate(ds);
  3. 调用方法来CRUD(增删改)操作
    • update():执行DML语句。增删改
    • queryForMap():查询结果,将结果集封装为Map。
      • 列名作为key,值作为value,将这条记录封装为map集合。(长度只能为1)
    • queryForList():查询结果,将将结果集封装为List。
      • 将每一条Map封装为Map集合,再将Map集合装载到List集合中
    • query():查询结果,将结果封装为javaBean对象
      • query的参数:RowMapper。一般我们使用BeanPropertyRowMapper实现类,它可以完成数据到javaBean的自动封装
      • 使用方法:new BeanPropertyRowMapper<类名>(类名.Class)
    • queryForObject():查询结果,将结果封装为指定对象
      • 一般用于聚合函数的查询。比方说返回的结果是Long类型,就需要将结果封装
1
2
3
4
5
6
7
8
9
10
public class Demo01JDBCTemplate {
public static void main(String[] args) {
//创建JdbcTemplate
JdbcTemplate temp=new JdbcTemplate(JDBCPoolUtils.getDataSource());
//调用方法
String sql="update memeber set gender=? where name=?";
int count=temp.update(sql,"男","唐三");
System.out.println(count);
}
}

使用

需求

  1. 修改id为1数据salary为10000
  2. 添加一条记录
  3. 删除刚才添加的记录
  4. 查询id为1的记录,将其封装为Map集合
  5. 查询所有记录,封装为List集合
  6. 查询所有记录,封装为Emp对象的List集合
  7. 查询总记录数

实现

通过Junit单元测试来实现每个需求

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
/**
* 使用Spring以及Junit单元测试
*
* @author https://github.com/meethigher
*/
public class Demo02JDBCTemplate {
private JdbcTemplate temp = new JdbcTemplate(JDBCPoolUtils.getDataSource());

//修改id为1001数据salary为10000
@Test
public void test1() {
String sql = "update emp set salary=10000 where id=1001";
int count = temp.update(sql);
if (count > 0) {
System.out.println("成功");
} else {
System.out.println("失败");
}
}

//添加一条记录
@Test
public void test2() {
String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
int count = temp.update(sql, 1015, "胡列娜", 10);
if (count > 0) {
System.out.println("成功");
} else {
System.out.println("失败");
}
}

//删除刚添加的记录
@Test
public void test3() {
String sql = "delete from emp where id=?";
int count = temp.update(sql, 1015);
if (count > 0) {
System.out.println("成功");
} else {
System.out.println("失败");
}
}

//查询id为1001的记录,将其封装为Map集合
@Test
public void test4() {
String sql = "select * from emp where id=?";
Map<String, Object> map = temp.queryForMap(sql, 1001);
System.out.println(map);
}

//查询所有记录,将其封装为List集合
@Test
public void test5() {
String sql = "select * from emp";
List<Map<String, Object>> list = temp.queryForList(sql);
for (Map<String, Object> map : list) {
System.out.println(map);
}
}

//查询所有记录,将其封装为Emp对象的List集合
//自己实现
@Test
public void test6() {
String sql = "select * from emp";
List<Emp> list = temp.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp = new Emp();
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date joindate = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
emp.setId(id);
emp.setEname(ename);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
emp.setJoindate(joindate);
emp.setMgr(mgr);
emp.setSalary(salary);
emp.setJob_id(job_id);
return emp;
}
});
for (Emp emp : list) {
System.out.println(emp);
}
}

//查询所有记录,将其封装为Emp对象的List集合
//用别人提供的实现类
@Test
public void test7() {
String sql = "select * from emp";
/*
此处如果数据库的数据中是null的话,传给int或者double这种基本类型是会报错的
所以,需要修改成引用类型Integer和Double
*/
List<Emp1> list = temp.query(sql, new BeanPropertyRowMapper<Emp1>(Emp1.class));
for (Emp1 emp : list) {
System.out.println(emp);
}
}

//查询总记录数
@Test
public void test8() {
String sql = "select count(id) from emp";
Long total = temp.queryForObject(sql, Long.class);
System.out.println(total);
}
}
发布:2020-05-23 16:56:49
修改:2021-02-28 07:00:19
链接:https://meethigher.top/blog/2020/java-database-connectivity/
标签:java 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏