一、什么是 JDBC
Java Data Base Connectivity(Java 数据库连接),它是 Java 定义的一些接口,程序员在Java 程序中用这些接口里面的方法,就能像客户端访问数据库一样连接数据库、发送 SQL 语句、得到数据库执行结果、关闭连接。
不同的数据库对于相同的操作往往协议、参数不同,程序员自己实现 JDBC 接口是费时费力的,因此 JDBC 的具体实现是由数据库厂商完成的,我们只需要会使用 Java 定义的这些方法。
二、使用 JDBC
1、创建 Maven 工程并配置环境
1.1、创建一个 Maven 工程
Maven 工程可根据配置文件 pom.xml 自动从中央仓库下载 jar 依赖包到本地仓库,并且项目结构以及构建流程统一,降低了团队协作成本。
统一的目录结构:红色,未添加到GIT暂存区;绿色,已添加到GIT暂存区;白色,已添加到GIT仓库。
1.2、配置 pom.xml 文件并下载依赖包
从中央仓库(Maven 仓库)找到想要下载的依赖(Connector/J),并复制到 pom.xml :
Maven Repository: Search/Browse/Explore
下载好的 MySQL JDBC 实现包:
这里也能看:
1.3、更改下载镜像源
避免因仓库在国外而依赖下载慢,配置成国内镜像源(阿里镜像源):
单独安装的Maven配置文件路径为:maven安装目录/conf/settings.xml
IDEA自带的Maven配置文件路径为:
IDEA安装目录/plugins/maven/lib/maven3/conf/settings.xml
2、使用 JDBC 的基本流程
2.1、方式一
反射机制,加载驱动类:
查看一下 mysql 实现的驱动类:
连接数据库:
执行 SQL 语句并获取结果集:
处理结果集:
finally 从后往前释放资源,无论什么情况,最后都要释放:
执行结果:
2.2、方式二(推荐使用)
Datasource:
建立连接:
执行 SQL 语句:
后面跟方法一 一样。
2.3、DriverManager 和 DataSource 区别
- DriverManager:getConnection 时会创建新的连接,释放资源时关闭真实的连接,造成资源浪费。
- DataSource:运行程序初始化,会在线程池创建多个连接,用的时候直接从里面取,用完了就放回去。
2.4、增
现在我想把连接语句、释放语句打包成一个类,并且该类不能实例化。通过调用该类的方法获取固定数据库的连接、释放资源:
package utils;import com.mysql.cj.jdbc.MysqlDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;public class DBUtil {// 数据源private static DataSource dataSource;// 连接参数private static String url = "jdbc:mysql://127.0.0.1:3306/mydb" +"?characterEncoding=utf8" +"&serverTimezone=Asia/Shanghai" +"&allowPublicKeyRetrieval=true" +"&useSSL=false";// 用户名private static String username = "root";// 密码private static String password = "123456";// 加载类时,初始化数据源static {MysqlDataSource mysqlDataSource = new MysqlDataSource();mysqlDataSource.setURL(url);mysqlDataSource.setUser(username);mysqlDataSource.setPassword(password);dataSource = mysqlDataSource;}// 构造方法私有化,禁止实例化private DBUtil(){}// 获取连接public static Connection getConnection() throws SQLException {return dataSource.getConnection();}// 释放资源public static void close(Connection conn, java.sql.Statement stmt, java.sql.ResultSet rs){if (rs != null) {try {rs.close();} catch (SQLException e) {throw new RuntimeException(e);}}if(stmt != null){try {stmt.close();} catch (SQLException e) {throw new RuntimeException(e);}}if(conn != null){try {conn.close();} catch (SQLException e) {throw new RuntimeException(e);}}}
}
插入一条数据:
package com.cqut.demo;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
import utils.DBUtil;public class Demo04_Insert {public static void main(String[] args) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {// 获取数据库连接connection = DBUtil.getConnection();// 定义 sql 语句String sql = "insert into student2 (id, name, sno, age, gender, enroll_date, class_id) values" +"(?, ?, ?, ?, ?, ?, ?)";// 输入参数Scanner scanner = new Scanner(System.in);System.out.print("请输入学生id:");Long id = scanner.nextLong();System.out.println("请输入学生姓名:");String name = scanner.next();System.out.println("请输入学生学号:");String sno = scanner.next();System.out.println("请输入学生年龄:");int age = scanner.nextInt();System.out.println("请输入学生性别:");Boolean gender = scanner.nextBoolean();System.out.println("请输入入学日期:");String enrollDate = scanner.next();System.out.println("请输入班级id:");Long classId = scanner.nextLong();scanner.close();// 获取与处理对象preparedStatement = connection.prepareStatement(sql);// 替换占位符preparedStatement.setLong(1, id);preparedStatement.setString(2, name);preparedStatement.setString(3, sno);preparedStatement.setInt(4, age);preparedStatement.setBoolean(5, gender);preparedStatement.setString(6, enrollDate);preparedStatement.setLong(7, classId);// 执行插入int row = preparedStatement.executeUpdate();if (row > 0){System.out.println("插入成功!");} elseSystem.out.println("插入失败!");} catch (SQLException ex) {throw new RuntimeException(ex);} finally {// 释放资源DBUtil.close(connection, preparedStatement, resultSet);}}
}
2.5、删
package com.cqut.demo;import utils.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;public class Demo05_Delete {public static void main(String[] args) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {// 获取数据库连接connection = DBUtil.getConnection();// 定义 sql 语句String sql = "delete from student2 where sno = ?";// 输入参数Scanner scanner = new Scanner(System.in);System.out.print("请输入学生学号:");Long sno = scanner.nextLong();scanner.close();// 获取与处理对象preparedStatement = connection.prepareStatement(sql);// 替换占位符preparedStatement.setLong(1, sno);// 执行插入int row = preparedStatement.executeUpdate();if (row > 0){System.out.println("删除成功!");} elseSystem.out.println("删除失败!");} catch (SQLException ex) {throw new RuntimeException(ex);} finally {// 释放资源DBUtil.close(connection, preparedStatement, resultSet);}}
}
2.6、改
package com.cqut.demo;import utils.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;public class Demo06_Update {public static void main(String[] args) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {// 获取数据库连接connection = DBUtil.getConnection();// 定义 sql 语句String sql = "update student2 set age = ? where sno = ?";// 输入参数Scanner scanner = new Scanner(System.in);System.out.print("请输入学生学号:");Long sno = scanner.nextLong();System.out.print("请输入学生年龄:");int age = scanner.nextInt();scanner.close();// 获取与处理对象preparedStatement = connection.prepareStatement(sql);// 替换占位符preparedStatement.setInt(1, age);preparedStatement.setLong(2, sno);// 执行插入int row = preparedStatement.executeUpdate();if (row > 0){System.out.println("修改成功!");} elseSystem.out.println("修改失败!");} catch (SQLException ex) {throw new RuntimeException(ex);} finally {// 释放资源DBUtil.close(connection, preparedStatement, resultSet);}}
}
2.7、把各种数据库访问细节封装在 Dao 里
把表格写为 Java 类(去 Maven 仓库下载一个 Lombok 依赖):
Class:
package com.cqut.model;import lombok.Data;
import lombok.ToString;@Data
@ToString
public class Class {private Long id;private String name;
}
Student:
package com.cqut.model;import lombok.Data;
import lombok.ToString;@Data
@ToString
public class Student {private Long id;private String name;private String sno;private int age;private Boolean gender;private String enroll_date;private Classes class_obj;
}
在 ClassDao 里面写一个插入语句:
package com.cqut.dao;import com.cqut.model.Classes;
import utils.DBUtil;
import java.sql.*;public class ClassDao {public int insert(Classes record) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {// 获取数据库连接connection = DBUtil.getConnection();// 执行 SQL语句String sql = "insert into class values(null,?)";preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1, record.getName());int count = preparedStatement.executeUpdate();return count;} catch (SQLException e) {throw new RuntimeException(e);}finally {DBUtil.close(connection, preparedStatement, resultSet);}}
}
写一个插入示例:
package com.cqut.demo;import com.cqut.dao.ClassDao;
import com.cqut.model.Classes;import java.util.Scanner;public class Demo07_InsertClass {public static void main(String[] args) {// 输入班级信息Scanner scanner = new Scanner(System.in);System.out.print("请输入班级名称:");String className = scanner.nextLine();// 创建 Class 对象Classes class_obj = new Classes();class_obj.setName(className);// 插入 class 表中ClassDao classDao = new ClassDao();int row = classDao.insert(class_obj);if (row > 0) {System.out.println("班级信息插入成功!");} else {System.out.println("班级信息插入失败!");}}
}
在 StudentDao 里写一个联合查询 student2 和 class 表:
package com.cqut.dao;import com.cqut.model.Classes;
import com.cqut.model.Student;
import utils.DBUtil;import java.sql.*;
import java.util.ArrayList;
import java.util.List;public class StudentDao {public List<Student> selectStudentsAndClassed(Long classId) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;List<Student> students = null;try {// 连接数据库connection = DBUtil.getConnection();// 编写 SQL 语句String sql = "select s.id, s.name, s.sno, s.age, s.gender, s.enroll_date, c.id, c.name " +"from student2 s, class c where s.class_id = c.id and c.id = ?";preparedStatement = connection.prepareStatement(sql);preparedStatement.setLong(1, classId);resultSet = preparedStatement.executeQuery();// 处理结果集while (resultSet.next()) {// 把查询结果放到 Student、Classes 对象中Student student = new Student();student.setId(resultSet.getLong("s.id"));student.setName(resultSet.getString("s.name"));student.setSno(resultSet.getString("s.sno"));student.setAge(resultSet.getInt("s.age"));student.setGender(resultSet.getBoolean("s.gender"));student.setEnroll_date(resultSet.getString("s.enroll_date"));Classes class1 = new Classes();class1.setId(resultSet.getLong("c.id"));class1.setName(resultSet.getString("c.name"));student.setClass_obj(class1);if(students == null) {students = new ArrayList<>();}students.add(student);}return students; // 返回查询结果对象集} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, preparedStatement, resultSet);}}
}
写一个示例查询2班所有学生:
package com.cqut.demo;import com.cqut.dao.StudentDao;
import com.cqut.model.Student;import java.util.List;
import java.util.Scanner;public class Demo08_SelectStudentAndClass {public static void main(String[] args) {// 输入Scanner scanner = new Scanner(System.in);System.out.print("请输入班级id:");Long id = scanner.nextLong();// 访问数据库StudentDao studentDao = new StudentDao();List<Student> students = studentDao.selectStudentsAndClassed(id);// 输出for (Student student : students) {System.out.println(student);}}
}
3、SQL 注入
输入 name,查询结果:
下面这种输入也会构成合法的 SQL 查询语句,查询出整个结果集:
这种用特殊字符构成合法的 SQL 语句,非法获取、篡改数据的手段,叫 SQL 注入。