不用注解而用xml的MyBatis(二)—— 进阶

除了mybatis,其他框架都是用注解更方便。由于mybatis中需要写sql语句的特殊性,mybatis是个例外,用xml配置文件更方便

一、配置文件详解

1.1 核心配置文件

官方建议命名为mybatis-config.xml,核心配置文件里可以进行如下的配置:

 <environments> <environment>

mybatis可以配置多套环境(开发一套、测试一套、、、),

 在<environment>里必须要配置<transactionManager><dataSource>两个标签,<transactionManager>有两种选择JDBC|MANAGED,一般就写JDBC就行。

<properties>标签引入外部 xx.properties文件,我们把各种配置信息写在properties文件里,然后在<property>标签用${}符号引入。实现了其他配置信息和mybatis配置信息的解耦。

新建一个db.properties文件,

这样,如果数据库的信息需要更改,我们只需要更改db.properties文件就可以了,和mybatis毫无关系。

当然,我们也可以在<properties>标签里配置属性,但是不推荐这样。当同时在db.properties文件和<properties>标签里配置了相同的属性时,优先使用.properties文件里的。如下图所示,虽然我们在<properties>标签里配置了driver的属性,但是生效的依然是db.properties文件里的配置。

 <typeAliases>起别名:之前我们在Mapper.xml里指定resultType和parameterType时,一直用的全限定名,有多个SQL语句就要重写多次全限定名,别名的作用就在于简化全限定名,意义仅在于让我们少打几个字,看着舒服些。有两种起别名的方式:

方式(一):直接起别名

 方式(二):指定包名,这个包下的所有类都可以用自己类名的小写(推荐小写,也可以用大写)作为别名了。和方式(一)相比的缺点在于不能自己diy一个新的名字,也不是不可以,可以用@Alias注解实现。。。

  <settings>标签

<settiings>标签极为重要,因为会改变mybatis的运行时行为。下面框出来的两个是最常用的设置。除此之外,还有日志工厂的设置,下面会单独拿出来讲。

<plugins> 一些强有力的插件 

<mappers>映射器标签,有4种定义方式,以后一律用第1种。

但是还是讲一下方式(三)和方式(四)必须满足的两点:

  • UserMapper.xml文件和UserMapper接口必须同名
  • UserMapper.xml文件和UserMapper接口必须在同一个包下

二、生命周期和作用域

生命周期和作用域是至关重要的,因为错误的使用会导致严重的并发问题

SqlSessionFactoryBuilder

  • 一旦创建了SqlSessionFactory,就不再需要它了
  • 只使用一次,所以它是局部变量

SqlSessionFactory

  • 可以想象为数据库连接池
  • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没问任何理由丢弃它或重新创建另一个实例
  • 因此SqlSessionFactory的最佳作用域是应用作用域
  • 最简单就是使用单例模式或者静态单例模式

SqlSession

  • 想象为连接到连接池的一个请求
  • SqlSession的实例不是线程安全的,因此是不能被共享的,最好的实践是把它放在一个方法里
  • 用完之后需要赶紧关闭 sqlSession.close() ,否则会占据资源

三、ResultMap结果集 是mybatis最重要的属性

结果集标签<resultMap>的作用是什么?当实体类的属性名字和数据库中的字段名字不匹配时,用<resultMap>标签将二者映射/对应起来。

比如,当我们将实体类User的密码属性从“passWord”改为“pwdpwd”,而数据库中的字段仍然是password时,密码会被mysql查出来,但是映射不到程序当中去,所以程序产出的结果是null。

有两个很简单的办法,一个是更改代码,一个是更改mysql查询语句,但是都不推荐,因为我们不希望代码发生变动。先来看一下

① 更改代码:只需要把setter和getter方法的名字改成数据库字段的名字。mybatis的类型匹配器就自动给匹配上了。对了,之前没提到过类型匹配器。它的作用就是在如下代码中把mysql的查询结果根据命名,依靠setter方法,set到实体类上去。

List<User> allUser = userMapper.getAllUser();  //类型匹配器负责把mysql的结果匹配到实体类上

 ② 修改SQL语句,给数据库字段起别名,别名就是实体类中跟字段名不一样的那个的属性名

 

这两种方法都修改了原来已经写好的代码,并不建议这样做!

真正实践中的做法是用<resultMap>标签实现数据库字段名和实体类属性名的映射。

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

<mapper namespace="dao.UserMapper">
    <!--随便起一个id, 被下面的select标签的resultMap属性引用-->
    <resultMap id="UserMap" type="pojo.User">
        <result column="password" property="pwdpwd"/> <!--column是数据库的字段,property是实体类的属性-->
    </resultMap>
        
                            <!--注意是resultMap不是resultType-->
    <select id="getAllUser" resultMap="UserMap" >  
        select * from `user`		
    </select>
</mapper>

 但是,世界总是这么简单就好了。上面我们只是查了user这一个数据库表,但是很多时候需要连表查询,那么resultMap标签的type属性就不能是任何一个属性了,因为是多个表的字段组成的结果,这时候这么办呢?

四、日志工厂

在mybatis核心配置文件mybatis-config.xml里配用<settings><setting>标签置日志工厂。

作用:输出完整的SQL语句和执行过程,帮助我们排错

<configuration>
  <settings>
    ...
    <setting name="logImpl" value="LOG4J"/>
    ...
  </settings>
</configuration>

logImpl 可选的值有:

  • SLF4J
  • LOG4J (常用)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING (常用,标准日志)
  • NO_LOGGING
  • 或者其他

logImpl不带有默认值,所以当我们没有手动设置logImpl时,默认不输出任何日志。

以标准日志为例演示:

4.1 Log4j 讲解

1、先导包

使用一个外部类,先导包,一个开源包,被很多公司使用

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

 19年教程里正常是导log4j包,但是我看他说以后在log4j-core更新,那我就下一个log4j-core试一试

<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.19.0</version>
</dependency>

 并且我看jd也是这样用的

不行,log4j-core要配合slf4j这个统一接口才能使用,单独用会出错,单独用的话还是下载2012年停止更新(因为太完善了)的log4j 1.2.17。

PS: 解释什么是slf4j

① 当我们使用Log4j这个日志库处理日志信息时,就与其特定的类库发生了耦合,而日志库还有java.util.logging、logback等等,想象一下,若你的项目使用的是Log4j,当哪天你的项目需要引入别人编写的一个组件,而这个组件却是使用logback处理日志的,那我们就不得不导入两个实现同样功能的jar包并且维护两套日志配置了。
这时候就体现出Slf4j统一规范接口的价值了,因为Slf4j只是一个接口,我们只是通过调用slf4j接口的日志方法统一打印日志,可以忽略日志的具体实现方法。

② 而log4j-core就是配合slf4j这个接口而写的,所以它叫“core”,外面再去套slf4j这个接口

2、配置文件

 Log4j支持两种配置文件格式,一种是XML格式的文件,一种是properties(key=value)文件,其中properties格式的配置文件最为常用,其有一个固定的文件名log4j.properties

PS: 貌似log4j2开始,xml更常用了,因为jd用的就是log4j2.xml。在springboot里我们也讲过xml比properties减少了冗余。 

log4j.rootLogger=DEBUG,console,file


#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis = DEBUG
log4j.logger.java.sql = DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3、使用

因为不止一个方法里用到logger去打印日志,所以我们要给它提升作用域,作为类的static final变量。

static final Logger logger = Logger.getLogger(MyTest.class);

五、分页查询

法(一)用limit

法(二)RowBounds——不会SQL语句也能实现的分页

老方法了,没有mysql效率快,现在没人用了

六、使用注解开发

学了spring之后我们知道注解可以代替xml配置文件,mybatis里自然也可以。

我们把UserMapper.xml删掉,核心配置文件里自然也就不需要绑定Mapper.xml配置文件,而是直接绑定UserMapper接口,在接口方法上用@Select注释写sql语句。

但是,我们很容易想到,@Select注释只适合于简单的sql语句(确实会让简单的sql变得更简单),倘若sql需要入参,或者当数据库字段名和实体类变量名不匹配需要resultMap来解决的时候,使用注解会格外复杂。所以mybatis这个框架不用注解,只用配置文件,而其他框架都推荐用注解。

6.1 使用注解实现增删改查

public interface UserMapper {
    @Select("select * from user")
    List<User> getAllUser();

    @Insert("insert into `user` values(#{id}, #{username}, #{password})")
    void addUser(User user);

    @Update("update `user` set username=#{username},password=#{password} where id=#{id}")
    void updateUser(User user);

    @Delete("delete from `user` where id = #{id}")
    void deleteUser(@Param("id") String id);
}
@Test
    public void test01(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        try {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            //1. --------------查
            /*List<User> allUser = mapper.getAllUser();
            for(User u:allUser){
                System.out.println(u.toString());
            }*/
            
            //2.---------------增
//            mapper.addUser(new User("44","newUser","88888"));
//            sqlSession.commit();
//
//            List<User> allUser = mapper.getAllUser();
//            for(User u:allUser) {
//                System.out.println(u.toString());
//            }
            
            //3.---------------改
//            mapper.updateUser(new User("44","7/4","李涵颖去淄博烧烤"));
//            sqlSession.commit();
//
//            List<User> allUser = mapper.getAllUser();
//            for(User u:allUser) {
//                System.out.println(u.toString());
//            }
            
            //4.--------------删
            mapper.deleteUser("44");
            sqlSession.commit();
            List<User> allUser = mapper.getAllUser();
            for(User u:allUser) {
                System.out.println(u.toString());
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

七、mybatis的执行流程

之前我们跟着教程创建mybatis工具类,写Mapper.xml,写Mapper接口,就可以执行SQL语句并返回结果了,那么mybatis究竟是怎么做到的呢?

通过打断点,一步步debug看变量总结出:

粗框部分是我们写代码可见的,其他是底层帮我们去做的。

八、多对一、一对多情况下mybatis如何应对

8.1 多个学生对一个老师

创建学生表和老师表,5个学生对应同一个老师。

CREATE TABLE  `teacher`(
    `id` INT(10) NOT NULL,
    `name` VARCHAR(30) DEFAULT NULL,
    PRIMARY KEY (`id`)
)ENGINE = INNODB DEFAULT CHARSET =utf8

INSERT INTO teacher(`id`,`name`) VALUES(1,'瓜老师')

CREATE TABLE `student`(
    `id` INT(10) NOT NULL,
    `name` VARCHAR(30) default NULL,
    `tid` INT(10) DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `fktid`(`tid`),
    CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher`(`id`)
)ENGINE=INNODB DEFAULT CHARSET =utf8

INSERT INTO `student`(`id`,`name`,`tid`) VALUES('1','小明','1');
INSERT INTO `student`(`id`,`name`,`tid`) VALUES('2','小赵','1');
INSERT INTO `student`(`id`,`name`,`tid`) VALUES('3','小牛','1');
INSERT INTO `student`(`id`,`name`,`tid`) VALUES('4','小李','1');
INSERT INTO `student`(`id`,`name`,`tid`) VALUES('5','小子','1');

创建实体类Student,Teacher,创建对应的mapper接口和mapper.xml文件并绑定到mybatis核心配置文件中。

=============Student.java==============
@Data
public class Student {
    int id;
    String name;
    Teacher teacher;  //学生对应的老师
}

=============Teacher.java==============

@Data
public class Teacher {
    int id;
    String name;
    //老师下面暂时没有对应的学生
}
=============StudentMapper.java==============

public interface StudentMapper {
    public List<Student> getStudent();
}

=============TeacherMapper.java==============
public interface TeacherMapper {

    Teacher getTeacherById(@Param("id") int id);

}

测试环境搭建好了。现在我们要查“所有学生以及学生对应的老师是谁”,是多对一

法(一) 基于子查询的嵌套——不推荐使用 (难)

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

<mapper namespace="dao.StudentMapper">
    <select id="getStudent" resultMap="StudentTeacher">
        select * from student
    </select>

    <resultMap id="StudentTeacher" type="pojo.Student">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <!--对象:association 集合:collection-->
        <association property="teacher" column="tid" javaType="pojo.Teacher" select="getTeacherById"></association>
    </resultMap>

    <select id="getTeacherById" resultType="pojo.Teacher">
        select * from teacher where id = #{id}
    </select>
</mapper>

结果如下: 

 法(二)SQL查询结果嵌套

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

<mapper namespace="dao.StudentMapper">
    <select id="getStudent" resultMap="StuTeaMap">
        select s.id as sid, s.name as sname, t.name as tname
        from student s,teacher t
        where s.tid=t.id
    </select>

    <resultMap id="StuTeaMap" type="pojo.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="pojo.Teacher"> <!--Student的teacher属性是Teacher类型的-->
            <result property="name" column="tname"/>   <!--Teacher类的name属性对应tname字段-->
        </association>
    </resultMap>

</mapper>

8.2 一个老师对应多个学生

首先也是环境搭建,与上面的唯一不同就在于实体类的定义上。学生只需要记录老师的id即可,老师记录自己教的学生列表。


@Data
public class Student {
    int id;
    String name;
    //Teacher teacher;
    int tid; //现在学生只需要记录老师的id即可
}

@Data
public class Teacher {
    int id;
    String name;
    List<Student> studentList;
}

现在我们要根据id查老师,并查出老师管理的学生都有谁,我们希望的结果是这样的

Teacher(id=1, name=瓜老师, studentList=[Student(id=1, name=小明, tid=1), Student(id=2, name=小赵, tid=1), Student(id=3, name=小牛, tid=1), Student(id=4, name=小李, tid=1), Student(id=5, name=小子, tid=1)])

法(一):按结果嵌套查询。

teacherMapper.xml

<mapper namespace="dao.TeacherMapper">
    <select id="getTeacherById" resultMap="TeaStuMap">
        select t.id as tid, t.name as tname, s.id as sid ,s.name as sname
        from teacher t,student s
        where t.id = s.tid AND  t.id=#{id}
    </select>

    <resultMap id="TeaStuMap" type="pojo.Teacher">
        <result property="id" column="tid"></result>
        <result property="name" column="tname"></result>
        <collection property="studentList" ofType="pojo.Student"> 
            <!--注意这里是ofTyoe,不是javaType。如果用javaType的话,应该填“List”,因为
            studentList的java类型就是List,但我们想要的是List里面元素的类型-->
            <result property="id" column="sid"></result>
            <result property="name" column="sname"></result>
            <result property="tid" column="tid"></result>
        </collection>
    </resultMap>
</mapper>

注意:ofType和javaType的区别。

结果:

法(二):子查询嵌套

不用也不想写了。

九、动态SQL

动态SQL:之前在mybatis里写的SQL语句都是我们一气呵成写完的。mybatis的强大之处在可以根据条件拼接SQL,叫做动态SQL

9.1 学习环境搭建

先建立一个数据库表

CREATE TABLE `blog`(
    `id` varchar(50) NOT NULL COMMENT '博客id',
    `title` varchar(100) NOT NULL COMMENT '博客标题',
    `author` varchar(30) NOT NULL COMMENT '博客作者',
    `create_time` datetime NOT NULL COMMENT '创建时间',
    `views` int(30) NOT NULL COMMENT '浏览量'
)ENGINE=InnoDB DEFAULT CHARSET =utf8

然后我们新建一个项目,利用mybatis向表里插入数据。

==============Blog.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
    private String id; //博客id
    private String title; //博客标题
    private String author; //博客作者
    private Date createTime; //1.注意这里要用java.util.Date而不是java.sql.Date
                            //2.数据库里是用下划线分割“create_time”,而java里我们习惯用的是驼峰规则。当然可以通过resultmap实现映射,但是还有一个更简单的方法是直接在settings配置里开启转换
    private int views; //浏览量
}

============mybatis核心配置文件开启驼峰转换
<configuration>
     ......
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
      ......
</configuration>

==========BlogMapper.java
public interface BlogMapper {
    int addBlog(Blog blog);
}

===========BlogMapper.xml
<mapper namespace="dao.BlogMapper">
    <insert id="addBlog" parameterType="pojo.Blog">
        insert into blog
        values(#{id},#{title},#{author},#{createTime},#{views})
    </insert>
</mapper>

===========新增一个生成UUTD的工具类
public class UUIDutils {
    public static String getUUID(){
        return UUID.randomUUID().toString().replaceAll("-","");// 生成没有短横杠的UUID
    }
}

=============测试类
@Test
    public void test(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog01 = new Blog(UUIDutils.getUUID(),"Mybatis如此简单","author",new Date(),888);
        Blog blog02 = new Blog(UUIDutils.getUUID(),"spring如此简单","author",new Date(),765);
        Blog blog03 = new Blog(UUIDutils.getUUID(),"springMVC如此简单","author",new Date(),2351);
        Blog blog04 = new Blog(UUIDutils.getUUID(),"springboot如此简单","author",new Date(),34);
        
        mapper.addBlog(blog01);
        mapper.addBlog(blog02);
        mapper.addBlog(blog03);
        mapper.addBlog(blog04);
        sqlSession.commit(); //提交!
        sqlSession.close();
        
    }

注意01:企业里数据库表的id不会手动添加,都是用UUID随机生成。因为如果是手动添加的话,比如添加了从id=1到id=9的9个用户,然后删掉了id=7的用户,那么这个表里再也不会出现id=7的用户,除非你insert进去一个。 

注意02:在<settings>里开启带短横杠数据库字段名向驼峰命名的转换

 9.2 动态SQL之IF语句

需求:写一个查找博客的接口,如果参数传入title,就按title去查,如果传入作者,就按作者去查,如果二者都有,就用AND去查,如果都没有,就查出来全部博客。这就用到了动态SQL语句!

==========新增查询接口
public interface BlogMapper {
    List<Blog> queryBlogs(Map map);
}

==========BlogMapper.xml新增
   <select id="queryBlogs" parameterType="map" resultType="pojo.Blog">
        select * from blog where 1=1
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author !=null ">
            and author = #{author}
        </if>
    </select>

============测试类
@Test
    public void test(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap<String, String> map = new HashMap<String, String>();
        //1.查询条件title和author都有
        map.put("title","mybatis如此简单");
        map.put("author","author");
        
        //2.查询条件:只有title
        //map.put("title","mybatis如此简单");
        
        //3.查询条件,只有author
        //map.put("author","author");
        List<Blog> blogs = mapper.queryBlogs(map);
        for(Blog b:blogs){
            System.out.println(b.toString());
        }
        sqlSession.close();

    }

无论查询条件是否有title,是否有author,都能用queryBlog这一个接口查。

9.3 <where>标签

第一个<if>标签的例子其实有个弊端

 那<where>标签就是来解决这个问题的:

  • 当所有查询条件都为null时,<where>标签会帮我们删除where子句
  • 当AND开头的查询条件被迫开头时,<where>标签会帮我们删除“AND”

 

 

9.4 <choose> <when> <otherwise>标签

需求:当查询条件有A时,我们直接用A去查,当没有A但有B时,我们用B去查;当A也有,B也有的时候,我们只用A去查(其实就是A的优先级高于B),二者都没有的时候我们设置一个“保底”的条件去查。和switch语句的含义一样。

=============BlogMapper.java新增接口
public interface BlogMapper {
    List<Blog> queryBlogsChoose(Map map);
}


==============BlogMapper.xml
<select id="queryBlogsChoose" parameterType="map" resultType="pojo.Blog">
        select * from blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author != author">
                    AND author = #{author}
                </when>
                <otherwise>
                    views > 200
                </otherwise>
            </choose>
        </where>
</select>

 

9.5 <set>标签

作用:同<where>标签。<set>标签用于update语句,会自动加上set关键字,并且会自动删去无关的逗号

9.6 <where>和<set>的爸爸——<trim>标签

相当于<where>,注意“AND|OR”是前缀

<trim prefix="where" prefixOverrides="AND|OR"></trim>

 相当于<set>,注意“,”是SQL语句的后缀

<trim prefix="set" suffixOverrides=","></trim>

9.7 <foreach>标签

作用:对子查询(IN条件语句)得到的集合做遍历。

适合场景:因为下面的id可以有多个,在java程序里我们是通过ArrayList给出的,所以我们需要写成动态代码。如何拆分括号里的集合呢?

select * from blog where 1=1 AND (id=1 or id=2 or id=3 or id=.....)

 为了测试方便,我们将博客的id改成了1,2,3,4.

 

9.8 <sql>提取sql片段

作用:将常用的sql片段提出出来,实现代码复用。在需要使用的地方用<include>标签引入<sql>代码片段。

注意:一般我们提取的都是<if>标签的一系列判断语句,不要把提取范围扩大到<where>和<set>标签包括的范围,不然实现不了复用。

十、mybatis缓存

使用场景:经常查询但不怎么改变的数据

一级缓存和二级缓存:mybatis默认开启一级缓存,而且关不掉,也叫sqlsession级别的缓存,仅对一次会话产生的数据进行缓存。二级缓存的作用范围比一级更大,其作用域是namespace,如果觉得一级缓存还不够,想要开启二级缓存,就要用到<cache/>标签。

10.1 一级缓存

一级缓存测试: 

开启日志,用标准日志工厂就行

一旦缓存达到阈值,就会开启缓存清除策略

  • LRU最近最少使用:移除最长时间不被使用的对象 ( 默认策略)
  • FIFO:先进先出 
  • 。。。

一级缓存失效的几种情况:

  • 第一次查和第二次查不同东西,当然用不到的缓存
  • 一旦有了增删改操作,必定会刷新缓存。比如在同一个sqlSession里先查id=1的博客,然后插入了一条新博客,再去查id=1的博客的时候不走缓存。
创建sqlSession
.....
mapper.getBlog(1);
mapple.addBlog(new Blog(4,"后面属性不写了"));  //插入操作会更新缓存
mapper.getBlog(1);   //这里不走缓存!

....
sqlSession.close();
  • 查询不同的Mapper.xml肯定不走缓存
  • 手动清理缓存

10.2 二级缓存

  • 二级缓存也叫全局缓存,一级缓存的作用域太低了,所以诞生了二级
  • 基于namespace级别的缓存
  • 工作机制
    • 一个sqlSession会话查询的数据被放在这个session的以及缓存中
    • 当这个session关闭了,对应的一级缓存也就没了。但是我们想要的是,会话关闭以后,一级缓存的数据保存到二级缓存中
    • 此后开启的新的session可以从二级缓冲中获取已经“死掉”的session的数据
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中
  • 如何开启二级缓存 

1、在核心配置文件中显式地开启全局缓存 

 2、在mapper.xml文件中用<cache/>标签

3、测试

注意:两个会话必须访问的是同一个mapper,二级缓存才能生效

 如果报错“NotSerilizable”,就把实体类序列化一下        

 10.3 缓存模型

当一个sqlsession创建了一个mapper去查询的时候,先去二级缓存里找,看看这个结果是不是被其他sqlSession之前查过,查过直接拿来用就行。如果二级缓存没找到,再去一级缓存里找,看看自己(本sqlSession)是不是之前搜索过,如果一级缓存里也没有,那就直接去访问数据库吧!

 10.4 关闭缓存

如果一个字段被频繁更改,就可以设置查这个字段的时候不要从缓存当中读,而是每次都查数据库,也就是关闭缓存。

 10.5 自定义缓存EHcache

  • 一个第三方包,用的话要导入jar包。还要导一个xml配置文件
  • 比二级缓存功能更强大,可以把缓存持久化到硬盘上。
  • 但是现在被redis给淘汰了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MyBatis SQL语句的进阶主要包括以下几个方面: 1. 通过查询时自定义别名的方式解决名称不一致而导致的无法封装数据的问题。在查询语句中,可以使用AS关键字为查询结果的列指定别名,从而解决结果集与实体类属性名称不一致的问题。 2. 使用resultMap节点解决名称不一致而导致的无法封装数据的问题。resultMap节点可以在映射文件中定义,用于将查询结果映射到实体类中。通过定义resultMap节点,我们可以灵活地指定查询结果列与实体类属性之间的对应关系,从而解决名称不一致的问题。 3. 一对一的关联查询。在MyBatis中,可以通过嵌套查询或者使用association标签来实现一对一的关联查询。嵌套查询是指在resultMap中定义一个嵌套的resultMap,通过association标签来指定关联关系。这样可以在查询数据的同时,将关联的数据一并查询出来并映射到实体类中。 4. 一对多的关联查询。在MyBatis中,可以通过collection标签来实现一对多的关联查询。collection标签可以在resultMap中定义,用于指定集合属性的映射关系。通过定义collection标签,我们可以在查询数据的同时,将关联的多个数据一并查询出来并映射到实体类中。 5. 通过使用动态SQL来实现更灵活的查询。MyBatis提供了丰富的动态SQL语法,在查询时可以根据条件进行判断、循环等操作,从而实现更灵活的查询。比如可以使用if标签、choose标签、foreach标签等来动态拼接查询条件,以满足不同的查询需求。 总结:MyBatis SQL语句的进阶包括通过自定义别名、使用resultMap节点解决名称不一致的问题,实现一对一和一对多的关联查询,以及使用动态SQL实现更灵活的查询。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值