String username = “root”;
String password = “0908”;
String deleteString = “DELETE FROM students WHERE id = ?”;
try (Connection connection = DriverManager.getConnection(url, username, password); PreparedStatement preparedStatement = connection.prepareStatement(deleteString)😉 {
System.out.println(“连接成功”);
preparedStatement.setLong(1, 101);
preparedStatement.executeUpdate();
System.out.println(“删除成功”);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
删除数据后,接着查询数据,得到如下结果,可以看到 id = 101
的数据列已经被删除了,说明我们删除数据成功了!
修改数据
修改数据的方式同删除数据和新增数据基本一致,最大的区别在于 SQL 语句的不同,修改操作利用的是 UPDATE
语句,能一次更新若干列。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
-
@author : cunyu
-
@version : 1.0
-
@className : UpdateTest
-
@date : 2021/4/23 15:23
-
@description : 更新数据
*/
public class UpdateTest {
public static void main(String[] args) {
try {
Class.forName(“com.mysql.cj.jdbc.Driver”);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url = “jdbc:mysql://localhost:3306/javalearning?charactersetEncoding=UTF-8”;
String username = “root”;
String password = “0908”;
String updateString = “UPDATE students SET name = ? WHERE id = ?”;
try (Connection connection = DriverManager.getConnection(url, username, password); PreparedStatement preparedStatement = connection.prepareStatement(updateString)😉 {
System.out.println(“连接成功”);
preparedStatement.setString(1, “村雨遥”);
preparedStatement.setLong(2, 201);
preparedStatement.executeUpdate();
System.out.println(“更新成功”);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
修改数据后,接着查询数据,得到如下结果,可以看到 id = 201
对应的数据列中,name
从小黄变成了村雨遥,说明数据更新成功。
注意
当我们的数据库表设置自增主键后,在新增数据时无需指定主键也会自动更新。但是在获取自增主键的值时,不能先插入再查询,否则可能会导致冲突。要正确获取自增主键,需要在创建 PreparedStatement
时,指定一个标志位 RETURN_GENERATED_KEYS
,用于表示 JDBC 驱动必须返回插入的自增主键。
假设我们创建表时,设置了自增长的键:
CREATE TABLE students(
id int(11) AUTO_INCREMENT,
…
);
此时无论是 executeQuery()
还是 execureUpdate()
都不会返回这个自增长的 id
,所以需要在创建 PreparedStatement
对象时加入 Statement.RETURN_GENERATED_KEYS
参数以确保会返回自增长 ID,然后通过 getGeneratedKeys
获取该字段;
import java.sql.*;
/**
-
@author : cunyu
-
@version : 1.0
-
@className : QueryTest
-
@date : 2021/4/23 18:01
-
@description : 自增主键查询
*/
public class QueryTest {
public static void main(String[] args) {
try {
Class.forName(“com.mysql.cj.jdbc.Driver”);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url = “jdbc:mysql://localhost:3306/javalearning?characterEncoding=UTF-8”;
String username = “root”;
String password = “12345”;
String queryString = “INSET INTO students VALUES(null,?,……)”;
try (Connection connection = DriverManager.getConnection(url, username, password); PreparedStatement preparedStatement = connection.prepareStatement(queryString, Statement.RETURN_GENERATED_KEYS); ResultSet resultSet = preparedStatement.getGeneratedKeys()😉 {
System.out.println(“连接成功”);
preparedStatement.setString(1, “村雨遥”);
……
preparedStatement.executeUpdate();
System.out.println(“查询到的信息如下:”);
while (resultSet.next()) {
// 查询到的结果索引从 1 开始
System.out.println(“id:” + resultSet.getLong(1));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
JDBC 工具类
观察上面的代码,我们可以注意到每次都需要注册驱动、传递参数,关闭连接等操作,为了提高工具通用性,我们利用配置文件来配置数据库相关信息,然后创建一个 JDBC 工具类来简化上述操作。
- 首先在
src
目录下创建一个配置文件jdbc.properties
,并且填入数据库的相关信息;
url=jdbc:mysql://localhost/demo?characterEncoding=UTF-8
user=root
password=“12345”
driver=com.mysql.jdbc.cj.Driver
- 创建工具类
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/**
-
@author : cunyu
-
@version : 1.0
-
@className : JDBCUtils
-
@date : 2021/4/24 15:10
-
@description : JDBC 工具类
*/
public class JDBCUtils {
// 配置文件中的各个参数
private static String url;
private static String user;
private static String password;
private static String driver;
// 静态代码块
static {
try {
// 读取配置文件并获取参数值
// 创建集合类
Properties properties = new Properties();
// 获取配置文件所在位置
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL resource = classLoader.getResource(“jdbc.properties”);
String path = resource.getPath();
System.out.println(“配置文件所在位置”);
// 加载配置文件
properties.load(new FileReader(path));
// 获取参数的值并赋值
url = properties.getProperty(“url”);
user = properties.getProperty(“user”);
password = properties.getProperty(“password”);
driver = properties.getProperty(“driver”);
// 注册驱动
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
-
@param
-
@return 连接对象
-
@description 获取连接
-
@date 2021/4/24 15:24
-
@author cunyu1943
-
@version 1.0
*/
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
/**
-
@param preparedStatement 预声明
-
@param connection 连接对象
-
@return
-
@description 关闭连接
-
@date 2021/4/24 15:27
-
@author cunyu1943
-
@version 1.0
*/
public static void close(PreparedStatement preparedStatement, Connection connection) {
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
/**
-
@param resultSet 结果集
-
@param preparedStatement 预声明对象
-
@param connection 连接对象
-
@return
-
@description 关闭连接
-
@date 2021/4/24 15:28
-
@author cunyu1943
-
@version 1.0
*/
public static void close(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
事务 4 大特性
事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行结果必须使数据库从一种一致性状态切换到另一中一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。事务能够在数据库提交工作时确保要么所有修改都保存,要么所有修改都不保存。即事务是逻辑上的一组操作,要么都执行,要么都不执行。
- 原子性(Atomicity)
原子性是整个数据库事务中不可分割的工作单位,只有事务中的所有的数据库操作都执行成功,才代表整个事务成功,如果其中任一环节执行失败,那么就算已经执行成功的 SQL 语句也必须撤销,回滚到事务执行前的状态。即原子性能够保证 动作要么全部完成,要么完全不起作用。 即事务是最小的执行单位,不允许分割。
- 一致性(Consistency)
指事务将数据库从一种一致性状态变为另一种一致性状态。在事务开始前后,数据库的完整性约束未被破坏。在事务执行前后,数据能够保持一致,多个事务对统一数据读取的结果相同。
- 隔离性(Isolation)
并发访问数据库时,隔离性要求每个读写事务对其他事务的操作对象能够相互分离,即一个用户的事务不被其他事务所干扰,各并发事务间数据库是独立的;
- 持久性(Durability)
表示事务一旦被提交,其结果就是永久性的,它对数据库中数据的改变是持久的,即便数据库发生故障也不应该对其产生影响;
脏读、幻读 & 不可重复读
了解事务隔离级别之前,先来看看这几个读的概念:
- 脏读(Dirty Read)
表示某一事务已经更新了一份数据,另一个事务在此时读取了同一份数据。当前一个事务撤销操作后,就会导致后一个事务所读取的数据不正确。
- 幻读(Phantom Read)
在一个事务的两次查询中数据量不一致,假如有一个事务查询了几列数据,同时另一个事务中在此时查询了新的数据,则查询事务在后续查询中,就会发现数据比最开始的查询数据更丰富。
- 不可重复读(Non-repeatable Read)
一个事务中两次查询数据不一致,有可能是因为两次查询过程中插入了一个更新原有数据的事务。
注意:不可重复读和幻读的区别在于:
不可重复读的重点在于修改, 比如多次读取一条记录发现其中某些列的值被修改,而 幻读的重点在于新增或删除,比如多次读取一条记录发现记录增多或减少了。
隔离级别
SQL 标准定义了 4 个隔离级别,隔离级别从低到高分别是:
- READ-UNCOMMITTED(读取未提交)
最低的隔离级别,允许读取尚未提交的数据变更,可能导致脏读、幻读或不可重复读。
- READ-COMMITTED(读取已提交)
允许读取并发事务已经提交的数据,能够阻止脏读,但可能导致幻读或不可重复读。
- REPEATABLE-READ(可重复读)
对同一字段的多次读取结果时一致的,除非数据是被本身事务自己所修改,能够阻止脏读和不可重复读,但可能导致幻读。
- SERIALIZABLE(可串行化)
最高的隔离级别,完全服从 ACID 的隔离级别,所有事务依次逐个执行,这样事务之间就完全不可能产生干扰,能够防止脏读、幻读以及不可重复读。
以下是 SQL 隔离级别和各种读之间的关系:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
| — | — | — | — |
| READ-UNCOMMITTED
| ✔ | ✔ | ✔ |
| READ-COMMITTED
| ❌ | ✔ | ✔ |
| REPEATABLE-READ
| ❌ | ❌ | ✔ |
| SERIALIZABLE
| ❌ | ❌ | ❌ |
实例
关于回滚,主要涉及 Connection
对象,常用的三个方法如下:
| 返回值 | 方法 | 描述 |
| — | — | — |
| void
| setAutoCommit(boolean autoCommit)
| 设定连接的自动提交模式,true
表示自动提交,false
表示手动提交 |
| void
| commit()
| 使上次提交/回滚以来所做的所有更改成为永久更改,并释放此 Connection
对象当前持有的所有数据库锁 |
| void
| rollback()
| 撤销当前十五中所做的所有更改,并释放此 Connection
对象当前持有的所有数据库锁 |
以下是一个回滚实例,我们当我们第一次插入一条数据时,由于是新数据,所以不会报错,但是如果我们执行一次程序之后再次执行,此时按理来说就会报错,因为插入的数据重复,这时候利用事务就可以十分方便的解决这个问题,我们设置插入出错就回滚到未出错之前的状态,这样就能保证插入数据不会报错了。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
-
@author : cunyu
-
@version : 1.0
-
@className : AffairTest
-
@date : 2021/4/23 22:35
-
@description : 事务
*/
public class AffairTest {
public static void main(String[] args) {
try {
Class.forName(“com.mysql.cj.jdbc.Driver”);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url = “jdbc:mysql://localhost:3306/javalearning?characterEncoding=UTF-8”;
String username = “root”;
String password = “12345”;
String insertString = “INSERT INTO students VALUES (?,?,?,?,?)”;
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = DriverManager.getConnection(url, username, password);
// 关闭自动提交
connection.setAutoCommit(false);
preparedStatement = connection.prepareStatement(insertString);
System.out.println(“连接成功”);
// 依次插入数据
preparedStatement.setLong(1, 401);
preparedStatement.setString(2, “小紫”);
preparedStatement.setInt(3, 0);
preparedStatement.setLong(4, 4);
preparedStatement.setLong(5, 88);
preparedStatement.executeUpdate();
// 如果没有出错,则提交事务
connection.commit();
System.out.println(“插入数据成功”);
} catch (SQLException throwables) {
// 一旦出错,则回滚事务
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
// 最后关闭连接
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
除了上述回滚的方式外,JDBC 还支持设置保存点的方式,我们可以使用事务回滚到指定的保存点,主要涉及的方法如下:
-
setSavepoint(String savePointName)
:创建新的保存点,返回一个SavePoint
对象; -
rollback(String savePointName)
:回滚到指定保存点;
简介
当我们使用多线程时,每个线程如果都需要连接数据库来执行 SQL 语句,那么每个线程都得创建一个连接,然后在使用之后关闭。这个创建和关闭连接的过程是十分耗时的,一旦多线程并发时,就容易导致系统卡顿。针对这一问题,提出使用数据库连接池。数据库连接池,其实就相当于一个集合,是一个存放数据库连接的容器。当我们的系统初始化好之后,集合就被创建,集合中会申请一些连接对象,当用户来访问数据库时,从集合中获取连接对象,一旦用户访问完毕,就将连接对象返还给容器。
使用数据库连接池的优点:一来是节约资源,二来提高了用户访问的效率。
常用数据库连接池
C3P0
- 导包
首先需要导包,先去下载 C3P0 对象的 jar 包,下载地址:https://sourceforge.net/projects/c3p0/,然后将其中的如下两个包导入;
- 定义配置文件
创建 C3P0 对应的配置文件,注意:配置文件一般放在 src
路径下,而且文件的名称要必须为以下其中的一个:
先自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以扫码领取!
最后
面试题文档来啦,内容很多,485页!
由于笔记的内容太多,没办法全部展示出来,下面只截取部分内容展示。
1111道Java工程师必问面试题
MyBatis 27题 + ZooKeeper 25题 + Dubbo 30题:
Elasticsearch 24 题 +Memcached + Redis 40题:
Spring 26 题+ 微服务 27题+ Linux 45题:
Java面试题合集:
则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
因此收集整理了一份《Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-q2bn3iuq-1711369893665)]
[外链图片转存中…(img-FuDnVGuw-1711369893665)]
[外链图片转存中…(img-2iA4wTzf-1711369893666)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以扫码领取!
最后
面试题文档来啦,内容很多,485页!
由于笔记的内容太多,没办法全部展示出来,下面只截取部分内容展示。
1111道Java工程师必问面试题
[外链图片转存中…(img-x2ci9BbZ-1711369893666)]
MyBatis 27题 + ZooKeeper 25题 + Dubbo 30题:
[外链图片转存中…(img-UifX83Ll-1711369893667)]
Elasticsearch 24 题 +Memcached + Redis 40题:
[外链图片转存中…(img-XzmOD5Lb-1711369893667)]
Spring 26 题+ 微服务 27题+ Linux 45题:
[外链图片转存中…(img-mOcLeJq9-1711369893667)]
Java面试题合集:
[外链图片转存中…(img-9MDJai4w-1711369893667)]
需要更多Java资料的小伙伴可以帮忙点赞+关注,点击传送门,即可免费领取!