MyBatis基础学习笔记

130 阅读12分钟

5月1日

一、MyBatis特性

  • MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架
  • MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
  • MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和java的POJO映射成数据库中的记录
  • MyBatis是一个半自动的ORM框架

二、MyBatis环境搭建

1、加入依赖

mysql-connector-java、mybatis

2、创建MyBatis核心配置文件加入配置。

一般命名为mybatis-config.xml

<?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>
<!--    配置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="136011760"/>
            </dataSource>
        </environment>
    </environments>
<!--    引入映射文件-->
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
</configuration>

3、创建表所对应的POJO类

4、创建Mapper接口

MyBatis中的mapper接口相当于以前的dao,但区别在于,mapper仅仅是接口,我们不需要提供实现类。命名规则:POJO类名+Mapper

5、创建MyBatis映射文件

1)映射文件的命名规则

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

  • 例如:表t_user,映射的实体类为User,Mapper接口为UserMapper,MyBatis映射文件为UserMapper.xml

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

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

  • 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.cjy.mapper.UserMapper">
    <insert id="insertUser">
        INSERT INTO t_user VALUES (null, 'cjy', '136011760')
    </insert>
</mapper>

6、在MyBatis核心配置文件引入MyBatis映射文件

<!--    引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>

三、MyBatis的使用

1、加载MyBatis的核心配置文件

InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

2、创建SqlSessinonFactoryBuilder对象

SqlSessinon工厂的制造对象

SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

3、获取SqlSessionFactory对象

根据核心配置文件的输入流获取

SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);

4、获取SqlSession对象

SqlSession:代表java程序和数据库之间的会话。(HttpSession是java程序和浏览器之间的会话)

SqlSession sqlSession = sqlSessionFactory.openSession();

1)获取可以自动提交事务的SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession(true);

2)获取SqlSeesion对象的主要过程

  1. 从配置中获取Environment

  2. 从Environment获取DataSource

  3. 从Environment中获取TransactionFactory

  4. 从DataSource里获取数据库连接对象Connection

  5. 在取得的数据库连接上创建事务对象Transaction

  6. 创建执行器Executor(该对象非常重要,事实上SqlSession的所有操作都是通过它完成的)

  7. 创建SqlSession对象

5、获取Mapp接口

原理是通过动态代理,增强的功能是帮我们创建接口实现类。

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

6、功能实现

执行流程

  1. 调用接口的方法时会先找到与接口对应的映射文件
  2. 将方法名与编写SQL的标签的id匹配
  3. 执行sql语句
  4. 返回结果(如果返回的一条或多条记录则返回的是POJO对象)
int insertUser = mapper.insertUser();

7、提交事务

默认会设置事务,需要自己手动提交

sqlSession.commit();

8、添加日志框架(可选)

日志的级别:FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试) 从左到右打印的内容越来越详细

1)添加依赖

log4j

2)配置log4j.xml

  • 文件名必须为log4j.xml

  • 放在src/main/resources目录下

    <?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>
    <!--    执行sql输出debug日志-->
        <logger name="java.sql">
            <level value="debug" />
        </logger>
    <!--    执行mybatis输出警告信息-->
        <logger name="org.apache.ibatis">
            <level value="info" />
        </logger>
        <root>
            <level value="debug" />
            <appender-ref ref="STDOUT" />
        </root>
    </log4j:configuration>
    

四、MyBatis的查询功能

1、在映射文件的SQL标签必须加入resultType属性

  • 如果查询的是某个字段,则resultType设置为相应的属性,例如:resultType="java.lang.String"
  • 如果查询的是一条或多条记录,则resultType设置为对应的POJO类,例如:resultType="com.cjy.pojo.User"

五、MyBatis核心配置文件

1、configuration标签下各个标签的顺序

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

2、environments标签

配置多个连接数据库的环境

  • default属性

    default=“具体的某一个数据库环境的id”,设置默认的数据库环境。

  • environment标签

    • id属性

      数据库环境的id

    • transaction标签的type属性

      type="JDBC|MANAGED" ,设置事务管理的方式

      • JDBC:表示当前环境中,执行SQL时,使用JDBC原生的事务管理方式。事务的提交和回滚需要手动设置
      • MANAGED:被管理,例如Spring
    • dataSorce标签的type属性

      type="POOLED|UNPOOLED|JNDI", 设置数据源的类型

      • POOLED:表示使用数据库连接池缓存数据库连接
      • UNPOOLED:表示不适用数据库连接池
      • JNDI:表示使用上下文中的数据源

3、引入properties文件

  1. 在src/main/resources目录下建立properties文件

    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
    jdbc.username=root
    jdbc.password=136011760
    
  2. 在configuration标签下加入properties标签

    <properties resource="jdbc.properties"/>
    
  3. 在dataSource下的property标签的value属性设置对应的值

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

4、typeAliases标签

写在configuration标签下,用于给全类名起别名,方便在映射文件中使用

  • 使用typeAlias标签

    用于给某一个全类名起别名,别名不区分大小写。

        <typeAliases>
            <typeAlias type="com.cjy.pojo.User" alias="User"/>
        </typeAliases>
    
    • type=“某个类的全类名”
    • alias="别名",别名不区分大小写。如果没有alise属性则默认属性为简单类名,也不区分大小写
  • 使用package标签

    以包为单位,给包中的所有类的全类名起别名,别名为简单类名,且不区分大小写。

        <typeAliases>
            <package name="com.cjy.pojo"/>
        </typeAliases>
    

5、mappers标签

用于引入映射文件

  • 使用mapper

    引入单个映射文件

        <mappers>
            <mapper resource="mappers/UserMapper.xml"/>
        </mappers>
    
  • 使用package

    引入包中的所有映射文件

    • 在src/main/resources目录下创建多级包需要以/分割(在java目录中创建多级包用.)
    • 映射文件所在的全包名要和mapper接口所在的全包名一致
    • 映射文件名字要和mapper接口名字一致
        <mappers>
            <package name="com.cjy.mapper"/>
        </mappers>
    



5月2日

一、MyBatis获取参数值

1、使用#{}

相当于占位符?

    <select id="checkLogin" resultType="User">
        SELECT * FROM t_user WHERE uname=#{arg0} AND pawd=#{arg1}
    </select>
  • 当只传入一个参数时,#{}里面可以不填或者填任意字符

  • 当传入多个参数时,且使用默认的键,#{}里面要根据Mapper接口的参数顺序填写

    Mapper接口中的参数在执行过程中会被放入map中

    • Mapper接口里的第一个参数是arg0或param0
    • Mapper接口里的第二个参数是arg1或param1
  • 当传入多个参数时,且使用自定义的map,#{}里面要根据Mapper接口的参数填写自定义的键

    1. Mapper接口中的方法,直接把map作为参数
     User checkLogin3(Map<String, Object> map);
    
    1. 在映射文件中

      <select id="checkLogin3" resultType="User">
          SELECT * FROM t_user WHERE uname=#{uname} AND pawd=#{pawd}
      </select>
      
    2. 在使用时

       HashMap<String, Object> hashMap = new HashMap<>();
       hashMap.put("uname", "陈金雨");
       hashMap.put("pawd", "136011760");
       User user = mapper.checkLogin3(hashMap);
      
  • 当传入多个参数时,直接在Mpper接口参数加入@Param注解

    User checkLogin4(@Param("uname") String uname, @Param("pawd") String pawd);
    
  • 当传入的参数为对象时,可以直接在#{}里填属性

2、使用${}

相当于字符串拼接,因为是字符串拼接,所以要注意单引号的使用。

    <select id="checkLogin2" resultType="User">
        SELECT * FROM t_user WHERE uname='${arg0}' AND pawd='${arg1}'
    </select>
  • 当只传入一个参数时,${}里面可以不填或者填任意字符

  • 当传入多个参数时,且使用默认的键,${}里面要根据Mapper接口的参数顺序填写

    Mapper接口中的参数在执行过程中会被放入map中

    • Mapper接口里的第一个参数是arg0或param0
    • Mapper接口里的第二个参数是arg1或param1
  • 当传入多个参数时,且使用自定义的map,${}里面要根据Mapper接口的参数填写自定义的键

    1. Mapper接口中的方法,直接把map作为参数

      User checkLogin3(Map<String, Object> map);
      
    2. 在映射文件中

      <select id="checkLogin3" resultType="User">
          SELECT * FROM t_user WHERE uname='${uname}' AND pawd='${pawd}'
      </select>
      
    3. 在使用时

       HashMap<String, Object> hashMap = new HashMap<>();
       hashMap.put("uname", "陈金雨");
       hashMap.put("pawd", "136011760");
       User user = mapper.checkLogin3(hashMap);
      
  • 当传入多个参数时,直接在Mpper接口参数加入@Param注解

    User checkLogin4(@Param("uname") String uname, @Param("pawd") String pawd);
    
  • 当传入的参数为对象时,可以直接在${}里填属性

二、MyBatis默认的类型别名

Alias (别名)Mapped Type
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

三、查询记录以map形式返回

1、查询一条记录

返回的map,以字段名为键。

image-20220502140403408

  1. mapper接口

    Map<String, Object> selectOne(@Param("id") Integer id);
    
  2. 映射文件

        <select id="selectOne" resultType="map">
            SELECT * FROM t_user WHERE id=#{id}
        </select>
    

2、查询多条记录

  1. 用List<Map<String, Object>>作为返回类型

    返回的是以map为单位的集合,map中的键任然是字段名

    image-20220502140946974

  2. 加入参数@MapKey()

    @MapKey()里面填字段名,且该字段的值不会重复出现,例如@MapKey("id")。

    返回的map,以该字段的值为键,值就是每一条记录。

    image-20220502141830264

    mapper接口

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

四、模糊查询

1、直接使用字符串拼接

只能用${},不能用#{}

SELECT * FROM t_user WHERE uname LIKE '%${name}%'

2、使用concat

${}和#{}都能用

SELECT * FROM t_user WHERE uname LIKE CONCAT('%',#{name},'%')

五、批量删除

用IN

DELETE FROM t_user WHERE id IN (${ids})

这里只能用${},因为这里ids是以逗号为分割的有多个id的字符串如“1,2,3”,如果用#{}会自动加上单引号,

就会变成IN (‘1,2,3’)不能被解析。所以应该用${},就变成了IN (1,2,3)可以被解析

六、动态设置表名

只能用${},因为#{}会自动加上单引号

SELECT * FROM ${tableName}

七、插入数据时获取自动递增的主键的值

  1. 在SQL标签,设置属性useGeneratedKeys="true",设置keyProperty="传输到映射文件的对象的某个属性"

    useGeneratedKeys:设置当前标签中的sql使用了自增的主键

    keyProperty:将自增的主键的值赋值给传输到映射文件的对象的某个属性

        <insert id="insertUser2" useGeneratedKeys="true" keyProperty="id">
            INSERT INTO t_user values (null, #{uname}, #{pawd})
        </insert>
    
  2. 使用

        public void insertUser2(){
            SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            User user = new User(null, "cjj", "151151");
    
            mapper.insertUser2(user);
    
            System.out.println(user.getId());
    
        }
    

    image-20220502163647620

八、数据库字段名和POJO属性名不一致

1、给字段名起别名

SELECT id,dep_name depName FROM t_dep WHERE id=#{id}

2、在核心配置文件添加settings标签

写在configuration标签下

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

给带下划线的字段名起驼峰别名,全局有效。

3、使用resultMap

    <resultMap id="depMap" type="Dep">
        <id property="id" column="id"></id>
        <result property="depName" column="dep_name"></result>
    </resultMap>
    <select id="getDep" resultMap="depMap">
        SELECT * FROM t_dep
    </select>

select标签中的resultMap属性中,填resultMap标签的id。

resultMap标签中,type属性填表所对应的POJO,id标签只能用与映射表的主键,result标签用于普通字段,property属性填POJO的属性,column填表的字段名。(使用resultMap需要把所有的POJO属性与表的字段映射起来,即使有些属性与字段名一致)

5月3日

一、处理多对一关系

在多的一方的POJO设置一的属性

public class Emp {

  private Integer empId;
  private String empName;
  private Integer empSex;
  private Dep dep;

1、通过级联赋值

SQL语句要连表查询,然后再通过resultMap给属性赋值

    <resultMap id="EmpMap" type="Emp">
        <id property="empId" column="emp_id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <result property="dep.depId" column="dep_id"></result>
        <result property="dep.depName" column="dep_name"></result>
    </resultMap>
    <select id="selectEmpInfo" resultMap="EmpMap">
        SELECT * FROM t_emp LEFT OUTER JOIN t_dep td on td.dep_id = t_emp.dep_id
    </select>

2、通过association标签

association标签专门用来处理多对一映射关系,javaType里填写该属性的类型

    <resultMap id="EmpMap" type="Emp">
        <id property="empId" column="emp_id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <association property="dep" javaType="Dep">
            <id property="depId" column="dep_id"></id>
            <result property="depName" column="dep_name"></result>
        </association>
    </resultMap>
    <select id="selectEmpInfo" resultMap="EmpMap">
        SELECT * FROM t_emp LEFT OUTER JOIN t_dep td on td.dep_id = t_emp.dep_id
    </select>

3、分步查询

    <resultMap id="EmpMap2" type="Emp">
        <id property="empId" column="emp_id"></id>
        <result property="empName" column="emp_name"></result>
        <result property="empSex" column="emp_sex"></result>
        <association property="dep" column="dep_id" select="com.cjy.mapper.DepMapper.getDepNameById" ></association>
    </resultMap>
    <select id="selectEmpInfoByStep" resultMap="EmpMap2">
        SELECT * FROM t_emp
    </select>

column="dep_id"的dep_id会传入第二个SQL中,第二个SQL的查询结果再赋值给dep

分布查询的优点

  • 可以实现延迟加载

    延迟加载:第二步sql只会在需要的时候才调用

    比如你只使用了查询结果中第一步SQL获得的属性,那么它只会执行第一步,第二步不会执行

    默认是不会开启延迟加载的。需要在核心配置文件中配置。

    开启lazyLoadingEnabled:延迟加载的全局开关,当开启是所有关联对象都会延迟加载

    关闭aggressiveLazyLoading:当开启时任何方法调用都会加载该对象的所有属性。关闭则每个属性会按需加载。

        <settings>
            <setting name="lazyLoadingEnabled" value="true"/>
            <setting name="aggressiveLazyLoading" value="false"/>
        </settings>
    

    开启了延迟加载后,也可在association标签使用fetchType关闭延迟加载

    <association property="dep" column="dep_id" select="com.cjy.mapper.DepMapper.getDepNameById" fetchType="eager"></association>
    



5月4日

一、处理一对多关系

在一的POJO设置多的集合

1、级联查询使用collection标签

    <resultMap id="DepInfoMap" type="Dep">
        <id property="depId" column="dep_id"></id>
        <result property="depName" column="dep_name"></result>
        <collection property="emps" ofType="Emp">
            <id property="empId" column="emp_id"></id>
            <result property="empName" column="emp_name"></result>
            <result property="empSex" column="emp_sex"></result>
        </collection>
    </resultMap>

    <select id="getDepInfoById" resultMap="DepInfoMap">
        SELECT * FROM t_dep LEFT JOIN t_emp ON t_dep.dep_id=t_emp.dep_id WHERE t_dep.dep_id=#{depId}
    </select>

collection标签的ofType属性中填集合中元素的类型

不能在emp中又查询dep

2、分布查询

    <resultMap id="DepAndEmpByStep" type="Dep">
        <id property="depId" column="dep_id"></id>
        <result property="depName" column="dep_name"></result>
        <collection property="emps" column="dep_id" select="com.cjy.mapper.EmpMapper.selectEmpByDepId"></collection>
    </resultMap>

    <select id="getDepAndEmpByStep" resultMap="DepAndEmpByStep">
        SELECT * FROM t_dep WHERE dep_id=#{depId}
    </select>

分布查询可以实现延迟加载

二、动态SQL

1、if标签

使用if标签必须搭配test属性

    <select id="getEmpByCondition" resultMap="EmpByCondition">
        SELECT * FROM t_emp WHERE 1=1
        <if test="empName != null and empName != ''">
            AND emp_name=#{empName}
        </if>
        <if test="empSex != null">
            AND emp_sex=#{empSex}
        </if>
        <if test="dep.depId != null">
            AND dep_id=#{dep.depId}
        </if>
    </select>
  • 在if标签的test属性中,获取传入的参数不需要#{}或${},直接写参数名就行了。

  • 在if标签的test属性中,并且用and,或者用or。

  • 只有if条件成立才会把if标签中的sql语句拼接上去。

  • 1=1是为了解决AND的拼接问题

2、where标签

        SELECT * FROM t_emp
        <where>
            <if test="empName != null and empName != ''">
                AND emp_name LIKE concat('%',#{empName},'%')
            </if>
            <if test="empSex != null">
                AND emp_sex #{empSex}
            </if>
            <if test="dep.depId != null">
                AND dep_id=#{dep.depId}
            </if>
        </where>
  • 如果where标签的if有一个成立则在执行sql时自动生成一个WHERE
  • where标签可以自动去掉内容前,多余的AND或者OR
  • where标签不可以自动去掉内容后的,多余的AND或者OR

3、trim标签

    SELECT * FROM t_emp
    <trim prefix="WHERE" prefixOverrides="AND">
        <if test="empName != null and empName != ''">
            AND emp_name LIKE concat('%',#{empName},'%')
        </if>
        <if test="empSex != null">
            AND emp_sex=#{empSex}
        </if>
        <if test="dep.depId != null">
            AND dep_id=#{dep.depId}
        </if>
    </trim>
  • 只要trim标签中有一个if成立,则prefix和suffix属性的值,会被加到trim标签整个SQL的首部或尾部
  • prefixOverrides,去除sql语句前面的关键字或者字符
  • suffixOverrides,去除sql语句后面的关键字或者字符

4、foreach标签实现批量操作

    <insert id="insertEmp">
        INSERT INTO t_emp VALUES
        <foreach collection="emps" item="emp" separator=",">
            (#{emp.empId},#{emp.empName},#{emp.empSex},#{emp.dep.depId})
        </foreach>
    </insert>
    <delete id="deleteEmp">
        DELETE FROM t_emp WHERE emp_id IN
        <foreach collection="empIdS" item="empId" separator="," open="(" close=")">
            #{empId}
        </foreach>
    </delete>
  • collection属性:填写传入的集合或者数组对象,不用加#{}或${}
  • item:代表每次循环取出的集合或者数组的元素
  • separator:填写循环体之间的分隔符
  • open:foreach标签循环的整个所有内容的开始符
  • close:foreach标签循环的整个所有内容的结束符

5、sql标签

用于设置sql片段

  • 设置sql片段

    <sql id="empColumns">emp_id,emp_name,emp_sex,dep_id</sql>
    
  • 引用sql片段

    SELECT <include refid="empColumns"></include> FROM t_emp
    



5月5日

一、MyBatis缓存

1、一级缓存

一级缓存缓存是默认开启的

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。

使一级缓存失效的四种情况

  • 不同的SqlSession对应不用的一级缓存

  • 同一个SqlSession但是查询条件不同

  • 同一个SqlSession两次查询期间执行了任何一次增删改操作

  • 同一个SqlSeesion两次查询期间手动清空了缓存

    sqlSession.clearCache();
    

2、二级缓存

二级缓存是SqlSessionFactory级别,通过一个SqlSessionFactory创建的SqlSession查询的结果会被缓存。若再次执行相同的查询语句,结果就会从缓存中获取

1)二级缓存开启条件

  1. 在核心配置文件中,设置全局属性(setting标签中设置)cacheEnabled="true",默认就是为true,所以不需手动设置

  2. 在映射文件中设置标签

  3. 二级缓存必须在SqlSession关闭或提交之后才有效

    sqlSession.commit() 或 sqlSession.close()

    就算在生成sqlSession时开启了自动提交事务,也要使用二级缓存还需使用commit或close,因为自动提交事务只是提交事务,一级缓存中的数据不会写入二级缓存。commit或close才能将一级缓存数据写入二级缓存。

  4. 查询的数据所转换的实体类型必须实现序列化接口Serializable

2)二级缓存失效的情况

两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

3)二级缓存的相关配置

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

  • eviction属性:缓存回收策略
    • LRU(Least Recently Used)- 最近最少使用的:移除最长时间不被使用的对象
    • FIFO(Fist In First Out) - 先进先出:按对象进入缓存的顺序来移除它们
    • SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象
    • WEAK - 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    • 默认是LRU
  • flushInterval属性:刷新缓存的间隔,单位毫秒
    • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用增删改语句时刷新
  • size属性:引用数目,正整数
    • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出。
  • readOnly属性:只读,true/false
    • true:只能读缓存,会给所有调用者返回缓存对象实例。因此这些对象不能被修改(返回的是缓存对象实例本身,如果被修改,下次读取的就是被修改后的值)。这提供了很重要的性能优势。
    • false:能读能写缓存,会返回缓存对象的拷贝(通过序列化)。这回慢一些,但是安全。
    • 默认false

4)缓存查询的顺序

  1. 先查询二级缓存
  2. 如果二级缓存没有命中,再查询一级缓存
  3. 如果一级缓存也没有命中,则查询数据库
  4. sqlSession提交(commit)或关闭(close)之后,一级缓存中的数据会写入二级缓存。

3、整个第三方缓存EHCache

第三方二级缓存,只有二级缓存才能用第三方的,一级缓存只能用默认的。

  1. 添加依赖

        <!-- 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>
    
  2. 创建EHCache的配置文件ehcache.xml

    文件名必须为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="C:\Users\73577\Desktop"/>
        <defaultCache
                maxElementsInMemory="1000"
                maxElementsOnDisk="10000000"
                eternal="false"
                overflowToDisk="true"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>
    
  3. 在cache标签设置使用第三方二级缓存

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    
  4. 创建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>
        <!-- 根据特殊需求指定局部日志级别 -->
        <logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
    </configuration>
    

二、MyBatis逆向工程

帮我们生成POJO,MAPPER,映射文件

  1. 在pom中加入插件

    插件中所需的依赖要先下载好才能使用

    不能写在标签里

    <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
            <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.4.0</version>
                </dependency>
                <!-- 数据库连接池 -->
                <dependency>
                  <groupId>com.mchange</groupId>
                  <artifactId>c3p0</artifactId>
                  <version>0.9.5.5</version>
                </dependency>
                <!-- MySQL驱动 -->
                <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.27</version>
                </dependency>
              </dependencies>
            </plugin>
    
  2. 创建逆向工程的配置文件

    文件名必须是: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="MyBatis3Simple">
    
    <!--        <properties resource="jdbc.properties"/>-->
            <!-- 数据库的连接信息 -->
            <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                            connectionURL="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"
                            userId="root"
                            password="136011760">
            </jdbcConnection>
            <!-- javaBean的生成策略-->
            <javaModelGenerator targetPackage="com.cjy.mybatis.pojo" targetProject=".\src\main\java">
                <property name="enableSubPackages" value="true" />
                <property name="trimStrings" value="true" />
            </javaModelGenerator>
            <!-- SQL映射文件的生成策略 -->
            <sqlMapGenerator targetPackage="com.cjy.mybatis.mapper"
                             targetProject=".\src\main\resources">
                <property name="enableSubPackages" value="true" />
            </sqlMapGenerator>
            <!-- Mapper接口的生成策略 -->
            <javaClientGenerator type="XMLMAPPER"
                                 targetPackage="com.cjy.mybatis.mapper" targetProject=".\src\main\java">
                <property name="enableSubPackages" value="true" />
            </javaClientGenerator>
            <!-- 逆向分析的表 -->
            <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
            <!-- domainObjectName属性指定生成出来的实体类的类名 -->
            <table tableName="t_emp" domainObjectName="Emp"/>
            <table tableName="t_dep" domainObjectName="Dep"/>
        </context>
    </generatorConfiguration>
    
    
    • 数据库的连接信息不能引入外部的properties
    • 如果包或者目录不存在会帮我们自动创建
  3. 点击mybatis-generator自动帮我们生成POJP,MAPPER,映射文件

    重新生成时,最好把以前生成的文件删除了,再重新生成

  4. 使用

        public void test(){
    
            SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    
            EmpExample empExample = new EmpExample();
    
            empExample.createCriteria().andEmpNameLike("%王%");
            empExample.or().andEmpNameEqualTo("李四");
    
            List<Emp> emps = mapper.selectByExample(empExample);
    
            for ( Emp emp : emps ){
                System.out.println(emp);
            }
    
        }
    }
    
    • selectByExample:按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据
    • example.createCriteria().xxx:创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系
    • example.or().xxx:将之前添加的条件通过or拼接其他条件
    • updateByPrimaryKey:通过主键进行数据修改,如果某一个值为null,也会将对应的字段改为null
    • updateByPrimaryKeySelective():通过主键进行选择性数据修改,如果某个值为null,则不修改这个字段

三、分页插件

1、配置分页插件

  1. 添加依赖

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.3.0</version>
    </dependency>
    
    
  2. 在MyBatis核心配置文件中配置插件

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

2、使用分页插件

  • 在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能

    • pageNum:当前页的页码
    • pageSize:每页显示的条数
  • 获取与当前页相关的数据

    • 在查询获取list集合之后,使用PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)获取分页相关数据
      • list:分页之后的数据,查询结果得到的list
      • navigatePages:导航分页的页码数
      • 其中泛型填查询结果list中元素的属性
  • 常用数据

    • pageNum:当前页的页码

    • pageSize:每页显示的条数

    • size:当前页显示的真实条数

    • total:总记录数

    • pages:总页数

    • prePage:上一页的页码

    • nextPage:下一页的页码

    • isFirstPage/isLastPage:是否为第一页/最后一页

    • hasPreviousPage/hasNextPage:是否存在上一页/下一页

    • navigatePages:导航分页的页码数

    • navigatepageNums:导航分页的页码,[1,2,3,4,5]