MyBatis(下)

1、MayBatis的缓存:

        缓存通过减少IO(读写文件)的方式,来提高程序的执行效率。mybatis的缓存将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取,不再查数据库。一方面是减少了IO,另一方面不再执行繁琐的查找算法,效率大大提升。

mybatis缓存包括:

  • 一级缓存:将查询到的数据存储到SqlSession中。(当前的会话)
  • 二级缓存:将查询到的数据存储到SqlSessionFactory中。(对于整个数据库)
  • 或者集成其它第三方的缓存:比如EhCache【Java语言开发的】、Memcache【C语言开发的】等。

缓存只针对于DQL语句,也就是说缓存机制只对应select语句

(1)一级缓存:

        默认开启,同一个SqlSesion级别共享的缓存,在一个SqlSession的生命周期内,如果执行2次相同的SQL查询,那么第二次SQL查询会直接取缓存的数据,而不是到数据库中查询,当然,若第一次和第二次相同的SQL查询之间,执行了DML(INSERT/UPDATE/DELETE),则一级缓存会被清空,第二次查询相同SQL仍然会到数据库中查找数据。

一级缓存在下面情况会被清除

  1. 在同一个SqlSession下执行增删改操作时(不必提交),会清除一级缓存(DML的操作和表没有关系,即使操作的不是同一个表也会触发这操作!)
  2. SqlSession提交或关闭时(关闭时会自动提交),会清除一级缓存
  3. 对mapper.xml中的某个CRUD标签,设置属性flushCache=true,这样会导致该MappedStatement的一级缓存,二级缓存都失效,在全局配置文件中设置 <setting name="localCacheScope" value="STATEMENT"/>,这样会使一级缓存失效,二级缓存不受影响
  4. 执行sqlSession的clearCache(),方法后一级缓存会被清空(手动清空缓存)

MappedStatement是指:一个CRUD标签在mybatis中会被封装成一个MappedStatement。

测试

mapper接口: 

 //通过id查询汽车
    Car selectById(Long id);

mapper映射文件:

    <select id="selectById" resultType="Car">
        select * from t_car where id=#{id}
    </select>

测试类:  使用的是同一个mapper

 //测试一级缓存
    @Test
    public  void test1(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car1 = mapper.selectById(194L);
        System.out.println(car1);

        Car car2 = mapper.selectById(194L);
        System.out.println(car2);

        //关闭
        sqlSession.close();

    }

测试结果:

执行相同的sql的时候,第二次没有执行sql语句,而是直接使用缓存! 

换换测试方式:既然是在同一个sqlsession那么和创建的mapper应该没有什么关系吧

 //使用的是不同mapper(狗头保命sqlsession)
    @Test
    public  void test2(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car1 = mapper.selectById(194L);
        System.out.println(car1);

        Car car2 = mapper.selectById(194L);
        System.out.println(car2);

        //关闭
        sqlSession.close();

    }

 测试结果 还是一个

获取不同的Sqlsession对象测试:

 @Test
    public  void test3() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsReader("mybatis-config.xml"));
        SqlSession sqlSession = sessionFactory.openSession();
        SqlSession sqlSession1 = sessionFactory.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car = mapper.selectById(194L);
        System.out.println(car);

        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(194L);
        System.out.println(car1);
        sqlSession.close();
        sqlSession1.close();
    }

 测试结果:

        测试的结果是使用的是两个sqlsession对象,所以导致这样的情况发生。由于只会存在同一个SQL Session的缓存,所以此次的查询使用啦两个sql语句。

(2)二级缓存:

        默认关闭,可通过全局配置文件中的<settings name="cacheEnabled" value="true"/>开启二级缓存总开关,然后在某个具体的mapper.xml中增加<cache />,即开启了该mapper.xml的二级缓存。二级缓存是mapper级别的缓存,粒度比一级缓存大,多个SqlSession可以共享同一个mapper的二级缓存。注意开启二级缓存后,SqlSession需要提交或者关闭,查询的数据才会被刷新到二级缓存当中。

使用二级缓存需要具备以下几个条件:

1、<setting name="cacheEnabled" value="true"> 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认就是true,无需设置。

2、在需要使用二级缓存的SqlMapper.xml文件中添加配置:<cache />

3、使用二级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接口

4、SqlSession对象关闭或提交之后,一级缓存中的数据才会被写入到二级缓存当中。此时二级缓存才可用。

测试类:

@Test
    public  void test4() throws IOException {
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));

        SqlSession sqlSession = sessionFactory.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car = mapper.selectById2(194L);
        System.out.println(car);
        SqlSession sqlSession1 = sessionFactory.openSession();
        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById2(194L);
        System.out.println(car1);

      
    }

         这里避免了一级缓存的情况,使用的不同sqlSession对象,但是没有关闭和提交所以应该是两条的sql语句。

测试结果:

执行的是两个sql语句!

那么将上面的测试的代码稍稍稍修改一下,然后体现二级缓存的功能!

 

        关闭后才会将一级缓存存入二级缓存中,这样下一次执行相同的数据就会在缓存中获取查询结果。 

测试结果:

 触发二级缓存!只是执行啦一次的sql语句!

缓存机制:

        执行sql语句后------存入一级缓存------存入二级缓存----再次执行sql------会先到一级缓存(没有)------然后到二级缓存(没有)------到数据库中 

只要两个sql查询语句之间有DML操作执行,那么一级和二级缓存都会被清理!!!!

(3)二级缓存的相关配置:

(1)eviction:指定从缓存中移除某个对象的淘汰算法。默认采用LRU策略。

        1、LRU:Least Recently Used。最近最少使用。优先淘汰在间隔时间内使用频率最低的对象。(其实还有一种淘汰算法LFU,最不常用。)

        2、FIFO:First In First Out。一种先进先出的数据缓存器。先进入二级缓存的对象最先被淘汰。

        3、SOFT:软引用。淘汰软引用指向的对象。具体算法和JVM的垃圾回收算法有关。

        4、WEAK:弱引用。淘汰弱引用指向的对象。具体算法和JVM的垃圾回收算法有关。

(2)flushInterval:二级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存,只要内存足够大,一直会向二级缓存中缓存数据。除非执行了增删改。

(3)readOnly:

        1、true:多条相同的sql语句执行之后返回的对象是共享的同一个。性能好。但是多线程并发可能会存在安全问题。

        2、false:多条相同的sql语句执行之后返回的对象是副本,调用了clone方法。性能一般。但安全。

(4)size:设置二级缓存中最多可存储的java对象数量。默认值1024。

(4)使用第三方缓存:

        集成EhCache是为了代替mybatis自带的二级缓存。一级缓存是无法替代的。mybatis对外提供了接口,也可以集成第三方的缓存组件。比如EhCache、Memcache等。都可以。EhCache是Java写的。Memcache是C语言写的。所以mybatis集成EhCache较为常见,按照以下步骤操作,就可以完成集成:

导入依赖:

<!--mybatis集成ehcache的组件-->
<dependency>
  <groupId>org.mybatis.caches</groupId>
  <artifactId>mybatis-ehcache</artifactId>
  <version>1.2.2</version>
</dependency>

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存-->
    <diskStore path="e:/ehcache"/>
  
    <!--defaultCache:默认的管理策略-->
    <!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断-->
    <!--maxElementsInMemory:在内存中缓存的element的最大数目-->
    <!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上-->
    <!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false-->
    <!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问-->
    <!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问-->
    <!--memoryStoreEvictionPolicy:缓存的3 种清空策略-->
    <!--FIFO:first in first out (先进先出)-->
    <!--LFU:Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存-->
    <!--LRU:Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存-->
    <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                  timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>

</ehcache>

引入第三方缓存:在XXXMapper.xml文件中

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

2、MyBatis的逆向工程:

        mybatis官方提供了mapper自动生成工具mybatis-generator-core来针对单表,生成PO类,以及Mapper接口和mapper.xml映射文件。针对单表,可以不需要再手动编写xml配置文件和mapper接口文件了,非常方便。美中不足的是它不支持生成关联查询,一般做关联查询,就自己单独写SQL就好了。

        就是根据数据库表逆向生成Java的pojo类,SqlMapper.xml文件,以及Mapper接口类等。

配置信息包含:

  • pojo类名、包名以及生成位置。
  • SqlMapper.xml文件名以及生成位置。
  • Mapper接口名以及生成位置。
  • 连接数据库的信息。
  • 指定哪些表参与逆向工程。

第一步导入依赖:


    <!--定制构建过程-->
    <build>
        <!--可配置多个插件-->
        <plugins>
            <!--其中的一个插件:mybatis逆向工程插件-->
            <plugin>
                <!--插件的GAV坐标-->
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.4.1</version>
                <!--允许覆盖-->
                <configuration>
                    <overwrite>true</overwrite>
                </configuration>
                <!--插件的依赖-->
                <dependencies>
                    <!--mysql驱动依赖-->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.30</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

配置核心文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!--
        targetRuntime有两个值:
            MyBatis3Simple:生成的是基础版,只有基本的增删改查。
            MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!--防止生成重复代码-->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
      
        <commentGenerator>
            <!--是否去掉生成日期-->
            <property name="suppressDate" value="true"/>
            <!--是否去除注释-->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--连接数据库信息-->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/powernode"
                        userId="root"
                        password="root">
        </jdbcConnection>

        <!-- 生成pojo包名和位置 -->
        <javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java">
            <!--是否开启子包-->
            <property name="enableSubPackages" value="true"/>
            <!--是否去除字段名的前后空白-->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- 生成SQL映射文件的包名和位置 -->
        <sqlMapGenerator targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/resources">
            <!--是否开启子包-->
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- 生成Mapper接口的包名和位置 -->
        <javaClientGenerator
                type="xmlMapper"
                targetPackage="com.powernode.mybatis.mapper"
                targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 表名和对应的实体类名-->
        <table tableName="t_car" domainObjectName="Car"/>

    </context>
</generatorConfiguration>

使用插件生成文件:

 然后在修修改改其他的配置信息就可以进行测试啦!

创建简单的sql语句我觉得就可以,对于负载的sql觉得使用起来并不方便

3、MyBatis使用PageHelper分页插件:

分页的参数介绍:

pageNum           当前页码

pageSize            显示信息数

totalcount           总记录数

totalPage           总页码  

totalPage=totalCount % pageSize ==0?   (totalCount % pageSize) : (totalCount % pageSize) +1 ;

分页展示的时候要使用 limit  分页查询   使用这个关键字要传递两个参数,第一个 start 就是开始的索引,然后是查询的条数,也就是pageSize,那么start怎么计算呐!

start=(pagaNum-1) * pageSize

第一步导入依赖

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.3</version>
</dependency>

第二步在核心配置文件设置插件:

<plugins>
  <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

mapper接口:

  //分页插件查询所有
    List<Car> selectAll();

mapper映射文件:

  <select id="selectAll" resultType="Car">
    select  * from t_car
  </select>

这样看好像也没什么区别和之前的操作,是不是很迷惑!

测试类来啦:

 //使用分页插件
    @Test
    public void  test2(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        //在执行DQL语句之前开启分页功能
        int pageNum=2;
        int pageSize=5;
        PageHelper.startPage(pageNum,pageSize);
        List<Car> carList = mapper.selectAll();
        carList.forEach(System.out::println);
        sqlSession.close();
    }

另外说一下,pagehelper中可以将查询出来的数据封装到pageInfo中,然后这个对对象里面有很多的信息,真的就很牛 。节省很多功夫!这里有大概的数据展示可以了解一下!

PageInfo{
  pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=6, pages=3, 
  list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=6, pages=3, reasonable=false, pageSizeZero=false}
  [Car{id=86, carNum='1234', brand='丰田霸道', guidePrice=50.5, produceTime='2020-10-11', carType='燃油车'}, 
  Car{id=87, carNum='1234', brand='丰田霸道', guidePrice=50.5, produceTime='2020-10-11', carType='燃油车'}], 
  prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, 
  navigatePages=5, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]
}

4、基于注解开发:

        mybatis中也提供了注解式开发方式,采用注解可以减少Sql映射文件的配置。但是使用注解式开发的话,sql语句是写在java程序中的,这种方式也会给sql语句的维护带来成本。

        使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

注解方式插入数据:

//注解方式插入
    @Insert(value="insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})")
    int insert(Car car);

注解方式删除:

@Delete("delete from t_car where id = #{id}")
int deleteById(Long id);

注解方式修改:

@Update("update t_car set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType} where id=#{id}")
int update(Car car);

注解方式查询:

@Select("select * from t_car where id = #{id}")
@Results({
    @Result(column = "id", property = "id", id = true),
    @Result(column = "car_num", property = "carNum"),
    @Result(column = "brand", property = "brand"),
    @Result(column = "guide_price", property = "guidePrice"),
    @Result(column = "produce_time", property = "produceTime"),
    @Result(column = "car_type", property = "carType")
})
Car selectById(Long id);

多个参数的注解方式:

@Select("SELECT * FROM student WHERE name like '%${name}%' AND major like '%${major}%'")
	List<Student> find(@Param("name") String name, @Param("major") String major);

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值