MyBatis

时间:2024-04-26 07:36:37

搭建 MyBatis

MyBatis开发环境

MySQL不同版本的注意事项

        1、驱动类driver-class-name

MySQL 5版本使用jdbc5驱动,驱动类使用:com.mysql.jdbc.Driver

MySQL 8版本使用jdbc8驱动,驱动类使用:com.mysql.cj.jdbc.Driver

        2、连接地址url

MySQL 5版本的url:jdbc:mysql://localhost:3306/ssm

MySQL 8版本的url:jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC

创建maven工程

打包方式:jar

引入依赖:

<packaging>jar</packaging>

<dependencies>
    <!--junit测试-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    <!--mybatis核心-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.15</version>
    </dependency>
</dependencies>

创建MyBatis核心配置文件

        习惯上命名为mybatis. config.xm|,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略。

        核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息

        核心配置文件存放的位置,是src/main/resources目录下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <typeAliases>
        <package name="com.GLATY.mybatis.pojo"/>
    </typeAliases>

    <!--配置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/secondTable?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--SQL映射文件-->
        <!--<mapper resource="com/GLATY/mybatis/mappers/HeroMapper.xml"/>-->
        <package name="com.GLATY.mybatis.mappers"/>
    </mappers>
</configuration>

创建mapper接口

         MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类。

package com.GLATY.mybatis.mappers;

import com.GLATY.mybatis.pojo.Hero;

import java.util.List;

public interface HeroMapper {

    public List<Hero> selectAllHero();

    public Hero selectHeroByCondition(int id);

}

创建MyBatis的映射文件

文件的命名规则

        表所对应的实体类的类名 + Mapper .xml

        例如:表t _user,映射的实体类为User,所对应的映射文件为UserMapper.xml

        因此一个映射文件对应一个实体类, 对应一张表的操作。

        MyBatis映射文件用于编写SQL,访问以及操作表中的数据

MyBatis中可以面向接口操作数据, 要保证两个一致

        mapper接口的全类名和映射文件的命名空间(namespace) 保持一致

        mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.GLATY.mybatis.mappers.HeroMapper">
    <resultMap id="resultMap" type="Hero">  <!--resultMap:自定义映射,处理多对一或一对多的映射关系-->
        <result column="code_name" property="codeName"/>
    </resultMap>
    <select id="selectAllHero" resultType="com.GLATY.mybatis.pojo.Hero">  <!--resultType:设置结果类型要转换为的Java类型-->
        select * from secondtable.hero;
    </select>
    <select id="selectHeroByCondition" resultMap="resultMap">  <!--resultMap:自定义映射,处理多对一或一对多的映射关系-->
        select * from secondtable.hero where id = #{id}
    </select>
</mapper>

*测试

    @Test
    public void Test() throws IOException {
        //获取核心配置文件的输入流
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        //获取SqlSessionFactoryBuilder,并获取sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取sql的会话对象sqlSession,是MyBatis提供的操作数据库的对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取代理实现类对象
        HeroMapper heroMapper = sqlSession.getMapper(HeroMapper.class);
        //调用mapper接口
        List<Hero> heroes = heroMapper.selectAllHero();
        System.out.println(heroes);
        sqlSession.close();
    }

        事务默认回滚,执行 sqlSession.commit() 可以进行事务提交

        也可以在获取sqlSession时设置为自动提交

SqlSession sqlSession = sqlSessionFactory.openSession(true);

*加入log4j日志功能

        加入依赖

<!--log4j日志-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

        加入log4的配置文件

        log4j的配置文件名为log4j.xml,存放的位置是src/main/resources目录下 ( 配置文件名必须为log4j.xml)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n"/>
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info"/>
    </logger>
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

*日志的级别

        FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)

        从左到右打印的内容越来越详细

核心配置文件

environments

<!--
        environments:配置连接数据库的环境
        属性:
        default: 设置默认使用的环境的id
    -->
    <environments default="development">
        <!--
            environment: 设置一个具体的连接
            属性:
            id: 设置环境的唯一标识,不能重复
        -->
        <environment id="development">
            <!--
                transactionManager: 事务管理器
                属性:
                type: 设置事务管理的方式(有JDBC和MANAGED)
                    JDBC: 使用JDBC原生的事务管理方式
                    MANAGED: 被管理,例如spring
            -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource: 设置数据源
                属性:
                type: 设置数据源类型(有POOLED,UNPOOLED,JNDI)
                    POOLED: 表示使用数据库连接池
                    UNPOOLED: 表示不使用数据库连接池
                    JNDI: 表示使用为下文中的数据源
            -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/secondTable?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="Qq1297622137"/>
            </dataSource>
        </environment>
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/secondTable?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>

        </environment>
    </environments>

propertys

        写properties文件(以.properties结尾的文件)

jdbc.drive=com.mysql.cj.jdbc.Driver
jdbc.rel=jdbc:mysql://localhost:3306/secondTable?serverTimezone=UTC
jdbc.username=root
jdbc.password=123456

        引入properties文件

<properties resource="jdbc.properties" />    <!--resource: properties文件的路径-->

        引入properties文件后就可以在当前文件中使用${key}的方式访问value

<dataSource type="POOLED">
    <property name="driver" value="${jdbc.drive}"/>
    <property name="url" value="${jdbc.rel}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>

typeAliases

        设置类型别名,在MyBatis的范围中就可以用别名表示一个具体的类型

<typeAliases>
    <!--type: 设置需要起别名的类型    alias: 别名-->
    <typeAlias type="com.GLATY.mybatis.pojo.Hero" alias="HeroClass"/>
    <!--不写alias属性,别名默认为类名,且不区分大小写-->
    <typeAlias type="com.GLATY.mybatis.pojo.Hero"/>
    <!--通过包设置类型别名,指定包下都有的类型将全部拥有默认的别名-->
    <package name="com.GLATY.mybatis.pojo"/>
</typeAliases>

mappers

        引入mabatis的映射文件

<mappers>
    <!--SQL映射文件-->
    <mapper resource="com/GLATY/mybatis/mappers/HeroMapper.xml"/>
    <!--
        package: 以包的方式引入映射文件,但是必须满足两个条件:
        1.mapper接口和映射文件所在的包必须一致
        2.mapper接口的名字和映射文件的名字必须一致
    -->
    <package name="com.GLATY.mybatis.mappers"/>
</mappers>

*创建目录时,目录与目录之间用 / 分割,不能用 . 分割

*标签顺序 

        MyBatis核心配置文件中,标签的顺序:

        properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

         *核心配置文件可以在设置里的File and Code Templates中设置模板。

MyBatis获取参数值

        MyBatis获取参数值的两种方式:${ }和#{ }
        ${ }的本质就是字符串拼接,#{ }的本质就是占位符赋值
        ${ }使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时, 需要手动加单引号(在sql语句中加);但是#{ }使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号,而且可以防止sql注入

单个字面量类型

        若mapper接口中的方法参数为单个的字面量类型,此时可以使用${ }和#{ }以 任意的名称获取参数的值(但是一般会用传入的参数名获取参数的值)

多个字面量类型

        若mapper接口中的方法参数为多个时此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1...为键,以参数为值;以param1,param2...为键,以参数为值;因此只需要通过${ }和#{ }访问map集合的键就可以获取相对应的值

map集合类型

        若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过${ }和#{ }访问map集合的键就可以获取相对应的值(一般不会用该方法,一般使用@Param表示设置map集合的键)

实体类类型

        若mapper接口中的方法参数为实体类对象时,此时可以使用${ }和#{ },通过访问实体类对象中的属性名获取属性值
public List<Hero> selectByHero(Hero hero);
<select id="selectByHero" resultMap="resultMap">
    select * from secondtable.hero where id = #{hero.id};
</select>

使用@Param标识

        可以通过@Param注解标识mapper接口中的方法参数此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以param1,param2...为键,以参数为值;只需要通过${ }和#{ }访问map集合的键就可以获取相对应的值(@Param只是改变了arg的名字,还可以通过param找键值) 
public List<Hero> selectByNameAndCodeName(@Param("name")String name, @Param(:codeName)String codeName);
<select id="selectByNameAndCodeName" resultMap="resultMap">
    select *
    from secondtable.hero
    where name like '%'#{name}'%'
      and code_name like '%'#{codeName}'%'
</select>

MyBatis查询功能

查询一个list集合

        将返回值类型写为List

public List<Hero> selectAllHero();

查询多条数据为map集合

方式一:

        将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此 时可以将这些map放在一个list集合中获取

public List<Map<String, Object>> selectAllHeroToMap();

方式二:

        将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并 且最终要以一个map的方式返回数据,此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的 map集合

@MapKey("id") 
public Map<String, Object> selectAllHeroToMap();

特殊SQL的执行

模糊查询

        用#{}时不能直接放入引号里边,可以将%与#{}当做字符串拼接到一起(可以使用concat()函数)也可以直接使用${}

<select id="selectByNameAndCodeName" resultMap="resultMap">
    select *
    from secondtable.hero
    where name like "%"#{name}"%"    <!--%必须用""好引起来,不能用''-->
      and code_name like concat('%', #{codeName}, '%')
</select>

批量删除

        如果要删除的内容以字符串的形式传入,不可以用#{}要用${},因为#{}会自动在两边加入引号

public void deleteByIds(String ids);
<delete id="deleteByIds">
    delete from secondtable.hero where id in (${ids});
</delete>

        但是这种方法无法防止SQL注入,一般不用这种方法。后边还会学习新的方法用#{}以防止sql注入

动态设置表名

        动态设置表名也只能使用${},因为表名不能加引号

<select id="selectByTableName" resultMap="resultMap">
    select * from ${tableName};
</select>

添加功能获取自增的主键

        需要设置两个参数来返回数据,useGeneratedKeys设置为true表示要返回自增主键的值,keyProperty设置返回的数据赋值给实体类的哪一个变量。

public int addOneHero(Hero hero);
<insert id="addOneHero" useGeneratedKeys="true" keyProperty="id">
    insert into secondtable.hero values (#{name}, null, #{codeName}, #{ability})
</insert>

自定义映射resultMap

处理字段和属性的映射关系

        若字段名和实体类中的属性名不一致,可以为查询的字段设置别名和属性名保持一致。

        如果字段符合MySQL的要求使用_,而属性符合java的要求使用驼峰,此时可以在MyBatis的核心配置文件中设置一个全局配置settings, 可以自动将下划线映射为驼峰

<settings>
    <!--将下划线映射为驼峰-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

         还可以使用resultMap自定义映射处理

resultMap属性:

        id:表示自定义映射的唯一标识

        type:查询的数据要映射的实体类的类型

resultMap子标签:

        id:设置主键的映射关系

        result:设置普通字段的映射关系

        association:设置多对一的映射关系(处理实体类类型的属性)

        collection:设置一对多的映射关系

        子标签属性:

                property:设置映射关系中实体类中的属性名,是处理的实体类类型中的属性名

                column:设置映射关系中表中的字段名,是sql查询出的某个字段

<resultMap id="resultMap" type="Hero">  
    <result column="code_name" property="codeName"/>
</resultMap>

处理多对一映射

        方式一:级联

<resultMap id="resultMap" type="Hero">  
    <result column="code_name" property="codeName"/>
    <result column="ability" property="ability.ability"/>
    <result column="PH" property="ability.PH"/>
    <result column="mana" property="ability.mana"/>
</resultMap>

<select id="selectAllHero" resultMap="resultMap">
    select * from secondtable.hero join secondtable.ability a on hero.ability = a.ability;
</select>

        方式二:使用associate标签

<resultMap id="resultMap2" type="Hero">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="code_name" property="codeName"/>
    <association property="ability" javaType="Ability">
        <id column="ability" property="ability"/>
        <result column="PH" property="PH"/>
        <result column="mana" property="mana"/>
    </association>
</resultMap>

<select id="selectAllHero" resultMap="resultMap2">
    select * from secondtable.hero join secondtable.ability a on hero.ability = a.ability;
</select>

方式三:分步查询

        分为两步,通过asssociation标签进行分步查询

        分步查询的第一步:通过分步查询查询Hero信息

Emp getHeroByStepOne(@Param("id") int id);
 <resultMap id="stepOneMap" type="Hero">
     <id column="id" property="id"/>
     <result column="name" property="name"/>
     <result column="code_name" property="codeName"/>
     <association property="ability" select="com.GLATY.mybatis.mappers.AbilityMapper.selectAbilityByAbility" column="ability"/>
     <!--
         select:设置分步查询,查询某个属性的值的sql的标识(namespace.sqlId)
         column:将sql以及查询结果中的某个字段设置为分步查询的条件
     -->
 </resultMap>
 
 <select id="selectByStepOne" resultMap="stepOneMap">
     select * from secondtable.hero where id = #{id}
 </select>

        分步查询的第二步:根据Hero所对应的ability查询ability信息

Ability selectAbilityByAbility(int ability);
<select id="selectAbilityByAbility" resultType="ability">
    select * from secondtable.ability where ability = #{ability}
</select>

 分布查询优点:可以实现延迟加载
        但是必须在核心配置文件中设置全局配置信息:
        lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
        aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载
        此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType = "lazy(延迟加载)|eager(立即加载)"

处理一对多映射

        处理一对多映射时可以只用collection标签,也可以用分步查询

方式一:用collection标签

List<Ability> selectByCollection();
<resultMap id="selectByCollection" type="Ability">
    <id column="ability" property="ability"/>
    <result column="mana" property="mana"/>
    <result column="PH" property="PH"/>
    <collection property="heroList" ofType="Hero">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="code_name" property="codeName"/>
    </collection>
</resultMap>
<select id="selectByCollection" resultMap="selectByCollection">
    select * from secondtable.ability left join secondtable.hero h on ability.ability = h.ability;
</select>

方式二:分步查询

        第一步:通过分步查询查询ability信息

Ability selectOneByStep(int ability);
<resultMap id="selectByStep" type="Ability">
    <id column="ability" property="ability"/>
    <result column="mana" property="mana"/>
    <result column="PH" property="PH"/>
    <collection property="heroList" select="com.GLATY.mybatis.mappers.HeroMapper.selectByStepSecond" column="ability"/>
</resultMap>
<select id="selectOneByStep" resultMap="selectByStep">
    select * from secondtable.ability where ability = #{ability}
</select>

        第二步:根据ability查询Hero信息

public List<Hero> selectByStepSecond(int ability);
<select id="selectByStepSecond" resultType="Hero">
    select name, id, code_name from secondtable.hero where ability = #{ability}
</select>

动态SQL

        Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。

if

        if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行。

public Hero selectByCondition(@Param("id") int id,@Param("name") String name);
<select id="selectByCondition" resultType="hero">
    select * from secondtable.hero where true
    <if test="id != null and id != '' and id != 0">
        and id = #{id}
    </if>
    <if test="name != null and name != ''">
        and name = #{name}
    </if>
</select>

where

        where和if一般结合使用
        若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
        若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and去掉
        *where标签不能去掉条件最后多余的and

public Hero selectByConditionPro(@Param("id") int id,@Param("name") String name);
<select id="selectByConditionPro" resultType="hero">
    select * from secondtable.hero
    <where>
        <if test="id != null and id != '' and id != 0">
            id = #{id}
        </if>
        <if test="name != null and name != ''">
            and name = #{name}
        </if>
    </where>
</select>

trim

        trim用于去掉或添加标签中的内容

trim常用属性
属性名 说明
prefix 在trim标签中的内容的前面添加某些内容
prefixOverrides 在trim标签中的内容的前面去掉某些内容
suffix 在trim标签中的内容的后面添加某些内容
suffixOverrides 在trim标签中的内容的后面去掉某些内容
public Hero selectByConditionProMax(@Param("id") int id,@Param("name") String name);
<select id="selectByConditionProMax" resultType="hero">
    select * from secondtable.hero
    <trim prefix="where" suffixOverrides="and">
        <if test="id != null and id != '' and id != 0">
            id = #{id} and
        </if>
        <if test="name != null and name != ''">
            name = #{name}
        </if>
    </trim>
</select>

choose、when、otherwise

        choose、when、 otherwise相当于if...else if...else

        when至少设置一个,otherwise最多设置一个

public Hero selectByOneCondition(@Param("id") int id,@Param("name") String name);
<select id="selectByOneCondition" resultType="hero">
    select * from secondtable.hero
    <where>
        <choose>
            <when test="id != 0 and id != '' and id != null">
                id = #{id}
            </when>
            <when test="name != '' and name != null">
                name = #{name}
            </when>
        </choose>
    </where>
</select>

foreach

foreach属性
属性名 说明
collection 为遍历的数组
item 为遍历的元素名
separator 为分隔符
open 为开始时拼接的内容
close 为结束时拼接的内容
index 为当前循环的索引
public List<Hero> selectByIds(@Param("ids") int[] ids);
<select id="selectByIds" resultType="hero">
    select *
    from secondtable.hero
    where id in
          <foreach collection="ids" open="(" item="id" separator="," close=")">
              #{id}
          </foreach>
</select>

SQL片段

        sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入

<select id="selectByIds" resultType="hero">
    <include refid="heroColumns"/>
    where id in
          <foreach collection="ids" open="(" item="id" separator="," close=")">
              #{id}
          </foreach>
</select>
<sql id="heroColumns">
    select name, id, code_name, ability from secondtable.hero
</sql>

MyBatis缓存

一级缓存

         一级缓存默认开启

        一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
        使一级缓存失效的四种情况:
        ➢不同的SqlSession对应不同的一级缓存
        ➢同一个SqlSession但是查询条件不同
        ➢同一个SqlSession两次查询期间执行了任何一次增删改操作
        ➢同一个SqlSession两次查询期间手动清空了缓存

//清除一级缓存
sqlSession.clearCache();

二级缓存

        二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
        二级缓存开启的条件:
        ➢在核心配置文件中,设置全局配置属性cacheEnabled="true",有的默认为true不需要设置
        ➢在映射文件中设置标签<cache/>
        ➢二级缓存必须在SqlSession关闭或提交之后有效(提交只对增删改有效)
        ➢查询的数据所转换的实体类类型必须实现序列化的接口
        使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

<!--在核心配置文件中,设置全局配置属性cacheEnabled="true"-->
<settings>
    <!--......-->
    <setting name="cacheEnabled" value="true"/>
</settings>
//实体类类型实现序列化的接口
public class Hero implements Serializable {
    //......
}

 二级缓存相关配置

        在mapper配置文件中添加的cache标签可以设置一些属性:

eviction属性

        缓存回收策略,默认的是 LRU。

说明
LRU(Least Recently Used) 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) 先进先出:按对象进入缓存的顺序来移除它们。
SOFT 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。


flushInterval属性

        刷新间隔,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(执行增删改语句)时刷新

size属性

        引用数目,正整数。代表缓存最多可以存储多少个对象,太大容易导致内存溢出

readOnly属性

说明
true 只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false 读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

缓存查询顺序

        先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。如果二级缓存没有命中,再查询一级缓存。如果一级缓存也没有命中,则查询数据。SqlSession关闭之后,一级缓存中的数据会写入二级缓存

整合第三缓存EHCache

添加依赖

<!-- Mybatis EHCache整合包 -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency> <!-- slf4j日志门面的一个具体实现 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

各jar包功能

jar包名称 作用
mybatis-ehcache
Mybatis EHCache 的整合包
ehcache
EHCache 核心包
slf4j-api
SLF4J 日志门面包
logback-classic
支持 SLF4J 门面接口的一个具体实现

创建配置文件ehcache.xml

<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="D:\temporaryFile\ehcache"/>
    <defaultCache maxElementsInMemory="1000"
                  maxElementsOnDisk="10000000"
                  eternal="false"
                  overflowToDisk="true"
                  timeToIdleSeconds="120"
                  timeToLiveSeconds="120"
                  diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU"></defaultCache>
</ehcache>

设置二级缓存的类型

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

加入logback日志

        存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。 创建logback的配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是: 时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>
    <!-- 设置全局日志级别。日志级别按顺序分别是: DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT"/>
    </root>
    <!-- 根据特殊需求指定局部日志级别 -->
    <!-- name为mapper的路径 -->
    <logger name="com.GLATY.mybatis.mappers" level="DEBUG"/>
</configuration>

EHCache配置文件说明

属性名
作用
maxElementsInMemory
在内存中缓存的 element 的最大数目
maxElementsOnDisk
在磁盘上缓存的 element 的最大数目,若是 0 表示无 穷大
eternal
设定缓存的 elements 是否永远不过期。 如果为 true,则缓存的数据始终有效, 如果为 false 那么还 要根据timeToIdleSeconds timeToLiveSeconds 判断
overflowToDisk
设定当内存缓存溢出的时候是否将过期的 element 缓存到磁盘上
timeToIdleSeconds
当缓存在 EhCache 中的数据前后两次访问的时间超过timeToIdleSeconds 的属性取值时, 这些数据便会删除,默认值是0, 也就是可闲置时间无穷大
timeToLiveSeconds
缓存 element 的有效生命期,默认是 0, 也就是element存活时间无穷大
diskSpoolBufferSizeMB
DiskStore( 磁盘缓存 ) 的缓存区大小。默认是 30MB。每个 Cache 都应该有自己的一个缓冲区
diskPersistent
VM 重启的时候是否启用磁盘保存 EhCache 中的数 据,默认是false
diskExpiryThreadIntervalSeconds
磁盘缓存的清理线程运行间隔,默认是 120 秒。每 个120s , 相应的线程会进行一次 EhCache 中数据的清理工作
memoryStoreEvictionPolicy
当内存缓存达到最大,有新的 element 加入的时候, 移除缓存中element 的策略。 默认是 LRU (最近最少使用),可选的有LFU (最不常使用)和FIFO (先进先出)

MyBatis的逆向工程

        正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。 Hibernate是支持正向工
程的。
        逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
        Java实体类
        Mapper接口
        Mapper映射文件        

步骤

添加依赖和插件

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    <!-- junit测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
    <!-- log4j日志 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
    <!-- 构建过程中用到的插件 -->
    <plugins>
        <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.0</version>
            <!-- 插件的依赖 -->
            <dependencies>
                <!-- 逆向工程的核心依赖 -->
                <dependency>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-core</artifactId>
                    <version>1.3.2</version>
                </dependency>
                <!-- MySQL驱动 -->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.16</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

创建逆向工程的配置文件

        文件名必须是:generatorConfig.xml

<?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: 生成基本的CRUD(清新简洁版) 
         MyBatis3: 生成带条件的CRUD(奢华尊享版,几乎可以满足所有的单表的增删改查功能) 
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/secondTable?serverTimezone=UTC"
                        userId="root"
                        password="123456"/>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.GLATY.mybatis.pojo" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.GLATY.mybatis.mappers" targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.GLATY.mybatis.mappers"
                             targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="hero" domainObjectName="Hero"/>
        <table tableName="ability" domainObjectName="Ability"/>
    </context>
</generatorConfiguration>

执行MBG插件的generate目标

QBC查询

        进行条件查询时,条件需要添加在XXExample的createCriteria方法的相应方法中,条件中使用or时调用XXExample中的or方法

@Test
public void TestSelect(){
    SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession();
    HeroMapper mapper = sqlSession.getMapper(HeroMapper.class);
    HeroExample heroExample = new HeroExample();
    heroExample.createCriteria().andNameLike("%天%");
    heroExample.or().andNameLike("%李%");
    List<Hero> heroList = mapper.selectByExample(heroExample);
    System.out.println(heroList);
    sqlSession.close();
}

分页插件

添加插件

添加依赖

 <!--分页插件-->
 <dependency>
     <groupId>com.github.pagehelper</groupId>
     <artifactId>pagehelper</artifactId>
     <version>6.1.0</version>
 </dependency>

配置分页插件

<plugins>
    <!--设置分页插件-->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

使用插件

@Test
public void TestPage() {
    SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession();
    HeroMapper mapper = sqlSession.getMapper(HeroMapper.class);

    //在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能
    //pageNum为当前页,pageSize为每页显示的条数
    System.out.println(PageHelper.startPage(2, 4));
    List<Hero> heroList = mapper.selectByExample(null);
    System.out.println(heroList);

    //在查询获取list集合之后,可以使用PageInfo<T> pageInfo = new PageInfo<>(List<T> list, int navigatePages)获取分页相关数据
    PageInfo<Hero> pageInfo = new PageInfo<>(heroList, 5);
    System.out.println(pageInfo);
    sqlSession.close();
}

 分页相关数据

数据 说明
pageNum 当前页的页码
pageSize 每页显示的条数
size 当前页显示的真实条数
total 总记录数
pages 总页数
prePage 上一页的页码
nextPage 下一页的页码
isFirstPage/isLastPage 是否为第一页/最后一页
hasPreviousPage/hasNextPage 是否存在上一页/下一页
 
navigatePages 导航分页的页码数
navigatepageNums 导航分页的页码