首页 > 其他 > 详细

MyBatis源码分析------基础篇(二)

时间:2019-02-25 19:16:11      阅读:179      评论:0      收藏:0      [点我收藏+]

 

一,mapper文件解读

1.1,namespace

表空间

作用,关联到接口,区别类似java中的package,其实也可以不用的但是一般情况下,我们都会有的。

举例:

<mapper namespace="com.gupaoedu.mybatis.mapper.TestMapper">
 System.out.println(testMapper.selectByPrimaryKey(1));

 

 

  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from test
    where id = #{id,jdbcType=INTEGER}
  </select>

 

1.2,resultMap/resultType 

技术分享图片

resultType就是我们返回值返回的字段对应的都是实体类的属性,而resultMap对应的就是我们在mapper.xml中定义resultMap进行pojo和相应表字段的对应。

假如是单表的话推荐使用resultType,清晰明了。

假如是resultmap的话就是多表了,因为假如用resulttype的话会创建许多重复的无用的实体类。

1.3,一级缓存

首先我们来看张图:

技术分享图片

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

sqlsession就是会话,每次请求就可以把它当成一个会话。

①、我们在一个 sqlSession 中,对 User 表根据id进行两次查询,查看他们发出sql语句的情况。

@Test
public void testSelectOrderAndUserByOrderId(){
    //根据 sqlSessionFactory 产生 session
    SqlSession sqlSession = sessionFactory.openSession();
    String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID";
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //第一次查询,发出sql语句,并将查询的结果放入缓存中
    User u1 = userMapper.selectUserByUserId(1);
    System.out.println(u1);
     
    //第二次查询,由于是同一个sqlSession,会在缓存中查找查询结果
    //如果有,则直接从缓存中取出来,不和数据库进行交互
    User u2 = userMapper.selectUserByUserId(1);
    System.out.println(u2);
     
    sqlSession.close();
}

 

技术分享图片

②、 同样是对user表进行两次查询,只不过两次查询之间进行了一次update操作。

@Test
public void testSelectOrderAndUserByOrderId(){
    //根据 sqlSessionFactory 产生 session
    SqlSession sqlSession = sessionFactory.openSession();
    String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID";
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //第一次查询,发出sql语句,并将查询的结果放入缓存中
    User u1 = userMapper.selectUserByUserId(1);
    System.out.println(u1);
     
    //第二步进行了一次更新操作,sqlSession.commit()
    u1.setSex("");
    userMapper.updateUserByUserId(u1);
    sqlSession.commit();
     
    //第二次查询,由于是同一个sqlSession.commit(),会清空缓存信息
    //则此次查询也会发出 sql 语句
    User u2 = userMapper.selectUserByUserId(1);
    System.out.println(u2);
     
    sqlSession.close();
}

 

 

技术分享图片

③、总结

  1、第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。 

  2、如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

  3、第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

   技术分享图片

从上面的说明我们可以看到假如现在我们在两次查询中间跟新了表数据,而没有commit,这个时候,第二次查询的时候数据就会从缓存中取值了,这个时候就会产生脏数据了。取的值并不是我们想要的数据。

那既然会有这种情况,mybatis为什么还会默认一级缓存开启呢?

答案很简答,因为一级缓存开启的话会提高性能,并且像这样第一次查询,第二次跟新,第三次在查询相同的数据的操作我们在实际的开发中很少发生的,即使发生了,我们也可以commit提交之后在操作的。

1.4,二级缓存

 ②、二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取。但是一级缓存是基于 sqlSession 的,而 二级缓存是基于 mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。

 技术分享图片

那么二级缓存是如何使用的呢?

  ①、开启二级缓存

  和一级缓存默认开启不一样,二级缓存需要我们手动开启

  首先在全局配置文件 mybatis-configuration.xml 文件中加入如下代码:

<!--开启二级缓存  -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

  其次在 UserMapper.xml 文件中开启缓存

<!-- 开启二级缓存 -->
<cache></cache

 

 

 测试二级缓存和sqlSession 无关

@Test
public void testTwoCache(){
    //根据 sqlSessionFactory 产生 session
    SqlSession sqlSession1 = sessionFactory.openSession();
    SqlSession sqlSession2 = sessionFactory.openSession();
     
    String statement = "com.ys.twocache.UserMapper.selectUserByUserId";
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    //第一次查询,发出sql语句,并将查询的结果放入缓存中
    User u1 = userMapper1.selectUserByUserId(1);
    System.out.println(u1);
    sqlSession1.close();//第一次查询完后关闭sqlSession
     
    //第二次查询,即使sqlSession1已经关闭了,这次查询依然不发出sql语句
    User u2 = userMapper2.selectUserByUserId(1);
    System.out.println(u2);
    sqlSession2.close();
}

可以看出上面两个不同的sqlSession,第一个关闭了,第二次查询依然不发出sql查询语句。因为假如是一级缓存的话是会再次向数据库发出请求数据的。

 

测试二级缓存的存在

 

public class Mybatis_mappertest {
    
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void setup() throws IOException
    {   String resource="SqlMapConfig.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        //主要是生成SqlsessionFactory。
        this.sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    }
    @Test
    public void testMaper()
    {
        SqlSession sqlSession1=null;
        SqlSession sqlSession2=null;
        sqlSession1=sqlSessionFactory.openSession();
        sqlSession2=sqlSessionFactory.openSession();
        //生成代理类
        OrdersMapperCustom orderMapper=sqlSession1.getMapper(OrdersMapperCustom.class);
        OrdersMapperCustom orderMapper2=sqlSession2.getMapper(OrdersMapperCustom.class)
        User user=orderMapper.finduserByid(1);
        //这个close必须要加,不然缓存存不进去。
        sqlSession1.close();
        User user2=orderMapper2.finduserByid(1);
        sqlSession2.close();
    
    }

}

 

 

 

 运行结果:DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5bae4145]
DEBUG [main] - ==>  Preparing: SELECT * FROM USER WHERE ID=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5bae4145]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5bae4145]
DEBUG [main] - Returned connection 1538146629 to pool.
DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.OrdersMapperCustom]: 0.5

 

发现只有一条select语句,说明在执行第二条 User user2=orderMapper2.finduserByid(1)时,二级缓存发挥了作用。

这里的orderMapper,orderMapper2是不同的sqlSession生成的。但是没关系,二级缓存的作用范围是1.跨sqlSession. 2.

每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。

 

二级缓存存在的问题:

现在假如是联合查询:有订单表和用户表,订单mapper中联合查询用户表的信息,以订单表的namespace为主,开启二级缓存,

问题1:脏数据

1,我们第一遍查了一下订单表并且联合也查询了用户表,。

2,我们现在单独跟新了用户表。

3,在一次查询订单表并且查询关联的用户表。

4,这个时候的用户表就会存在脏数据了、

问题2:全部失效

1,我们第一遍查了一下订单表并且联合也查询了用户表,这个时候我们查询的用户表的id为1。

2,清除掉用户表id为2的缓存。

3,在一次查询订单表并且查询关联的用户表。

4,二级缓存失效,这个时候假如我们查了一下订单表并且联合也查询了用户表,这个时候我们查询的用户表的id为1,还需要重新查询,不符合缓存的定义。

 

1.5,一二级缓存总结

1,首先一级缓存是默认开启的,二级缓存需要手动开启。

2,一级缓存SqlSession级别的缓存,是同一个sqlsession的(是以sqlsession),二级缓存二级缓存是mapper级别的缓存,是多个sqlsession的。

3,两者都会出现脏数据的问题的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

lll

MyBatis源码分析------基础篇(二)

原文:https://www.cnblogs.com/qingruihappy/p/10432568.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!