JDBC

JDBC

1 JDBC介绍

JDBC: java database connectivity
"编写java程序,实现任意一个DBMS软件的数据进行增删改查,都需要使用JDBC"
JDBC是sun公司发布的一套关于数据库的规范
JDBC实际是一套接口,各个数据库厂商都需要实现这个接口。实现接口中的方法。
好处:程序员只需要学会JDBC接口,就可以调用各个数据库厂商的内容,轻松实现增删改查
各个数据库厂商需要提供JDBC接口的实现,这些实现称之为驱动。

2 获取DBMS连接

获取特定的DBMS的连接

-- 1.用户名
-- 2.密码
-- 3.数据库的ip和端口号和数据库名称
-- 4.mysql驱动

2.1 获取连接

  • 导入jar包,并且添加到类路径下

image-20250721114508308

  • 编写代码
public static void main(String[] args) {
        // 用户名
        String username = "root";
        // 密码
        String password = "root";
        // 数据库的ip和端口号和数据库名称
        String url = "jdbc:mysql://localhost:3306/mydb";
        
        
        Connection connection = null;
        try {
            // 注册驱动
            DriverManager.registerDriver(new Driver());
            // 获取连接
            connection = DriverManager.getConnection(url, username, password);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            if (connection != null){
                // 关闭连接
                try {
                    connection.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        System.out.println(connection);
    }

2.2 注册驱动代码优化

在Driver类中的静态代码块中已经注册过驱动

image-20250721144829447

我们只需要让类加载到内存中,就会自动调用静态代码块去注册驱动

image-20250721144901458

2.3 工具类封装获取连接

在src目录下创建jdbc.properties文件

jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql:///mydb
jdbc.driver=com.mysql.cj.jdbc.Driver

编写代码

package cn.javas.utils;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @className: DBUtil
 * @description:
 * @author: gfs
 * @date: 2025/7/21 14:50
 * @version: 0.1
 * @since: jdk17
 */
public class DBUtil {
    private static Properties properties = new Properties();
    
    private DBUtil(){}
    
    static {
        try {
            // 读取文件  在静态代码块中只会读取一次
            properties.load(new FileInputStream("src\\jdbc.properties"));
            // 注册驱动
            Class.forName(properties.getProperty("jdbc.driver"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取连接
     */
    public static Connection getConnection(){
        
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(properties.getProperty("jdbc.url"), properties.getProperty("jdbc.username"), properties.getProperty("jdbc.password"));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return connection;
    }

    /**
     * 关闭连接
     */
    public static void release(Connection connection){
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

3 CRUD

3.1 添加 insert

    /**
     * 添加用户
     * @return  影响数据库的行数
     */
    int addUser();

@Override
    public int addUser() {
        // 1. 获取数据库连接
        Connection connection = DBUtil.getConnection();

        // 2. 编写sql语句
        String sql = "insert into user (username,age,password,balance) values('张翠山',20,'345',2000.1)";
        int row = 0;
        PreparedStatement preparedStatement = null;
        try {
            // 3. 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 4. 指定sql语句,获取返回结果 -- 数据库受影响的行数
            row = preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 5. 释放资源
            DBUtil.release(connection,preparedStatement);
        }
        return row;
    }

优化代码

上面的代码每次添加的都是一个人,没有实际意义

  • 定义实体类
package cn.javasm.entity;

import lombok.Data;
import lombok.experimental.Accessors;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * @className: User
 * @description:
 * @author: gfs
 * @date: 2025/7/21 15:16
 * @version: 0.1
 * @since: jdk17
 */
@Data
@Accessors(chain = true)
public class User {
    /**
     * 用户id
     */
    private Long id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 密码
     */
    private String password;

    /**
     * 头像
     */
    private String image;

    /**
     * 存款
     */
    private BigDecimal balance;

    /**
     * 注册时间
     */
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;


}

  • UserDao接口中
  /**
     * 添加用户
     * @param user 添加的用户对象
     * @return  影响数据库的行数
     */
    int addUser(User user);
  • UserDao实现类中
 @Override
    public int addUser(User user) {
        // 获取数据库连接
        Connection connection = DBUtil.getConnection();
        // 编写sql语句
        // ? 代表占位符, 在jdbc中一个?占一个位置,代表一个数据
        String sql = "insert into user (username,age,password) values(?,?,?)";
        PreparedStatement preparedStatement = null;
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 给?赋值
            preparedStatement.setString(1, user.getUsername());
            preparedStatement.setInt(2,user.getAge());
            preparedStatement.setObject(3,user.getPassword());

            // 执行sql语句,获取结果
            int row = preparedStatement.executeUpdate();
            return row;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放连接
            DBUtil.release(connection,preparedStatement);
        }
    }

3.2 删除 delete

  • UserDao接口中
    /**
     * 根据id删除用户
     * @param id 用户的id
     * @return  影响数据库的行数
     */
    int deleteUserById(Long id);
  • UserDapImpl类中
@Override
    public int deleteUserById(Long id) {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 编写sql语句
        String sql = "delete from user where id = ?";
        PreparedStatement preparedStatement = null;
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 给?赋值
            preparedStatement.setLong(1,id);
            // 执行sql语句,返回结果
            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放资源
            DBUtil.release(connection,preparedStatement);
        }
    }

3.3 更新 update

update user set username = ?,age = ?,password = ? ,image = ? where id = ?
  • UserDao接口中
    /**
     * 更新用户
     * @param user 用户信息
     * @return 影响数据库的行数
     */
    int updateUserById(User user);
  • UserDaoImpl实现类中
 @Override
    public int updateUserById(User user) {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 编写sql语句
        String sql = "update user set username = ?,age = ?,password = ? ,image = ? where id = ?";
        PreparedStatement preparedStatement = null;
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 给?赋值
            preparedStatement.setString(1,user.getUsername());
            preparedStatement.setInt(2,user.getAge());
            preparedStatement.setString(3,user.getPassword());
            preparedStatement.setString(4,user.getImage());
            preparedStatement.setLong(5,user.getId());
            // 执行sql语句,返回结果
            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放资源
            DBUtil.release(connection,preparedStatement);
        }
    }

3.4 查询 select

3.4.1 根据id查询

  • UserDao接口中
    /**
     * 根据id查询用户
     * @param id  用户的id
     * @return 查询的用户
     */
    User queryUserById(Long id);
  • UserDaoImpl实现类中
@Override
    public User queryUserById(Long id) {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 编写sql
        String sql = "select * from user where id = ?";
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 给?赋值
            preparedStatement.setLong(1,id);
            // 执行sql语句,获取结果集
            resultSet = preparedStatement.executeQuery();
            // next() 返回true代表有下一个记录
            if (resultSet.next()){
                // 获取记录的数据
                // 根据表中的字段位置获取
                long uid = resultSet.getLong(1);
                // 根据表中的字段名称获取
                String username = resultSet.getString("username");
                int age = resultSet.getInt(3);
                String password = resultSet.getString(4);
                String image = resultSet.getString("image");
                BigDecimal balance = resultSet.getBigDecimal(6);
                LocalDateTime createTime = (LocalDateTime) resultSet.getObject(7);
                LocalDateTime updateTime = (LocalDateTime) resultSet.getObject(8);
                // 把查询出来的结果封装成一个对象并返回
                User user = new User().setImage(image).setId(uid).setUsername(username).setAge(age).setPassword(password).setBalance(balance).setCreateTime(createTime).setUpdateTime(updateTime);
                return user;
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.release(connection,preparedStatement,resultSet);
        }
        return null;
    }

3.4.2 查询所有

  • UserDao接口中
    /**
     * 查询所有
     * @return 所有的用户
     */
    List<User> queryAll();
  • UserDaoImpl实现类中
@Override
    public List<User> queryAll() {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 编写sql语句
        String sql = "select * from user";
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        // 创建集合
        List<User> list = new ArrayList<>();
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 执行sql语句,获取结果集
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                // 获取数据,封装对象
                list.add(getUser(resultSet));
            }
            return list;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.release(connection,preparedStatement,resultSet);
        }
    }

3.4.3 分页查询

后端需要知道当前请求的是哪一页,每一页显示几条数据

前端需要知道当前页的数据以及页面的总数量

select * from user limit ?,?
第一个?:  (page - 1) * pageSize
第二个?:  pageSize

select count(*) from user;
  • UserDao接口中
    /**
     * 查询数据的总数量
     * @return 数据的总数量
     */
    int queryUserCount();

    /**
     * 分页查询
     * @param page     第几页
     * @param pageSize 一页显示多少
     * @return  分页查询后的数据
     */
    List<User> queryUserByPage(int page,int pageSize);
  • UserDaoImpl中
@Override
    public List<User> queryUserByPage(int page, int pageSize) {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 编写sql语句
        String sql = "select * from user limit ?,?";
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<User> list = new ArrayList<>();
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 给?赋值
            preparedStatement.setInt(1,(page - 1) * pageSize);
            preparedStatement.setInt(2,pageSize);
            // 执行sql语句,返回结果集
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                list.add(getUser(resultSet));
            }
            return list;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放资源
            DBUtil.release(connection,preparedStatement,resultSet);
        }
    }

3.4.4 多表查询

-- 查询用户的id,用户名,用户年龄,用户角色,用户的部门
SELECT
u.id,u.username,u.age,r.role_name,d.dname
from 
user u,role r,mydb2.dept d
where 
u.rid = r.id
AND
u.deptno = d.deptno

实现方式一:

  • 定义结果类
@Data
@Accessors(chain = true)
public class UserRoleDeptVo {

    /**
     * 用户id
     */
    private Long id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 用户年龄
     */
    private Integer age;

    /**
     * 角色名称
     */
    private String roleName;

    /**
     * 部门名称
     */
    private String dname;
}

  • UserDao接口中添加抽象方法
    /**
     * 查询用户角色和部门
     * @return 查询结果
     */
    List<UserRoleDeptVo> queryUserAndRoleAndDept();
  • UserDaoImpl实现类中
@Override
    public List<UserRoleDeptVo> queryUserAndRoleAndDept() {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 编写sql语句
        String sql = "SELECT\n" +
                "u.id,u.username,u.age,r.role_name,d.dname\n" +
                "from \n" +
                "user u,role r,mydb2.dept d\n" +
                "where \n" +
                "u.rid = r.id\n" +
                "AND\n" +
                "u.deptno = d.deptno";
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<UserRoleDeptVo> list = new ArrayList<>();
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 执行sql语句,获取结果集
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                // 获取数据
                long uid = resultSet.getLong(1);
                String username = resultSet.getString(2);
                int age = resultSet.getInt(3);
                String roleName = resultSet.getString(4);
                String dname = resultSet.getString(5);
                // 创建对象并赋值
                UserRoleDeptVo userRoleDeptVo = new UserRoleDeptVo().setId(uid).setUsername(username).setAge(age).setRoleName(roleName).setDname(dname);
                list.add(userRoleDeptVo);
            }
            return list;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放资源
            DBUtil.release(connection,preparedStatement,resultSet);
        }
    }

实现方式二

  • 定义 Role类
@Data
@Accessors(chain = true)
public class Role {

    /**
     * 用户id
     */
    private Long id;

    /**
     * 角色名称
     */
    private String roleName;

    /**
     * 角色描述
     */
    private String roleDesc;
}
  • 定义Dept类
@Data
@Accessors(chain = true)
public class Dept {

    /**
     * 部门id
     */
    private Integer deptno;

    /**
     * 部门名称
     */
    private String dname;

    /**
     * 部门位置
     */
    private String loc;
}

  • 定义UserRoleDept类
@Data
@Accessors(chain = true)
public class UserRoleDept {
    /**
     * 用户
     */
    private User user;
    /**
     * 角色
     */
    private Role role;
    /**
     * 部门
     */
    private Dept dept;
}

  • UserDao接口中
    /**
     * 查询用户角色部门信息
     */
    List<UserRoleDept> findUserAndRoleAndDept();
  • UserDaoImpl类中
    @Override
    public List<UserRoleDept> findUserAndRoleAndDept() {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 编写sql语句
        String sql = "SELECT\n" +
                "u.*,r.*,d.*\n" +
                "from \n" +
                "user u,role r,mydb2.dept d\n" +
                "where \n" +
                "u.rid = r.id\n" +
                "AND\n" +
                "u.deptno = d.deptno";
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<UserRoleDept> list = new ArrayList<>();
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(sql);
            // 执行sql语句
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                User user = getUser(resultSet);
                long rid = resultSet.getLong(11);
                String roleName = resultSet.getString(12);
                String roleDesc = resultSet.getString(13);
                int deptno = resultSet.getInt(14);
                String dname = resultSet.getString(15);
                String loc = resultSet.getString("loc");
                Role role = new Role().setId(rid).setRoleName(roleName).setRoleDesc(roleDesc);
                Dept dept = new Dept().setDeptno(deptno).setDname(dname).setLoc(loc);
                UserRoleDept userRoleDept = new UserRoleDept().setUser(user).setRole(role).setDept(dept);
                list.add(userRoleDept);
            }
            return list;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放资源
            DBUtil.release(connection,preparedStatement,resultSet);
        }
    }

3.4.5 根据条件查询并分页

-- 条件
1 根据用户名模糊查询
2 根据年龄在区间范围内查询  minAge  maxAge
3 create_time区间范围内查询    beginTime  endTime

-- 没有条件
select * from user limit ?,?

-- 用户名
select * from user where username like ? limit ?,?

-- 用户名 年龄
select * from user where username like ? AND age between ? AND ? limit ?,?

-- 用户名 年龄 时间
select * from user where username like ? AND age between ? AND ? AND create_time between ? AND ? limit ?,?



-- 没有条件
select count(*) from user;

-- 用户名
select count(*) from user where username like ?;

-- 用户名年龄
select count(*) from user where username like ? AND age between ? AND ?


-- 动态拼接sql
where username like ? AND age between ? AND ? AND create_time between ? AND ?
  • UserDao接口中
    /**
     * 按照条件分页查询
     * @param searchParamVo  查询条件
     * @param page  第几页
     * @param pageSize  每页显示的数量
     * @return 结果
     */
    List<User> queryUserByParamAndPage(SearchParamVo searchParamVo,int page,int pageSize);

    /**
     * 按照条件查询总数量
     * @param searchParamVo 条件
     * @return 总数量
     */
    int queryUserByParamCount(SearchParamVo searchParamVo);
  • UserDaoImpl实现类中
package cn.javasm.dao.impl;

import cn.javasm.dao.UserDao;
import cn.javasm.entity.User;
import cn.javasm.utils.DBUtil;
import cn.javasm.vo.SearchParamVo;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @className: UserDaoImpl
 * @description:
 * @author: gfs
 * @date: 2025/7/22 10:07
 * @version: 0.1
 * @since: jdk17
 */
public class UserDaoImpl implements UserDao {
    @Override
    public List<User> queryUserByParamAndPage(SearchParamVo searchParamVo, int page, int pageSize) {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 拼接sql语句
        StringBuilder stringBuilder = new StringBuilder();
        // 拼接sql语句前面固定的内容
        stringBuilder.append("select * from user ");
        // 动态拼接sql
        appendSql(searchParamVo,stringBuilder);

        stringBuilder.append(" limit ?,?");

        System.out.println(stringBuilder);

        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<User> list = new ArrayList<>();
        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(stringBuilder.toString());
            int count = setParamterValue(searchParamVo, preparedStatement);
            // 给limit?赋值
            preparedStatement.setInt(count++,(page - 1) * pageSize);
            preparedStatement.setInt(count,pageSize);

            // 执行sql语句,获取结果集
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                list.add(getUser(resultSet));
            }
            return list;

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放资源
            DBUtil.release(connection,preparedStatement,resultSet);
        }
    }

    private int setParamterValue(SearchParamVo searchParamVo,PreparedStatement preparedStatement) throws SQLException {
        String nameParam = searchParamVo.getNameParam();
        Integer minAge = searchParamVo.getMinAge();
        Integer maxAge = searchParamVo.getMaxAge();
        String beginTime = searchParamVo.getBeginTime();
        String endTime = searchParamVo.getEndTime();
        // 记录参数的位置
        int count = 1;
        // 给?赋值
        if (nameParam != null && !nameParam.isBlank()){
            preparedStatement.setString(count++,"%" + nameParam + "%");
        }
        if (minAge != null){
            preparedStatement.setInt(count++,minAge);
            preparedStatement.setInt(count++,maxAge);
        }
        if (beginTime != null && !beginTime.isBlank()){
            preparedStatement.setString(count++,beginTime);
            preparedStatement.setString(count++,endTime);
        }
        return count;
    }

    private void appendSql(SearchParamVo searchParamVo,StringBuilder stringBuilder){
        // 获取搜索条件
        String nameParam = searchParamVo.getNameParam();
        Integer minAge = searchParamVo.getMinAge();
        String beginTime = searchParamVo.getBeginTime();
        // 判断是否已经添加过where   true代表添加过
        boolean flag = false;

        if (nameParam != null && !nameParam.isBlank()){
            stringBuilder.append(" WHERE ");
            stringBuilder.append(" username like ? AND");
            flag = true;
        }
        if (minAge != null){
            if (!flag){
                stringBuilder.append(" WHERE ");
                flag = true;
            }
            stringBuilder.append(" age between ? AND ? AND");
        }
        if (beginTime != null && !beginTime.isBlank()){
            if (!flag){
                stringBuilder.append(" WHERE ");
                flag = true;
            }
            stringBuilder.append(" create_time between ? AND ? AND");
        }

        if (flag){
            // 删除最后多余的一个AND
            stringBuilder.delete(stringBuilder.lastIndexOf("AND"),stringBuilder.length());
        }
    }

    private static User getUser(ResultSet resultSet) throws SQLException {
        // 根据表中的字段位置获取
        long uid = resultSet.getLong(1);
        // 根据表中的字段名称获取
        String username = resultSet.getString("username");
        int age = resultSet.getInt(3);
        String password = resultSet.getString(4);
        String image = resultSet.getString("image");
        BigDecimal balance = resultSet.getBigDecimal(6);
        LocalDateTime createTime = (LocalDateTime) resultSet.getObject(7);
        LocalDateTime updateTime = (LocalDateTime) resultSet.getObject(8);
        // 把查询出来的结果封装成一个对象并返回
        User user = new User().setImage(image).setId(uid).setUsername(username).setAge(age).setPassword(password).setBalance(balance).setCreateTime(createTime).setUpdateTime(updateTime);
        return user;
    }

    @Override
    public int queryUserByParamCount(SearchParamVo searchParamVo) {
        // 获取连接
        Connection connection = DBUtil.getConnection();
        // 拼接sql
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("select count(*) from user ");
        appendSql(searchParamVo,stringBuilder);

        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 获取预编译对象
            preparedStatement = connection.prepareStatement(stringBuilder.toString());
            // 给?赋值
            setParamterValue(searchParamVo,preparedStatement);
            // 执行sql,获取结果集
            resultSet = preparedStatement.executeQuery();
            if (resultSet.next()){
                int count = resultSet.getInt(1);
                return count;
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放资源
            DBUtil.release(connection,preparedStatement,resultSet);
        }
        return 0;
    }
}

3.5 新增角色

-- 展示所有的权限菜单
select * from menu;

-- 在角色表中新增一条记录
insert into role (role_name,role_desc) values(?,?)

-- 在中间表中添加角色和菜单的关系
insert into role_menu(rid,mid) values(?,?),(?,?)...

4 DCL(事务)

事务是逻辑上的一组操作,组成这组操作的单元要么同时成功,要么同时失败

准备工作:

create table account(
  id int PRIMARY KEY auto_increment,
	name varchar(200),
	money double
);

insert into account values(null,'zs',1000),(null,'ls',1000),(null,'ww',1000);

4.1 Mysql进行事务管理

  • 手动开启事务
start transaction;  开启事务
commit;  提交事务
rollback; 回滚事务
start transaction; -- 开启事务
update account set money = money - 100 where name = 'zs';
update account set money = money + 100 where name = 'ls';

commit;-- 提交事务

rollback;-- 回滚事务

4.2 事务的特性和隔离级别【面试题】

4.2.1 事务的特性

  • 原子性:原子性是指事物是一个不可分割的工作单位,事务中的操作要么都成功,要么都失败
eg: zs 1000 ls 1000
zs给ls转100
要么都成功 zs 900  ls  1100
要么都失败 zs 1000 ls  1000
  • 一致性:事务前后数据的完整性要保持一致
eg:
zs 1000 ls 1000       总共2000
成功 zs 900 ls 1100    总共2000
失败 zs 1000 ls 1000   总共2000
  • 持久性:指一个事务一旦被提交,它对数据库中的数据改动是永久性的。
  • 隔离性:事务的隔离性是指多个用户并发操作数据库时,一个用户的事务不能被其他用户的事务所影响。多个并发事务之间的数据要相互隔离。简单来说,事务之间互不干扰。

4.2.2 没有隔离性会引发问题

可能出现的问题 含义
脏读 一个事务读取到了另一个事务尚未提交的数据
不可重复读 一个事务中两次读取到的数据内容不一致,这是事务update引起的
幻读 一个事务中两次读取到的数据的数量不一致,这是insert或者delete引起的

4.2.3 事务的隔离级别

级别 名字 隔离级别 脏读 不可重复读 幻读 数据库默认级别
1 读未提交 read uncommitted
2 读已提交 read committed oracle
3 可重复读 repeatable read mysql
4 串行化 serializable

隔离级别越高,安全性越高,性能效率越差

查看当前的隔离级别

select @@transaction_isolation;

临时设置数据库的隔离级别

 set session transaction isolation level 隔离级别

4.2.4 演示脏读

一个事务中读取到了另一个事务没有提交的数据 read uncommitted;

1 开启AB窗口

2 设置A窗口的隔离级别是 读未提交

set session transaction isolation level read uncommitted

3 AB窗口中都开启事务

4 在B中把zs的金额减少100,不要提交事务

5 在A中查询,发现账户内容出现了改动,就是脏读

4.2.5 演示不可重复读

不可重复读是在一个事务里面,同一条sql语句,两次查询的结果不一致

1 开启AB窗口

2 设置A窗口的隔离级别是 读已提交

set session transaction isolation level read committed;

3 AB窗口都开启事务

4 在B中zs金额减少100,不要提交事务

5 在A中查询(避免了脏读)

6 在B中提交事务

7 在A中查看,发现数据改变,出现了不可重复读

mysql默认是可重复读,不会出现不可重复读

4.2.6 演示隔离级别串行化

1 开启AB窗口

2 设置A窗口的隔离级别是串行化

set session transaction isolation level serializable;

3 AB窗口都开启事务

4 在B中zs金额减少100,不要提交事务

5 在A中查询,会发现A中在等待B中的提交事务

6 在B中提交事务后,结果才能在A中查询

4.3 代码级别使用事务

// 开启手动管理事务
connection.setAutoCommit(false);

// 提交事务
connection.commit();

// 回滚事务
connection.rollback();
public class RoleSereviceImpl implements RoleService {
    @Override
    public int insertRoleService(Role role, String[] menuIdArr) {
        Connection connection = DBUtil.getConnection();
        RoleDao roleDao = new RoleDaoImpl(connection);
        try {
            // 开启事务
            connection.setAutoCommit(false);
            // 添加角色
            long rid = roleDao.addRole(role);
//            int i = 1 / 0;
            // 添加中间表
            int row = roleDao.addRoleAndMenu(rid, menuIdArr);
            // 提交事务
            connection.commit();
            return row;
        } catch (Exception e) {
            try {
                // 回滚事务
                connection.rollback();
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e);
        }finally {
            // 释放资源
            DBUtil.release(connection);
        }
    }
}

posted @ 2025-07-23 14:39  小胡coding  阅读(15)  评论(0)    收藏  举报