Java复习之知识点整理(二十八)---MySQL脚本语句,关联查询,聚集函数查询,脏读,不可重复读,幻读,隔离级别 ,悲观锁,乐观锁,表级锁

一、脚本处理sql语句
-------------------------------------------------

    -- 创建脚本文件: 将需要执行的sql语句,写入一个文件即可
        
    -- 脚本文件后缀:  .sql
        
    -- 执行脚本指令:  source d:\\tsSql.sql;
         
    -- 删除子表orders
    drop table if exists orders;
        
    -- 删除customers(主表) 
    drop table if exists customers;
        
    -- 创建 customers 表
    create table customers(id int primary key auto_increment , name varchar(20));
        
    -- 创建 orders 表
    create table orders(id int primary key auto_increment , ordernum varchar(20)
                        price float,
                        cid int);
                            
    -- 给orders 的 cid 添加外检约束(cid必须是customers的id存在)
    alter table orders add constraint foreign key (cid) references customers(id);
        
    -- 添加数据
    insert into customers (name) values ('tom');
    insert into customers (name) values ('tomsen');
    insert into customers (name) values ('tomsenlee');
        
    insert into orders(ordernum, price, cid) values ('no001',100.01,1);
    insert into orders(ordernum, price, cid) values ('no002',100.02,1);
    insert into orders(ordernum, price, cid) values ('no003',100.03,2);
    insert into orders(ordernum, price, cid) values ('no004',100.04,3);
    
    
         
二、关联查询(内连接,左外,右外连接)
-------------------------------------------------------------------------------
    -- 内连接:无条件(笛卡尔积)
    select a.* , b.* from customers a, orders b order by a.id,b.id;
    select a.id as aid , a.name as aName , b.id as bid , b.ordernum as bNum from customers a , orders b order by a.id,b.id;
        
    -- 内连接:有条件(自定义条件)
    select a.* , b.* from customers a, orders b where a.id = b.cid order by a.id;
        
    -- 外连接(左外连接 left outer join 向左看齐, 右外连接 right outer join ,向右看齐)
    -- 空值自动补全,补NULL
    select a.* , b.* from customers a left outer join orders b on a.id = b.cid order by a.id;
       

 /**
     * 测试内连接
     */
    public static void tsInnerJoin()
    {
        
        try {
            Connection conn = Ts01.getConn();
            String sql = "select a.* , b.*  from customers a , orders b " + "where a.id = b.cid and a.id = 1";
            PreparedStatement pt = conn.prepareStatement(sql);
            ResultSet set = pt.executeQuery();
            while(set.next())
            {
                int aid = set.getInt(1);
                String aName = set.getString(2);
                
                int bid = set.getInt(3);
                String bName = set.getString(4);
                float bPrice = set.getFloat(5);
                
                System.out.println(aid + "," + aName  +  "," + bid + "," + bName +"," + bPrice);
            }
            
            set.close();
            pt.close();
            conn.close();
            
            
            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    ---------------------------------------------------------------------------------------------------------
    /**
     * 测试右外连接
     */
    public static void tsRightOutJoin()
    {
        
        try {
            Connection conn = Ts01.getConn();
            String sql = "select a.* , b.*  from customers a right outer join orders b " + "on a.id = b.cid and a.id = 1";
            PreparedStatement pt = conn.prepareStatement(sql);
            ResultSet set = pt.executeQuery();
            while(set.next())
            {
                int aid = set.getInt(1);
                String aName = set.getString(2);
                
                int bid = set.getInt(3);
                String bName = set.getString(4);
                float bPrice = set.getFloat(5);
                
                System.out.println(aid + "," + aName  +  "," + bid + "," + bName +"," + bPrice);
            }
            
            set.close();
            pt.close();
            conn.close();

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    --------------------------------------------------------------------------------------------------------
    /**
     * 测试左外连接
     */
    public static void tsLeftOutJoin()
    {
        
        try {
            Connection conn = Ts01.getConn();
            String sql = "select a.* , b.*  from customers a left outer join orders b " + "on a.id = b.cid and a.id = 1";
            PreparedStatement pt = conn.prepareStatement(sql);
            ResultSet set = pt.executeQuery();
            while(set.next())
            {
                int aid = set.getInt(1);
                String aName = set.getString(2);
                
                int bid = set.getInt(3);
                String bName = set.getString(4);
                float bPrice = set.getFloat(5);
                
                System.out.println(aid + "," + aName  +  "," + bid + "," + bName +"," + bPrice);
            }
            
            set.close();
            pt.close();
            conn.close();
            
            
            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


        
        
三、聚集函数查询
--------------------------------------------------------------------------------
    -- 1.Max/Min 最大最小值查询
    select max(price) form orders;            -- 查询orders表 price 列中的最大值
    select min(price) form orders;            -- 查询orders表 price 列中的最小值
    
    -- 2.avg 平均值查询
    select avg(price) from orders;            -- 查询orders表 price 列的平均值
    
    -- 3.count 统计数值
    select count(price) from orders;        -- 查询orders表 price 列的总个数
    
    -- 4.sum 统计数值
    select sum(price) from orders;            -- 查询orders表 price 列的总和
    
    -- 5.group by 分组查询最大值
    select cid max(price) from orders group by cid;        -- 以cid分组,并查询每组价格的最大值
    
    -- 6.having 对分组之后的值进行过滤,where是对分组之前进行过滤
    select cid max(price) from orders group by cid having max(price) > 100.07;        -- 以cid分组,并查询最大值大于100.07的数据
    
    
四、OLTP / LOAP
----------------------------------------------------------
    OLTP: online transaction process, 在线事务处理
    OLAP: online analyze process, 在线分析处理 
    
    
五、数据库的并发执行容易导致三个现象
-------------------------------------------------------------
    1.脏读:读未提交 -- A事务读取了B事务改写但是未提交的数据,B事务紧接着进行回滚
    2.不可重复读:同一个事务,进行多次连续相同条件的查询,由于查询期间其他事务对数据进行了update操作,导致每次结果都不同。
    3.幻读:不同时刻,进行相同条件的查询,由于查询期间其他事务对数据进行了insert/delete操作,导致数据数量增加或者减少
    
    
六、事务操作的特点
------------------------------------------------------------
    1.独占写锁:写操作一个时刻只能一个事务去写
    2.共享读锁:同一时刻,可以多个事务去读
    
    
七、ANSI(美国国家标准机构) SQL
------------------------------------------------------------
    1.
    2.
    3.
    4.
    
    
八、隔离级别 isolation level
------------------------------------------------------------------
    1. --level 1    读未提交  read uncommitted,可能导致脏读 
    2. --level 2    读已提交  read committed, 避免了脏读,但是依然存在不可重复读和幻读(oracle默认隔离级别)
    3. --level 4    可以重复读 repeatable read, 避免了脏读和不可重复读,但是依然存在幻读(mysql默认隔离级别)
    4. --level 8    串行 serializable。事务之间完整隔离。避免了脏读,不可重复读和幻读
    
    
九、设置事务隔离级别
------------------------------------------------------------------
    set session transaction isolation level read uncommitted;  -- session 表示设置当前会话的隔离级别为uncommitted 可读不提交,当前会话生效
    set global transaction isolation level read committed;  -- global 表示设置全局服务器的隔离级别为committed 可读提交,全局生效
    set transaction isolation level repeatable read;  -- 不写修饰默认为为下一个事务设定隔离级别为repeatable 可重复读,并且仅下一个事务期间生效
    set transaction isolation level serializable;  -- 不写修饰默认为为下一个事务设定隔离级别为serializable 串行化,并且仅下一个事务期间生效
    
    
十、通过mysql客户端验证事务隔离级别(开启两个服务器客户端,进行两个事务A和B)
-------------------------------------------------------------------

 【脏读】
     -- 事务B读取事务A修改但未提交的数据。造成事务B脏读 
    -- 1.设置事务B的隔离级别,可以读取未提交的数据
    set transaction isolation level read uncommitted(读非提交的); 
    -- 2.关闭事务A的自动提交
    set autocommit = 0;
    -- 3.事务A进行更新操作,但不提交
    update customers set name = 'jerry' where id = 1;
    -- 4.事务B进行查询操作
    select * from customers where id = 1; -- 此时就出现了脏读,B读取了A未提交的数据
    
【不可重复读】
    -- 事务B在事务A对数值进行了update操作之前和之后,分别读取数值,发现数值改变
    -- 1.设置事务B的隔离级别,read committed(读提交的).
    set transaction isolation level read committed;
    -- 2.读取数据
    select * from customers where id = 1;
    -- 3.开启事务A的自动提交
    set autocommit = 1;
    -- 4.事务A进行update操作
    update customers set name = 'jerry' where id = 1;
    -- 5.事务B再次执行查询
    select * from customers where id = 1;
    
【幻读】
    -- 事务B在事务A对数值进行了insert操作之前和之后,分别读表,发现表结构改变
    -- 1.设置事务B的隔离级别,repeatable read(可重复读)
    --- mysql中的repeatable read(可重复读)隔离级别,同时避免了不可重复读和幻读
    set transaction isolation level repeatable read;
    -- 2.读取数据
    select * from customers where id = 1;
    -- 3.开启事务A的自动提交
    set autocommit = 1;
    -- 4.事务A进行insert操作
    insert into customers (name) values ('join');
    -- 5.事务B再次执行查询
    select * from customers where id = 1;

【串行化】
    -- 1.事务A设置隔离级别,serializable(串行化)
    set transaction isolation level serializable;
    -- 2.事务A关闭自动提交
    set autocommit = 0;
    -- 3.事务A开启事务
    start transaction;
    -- 4.事务A查询
    select * from customers;
    -- 5.事务B进行insert操作
    insert into customers (name) values ('join');   --- 发现事务B会阻塞,直到事务A进行了commit(当然如果两个事务之前没有数据交叉,共用,是不会阻塞的)    
    -- 6.事务A进行提交
    commit;
    -- 7.事务A查询
    select * from customers;
    
   

/**
     * 测试隔离级别--脏读--事务A
     */
    @Test
    public void tsDirtyRead_A()
    {
        
        try {
            //建立连接
            Connection conn = Ts01.getConn();
            //设置隔离级别
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);            
            String sql = "select * from customers where id = 1";
            PreparedStatement pt = conn.prepareStatement(sql);
            ResultSet set = pt.executeQuery();
            while(set.next())
            {
                String aName = set.getString(2);
                System.out.println("name" + aName);
            }
            
            set.close();
            pt.close();
            conn.close();
            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
------------------------------------------------------------------------------------------------------------
    /**
     * 测试隔离级别--脏读--事务B
     */
    @Test
    public void tsDirtyRead_B()
    {
        
        try {
            //建立连接
            Connection conn = Ts01.getConn();
            //关闭自动提交
            conn.setAutoCommit(false);
            //更新数据
            String sql = "update customers set name = 'xxx' where id = 1";
            PreparedStatement pt = conn.prepareStatement(sql);
            //更新但不提交
            pt.executeUpdate();
            System.out.println("--------------------------");
            pt.close();
            conn.close();
            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    
十二、查询列转行显示
---------------------------------------------------------------
    select * from customers \G;
    
    
十三、悲观锁、乐观锁
---------------------------------------------------------------
【悲观锁】
    -- 不支持并发 :serializable 就是悲观锁
    -- 业务层面实现:增加一个字段 lock 0/1; 
        

【乐观锁】
    -- 支持并发,但是要判断版本之后再进行数据库操作
    -- 业务层面实现: 增加一个字段,version,存放整数值。
    -- 事务开启的时候查询版本号,更新数据和版本号之前先判断版本号和之前查询的是否一致,一致说明没有其事务操作数组。可以更新。否则不可以更新


十四、读操作时上写锁(我读时别人不能写)
-----------------------------------------------------------------
    1.使用serializable隔离级别
    2.单语句添加写锁for update  -- select * from customers where id = 1 for update;
    

十五、查询事务的隔离级别
-----------------------------------------------------------------
    select @@global.tx_isolation;        //查询全局的
    select @@session.tx_isolation;        //查询当前会话的
    select @@tx_isolation;                //查询下一次事务的


十六、行级锁、表级锁
-----------------------------------------------------------------
    【表级锁】
    -- lock tables ..... unlock tables;
    -- lock tables customers read local;     //加锁,其他事务无法使用该表
    -- unlock tables;                         //解锁

    

猜你喜欢

转载自blog.csdn.net/xcvbxv01/article/details/81262135