1.mybatis中的连接池

        1.1什么是连接池

                        连接池是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个池子,叫连接池。由应用程序动态地对池中的连接                   进行申请、使用和释放。应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。连接池是面向数据库连的,是为了优                   化数据库连接资源。

        1.2Mybatis中的连接池

                        在 Mybatis 的配置文件中,通过<dataSource type="pooled”>来实 现 Mybatis 中连接池的配置。在mybatis中数据库连接池可以分                   为以下三类:1.UNPOOLED不使用连接池的数据源。2.POOLED使用连接池的数据源。3.JNDI使用JNDI实现数据库连接池

        1.3 UNPOOLED连接过程分析

                        UNPOOLED 不使用连接池的数据源,当 dateSource 的type属性被配置成了UNPOOLED,MyBatis 首先会实例化一个                                  UnpooledDataSourceFactory工厂实例,然后通过.getDataSource() 方法返回一个UnpooledDataSource 实例对象引用

public class UnpooledDataSource implements DataSource {
    private ClassLoader driverClassLoader;
    private Properties driverProperties;
    private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap();
    private String driver;
    private String url;
    private String username;
    private String password;
    private Boolean autoCommit;
    private Integer defaultTransactionIsolationLevel;

    public UnpooledDataSource() {
    }

    public UnpooledDataSource(String driver, String url, String username, String password){
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
    }

    public Connection getConnection() throws SQLException {
        return this.doGetConnection(this.username, this.password);
    }

    private Connection doGetConnection(String username, String password) throws SQLException {
        Properties props = new Properties();
        if(this.driverProperties != null) {
            props.putAll(this.driverProperties);
        }

        if(username != null) {
            props.setProperty("user", username);
        }

        if(password != null) {
            props.setProperty("password", password);
        }

        return this.doGetConnection(props);
    }

    private Connection doGetConnection(Properties properties) throws SQLException {
        this.initializeDriver();
        Connection connection = DriverManager.getConnection(this.url, properties);
        this.configureConnection(connection);
        return connection;
    }
}

        1.4 POOLED 数据源 连接池 

                PooledDataSource: 将java.sql.Connection对象包裹成PooledConnection对象放到了PoolState类型的容器中维护。 MyBatis将连接池             中的PooledConnection分为两种状态: 空闲状态(idle)和活动状态(active),这两种状态的PooledConnection对象分别被存储到                         PoolState容器内的idleConnections和activeConnections两个List集合中

                空闲(idle)状态PooledConnection对象被放置到此集合中,表示当前闲置的没有被使用的PooledConnection集合,调用                                   PooledDataSource的getConnection()方法时,会优先从此集合中PooledConnection对象。

                活动(active)状态的PooledConnection对象被放置到名为activeConnections的ArrayList中,表示当前正在被使用的PooledConnection             集合,调用PooledDataSource的getConnection()方法时,会优先从idleConnections集合中取PooledConnection对象

2.mybatis中的事务和隔离级别

        2.1jdbc中的事务

                Mybatis 框架因为是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC的 setAutoCommit()方法来设置事务提交           方式的。

        2.2mybatis中的事务提交方式

                Mybatis 中事务的提交方式,本质上就是调用 JDBC 的 setAutoCommit()来实现事务控制。@BeforeEach: 在每个方法之前加一下些操作 @AfterEach: 在每个方法之后加一些操作。我们设置了自动提交事务之后,就不需要在进行commit操作了

        2.3事务的回滚

                为了保证数据的一致性我们就需要进行事务的回滚

//用于在测试方法执行之前执行
    @BeforeEach
    public void init()throws Exception{
        // 设置为自动提交
        sqlSession = MybatisUtils.openSession();
        //获取dao的代理对象
        userMapper = sqlSession.getMapper(IUserMapper.class);
    }

    // 在测试结束之后执行
    @AfterEach
    public void destroy()throws Exception{
        //提交事务
        sqlSession.commit();
        //释放资源
        sqlSession.close();
    }

    @Test
    public void testRollback(){
        try{
            User condition = new User();
            condition.setId(1);
            condition.setNickname("尚云科技111299");
            int i = userMapper.update(condition);
            assertEquals(i,1);
            throw new Exception();
        }catch (Exception e){
            e.printStackTrace();
            // 进行数据回滚操作
            sqlSession.rollback();
        }
    }

        2.4事务的隔离级别 

                脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问 这个数据,然后使用了这个数据。

                不可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。

                幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据


脏读

不可重复读

幻读

说明

Read uncommitted

直译就是"读未提交",意思就是即使一个更新语句没有提交,但是别 的事务可以读到这个改变.这是很不安全的。允许任务读取数据库中未提交的数据更改,也称为脏读。

Read committed

×

直译就是"读提交",可防止脏读,意思就是语句提交以后即执行了COMMIT以后,别的事务才能读到这个改变. 只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 

Repeatable read

×

×

直译就是"可以重复读",这是说在同一个事务里面先后执行同一个查询语句的时候,得到的结果是一样的.在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读。

Serializable

×

×

×

直译就是"序列化",意思是说这个事务执行的时候不允许别的事务并发执行. 完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

3.延迟加载策略

        3.1什么是延迟加载

                就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。优点:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。缺点:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

        3.2association实现延迟加载

                1.添加懒加载配置        2.aggressiveLazyLoading开启属性        3.配置每个关联字段的加载方式        4.association实现懒加载

4.使用注解进行开发

4.1 mybatis常用注解说明

        @Insert:实现新增  

        @Update:实现更新  

        @Delete:实现删除  

        @Select:实现查询  

        @Result:实现结果集封装  

        @Results:可以与@Result 一起使用,封装多个结果集  

        @ResultMap:实现引用@Results 定义的封装  

        @One:实现一对一结果集封装  

        @Many:实现一对多结果集封装

        4.2实现基本的CRUD

public interface IAddressDao {
    @Insert("insert into t_address (addr, phone, postcode, user_id) VALUES (#{addr},#{phone},#{postcode},#{userId})")
    int insert(Address address);

    @Delete("delete from t_address where id = #{id}")
    int delete(int id);

    @Update("update t_address set addr = #{addr} where id = #{id}")
    int update(Address address);

    @Select("select * from t_address where id = #{id}")
    Address selectById(int id);
}

        4.3使用Result进行映射 

@Select("select * from t_address where id = #{id}")
    @Results(id = "addressRes", value = {
            //id = true 标志这个字段是主键
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "addr", property = "addr"),
            @Result(column = "phone", property = "mobile"),
    })
    Address selectById(int id);

        4.4注解进行关联查询 

                4.4.1一对一

@Select("select * from t_address where id = #{id}")
    @Results(id = "addressRes", value = {
            //id = true 标志这个字段是主键
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "addr", property = "addr"),
            @Result(column = "phone", property = "mobile"),
            @Result(column = "user_id", property = "user",
                    one = @One(select = "com.tledu.erp.mapper.IUserMapper.selectById", fetchType = FetchType.EAGER))
    })
    Address selectById(int id);

                4.4.2一对多

@Select("select * from t_user where id = #{id}")
    @Results(id = "addressRes", value = {
            //id = true 标志这个字段是主键
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "id", property = "addressList",
                    many = @Many(select = "com.tledu.erp.mapper.IAddressMapper.listByUserId", fetchType = FetchType.EAGER))
    })
    User selectById(int id);

5.缓存       

        Mybatis 中缓存分为一级缓存,二级缓存

        5.1一级缓存

                一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

        5.2二级缓存

                二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。

注意开启二级缓存需要配置<setting name="cacheEnabled" value="true"/>

@Data
public class Address implements Serializable {
    private Integer id;
    private String addr;
    private String phone;
    private String postcode;
    private Integer userId;
    /**
     * 创建用户,关联用户表
     */
    private User user;
}