文章目录
温故知新:回顾一下JDBC
0. 前言
(1)学习目标
JDBC 是 Java 持久层基础,是我们学习 Java 技术栈过程中需要了解的知识。
据说在遥远的年代, Mybatis 尚未流行,Java 工程的持久层基本上就由JDBC完成。学习JDBC,既能巩固面向对象的思想,又能为日后学习持久层框架打下基础。
学习目标
- 巩固SQL基础,暂未学SQL的同学也可以通过JDBC了解一下SQL
- 通过JDBC,能深入了解万物皆对象,面向对象的思想
- 承上启下,JDBC是较为原始的持久层实现方式,为日后学习持久层框架打下基础
(2) 准备工作
准备数据库
目前 Mysql 是最流行的轻量级关系型数据库之一,我们首先要准备一个数据库,可以选择本地的,也可以选择远程的:
Mysql官网下载连接
https://dev.mysql.com/downloads/mysql/
具体安装方式本文不赘述,请参考其他文章。
准备JDBC依赖
MVN仓库-Mysql-Connector-Java
读者可通过 Maven 下载这个JDBC依赖,也可以像我一样手动拉取:
若是手动拉取的jar包,可参考以下文章将它添加至项目的Library:
优快云-往项目中添加jar包:https://blog.youkuaiyun.com/pan_1214_/article/details/126283072
测试驱动
黑猫白猫,抓到老鼠就是好猫,无论采用哪种方式,只要能使用JDBC依赖即可,我们做建个类做个测试:
public static void main(String[] args) {
//初始化驱动
try {
//驱动类com.mysql.cj.jdbc.Driver
//如果忘记了导包,就会抛出ClassNotFoundException
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("数据库驱动加载成功 !");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
连接数据库
参考下面代码连接数据库:
public static void main(String[] args) {
//初始化驱动
try {
//驱动类com.mysql.cj.jdbc.Driver
//如果忘记了导包,就会抛出ClassNotFoundException
Class.forName("com.mysql.cj.jdbc.Driver");
//连接数据库
Connection c = DriverManager.getConnection( "jdbc:mysql://127.0.0.1:3306/myfirstdb?characterEncoding=UTF-8",
"root", "root");
System.out.println("连接成功,获取连接对象: " + c);
} catch (Exception e) {
e.printStackTrace();
}
}
建表
建个表来做接下来的学习演示:
CREATE TABLE `user` (
`id` bigint NOT NULL COMMENT '主键id',
`user_account` varchar(255) DEFAULT NULL COMMENT '登录账号',
`username` varchar(255) DEFAULT NULL COMMENT '用户名',
`avatar_url` varchar(255) DEFAULT NULL COMMENT '头像url',
`gender` tinyint DEFAULT NULL COMMENT '性别',
`user_password` varchar(255) DEFAULT NULL COMMENT '密码',
`phone` varchar(255) DEFAULT NULL COMMENT '电话',
`email` varchar(255) DEFAULT NULL COMMENT '邮箱',
`user_status` int NOT NULL DEFAULT '0' COMMENT '状态 0-正常',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` int NOT NULL DEFAULT '0' COMMENT '是否删除 ',
`user_role` int NOT NULL DEFAULT '0' COMMENT '用户角色 0-普通用户 1-管理员',
`id_card` varchar(255) DEFAULT NULL COMMENT '实名注册,身份证号'
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
1. CRUD
面向对象编程的一大特点是我们可以直接用大佬们写好的各种类、各种方法。我们在准备工作中引入的JDBC 依赖 jar 包里就是要用到的各种类、方法。
JDBC 提供一个Statement 类 来执行 SQL 语句。
1.1 Statement执行增删查改
Statement对象
我们可以在连接对象中,创建Statement对象,然后通过这个对象执行CRUD
/连接数据库
Connection c = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/myfirstdb?characterEncoding=UTF-8",
"root", "root");
Statement s = c.createStatement();
String insert = "INSERT INTO `user` ( id, `user_account` , `username` , `email` ) VALUES (3,'sharry3', 'sharrycloud1', 'example@example.com')";
s.execute(insert);
//关闭连接
s.close();
c.close();
在上面例子中,我们不难发现,我们需要将SQL 语句写成String ,作为参数传递进Statement对象,执行完毕后记得像关流一样将其关闭。
CRUD
我们演示 使用 Statement 对象 执行 CRUD
先列举 增删改的例子,因为增删改的返回值都是影响行数,基本上用法都差不多:
public static void main(String[] args) throws SQLException {
//连接数据库
Connection c = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/myfirstdb?characterEncoding=UTF-8",
"root", "root");
Statement s = c.createStatement();
// 新增
String insert = "INSERT INTO `user` ( id, `user_account` , `username` , `email` ) VALUES (3,'sharry3', 'sharrycloud1', 'example@example.com')";
s.execute(insert);
String insert = "INSERT INTO `user` ( id, `user_account` , `username` , `email` ) VALUES (3,'sharry3', 'sharrycloud1', 'example@example.com')";
System.out.println("影响了"+s.execute(insert)+"行");
//更新数据
String update = "UPDATE `user` SET `username` = 'sharrycloud2' WHERE `user_account` = 'sharry2'";
System.out.println("影响了"+s.executeUpdate(update)+"行");
//删除数据
String delete = "DELETE FROM `user` WHERE `user_account` = 'sharry2'";
System.out.println("影响了"+s.executeUpdate(delete)+"行");
//关闭连接
s.close();
c.close();
}
查询举例:
我们可以使用 ResultSet 接收查询结果
//查询数据
String select = "SELECT * FROM `user`";
ResultSet rs = s.executeQuery(select);
while (rs.next()){
// 获取其他属性,依此类推
System.out.println(rs.getString("user_account"));
}
1.2 PreparedStatement
PreparedStatement 是预编译的 Statement ,优点是可以进行参数设置,并且性能与安全性相比Statement有所提升。
当前,除了特别老旧的项目或者特殊业务,我们已经极少直接使用JDBC进行持久层操作,因此哪怕PreparedStatement性能优越,也用得极少,此处作为了解。
由于PreparedStatement是基于SQL语句创建的,既可以当成Statement使用,也可以通过填充值的方式设置SQL语句,我们稍微举个例子,具体使用可以到真要用到的时候,再去查 JDBC 的 API 文档:
String sql = "insert into user values(null,?,?,?)";
PreparedStatement ps = c.prepareStatement(sql);
ps.setString(1, "sharry");
ps.setString(2, "sharrycloud1");
ps.setString(3, "");
ps.execute();
2. 事务
事务是持久层需要重视的问题,即便现在JDBC直接使用率很低,但是它也提供了事务处理。
2.1 概念回顾
首先我们来回顾一下事务的八股文:
事务:是一组操作的集合,它是一个不可分割的工作单元,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
事务的特性:ACID
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
-一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。 - 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变是永久的。
2.2 JDBC 事务
JDBC 提供的事务处理机制实际代码操作起来也比较简单,举个例子:
// 使用事务
c.setAutoCommit(false);
String sql = "insert into user values(null,?,?,?)";
PreparedStatement ps = c.prepareStatement(sql);
ps.setString(1, "sharry");
ps.setString(2, "sharrycloud1");
ps.setString(3, "");
ps.execute();
//提交事务
c.commit();
需要注意的是,由于 Mysql 的表需要的 InnoDB 才支持事务。具体请自行查阅Mysql 原理。
3. ORM
ORM:Object Relationship Database Mapping, 对象和关系数据库的映射。
ORM 很好理解,就是将对象的属性和值设置成数据库对应的字段和值,然后通过 JDBC 等持久化操作,实现用 Java 代码对数据库表的操作。
ORM 是目前我们常用的 Mybatis 持久层框架 原理, 由于Mybatis也是基于JDBC的,因此ORM其实早早地就由JDBC支持。
其实 使用 JDBC 的方式 实现 ORM 很简单, 就是和我们工作时一样,创建实体类,然后通过 Statement 或者 ResultSet 对相应的值进行对应,根据业务进行下一步操作,我们直接举个例子:
篇幅关系,我们只截取我们创建的表的几个字段来演示:
实体类
public class UserVO {
private Long id;
private String userAccount;
private String username;
private String avatarUrl;
}
ORM举例
public static void main(String[] args) throws Exception {
//连接数据库
Connection c = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/myfirstdb?characterEncoding=UTF-8",
"root", "root");
Statement s = c.createStatement();
//创建执行sql的对象
Statement s = c.createStatement();
//执行查询sql
String sql = "select * from user";
ResultSet rs = s.executeQuery(sql);
List<UserVO> userVOS = new ArrayList<>();
//遍历结果集
while (rs.next()) {
UserVO userVO = new UserVO();
userVO.setId(rs.getLong("id"));
userVO.setUserAccount(rs.getString("user_account"));
userVO.setUsername(rs.getString("username"));
userVO.setAvatarUrl(rs.getString("avatar_url"));
userVOS.add(userVO);
}
//关闭连接
s.close();
c.close();
}
4. JDBC 连接池
JDBC 也提供了连接池,支持多线程。具体原理可参考线程池原理。我们直接举例:
public class ConnectionPool {
List<Connection> cs = new ArrayList<Connection>();
int size;
public ConnectionPool(int size) {
this.size = size;
init();
}
public void init() {
try {
Class.forName("com.mysql.jdbc.Driver");
for (int i = 0; i < size; i++) {
Connection c = DriverManager
.getConnection("jdbc:mysql://127.0.0.1:3306/myfirstdb?characterEncoding=UTF-8",
"root", "root");
cs.add(c);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public synchronized Connection getConnection() {
while (cs.isEmpty()) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Connection c = cs.remove(0);
return c;
}
public synchronized void returnConnection(Connection c) {
cs.add(c);
this.notifyAll();
}
}
5. 总结
本文学习了常见持久层框架的原理:JDBC;通过学习JDBC,我们进一步体验了面向对象的思想,一切皆对象:我们可以直接使用大佬们写好的JDBC相关类与方法、可以通过ORM将数据库表字段与值通过ORM映射到实体类,实现面向对象的方式操作数据库表。最后,我们简单提了一下数据库连接池。通过本文的学习,为接下来持久层框架的学习打下基础。