JDBC(powernode CD2206)详尽版(内含教学视频、源代码、SQL文件)
包含:教学视频、源代码(与博客同步)、SQL文件
下载链接地址: https://download.csdn.net/download/weixin_46411355/87399872
目录
- JDBC(powernode CD2206)详尽版(内含教学视频、源代码、SQL文件)
- **`包含:教学视频、源代码(与博客同步)、SQL文件`**
- ==**下载链接地址:**== [https://download.csdn.net/download/weixin_46411355/87399872](https://download.csdn.net/download/weixin_46411355/87399872)
- 一、介绍
- 二、JDBC常用的接口和类
- 2.1 Driver接口
- 2.2 DriverManager类
- 2.3 Connection接口
- 2.4 Statement接口
- 2.5 PreparedStatement接口
- 2.6 ResultSet接口
- 2.7 DataSource接口
- 三、JDBC操作数据库的步骤
- 四、编写第一个JDBC程序
- 五、注册案例
- 六、登录案例
- 6.1 Statement
- 七、SQL注入
- 7.1 SQL注入
- 7.2 出现SQL注入的原因
- 7.3 解决方案
- 7.4 PreparedStatement接口
- 7.5 PreparedStatement如何解决SQL注入
- 7.6 使用PreparedStatement改进代码,解决SQL注入问题
- 八、编写JDBC工具类
- 九、CRUD操作
- 十、事务操作
- 十一、批处理
- 11.1 jdbc.properties
- 11.2 BatchDemo.java
- 11.3 没有进行批处理的耗时![在这里插入图片描述](https://img-blog.csdnimg.cn/f175843fd2664ec8ae0eb0ea068058ea.png)
- 11.4 进行批处理的耗时![在这里插入图片描述](https://img-blog.csdnimg.cn/0d88c77173dd4405966fa735b95fd2c3.png)
- 十二、连接池
- 12.1 连接池原理
- 12.2 DataSource接口
- 12.3 druid.properties
- 12.4 DruidUtils.java
- 12.5 使用Druid连接池完成CRUD
- 十三、BaseDAO的封装
- 13.1 什么是DAO
- 13.2 封装DML语句
- 13.3 封装DQL语句
- 13.3.1 思路
- 13.3.2 API
- 13.2.1 ResultSet
- 13.2.2 ResultSetMetaData接口
- ①getColumnCount()方法
- ②getColumnLabel(int column) 和 getColumnName(int column) 方法
- 13.3.3 javaBean
- 13.3.4 封装DQLSQL的方法
- 13.3.5 测试类
- 13.3.6运行效果
- 13.3.7 万能SQL模板(将DML语句和DQL语句同时封装)
- 十四、分页的封装
- 14.1 分页结果封装类 PageInfo
- 14.2 修改BaseDAO
- 14.3 测试类
一、介绍
JDBC:(Java DataBase Connectivity): java 数据库连接。 也就是java操作数据库。
二、JDBC常用的接口和类
JDBC常用的接口和类位于java.sql包下。
2.1 Driver接口
每个驱动程序类必须实现的接口。
每个驱动程序都应该提供一个实现 Driver 接口的类。
2.2 DriverManager类
管理一组 JDBC 驱动程序的基本服务。
2.3 Connection接口
与特定数据库的连接(会话)。在连接上下文中执行 SQL 语句并返回结果。
2.4 Statement接口
用于执行静态 SQL 语句并返回它所生成结果的对象。
静态 SQL 语句: 就是不需要动态拼接参数的SQL语句。
2.5 PreparedStatement接口
表示预编译的 SQL 语句的对象。
2.6 ResultSet接口
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet 对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next 方法将光标移动到下一行;因为该方法在 ResultSet 对象没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。
2.7 DataSource接口
该工厂用于提供到此 DataSource
对象所表示的物理数据源的连接。作为 DriverManager
工具的替代项,DataSource
对象是获取连接的首选方法。
DataSource
接口由驱动程序供应商实现。共有三种类型的实现:
- 基本实现 - 生成标准的
Connection
对象 - 连接池实现 - 生成自动参与连接池的
Connection
对象。此实现与中间层连接池管理器一起使用。 - 分布式事务实现 - 生成一个
Connection
对象,该对象可用于分布式事务,大多数情况下总是参与连接池。此实现与中间层事务管理器一起使用,大多数情况下总是与连接池管理器一起使用。
三、JDBC操作数据库的步骤
JDBC操作数据库的步骤:
- 加载驱动
- 创建连接
- 获取Statement语句对象
- 执行SQL语句
- 解析结果集ResultSet(查询语句)
- 释放资源
四、编写第一个JDBC程序
数据库sql
student.sql
/*
Navicat Premium Data Transfer
Source Server : MySQL80_3306
Source Server Type : MySQL
Source Server Version : 80030
Source Host : localhost:3306
Source Schema : powernode_jdbc
Target Server Type : MySQL
Target Server Version : 80030
File Encoding : 65001
Date: 27/01/2023 19:02:32
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`age` int NULL DEFAULT NULL,
`sex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (6, '小明3', 4, '女', '武汉3');
INSERT INTO `student` VALUES (7, '小明4', 51, '女', '武汉4');
INSERT INTO `student` VALUES (8, '小明5', 39, '女', '武汉5');
INSERT INTO `student` VALUES (9, '小明6', 38, '女', '武汉6');
INSERT INTO `student` VALUES (10, '小明7', 67, '男', '武汉7');
INSERT INTO `student` VALUES (11, '小明8', 73, '女', '武汉8');
INSERT INTO `student` VALUES (12, '小明9', 64, '男', '武汉9');
INSERT INTO `student` VALUES (13, '小明10', 74, '男', '武汉10');
INSERT INTO `student` VALUES (14, '小明11', 56, '男', '武汉11');
INSERT INTO `student` VALUES (15, '小明12', 50, '男', '武汉12');
INSERT INTO `student` VALUES (16, '小明13', 68, '男', '武汉13');
INSERT INTO `student` VALUES (17, '小明14', 47, '男', '武汉14');
INSERT INTO `student` VALUES (18, '小明15', 96, '女', '武汉15');
INSERT INTO `student` VALUES (19, '小明16', 86, '女', '武汉16');
INSERT INTO `student` VALUES (20, '小明17', 73, '女', '武汉17');
INSERT INTO `student` VALUES (21, '小明18', 31, '女', '武汉18');
INSERT INTO `student` VALUES (22, '小明19', 61, '女', '武汉19');
INSERT INTO `student` VALUES (23, '小明20', 30, '女', '武汉20');
SET FOREIGN_KEY_CHECKS = 1;
添加驱动jar包 mysql-connector-j-8.0.32.jar
代码
- package _01编写第一个JDBC程序;
import com.mysql.cj.jdbc.Driver;
import java.sql.*;
/**
* 编写第一个JDBC程序
*/
public class LoadDriverByThreeWays {
public static void main(String[] args) {
ResultSet resultSet = null;
Statement statement = null;
Connection connection = null;
try {
// 1. 加载驱动
//第一种方式
// DriverManager.registerDriver(new Driver());
//第二种方式
// new Driver();
//第三种方式
// Class.forName("com.mysql.cj.jdbc.Driver");
//第四种方式:直接不写
/*
* JDBC 4.0 Drivers 必须包括 META-INF/services/java.sql.Driver 文件。此文件包含 java.sql.Driver 的 JDBC 驱动程序实现的名称。
* 应用程序不再需要使用 Class.forName() 显式地加载 JDBC 驱动程序。
*
*/
// 2. 创建连接
/*
* JDBC连接数据库的url格式是:
* jdbc:子协议://subname
*
* JDBC连接MySQL数据库的url格式是:
* jdbc:mysql://主机名:端口号/数据库名 -----> jdbc:mysql://localhost:3306/db02
* 如果主机名:端口号是 localhost:3306 那么主机名:端口号可以省略 -----> jdbc:mysql:///db02
*/
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/powernode_jdbc", "root", "root");
// 3. 获取Statement语句对象
statement = connection.createStatement();
// 4. 执行SQL语句
String sql = "select id,name as xxx,age from student";
resultSet = statement.executeQuery(sql);
// 5. 解析ResultSet结果集
/*
* java.sql.SQLException: Before start of result set
* 出现该异常的原因是:目前ResultSet对象具有指向其当前数据行的光标,光标位于第一行数据之前
*
*/
while (resultSet.next()) {
/*
* resultSet中获取数据的方法getXXXX()都有两个重载方法。
* 一个是根据投影字段的索引获取数据;一个是根据投影字段的名称(别名)获取数据
*
* 注意:数据库索引从1开始
*/
// 根据投影字段的名称(别名)获取数据
String name = resultSet.getString("xxx");
/*
* 根据投影字段的索引获取数据 -- 不推荐使用
* 弊端:就是投影字段的顺序调整后,获取数据就就不对了
*/
// String name = resultSet.getString(2);
System.out.println("name= " + name);
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
// 6. 释放资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
resultSet = null;
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
statement = null;
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
connection = null;
}
}
}
}
五、注册案例
数据库sql
sys_user.sql
/*
Navicat Premium Data Transfer
Source Server : MySQL80_3306
Source Server Type : MySQL
Source Server Version : 80030
Source Host : localhost:3306
Source Schema : powernode_jdbc
Target Server Type : MySQL
Target Server Version : 80030
File Encoding : 65001
Date: 27/01/2023 19:04:21
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'admin', '123456');
INSERT INTO `sys_user` VALUES (2, 'zhangsan', '123');
INSERT INTO `sys_user` VALUES (3, '张三', '123456');
INSERT INTO `sys_user` VALUES (4, 'hhh', '123');
INSERT INTO `sys_user` VALUES (5, 'hhh', '123456');
SET FOREIGN_KEY_CHECKS = 1;
代码
package _02注册案例;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/**
* 注册案例
*/
public class RegisterDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
Connection connection = null;
Statement statement = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/powernode_jdbc", "root", "root");
statement = connection.createStatement();
/*
*DML语句使用:executeUpdate --> 返回受影响的行数
* DQL语句使用:executeQuery --> 返回查询到的结果集(虚拟表)
*/
String sql = "insert into sys_user VALUES(null,'"+username+"','"+password+"')";
System.out.println("sql = " + sql);
int rows = statement.executeUpdate(sql);
if(rows>0){
System.out.println("插入成功");
}else{
System.out.printf("插入失败");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
statement = null;
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
connection = null;
}
}
}
}
六、登录案例
6.1 Statement
package _03登录案例SQL注入._31Statement;
import java.sql.*;
import java.util.Scanner;
/**
* 登录案例
*
*/
public class LoginDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String inputUsername = scanner.nextLine();
System.out.println("请输入密码:");
String inputPassword = scanner.nextLine();
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/powernode_jdbc", "root", "root");
statement = connection.createStatement();
/*
*DML语句使用:executeUpdate --> 返回受影响的行数
* DQL语句使用:executeQuery --> 返回查询到的结果集(虚拟表)
*/
String sql = "select * from sys_user where username= '"+inputUsername+"'and password ='"+ inputPassword+"'";
System.out.println("sql = " + sql);
resultSet = statement.executeQuery(sql);
if(resultSet.next()){
String username = resultSet.getString("username");
String password = resultSet.getString("password");
System.out.println(username+"登录成功");
}else {
System.out.println("登录失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
resultSet = null;
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
statement = null;
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
connection = null;
}
}
}
}
正常情况
异常情况
分析:
而这里用的是 if(resultSet.next())
游标只向下移动一位,指向数据库表的第一个数据,所以结果查询出来的结果只有表中的第一条
我们发现以上输入的密码不是正确的密码,但是登录成功了。原因是以上代码出现了SQL注入的问题。引出SQL注入。
七、SQL注入
7.1 SQL注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
SQL注入: 就是SQL语句中接收的参数包含有SQL语句的关键字,破坏了SQL语句原有的功能。
7.2 出现SQL注入的原因
出现SQL注入的原因:是包含SQL语句关键字的的参数当做SQL语句进行编译了
7.3 解决方案
就是将SQL语句接收的参数当做普通的字符串参数使用,而不当做SQL语句,参与SQL语句的编译。Java中提供了PreparedStatement接口来解决SQL注入问题。
7.4 PreparedStatement接口
public interface PreparedStatement extends Statement
表示预编译的 SQL 语句的对象。
SQL 语句被预编译并存储在 PreparedStatement
对象中。然后可以使用此对象多次高效地执行该语句。
注意:用于设置 IN 参数值的设置方法(setShort
、setString
等等)必须指定与输入参数的已定义 SQL 类型兼容的类型。例如,如果 IN 参数具有 SQL 类型 INTEGER
,那么应该使用 setInt
方法。
如果需要任意参数类型转换,使用 setObject
方法时应该将目标 SQL 类型作为其参数。
7.5 PreparedStatement如何解决SQL注入
PreparedStatement会事先对SQL语句进行预编译。预编译后的SQL语句保存在PreparedStatement对象中。
预编译完成后再使用SQL接收的参数,所以此时SQL接收的参数是不会参与SQL编译了。
PreparedStatement是先编译,后使用参数。所以参数中即使有SQL关键字也只能当做普通字符串使用了。
7.6 使用PreparedStatement改进代码,解决SQL注入问题
package _03登录案例SQL注入._32PreparedStatement;
import java.sql.*;
import java.util.Scanner;
/**
* 登录案例
* 使用PreparedStatement改进代码,解决SQL注入问题
*
*/
public class LoginDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String inputUsername = scanner.nextLine();
System.out.println("请输入密码:");
String inputPassword = scanner.nextLine();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/powernode_jdbc", "root", "root");
/*
*DML语句使用:executeUpdate --> 返回受影响的行数
* DQL语句使用:executeQuery --> 返回查询到的结果集(虚拟表)
*/
/*
* ? 表示参数占位符
* SQL语句进行预编译的时候,是还没有给具体参数的,而是使用?来对参数进行占位
*/
String sql = "select * from sys_user where username = ? and password = ?";
//获取PreparedStatement对象
preparedStatement = connection.prepareStatement(sql);
// 预编译完后成,再使用参数替换占位符
// 第一个参数:表示第几个?
preparedStatement.setString(1,inputUsername);
preparedStatement.setString(2,inputPassword);
/*
* PreparedStatement的executeQuery()和executeUpdate()是无参的
* 因为PreparedStatement对象preparedStatement中就保存有SQL语句
*/
resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
String username = resultSet.getString("username");
String password = resultSet.getString("password");
System.out.println(username+"登录成功");
}else {
System.out.println("登录失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
resultSet = null;
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
preparedStatement = null;
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
connection = null;
}
}
}
}
测试结果
八、编写JDBC工具类
resources文件夹下
jdbc.properties
# JDBC四要素
# 驱动类名
driverClassName=com.mysql.cj.jdbc.Driver
# 数据库连接地址
url=jdbc:mysql://localhost:3306/powernode_jdbc
# 用户名
username=root
# 密码
password=root
代码:
package _04编写JDBC工具类;
import javax.print.DocFlavor;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* JDBC工具类
* 作用:
* 1. 获取连接
* 使用配置文件解决硬编码
* 2. 释放资源
*
*
*/
public class JDBCUtils {
private static String driverClassName;
private static String url;
private static String username;
private static String password;
private JDBCUtils(){
}
/**
* 配置文件只需要获取一次即可,所以编写在静态代码块中
* 驱动只需要加载一次即可,所以编写在静态代码块中
*/
static {
try {
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(inputStream);
driverClassName = properties.getProperty("driverClassName");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driverClassName);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接
* @return 数据库连接
*/
public static Connection getConnection(){
try {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 释放资源
* @param autoCloseable 需要释放的资源
*/
public static void close(AutoCloseable ...autoCloseable){
if(autoCloseable!=null){
for (AutoCloseable closeable : autoCloseable) {
try {
closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
九、CRUD操作
添加单元测试的jar包hamcrest-core-1.3.jar
和junit-4.13.2.jar
我的jar包的下载地址链接:https://download.csdn.net/download/weixin_46411355/87398726
怎么导入jar包,可查看笔者的另一篇博客《IDEA导入和删除第三方jar包》链接:https://huanghaoheng.blog.csdn.net/article/details/126332558
数据库sql
t_student.sql
/*
Navicat Premium Data Transfer
Source Server : MySQL80_3306
Source Server Type : MySQL
Source Server Version : 80030
Source Host : localhost:3306
Source Schema : powernode_jdbc
Target Server Type : MySQL
Target Server Version : 80030
File Encoding : 65001
Date: 27/01/2023 19:07:45
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_student
-- ----------------------------
DROP TABLE IF EXISTS `t_student`;
CREATE TABLE `t_student` (
`id` bigint NOT NULL AUTO_INCREMENT,
`sid` int NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`age` int NULL DEFAULT NULL,
`gender` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`province` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`create_time` datetime NULL DEFAULT NULL,
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_student
-- ----------------------------
INSERT INTO `t_student` VALUES (1, 1001, '张三', 20, 'male', '四川省', '2022-12-27 19:48:52', '2023-01-25 19:49:34');
INSERT INTO `t_student` VALUES (2, 1002, '李四', 20, 'female', '四川省', '2023-01-25 19:50:18', '2023-01-26 19:50:21');
INSERT INTO `t_student` VALUES (3, 1003, '王五', 23, 'male', '云南省', '2023-01-19 19:51:01', '2023-01-04 19:51:03');
INSERT INTO `t_student` VALUES (4, 2001, 'hhh', 24, 'male', '四川省', '2023-01-26 00:00:00', '2023-01-26 14:12:00');
SET FOREIGN_KEY_CHECKS = 1;
javaBean
package javabean;
import java.util.Objects;
/**
* 实体类 -- 对应数据表t_student
*
* 和数据表对应的实体类,成员变量的数据类型使用包装类,不使用基本类型
*
* 因为数据库中字段没有值是null,
* int类型的基本数据类型的默认值是0,0不能表示没有值
* 但是引用数据类型的默认值是null,符合数据库中字段没有值是null
*
*/
public class Student {
private Long id;
private Integer sid;
private String name;
// tinyint unsignd --> 使用Integer
private Integer age;
private String gender;
private String province;
private String create_time;
private String update_time;
public Student() {
}
public Student(Long id, Integer sid, String name, Integer age, String gender) {
this.id = id;
this.sid = sid;
this.name = name;
this.age = age;
this.gender = gender;
}
public Student(Long id, Integer sid, String name, Integer age, String gender, String province, String create_time, String update_time) {
this.id = id;
this.sid = sid;
this.name = name;
this.age = age;
this.gender = gender;
this.province = province;
this.create_time = create_time;
this.update_time = update_time;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCreate_time() {
return create_time;
}
public void setCreate_time(String create_time) {
this.create_time = create_time;
}
public String getUpdate_time() {
return update_time;
}
public void setUpdate_time(String update_time) {
this.update_time = update_time;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", sid=" + sid +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", province='" + province + '\'' +
", create_time='" + create_time + '\'' +
", update_time='" + update_time + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id) &&
Objects.equals(sid, student.sid) &&
Objects.equals(name, student.name) &&
Objects.equals(age, student.age) &&
Objects.equals(gender, student.gender) &&
Objects.equals(province, student.province) &&
Objects.equals(create_time, student.create_time) &&
Objects.equals(update_time, student.update_time);
}
@Override
public int hashCode() {
return Objects.hash(id, sid, name, age, gender, province, create_time, update_time);
}
}
CRUDDemo.java
package _05CRUD;
import _04编写JDBC工具类.JDBCUtils;
import javabean.Student;
import org.junit.Test;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
/**
* CRUD:增删查改
*/
public class CRUDDemo {
/**
* 插入数据
*/
@Test
public void testInsert(){
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
String sql = "insert into t_student values(null,?,?,?,?,?,?,?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,2002);
preparedStatement.setString(2,"罗龙江");
preparedStatement.setInt(3,24);
preparedStatement.setString(4,"male");
preparedStatement.setString(5,"贵州省");
preparedStatement.setDate(6,new Date(System.currentTimeMillis()));
//MySQL日期类型的数据可以使用String格式的时间表示
// preparedStatement.setString(7,"2023-1-26 14:12");
preparedStatement.setString(7,"2023/1/26 14:12");
int row = preparedStatement.executeUpdate();
System.out.println(row);
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(preparedStatement,connection);
}
}
/**
* 更新数据
*/
@Test
public void testUpdate(){
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
String sql = "update t_student set name = ? where sid = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"黄浩恒");
preparedStatement.setInt(2,2001);
int row = preparedStatement.executeUpdate();
System.out.println(row);
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(preparedStatement,connection);
}
}
/**
* 删除数据
*/
@Test
public void testDelete(){
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
String sql = "delete from t_student where name = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"罗龙江");
int row = preparedStatement.executeUpdate();
System.out.println(row);
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(preparedStatement,connection);
}
}
/**
* 查询一条数据
*/
@Test
public void testSelectOne(){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
String sql = "select * from t_student where sid = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,1002);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
Long id = resultSet.getLong("id");
int sid = resultSet.getInt("sid");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String gender = resultSet.getString("gender");
String province = resultSet.getString("province");
String create_time = resultSet.getString("create_time");
String update_time = resultSet.getString("update_time");
Student student = new Student(id,sid,name,age,gender);
student.setProvince(province);
student.setCreate_time(create_time);
student.setUpdate_time(update_time);
System.out.println(student);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,preparedStatement,connection);
}
}
/**
* 查询多条数据
*/
@Test
public void testSelectList(){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
List<Student> studentList = new ArrayList<>();
try {
connection = JDBCUtils.getConnection();
String sql = "select * from t_student";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
Long id = resultSet.getLong("id");
int sid = resultSet.getInt("sid");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String gender = resultSet.getString("gender");
String province = resultSet.getString("province");
String create_time = resultSet.getString("create_time");
String update_time = resultSet.getString("update_time");
Student student = new Student(id,sid,name,age,gender);
student.setProvince(province);
student.setCreate_time(create_time);
student.setUpdate_time(update_time);
studentList.add(student);
}
//遍历集合
studentList.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,preparedStatement,connection);
}
}
}
十、事务操作
数据库sql
t_account.sql
/*
Navicat Premium Data Transfer
Source Server : MySQL80_3306
Source Server Type : MySQL
Source Server Version : 80030
Source Host : localhost:3306
Source Schema : powernode_jdbc
Target Server Type : MySQL
Target Server Version : 80030
File Encoding : 65001
Date: 27/01/2023 19:09:51
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_account
-- ----------------------------
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`money` int NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_account
-- ----------------------------
INSERT INTO `t_account` VALUES (1, '张三', 1000);
INSERT INTO `t_account` VALUES (2, '李四', 1000);
SET FOREIGN_KEY_CHECKS = 1;
package _06事务操作;
import _04编写JDBC工具类.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 演示事务转账
* 转账的案例
*/
public class TransactionDemo {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
//张三给李四转100元 -- 整体是一个事务
try{
connection = JDBCUtils.getConnection();
//1.关闭自动提交:开启事务,手动提交
connection.setAutoCommit(false);
//张三的账户减少100
String sql = "update t_account set money = money - ? where name = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,100);
preparedStatement.setString(2,"张三");
preparedStatement.executeUpdate();
// System.out.println( 1/0 );
//李四的账户增加100
sql = "update t_account set money = money + ? where name = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,100);
preparedStatement.setString(2,"李四");
preparedStatement.executeUpdate();
//2.提交事务
connection.commit();
}catch (Exception e){
e.printStackTrace();
//3.回滚事务
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}finally {
JDBCUtils.close(preparedStatement,connection);
}
}
}
十一、批处理
批处理的配置文件中的url
必须要配置一个参数?rewriteBatchedStatements=true
resources目录下的
11.1 jdbc.properties
# JDBC四要素
# 驱动类名
driverClassName=com.mysql.cj.jdbc.Driver
# 数据库连接地址
# ?rewriteBatchedStatements=true批处理必须添加该参数
url=jdbc:mysql://localhost:3306/powernode_jdbc?rewriteBatchedStatements=true
# 用户名
username=root
# 密码
password=root
11.2 BatchDemo.java
package _07批处理;
import _04编写JDBC工具类.JDBCUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
/**
* JDBC批处理
*/
public class BatchDemo {
/**
* 不使用批处理来 插入1000条数据
*/
@Test
public void testNoBatch() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
String sql = "insert into t_student values(null,?,?,?,?,?,?,?)";
preparedStatement = connection.prepareStatement(sql);
long start = System.currentTimeMillis();
for (int i = 1; i <= 1000; i++) {
preparedStatement.setInt(1, i);
preparedStatement.setString(2, "hhh" + i);
preparedStatement.setInt(3, 24 + i);
preparedStatement.setString(4, "male" + i);
preparedStatement.setString(5, "贵州省" + i);
preparedStatement.setDate(6, new Date(System.currentTimeMillis()));
//MySQL日期类型的数据可以使用String格式的时间表示
// preparedStatement.setString(7,"2023-1-26 14:12");
preparedStatement.setString(7, "2023/1/26 14:12");
preparedStatement.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.close(preparedStatement, connection);
}
}
/**
*使用批处理来 插入1000条数据
*/
@Test
public void testBatch(){
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
String sql = "insert into t_student values(null,?,?,?,?,?,?,?)";
preparedStatement = connection.prepareStatement(sql);
long start = System.currentTimeMillis();
for (int i = 1; i <= 1000; i++) {
preparedStatement.setInt(1, i);
preparedStatement.setString(2, "hhh" + i);
preparedStatement.setInt(3, 24 + i);
preparedStatement.setString(4, "male" + i);
preparedStatement.setString(5, "贵州省" + i);
preparedStatement.setDate(6, new Date(System.currentTimeMillis()));
//MySQL日期类型的数据可以使用String格式的时间表示
// preparedStatement.setString(7,"2023-1-26 14:12");
preparedStatement.setString(7, "2023/1/26 14:12");
//添加到批处理中
preparedStatement.addBatch();
}
//执行批处理
preparedStatement.executeBatch();
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.close(preparedStatement, connection);
}
}
}
11.3 没有进行批处理的耗时
11.4 进行批处理的耗时
十二、连接池
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。
连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。
使用连接池可以减少数据库连接的频繁创建和销毁,提升效率。
12.1 连接池原理
12.2 DataSource接口
该工厂用于提供到此 DataSource
对象所表示的物理数据源的连接。作为 DriverManager
工具的替代项,DataSource
对象是获取连接的首选方法。
DataSource
接口由驱动程序供应商实现。共有三种类型的实现:
- 基本实现 - 生成标准的
Connection
对象 - 连接池实现 - 生成自动参与连接池的
Connection
对象。此实现与中间层连接池管理器一起使用。 - 分布式事务实现 - 生成一个
Connection
对象,该对象可用于分布式事务,大多数情况下总是参与连接池。此实现与中间层事务管理器一起使用,大多数情况下总是与连接池管理器一起使用。
在IDEA中导入druid的jar包
笔者druid的jar包下载链接:https://download.csdn.net/download/weixin_46411355/87399287
怎么导入jar包,可查看笔者的另一篇博客《IDEA导入和删除第三方jar包》链接:https://huanghaoheng.blog.csdn.net/article/details/126332558
12.3 druid.properties
# 约定大于配置
driverClassName=com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/powernode_jdbc?rewriteBatchedStatements=true
username=root
password=root
12.4 DruidUtils.java
package _08连接池;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.sql.dialect.odps.ast.OdpsAddTableStatement;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* 数据库连接池 Druid工具类
*/
public class DruidUtils {
private static Properties properties = new Properties();
private DruidUtils(){
}
static {
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("druid.properties");
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 从连接池中获取连接
* DruidDataSource默认是非公平锁
* @return 连接
*/
public static Connection getConnection() {
try {
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
return dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 释放资源
* @param autoCloseable 需要释放的资源
*/
public static void close(AutoCloseable ...autoCloseable){
if(autoCloseable!=null){
for (AutoCloseable closeable : autoCloseable) {
try {
closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
12.5 使用Druid连接池完成CRUD
CRUDDemo.java
package _08连接池;
import _04编写JDBC工具类.JDBCUtils;
import javabean.Student;
import org.junit.Test;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
/**
* CRUD:增删查改
*/
public class CRUDDemo {
/**
* 插入数据
*/
@Test
public void testInsert(){
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = DruidUtils.getConnection();
String sql = "insert into t_student values(null,?,?,?,?,?,?,?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,2002);
preparedStatement.setString(2,"罗龙江");
preparedStatement.setInt(3,24);
preparedStatement.setString(4,"male");
preparedStatement.setString(5,"贵州省");
preparedStatement.setDate(6,new Date(System.currentTimeMillis()));
//MySQL日期类型的数据可以使用String格式的时间表示
// preparedStatement.setString(7,"2023-1-26 14:12");
preparedStatement.setString(7,"2023/1/26 14:12");
int row = preparedStatement.executeUpdate();
System.out.println(row);
} catch (Exception e) {
e.printStackTrace();
}finally {
DruidUtils.close(preparedStatement,connection);
}
}
/**
* 更新数据
*/
@Test
public void testUpdate(){
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = DruidUtils.getConnection();
String sql = "update t_student set name = ? where sid = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"HHH");
preparedStatement.setInt(2,2001);
int row = preparedStatement.executeUpdate();
System.out.println(row);
} catch (Exception e) {
e.printStackTrace();
}finally {
DruidUtils.close(preparedStatement,connection);
}
}
/**
* 删除数据
*/
@Test
public void testDelete(){
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = DruidUtils.getConnection();
String sql = "delete from t_student where name = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"罗龙江");
int row = preparedStatement.executeUpdate();
System.out.println(row);
} catch (Exception e) {
e.printStackTrace();
}finally {
DruidUtils.close(preparedStatement,connection);
}
}
/**
* 查询一条数据
*/
@Test
public void testSelectOne(){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DruidUtils.getConnection();
String sql = "select * from t_student where sid = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,1002);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
Long id = resultSet.getLong("id");
int sid = resultSet.getInt("sid");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String gender = resultSet.getString("gender");
String province = resultSet.getString("province");
String create_time = resultSet.getString("create_time");
String update_time = resultSet.getString("update_time");
Student student = new Student(id,sid,name,age,gender);
student.setProvince(province);
student.setCreate_time(create_time);
student.setUpdate_time(update_time);
System.out.println(student);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DruidUtils.close(resultSet,preparedStatement,connection);
}
}
/**
* 查询多条数据
*/
@Test
public void testSelectList(){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
List<Student> studentList = new ArrayList<>();
try {
connection = DruidUtils.getConnection();
String sql = "select * from t_student";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
Long id = resultSet.getLong("id");
int sid = resultSet.getInt("sid");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String gender = resultSet.getString("gender");
String province = resultSet.getString("province");
String create_time = resultSet.getString("create_time");
String update_time = resultSet.getString("update_time");
Student student = new Student(id,sid,name,age,gender);
student.setProvince(province);
student.setCreate_time(create_time);
student.setUpdate_time(update_time);
studentList.add(student);
}
//遍历集合
studentList.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}finally {
DruidUtils.close(resultSet,preparedStatement,connection);
}
}
}
十三、BaseDAO的封装
13.1 什么是DAO
DAO:数据访问对象(Data Access Object)
13.2 封装DML语句
BaseDAO.java类中代码
/**
* DML语句操作的模板
*
* @param sql DML语句
* @param args sql语句的参数
* @return 受影响的函数
*/
public static int executeDMLSQL(String sql, Object... args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
int row = 0;
try {
connection = DruidUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
row = preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
DruidUtils.close(preparedStatement, connection);
}
return row;
}
测试类中代码
.
@Test
public void testDML(){
String updateSql = "update t_student set name = ? where sid = ?";
int row = BaseDAO.executeDMLSQL(updateSql, "hhh", 2001);
System.out.println("row = " + row);
}
13.3 封装DQL语句
13.3.1 思路
13.3.2 API
13.2.1 ResultSet
getMetaData()方法
ResultSetMetaData getMetaData() throws SQLException
获取此 ResultSet 对象的列的编号、类型和属性。
返回:
此 ResultSet 对象的列的描述
抛出:
SQLException - 如果发生数据库访问错误或在已关闭的结果集上调用此方法
13.2.2 ResultSetMetaData接口
①getColumnCount()方法
int getColumnCount() throws SQLException
返回此 ResultSet 对象中的列数。
返回:
列数
抛出:
SQLException - 如果发生数据库访问错误
②getColumnLabel(int column) 和 getColumnName(int column) 方法
getColumnLabel方法
String getColumnLabel(int column) throws SQLException
获取用于打印输出和显示的指定列的建议标题。建议标题通常由 SQL AS 子句来指定。如果未指定 SQL AS,则从 getColumnLabel 返回的值将和 getColumnName 方法返回的值相同。
参数:
column - 第一列是 1,第二个列是 2,……
返回:
建立列标题
抛出:
SQLException - 如果发生数据库访问错误
getColumnName方法
String getColumnName(int column) throws SQLException
获取指定列的名称。
参数:
column - 第一列是 1,第二个列是 2,……
返回:
列名称
抛出:
SQLException - 如果发生数据库访问错误
用代码测试出这两个方法的区别
BaseDAO.java中的代码片段
@Test
public void testexecuteDQLSQLHalf() {
String selectSql = "select * from t_student";
executeDQLSQLHalf(selectSql);
}
/**
* 这个测试代码可以测试出metaData.getColumnName和metaData.getColumnLabel的区别
*/
@Test
public void testDifferenceBetweenGetColumnNameAndGetColumnLabel() {
String selectSql = "select sid,name as xxx ,age from t_student";
executeDQLSQLHalf(selectSql);
}
public static <T> List<T> executeDQLSQLHalf(String sql, Object... args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
List list = new ArrayList();
try {
connection = DruidUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
resultSet = preparedStatement.executeQuery();
/*
* 思考:怎么做到表中数据和Java变量进行绑定(通用性)
* select-->虚拟表-->投影字段
* 投影字段和成员变量的名称一致的话,就可以使用反射来操作对象的字段
*/
/*
* resultSet是查询语句得到的虚拟表结果集
* 所以resultSet中有字段的信息
*/
/*
* resultSet.getMetaData()获取此 ResultSet 对象的列的编号、类型和属性。
*/
ResultSetMetaData metaData = resultSet.getMetaData();
//获取投影字段的数量
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
//获取字段
for (int i = 1; i <= columnCount; i++) {
//获取表中投影字段的名称
// String columnName = metaData.getColumnName(i);
// System.out.println("columnName = " + columnName);
//获取表中投影字段的名称,如果投影字段有别名,获取的是别名
String columnLabel = metaData.getColumnLabel(i);
System.out.println("columnLabel = " + columnLabel);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DruidUtils.close(resultSet, preparedStatement, connection);
}
return list;
}
运行效果:
getColumnName
getColumnLabel
小结:
getColumnName:获取表中投影字段的名称
getColumnLabel:获取表中投影字段的名称,如果投影字段有别名,获取的是别名
13.3.3 javaBean
Student.java
注意:create_time和update_time均为LocalDateTime 类型
package _09BaseDAO的封装.javabean;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 实体类 -- 对应数据表t_student
*
* 和数据表对应的实体类,成员变量的数据类型使用包装类,不使用基本类型
*
* 因为数据库中字段没有值是null,
* int类型的基本数据类型的默认值是0,0不能表示没有值
* 但是引用数据类型的默认值是null,符合数据库中字段没有值是null
*
*/
public class Student {
private Long id;
private Integer sid;
private String name;
private Integer age;
private String gender;
private String province;
private LocalDateTime create_time;
private LocalDateTime update_time;
public Student() {
}
public Student(Long id, Integer sid, String name, Integer age, String gender) {
this.id = id;
this.sid = sid;
this.name = name;
this.age = age;
this.gender = gender;
}
public Student(Long id, Integer sid, String name, Integer age, String gender, String province, LocalDateTime create_time, LocalDateTime update_time) {
this.id = id;
this.sid = sid;
this.name = name;
this.age = age;
this.gender = gender;
this.province = province;
this.create_time = create_time;
this.update_time = update_time;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public LocalDateTime getCreate_time() {
return create_time;
}
public void setCreate_time(LocalDateTime create_time) {
this.create_time = create_time;
}
public LocalDateTime getUpdate_time() {
return update_time;
}
public void setUpdate_time(LocalDateTime update_time) {
this.update_time = update_time;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", sid=" + sid +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", province='" + province + '\'' +
", create_time=" + create_time +
", update_time=" + update_time +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id) &&
Objects.equals(sid, student.sid) &&
Objects.equals(name, student.name) &&
Objects.equals(age, student.age) &&
Objects.equals(gender, student.gender) &&
Objects.equals(province, student.province) &&
Objects.equals(create_time, student.create_time) &&
Objects.equals(update_time, student.update_time);
}
@Override
public int hashCode() {
return Objects.hash(id, sid, name, age, gender, province, create_time, update_time);
}
}
13.3.4 封装DQLSQL的方法
在BaseDAO中
/**
* DQL语句的模板:
* 1.对象成员变量的数据类型要和表中字段的数据类型一致
* 2.对象成员变量的名称要和投影字段的名称(别名)一样
* @param clazz 对象的Class
* @param sql sql语句
* @param args sql语句的参数
* @param <T> 对象的Class的类型
* @return 集合
*/
public static <T> List<T> executeDQLSQL(Class<T> clazz, String sql, Object... args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
List list = new ArrayList();
try {
connection = DruidUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
resultSet = preparedStatement.executeQuery();
/*
* 思考:怎么做到表中数据和Java变量进行绑定(通用性)
* select-->虚拟表-->投影字段
* 投影字段和成员变量的名称一致的话,就可以使用反射来操作对象的字段
*/
/*
* resultSet是查询语句得到的虚拟表结果集
* 所以resultSet中有字段的信息
*/
/*
* resultSet.getMetaData()获取此 ResultSet 对象的列的编号、类型和属性。
*/
ResultSetMetaData metaData = resultSet.getMetaData();
//获取投影字段的数量
int columnCount = metaData.getColumnCount();
//while执行一次,就是获取表中一行的数据
while (resultSet.next()) {
//一行数据对应一个对象
//使用反射 创建javaBean对象
T t = clazz.newInstance();
/*
*getColumnName(i):获取表中投影字段的名称
* getColumnLabel(i):获取表中投影字段的名称,如果投影字段有别名,获取的是别名
*/
for (int i = 1; i <= columnCount; i++) {
//获取每一行的字段名称和字段值
//获取表中投影字段的名称
// String columnName = metaData.getColumnName(i);
// System.out.println("columnName = " + columnName);
//获取表中投影字段的名称,如果投影字段有别名,获取的是别名
String columnLabel = metaData.getColumnLabel(i);
// System.out.println("columnLabel = " + columnLabel);
//获取 结果集中 表字段对应的值
Object columnValue = resultSet.getObject(columnLabel);
//使用反射 根据投影字段的名称 对应的 获取JavaBean已声明的成员变量
Field field = clazz.getDeclaredField(columnLabel);//javabean的成员变量
//暴力破解,因为javabean的成员变量是由private修饰的
field.setAccessible(true);
//给javabean的成员变量赋值
field.set(t, columnValue);
}
list.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DruidUtils.close(resultSet, preparedStatement, connection);
}
return list;
}
13.3.5 测试类
@Test
public void testDQL(){
String selectSql = "select * from t_student";
List<Student> studentList = BaseDAO.executeDQLSQL(Student.class, selectSql);
studentList.forEach(System.out::println);
}
13.3.6运行效果
13.3.7 万能SQL模板(将DML语句和DQL语句同时封装)
在BaseDAO中
public static void executeSQL(Class<?>clazz,String sql,Object...args){
String[] sqlArray = sql.split(" ");
String sqlMethod = sqlArray[0];
if("select".equalsIgnoreCase(sqlMethod)){
if(clazz==null){
throw new RuntimeException("执行查询语句必须需要javaBean的Class对象");
}
List<?> list = executeDQLSQL(clazz, sql, args);
list.forEach(System.out::println);
}
else{
int row = executeDMLSQL(sql, args);
System.out.println("row = " + row);
}
}
测试类
/**
* 测试万能 先测试DML
*/
@Test
public void testSQL(){
String updateSql = "update t_student set name = ? where sid = ?";
BaseDAO.executeSQL(Student.class,updateSql,"hhh",2001);
}
/**
* 测试万能 后测试DQL
*/
@Test
public void testSQL2(){
String selectSql = "select * from t_student";
BaseDAO.executeSQL(null,selectSql);
}
十四、分页的封装
14.1 分页结果封装类 PageInfo
package _10分页的封装;
import java.util.List;
/**
* 分页结果封装类
* @param <T>
*/
public class PageInfo<T> {
private List<T> data;//每页具体的数据
private Long count;//符合条件的数据总条数
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
}
14.2 修改BaseDAO
分页查询的方法
/**
* 分页查询
* @param clazz 查询的数据封装的对象对应的Class
* @param sql sql语句
* @param currentPage 当前页数,也就是第几页
* @param pageSize 每页显示的条数
* @return 分页封装的对象
*/
public static <T> PageInfo<T> selectByPage(Class<T> clazz,String sql,int currentPage,int pageSize){
//分页的时候limit跳过的条数
int index = 0;
index = (currentPage-1)*pageSize;
String selectSql = sql+" limit "+index+","+pageSize;
// 查询获取当前指定页数的数据
List<T> list = executeDQLSQL(clazz, selectSql);
//获取总的条数
// select * from student
Long totalCount = getTotalCount(sql);
PageInfo<T> pageInfo = new PageInfo<>();
pageInfo.setData(list);
pageInfo.setCount(totalCount);
return pageInfo;
}
统计条数的方法
/**
* 获取总的条数
* @param sql sql语句
* @return 返回条数
*/
private static Long getTotalCount(String sql){
//子查询
//select count(1) from ( select * from student ) as s
String countSql = "select count(1) from ("+sql+") as s";
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DruidUtils.getConnection();
preparedStatement = connection.prepareStatement(countSql);
resultSet = preparedStatement.executeQuery();
resultSet.next();//先让光标指向数据的第一行
long totalCount = resultSet.getLong(1);
return totalCount;
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtils.close(resultSet,preparedStatement,connection);
}
return 0L;
}
14.3 测试类
package _10分页的封装;
import _09BaseDAO的封装.BaseDAO;
import newJavaBean.Student;
import org.junit.Test;
public class PageTest {
@Test
public void testPaging(){
String sql = "select * from t_student where id > 4";
PageInfo<Student> pageInfo = BaseDAO.selectByPage(Student.class, sql, 1, 2);
//总条数
System.out.println(pageInfo.getCount());
// 第一页显示的数据
System.out.println(pageInfo.getData());
}
}