数据库(3)

第三章.DBUtils工具包

1.准备工作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3YLP4IdL-1659014506282)(D:\Typora\bin\document\img\1656814736530.png)]

2.DBUtils的介绍

1.为什么要学DBUtils:之前我们使用原生jdbc开发,开发步骤很繁琐,降低了我们的开发速度,所以我们需要学DBUtils加快我们的开发效率
2.DBUtils:是一个加速数据库开发的一个工具包,大大的提高我们的开发效率
3.学习DBUtils的三大类:
  QueryRunner:执行sql
  ResultSetHandler:处理结果集
  DButils:DBUtils工具包中的一个工具类,在此类中定义了很多事务相关,连接相关的方法


3.QueryRunner

3.1.空参的QueryRunner的介绍以及使用

1.构造:QueryRunner()
2.特点:连接对象需要我们自己维护(自己获取,自己关闭)
3.方法:
  a.int update(Connection conn, String sql, Object... params)->执行sql,针对于增删改操作
    conn:传递连接对象
    sql:传递sql语句->支持占位符 ?
    params:?赋值,写一个参数,就是为第一个?赋值,写第二个参数,就是为第二个?赋值->自动匹配
  b.query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)
    conn:传递连接对象
    sql:传递sql语句->支持占位符
    rsh:处理结果集的方式->不用的实现类,处理结果集的方式不同
    params:?赋值->自动匹配


public class Test01 {
    @Test
    public void add()throws Exception{
        //1.创建QueryRunner对象
        QueryRunner qr = new QueryRunner();
        //2.准备sql
        String sql = "insert into category (cname) values (?)";
        //3.获取连接对象
        Connection connection = DruidUtils.getConnection();
        //4.执行sql
        qr.update(connection,sql,"手机");
        //5.关闭资源
        DruidUtils.close(connection,null,null);
    }

    @Test
    public void delete()throws Exception{
        //1.创建QueryRunner对象
        QueryRunner qr = new QueryRunner();
        //3.获取连接对象
        Connection connection = DruidUtils.getConnection();
        //4.执行sql
        qr.update(connection,"delete from category where cid = ?","104");
        //5.关闭资源
        DruidUtils.close(connection,null,null);
    }
}


3.2.有参QueryRunner的介绍以及使用

1.构造:
  QueryRunner(DataSource ds)
2.特点:
  自动维护连接对象,不用我们自己单独获取,单独关闭
3.方法:
  a.int update( String sql, Object... params)->执行sql,针对于增删改操作
    sql:传递sql语句->支持占位符 ?
    params:?赋值,写一个参数,就是为第一个?赋值,写第二个参数,就是为第二个?赋值->自动匹配
        
  b.query(String sql, ResultSetHandler<T> rsh, Object... params)
    sql:传递sql语句->支持占位符
    rsh:处理结果集的方式->不用的实现类,处理结果集的方式不同
    params:?赋值->自动匹配


public class DruidUtils {
    private static DataSource dataSource = null;
    static {
        try {
            //创建properties集合
            Properties properties = new Properties();
            //读取properties配置文件
            InputStream in = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            properties.load(in);

            //创建Druid的连接池实现类
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    
    
    //定义方法,返回创建好的连接池对象->新添加的方法
    public static DataSource getDataSource(){
        return dataSource;
    }

    //定义方法,用来获取连接
    public static Connection getConnection() {
        Connection connection = null;
        try {
            //从连接池中获取连接对象
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    //关闭资源
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}



public class Test02 {
    @Test
    public void add()throws Exception{
        //创建queryrunner对象
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
        String sql = "insert into category (cname) values (?)";
        //执行
        qr.update(sql,"化妆品");

    }

    @Test
    public void delete()throws Exception{
        //创建queryrunner对象
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
        //执行
        qr.update("delete from category where cid = ?","105");

    }
}


4.ResultSetHandler结果集

4.1.标准的JavaBean

1.私有属性,空参构造,有参构造,get/set,toString


2.细节:
  以后我们在定义javabean的时候,所有的属性类型都要写成引用数据类型
  
  因为包装类或者其他引用数据类型默认值为NULL
      
  如果我们的主键是自增的
  insert into 表名 values (NULL,)
      
  因为包装类中有方法,我们将来如果想要处理数据,我们可以调用包装类中的方法取处理,比较方便


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hfGGKl1C-1659014506283)(D:\Typora\bin\document\img\1656818321166.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o8fzMO5k-1659014506283)(D:\Typora\bin\document\img\1656818841557.png)]

4.2.BeanHandler

1.作用:将查询出来的结果集中的第一行数据封装成javabean对象
2.方法:
  query(String sql, ResultSetHandler<T> rsh, Object... params)>有参QueryRunner时使用
  query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)->空参QueryRunner时使用
3.构造:
  BeanHandler(Class type)
  传递的class对象其实就是我们想要封装的javabean类的class对象
  想将查询出来的数据封装成哪个javabean对象,就写哪个javabean的class对象
4.怎么理解:
  将查询出来的数据为javabean中的成员变量赋值


 public class Test01 {
    @Test
    public void beanHandler()throws Exception{
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
        String sql = "select * from category";
        Category category = qr.query(sql, new BeanHandler<>(Category.class));
        System.out.println(category);
    }
}


4.3.BeanListHandler

1.作用:将查询出来的结果每一条数据都封装成一个一个的javabean对象,将这些javabean对象放到List集合中
2.构造:
  BeanListHandler(Class type)
  传递的class对象其实就是我们想要封装的javabbean类的class对象
  想将查询出来的数据封装成哪个javabean对象,就写哪个javabean的class对象
      
3.怎么理解:
  将查询出来的多条数据封装成多个javabean对象,将多个javabean对象放到List集合中


    @Test
    public void beanListHandler()throws Exception{
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
        String sql = "select * from category";
        List<Category> list = qr.query(sql, new BeanListHandler<>(Category.class));
        for (Category category : list) {
            System.out.println(category);
        }
    }


4.4.ScalarHandler

1.作用:主要是处理单值的查询结果的,执行的select语句,结果集只有12.构造:
  ScalarHandler(int columnIndex)->不常用->指定第几列
  ScalarHandler(String columnName)->不常用->指定列名
  ScalarHandler()->常用
3.注意:
  ScalarHandler和聚合函数使用更有意义


    @Test
    public void scalarHandler()throws Exception{
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
        //String sql = "select * from category";
        //Object query = qr.query(sql, new ScalarHandler("cname"));

        String sql = "select count(*) from category";
        Object o = qr.query(sql, new ScalarHandler<>());
        System.out.println(o);
    }


4.5.ColumnListHandler

1.作用:将查询出来的结果中的某一列数据,存储到List集合中
2.构造:
  ColumnListHandler(int columnIndex)->指定第几列
  ColumnListHandler(String columnName)->指定列名
  ColumnListHandler()-> 默认显示查询结果中的第一列数据
3.注意:
 ColumnListHandler可以指定泛型类型,如果不指定,返回的List泛型就是Object类型


    @Test
    public void columnListHandler()throws Exception{
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
        String sql = "select * from category";
        //List<Category> list = qr.query(sql, new ColumnListHandler<Category>(2));
        //List<Category> list = qr.query(sql, new ColumnListHandler<Category>("cname"));
        List<Category> list = qr.query(sql, new ColumnListHandler<Category>());
        System.out.println(list);
    }


第四章.事务

1.事务

1.1.事务_转账分析图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZKVNRK2w-1659014506285)(D:\Typora\bin\document\img\1656828914759.png)]

CREATE TABLE account(
  `name` VARCHAR(10),
  money INT
);


1.2.实现转账(不加事务)

public class Test01 {
    public static void main(String[] args)throws Exception {
        //1.创建QueryRunner对象
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        //2.准备sql
        String outMoney = "update account set money = money-1000 where name = ?";
        String inMoney = "update account set money = money+1000 where name = ?";

        //3.执行sql

        qr.update(outMoney,"涛哥");

        //System.out.println(1/0);

        qr.update(inMoney,"彤彤");
    }
}



1.3.事务的介绍

1.作用:事务是用于管理一组操作的(多条sql),使这组操作要么全成功,要么全失败
2.注意1:
  我们mysql是自带事务管理的,一次只管理一条sql,所以如果我们想要让事务管理一组操作,需要将mysql自带事务关闭,开启手动事务
      
3.如何手动操作事务:Connection
  a.关闭mysql自带事务,开启手动事务
    setAutoCommit(false)
  b.提交事务:->一旦事务提交,数据将永久保存,还原不回去了
    commit()
  c.回滚事务: -> 将数据还原
    rollback()
4.注意2: 要想让手动事务成功管理操作,那么三个方法需要用同一条Connection连接对象调用


1.4.DBUtils实现转账(添加事务)

public class Test02 {
    public static void main(String[] args)throws Exception {
        //1.创建QueryRunner对象
        QueryRunner qr = new QueryRunner();

        //获取连接
        Connection connection = DruidUtils.getConnection();

        try{
            //开启手动事务,关闭mysql自动事务
            connection.setAutoCommit(false);


            //2.准备sql
            String outMoney = "update account set money = money-1000 where name = ?";
            String inMoney = "update account set money = money+1000 where name = ?";

            //3.执行sql

            qr.update(connection,outMoney,"涛哥");

            System.out.println(1/0);

            qr.update(connection,inMoney,"彤彤");

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

            System.out.println("转账成功");
        }catch (Exception e){
            //回滚事务
            connection.rollback();
            System.out.println("转账失败");
            e.printStackTrace();
        }finally {
            //关闭资源
            DruidUtils.close(connection,null,null);
        }


    }
}



1.5.mysql中操作事务_了解

开启事务: begin
提交事务: commit
回滚事务: rollback


-- 开启事务
BEGIN;


UPDATE account SET money = money-1000 WHERE `name` = '涛哥';

UPDATE account SET money = money+1000 WHERE `name` = '彤彤';


-- 提交事务
COMMIT;

-- 回滚事务
ROLLBACK;


1.6.分层思想介绍以及架构搭建

三层架构思想:  
  表现层(controller)  : 接收请求,根据不同的结果,给页面做不同的响应  
  业务层(service) : 处理业务逻辑判断,计算     
  持久层(dao): 专门和数据库打交道的,数据库操作在dao层写


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z4Gazgkd-1659014506286)(D:\Typora\bin\document\img\1656831806624.png)]

创建package
   1.包名都要是小写
   2.公司域名倒着写开头

com.atguigu.controller -> 表现层类
com.atguigu.service->业务层类
com.atguigu.dao-> 数据库相关类
com.atguigu.pojo -> 装javabean
com.atguigu.utils -> 装工具类


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pl7Yy8g3-1659014506286)(D:\Typora\bin\document\img\1656833146717.png)]

1.6.1.转账_表现层实现
public class AccountController {
    public static void main(String[] args) {
        //1.创建Scanner对象
        Scanner sc = new Scanner(System.in);
        //2.输入出钱的姓名
        System.out.println("请输入出钱人姓名:");
        String outName = sc.next();
        //3.输入收钱的姓名
        System.out.println("请输入收钱人姓名:");
        String inName = sc.next();
        //4.输入转账金额

        System.out.println("请输入转账金额:");
        int money = sc.nextInt();

        //5.调用service层转账方法,传递outName,inName,money
        AccountService accountService = new AccountService();
        accountService.transfer(outName,inName,money);
    }
}



1.6.2.转账_service层实现
public class AccountService {
    /**
     *
     * @param outName  出钱人姓名
     * @param inName   收钱人姓名
     * @param money    转账金额
     *
     */
    public void transfer(String outName, String inName, int money) {
        //调用dao层transfer方法,做出钱,收钱
        AccountDao accountDao = new AccountDao();

        try{
            //出钱
            accountDao.outMoney(outName,money);

            System.out.println(1/0);

            //收钱
            accountDao.inMoney(inName,money);

            System.out.println("转账成功");
        }catch (Exception e){

            System.out.println("转账失败");
            e.printStackTrace();
        }

    }
}



1.6.3.转账_dao层实现
public class AccountDao {
    /**
     *
     * @param outName  出钱人姓名
     * @param money    转账金额
     */
    public void outMoney(String outName, int money) throws SQLException {
        QueryRunner qr = new QueryRunner();
        Connection connection = DruidUtils.getConnection();
        String sql = "update account set money = money-? where name = ?";
        qr.update(connection,sql,money,outName);
        DruidUtils.close(connection,null,null);
    }


    /**
     *
     * @param inName  收钱人姓名
     * @param money   转账金额
     */
    public void inMoney(String inName, int money) throws SQLException {
        QueryRunner qr = new QueryRunner();
        Connection connection = DruidUtils.getConnection();
        String sql = "update account set money = money+? where name = ?";
        qr.update(connection,sql,money,inName);
        DruidUtils.close(connection,null,null);
    }
}



1.6.4.在service层添加事务(传递连接)
public class AccountService {
    /**
     *
     * @param outName  出钱人姓名
     * @param inName   收钱人姓名
     * @param money    转账金额
     *
     */
    public void transfer(String outName, String inName, int money) {
        //调用dao层transfer方法,做出钱,收钱
        AccountDao accountDao = new AccountDao();

        //获取连接
        Connection connection = DruidUtils.getConnection();

        try{
            //开启事务
            connection.setAutoCommit(false);

            //出钱
            accountDao.outMoney(connection,outName,money);

            System.out.println(1/0);

            //收钱
            accountDao.inMoney(connection,inName,money);


            //提交事务
            connection.commit();
            System.out.println("转账成功");
        }catch (Exception e){
            //回滚事务
            try {
                connection.rollback();
                System.out.println("转账失败");
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            DruidUtils.close(connection,null,null);
        }

    }
}



1.6.5.修改dao层代码
public class AccountDao {
    /**
     *
     * @param outName  出钱人姓名
     * @param money    转账金额
     */
    public void outMoney(Connection connection,String outName, int money) throws SQLException {
        QueryRunner qr = new QueryRunner();
        String sql = "update account set money = money-? where name = ?";
        qr.update(connection,sql,money,outName);

    }


    /**
     *
     * @param inName  收钱人姓名
     * @param money   转账金额
     */
    public void inMoney(Connection connection,String inName, int money) throws SQLException {
        QueryRunner qr = new QueryRunner();
        String sql = "update account set money = money+? where name = ?";
        qr.update(connection,sql,money,inName);

    }
}



为啥要分层:
  1.好维护
  2.做到在自己的类中,干自己的事儿,不要干别人的事儿
问题:
  在service层我们获取连接对象,是service层该干的事儿吗?不是昂
      
  那么获取连接对象,不在service层干,那么事务也没法操作了,连接对象也不能传递到dao层了,也无法保证service层和dao层的连接对象是同一个对象了
      
怎么办? 
 1.将获取连接这种事抽离出来,不在service写了
 2.还要保证service层中的连接对象和dao层的连接对象是同一个对象     


2.ThreadLocal

2.1.ThreadLocal基本使用和原理

1.概述:是一个容器
2.特点:
  a.只能存储一个数据,如果存储两个,后面的会把前面的覆盖
  b.一个线程在ThreadLocal中存储数据,其他线程获取不到
  c.一条线程在ThreadLocal中存储的元素,后面只要是属于整个线程中的一部分,都可以共享这个ThreadLocal中的数据
      
3.使用:
  a.创建对象: ThreadLocal<泛型> 名字 = new ThreadLocal<>()
  b.存数据:   set(数据)
  c.获取数据:  get()
      
4.实现原理:
  ThreadLocal底层其实是一个map集合
  key:当前线程
  value:存储的数据
      
  当在ThreadLocal中存储一个数据,此数据直接跟当前线程绑死,所以在当前线程中的任何一个位置都能共享ThreadLocal中的数据,而且是同一个数据


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a8yAa3KK-1659014506288)(D:\Typora\bin\document\img\1656836506624.png)]

public class Test01 {
    public static void main(String[] args) {
        ThreadLocal<String> tl = new ThreadLocal<>();
        tl.set("张三");
        tl.set("李四");

        System.out.println(tl.get());

        new Thread(()-> System.out.println(tl.get())).start();//null
    }
}



2.2.连接对象管理类(小秘书)

**
 * 连接对象管理类
 * Service层的秘书
 */
public class ConnectionManager {
    private static ThreadLocal<Connection> tl = new ThreadLocal<>();

    //定义方法,从连接池中获取连接,存到ThreadLocal中
    public static Connection getConn(){
        //从TheadLocal中获取连接对象
        Connection connection = tl.get();

        if (connection==null){
            connection = DruidUtils.getConnection();
            tl.set(connection);
        }

        return connection;
    }

    //开启事务
    public static void begin() throws SQLException {
        getConn().setAutoCommit(false);
    }

    //提交事务
    public static void commit() throws SQLException {
        getConn().commit();
    }

    //回滚事务
    public static void back() throws SQLException {
        getConn().rollback();
    }

    //关闭连接
    public static void closeConn(){
        Connection conn = getConn();
        DruidUtils.close(conn,null,null);
    }
}



public class AccountService {
    /**
     *
     * @param outName  出钱人姓名
     * @param inName   收钱人姓名
     * @param money    转账金额
     *
     */
    public void transfer(String outName, String inName, int money) {
        //调用dao层transfer方法,做出钱,收钱
        AccountDao accountDao = new AccountDao();

        try{
            //开启事务
            ConnectionManager.begin();

            //出钱
            accountDao.outMoney(outName,money);

            System.out.println(1/0);

            //收钱
            accountDao.inMoney(inName,money);


            //提交事务
            ConnectionManager.commit();
            System.out.println("转账成功");
        }catch (Exception e){
            //回滚事务
            try {
                ConnectionManager.back();
                System.out.println("转账失败");
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally {
           ConnectionManager.closeConn();
        }

    }
}



public class AccountDao {
    /**
     *
     * @param outName  出钱人姓名
     * @param money    转账金额
     */
    public void outMoney(String outName, int money) throws SQLException {
        QueryRunner qr = new QueryRunner();
        String sql = "update account set money = money-? where name = ?";
        qr.update(ConnectionManager.getConn(),sql,money,outName);
    }


    /**
     *
     * @param inName  收钱人姓名
     * @param money   转账金额
     */
    public void inMoney(String inName, int money) throws SQLException {
        QueryRunner qr = new QueryRunner();
        String sql = "update account set money = money+? where name = ?";
        qr.update(ConnectionManager.getConn(),sql,money,inName);

    }
}



3.事务的特性以及隔离级别

3.1.事务特性:ACID

  • 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  • 一致性(Consistency)事务前后数据的完整性必须保持一致。

  • 隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离,正常情况下数据库是做不到这一点的,可以设置隔离级别,但是效率会非常低。

  • 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

3.2 并发访问问题

如果不考虑隔离性,事务存在3中并发访问问题。

  1. 脏读:一个事务读到了另一个事务未提交的数据.

  2. 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。

  3. 虚读 /幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。

3.3 隔离级别:解决问题

  • 数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。
  1. read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。

    a)存在:3个问题(脏读、不可重复读、虚读)。

    b)解决:0个问题

  2. read committed 读已提交,一个事务读到另一个事务已经提交的数据。

    a)存在:2个问题(不可重复读、虚读)。

    b)解决:1个问题(脏读)

  3. repeatable read:可重复读,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交。

    a)存在:1个问题(虚读)。

    b)解决:2个问题(脏读、不可重复读)

    4.serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。

a)存在:0个问题。

b)解决:3个问题(脏读、不可重复读、虚读)

  • 安全和性能对比
    • 安全性:serializable > repeatable read > read committed > read uncommitted
    • 性能 : serializable < repeatable read < read committed < read uncommitted
  • 常见数据库的默认隔离级别:
    • MySql:repeatable read
    • Oracle:read committed

3.4 演示

  • 隔离级别演示参考:资料/隔离级别操作过程.doc【增强内容,了解】

  • 查询数据库的隔离级别

show variables like '%isolation%';
或
select @@tx_isolation;


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F9SUqvOt-1659014506289)(D:\Typora\bin\document\img\h.png)]

  • 设置数据库的隔离级别

    • set session transactionisolation level 级别字符串
    • 级别字符串:readuncommittedread committedrepeatable readserializable
    • 例如:set session transaction isolation level read uncommitted;
  • 读未提交:readuncommitted

    • A窗口设置隔离级别
      • AB同时开始事务
      • A 查询
      • B 更新,但不提交
      • A 再查询?-- 查询到了未提交的数据
      • B 回滚
      • A 再查询?-- 查询到事务开始前数据
  • 读已提交:read committed

    • A窗口设置隔离级别
      • AB同时开启事务
      • A查询
      • B更新、但不提交
      • A再查询?–数据不变,解决问题【脏读】
      • B提交
      • A再查询?–数据改变,存在问题【不可重复读】
  • 可重复读:repeatable read

    • A窗口设置隔离级别
      • AB 同时开启事务
      • A查询
      • B更新, 但不提交
      • A再查询?–数据不变,解决问题【脏读】
      • B提交
      • A再查询?–数据不变,解决问题【不可重复读】
      • A提交或回滚
      • A再查询?–数据改变,另一个事务
  • 串行化:serializable

    • A窗口设置隔离级别
    • AB同时开启事务
    • A查询
      • B更新?–等待(如果A没有进一步操作,B将等待超时)
      • A回滚
      • B 窗口?–等待结束,可以进行操作
- 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 
- 一致性(Consistency)事务前后数据的完整性必须保持一致。
- 隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离,正常情况下数据库是做不到这一点的,可以设置隔离级别,但是隔离级别越高,效率会非常低。
- 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。


如果不考虑隔离性,事务存在3中并发访问问题。(如果隔离级别低,事务跟事务之间有可能互相影响)

1. 脏读:一个事务读到了另一个事务未提交的数据.
2. 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。
3. 虚读 /幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。


总结:
  我们最理想的状态是:一个事务和其他事务互不影响
  
  但是如果不考虑隔离级别的话,就会出现多个事务之间互相影响
  
  而事务互相影响的表现方式为:
    脏读
    不可重复读
    虚读/幻读


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点份炸鸡778

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值