JDBC概述

JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现。

JDBCAPI

  • DriverManager驱动管理类 getConnection()获取连接
  • Connection接口
    • createStatement创建Statement对象
    • preparedStatement(sql)生成预处理对象
  • Statement接口
    • executeUpdate(sql)执行DML语句,返回影响的行数
    • executeQuery(sql)执行查询,返回ResultSet对象
    • execute(sql)执行任意的sql,返回boolean
  • PreparedStatement接口
    • executeUpdate(sql)执行DML语句,返回影响的行数
    • executeQuery(sql)执行查询,返回ResultSet对象
    • execute(sql)执行任意的sql,返回boolean
    • setObject(占位符索引,值)
  • Result结果集
    • next()向下一行移动
    • previous()向上移动一行

JDBC编程步骤

装载相应数据库的JDBC驱动并进行初始化

导入专用的jar包

访问MySQL数据库需要用到第三方的类,这些第三方的类都被压缩在jar包里。

mysql-connector-java-5.0.8-bin.jar

初始化驱动

通过初始化驱动类com.mysql.jdbc.Driver

1
2
3
4
5
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

Class.forName是把这个类加载到JVM中,加载的时候,就会执行其中的静态初始化块,完成驱动的初始化的相关工作。

建立JDBC和数据库之间的Connection连接

需要提供:

  • 数据库服务端的IP地址:127.0.0.1
  • 数据库的端口号:3306
  • 数据库名称:exam
  • 编码方式:UTF-8
  • 账号:root
  • 密码:admin

Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8", "root", "admin");

Connection是与特定数据库连接回话的接口,使用的时候需要导包,而且必须在程序结束的时候将其关闭。getConnection方法也需要捕获SQLException异常。

在进行数据库的增删改查的时候都需要与数据库建立连接,所以可以在项目中将建立连接写成一个工具方法,用的时候调用即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
      /**
* 取得数据库的连接
* @return 一个数据库的连接
*/
public static Connection getConnection(){
Connection conn = null;
try {
//初始化驱动类com.mysql.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8","root", "admin");
//该类就在 mysql-connector-java-5.0.8-bin.jar中,如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}
return conn;
}

创建Statement或者PreparedStatement接口,执行SQL语句

使用Statement

Statement接口创建之后,可执行SQL语句,完成对数据库的增删改查。其中,增删改查只需改变SQL语句的内容就能完成;而查询较复杂。在Statement中使用字符串拼接的方式,该方式存在语句复杂、容易犯错等缺点。

Statement在实际过程中使用的非常少

字符串拼接方式的SQL语句是非常繁琐的,中间有很多单引号和双引号的混用,容易出错

1
2
3
4
5
6
7
Statement s = conn.createStatement();
// 准备sql语句
// 注意: 字符串要用单引号'
String sql = "insert into t_courses values(null,"+"'数学')";
//在statement中使用字符串拼接的方式,这种方式存在诸多问题
s.execute(sql);
System.out.println("执行插入语句成功");

使用PreparedStatement

PreparedStatement需要根据sql语句创建

使用PreparedStatement时,他的sql语句不再采用字符串拼接的方式,而是采用“ ?” 占位符的方式。这种方式除了可以避免拼接字符串的繁琐之外,还能提高性能。每次SQL语句都是一样的,java类就不会再次编译,这样能显著提高性能。

String sql = "update t_course set course_name =? where course_id=?";

然后使用PreparedStatement接口中的set方法给占位符进行赋值。[索引是从1开始的]

1
2
3
4
5
6
//预编译
pstmt = (PreparedStatement) conn.prepareStatement(sql);
//利用Preparedstatement的set方法给占位符赋值
pstmt.setString(1, courseName);
pstmt.setInt(2, courseId);
pstmt.executeUpdate();

增删改都使用pstmt.executeUpdate();语句进行SQL语句的提交 ,查找则使用executeQuery()方法

在添加的过程中,如果添加的数据比较大,可以使用批量添加。

1
2
3
4
5
6
7
8
9
for(int i=1;i<100;i++){
pstmt.setInt(1,8000+i);
pstmt.setString(2,"赵_"+i);
pstmt.addBatch();
//批量更新
if(i%10==0){
pstmt.executeBatch();
}
}

查询操作

查询表student中数据id,name

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
public class Ch02 {
public static void main(String[] args) throws SQLException {
Ch02 ch02 = new Ch02();
List<Student> stuList = ch02.findCourseList();
System.out.println(stuList);

}
public List<Student> findCourseList() throws SQLException {
String sql = "select * from stu order by id";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//创建集合对象存放查询到的数据
List<Student> stuList = new ArrayList<>();


try {
conn = JdbcUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
rs = (ResultSet) pstmt.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
Student student = new Student(name,id);
stuList.add(student);

}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn);
JdbcUtil.close(pstmt);
}
return stuList;
}
}

class Student{
private String name;
private int id;

public Student(String name, int id) {
this.name = name;
this.id = id;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}

处理和显示结果

执行查询语句,并把结果返回给集合ResultSet

ResultSet rs = s.executeQuery(sql);

利用While(ResultSet.net()){…}循环将ResultSet中的结果遍历出来。

ResultSet.getxxx();

get方法里可以填写属性值或者填写该属性在数据表中的列号

一般不推荐使用列号,因为一段数据表中各个属性值顺序会发生变化

1
2
3
4
5
6
7
8
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
//记录对应的对象
Student student = new Student(name,id);
stuList.add(student);

}

ResultSet

  1. 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
  2. ResultSet对象保持一个光标指向其当前的数据行。最初光标位于第一行之前
  3. next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false。因此可以在while循环中使用循环来遍历结果集

在JDBC编码的过程中我们创建了Connection、ResultSet等资源,这些资源在使用完毕之后是一定要进行关闭的。关闭的过程中遵循从里到外的原则。

创建工具类

为了使代码更简单,增加复用性,将一些频繁使用的操作放到一个工具类里

  • 使用反射
  • 连接数据库getConnection()
  • 释放资源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
59
60
61
62
63
64
65
66
public class JdbcUtil {
public static Connection getConnection() throws ClassNotFoundException, SQLException, IOException {

Properties properties = new Properties();
//读取外部的p文件
properties.load(Test01.class.getClassLoader().getResourceAsStream("jdbc.properties"));
//System.out.println(properties);
String url = properties.getProperty("mysql.url");
String driverName = properties.getProperty("mysql.driverName");
String username = properties.getProperty("mysql.username");
String password = properties.getProperty("mysql.password");

Class clazz = Class.forName(driverName);

Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}

/**
* 封装三个关闭方法
* @param pstmt
*/
public static void close(PreparedStatement pstmt){
if(pstmt != null){ //避免出现空指针异常
try{
pstmt.close();
}catch(SQLException e){
e.printStackTrace();
}

}
}

public static void close(Connection conn){
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}

public static void close(ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public static void close(Statement stmt){
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}

Statement和PreparedStatement的异同及优缺点

同:两者都是用来执行SQL语句的

异:PreparedStatement需要根据SQL语句来创建,它能够通过设置参数,指定相应的值,不是像Statement那样使用字符串拼接的方式

PreparedStatement的优点

  1. 使用参数设置,可读性好,不易记错。在statement中使用字符串拼接,可读性和维护性比较差
  2. 具有预编译机制,性能比statement更快
  3. 能够有效防止SQL注入攻击

execute和executeUpdate的区别

相同点:二者都能执行增删改查

不同点:

  1. execute可以执行查询语句,然后通过getResult把结果取出来。esecuteUpdate不能查询语句
  2. execute返回Boolean类型,true表示执行的是查询语句,false表示执行的insert、delete、update等。executeUpdate的返回值是int,表示有多少条数据受到了影响

案例

模拟登录功能。在前台输入用户名和密码,后台判断信息是否正确,并给出前台反馈信息,前台输出反馈

信息。

  1. 创建数据库表 account 包含(aid,username,password)
  2. 开发前台代码 用户操作的页面
  3. 工具类JdbcUtil
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
public class Test02 {

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
Account account = findAccount(username,password);
System.out.println(account != null? "登录成功":"登录失败");

}

public static Account findAccount(String name,String pwd){
//sql语句
String sql = "select * from account where username = ? and password = ?";
// 连接
Connection conn = null;
//执行SQL语句
PreparedStatement pstmt = null;
//执行查询语句,并把结果返回给集合ResultSet
ResultSet rs = null;
Account login = null;


try {
//建立连接
conn =JdbcUtil.getConnection();
//预编译
pstmt = (PreparedStatement) conn.prepareStatement(sql);
//给占位符赋值,下标是从1开始的
pstmt.setString(1,name);
pstmt.setString(2,pwd);
//执行查询语句,并把结果返回给集合ResultSet
rs = (ResultSet) pstmt.executeQuery();
//判断是否有值
if (rs.next()){
login = new Account(rs.getString("username"),rs.getString("password"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn);
JdbcUtil.close(pstmt);
}
return login;
}
}

class Account{
private String username;
private String password;

public Account() {
}

public Account(String username, String password) {
this.username = username;
this.password = password;
}

@Override
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}

数据库连接池

德鲁伊连接池

  1. 加入Druid jar包
  2. 加入配置文件,拷贝到src目录
  3. 创建