Java开发——Mybatis进阶

1. 简介

  在掌握了Mybatis的基本使用方式之后,我们需要掌握Mybatis相关的:

  • 参数性质
  • 调优方式
  • 日志
  • 动态sql编写
      同样的,官方文档仍然是最具备参考价值的。其可配置的内容如下,常用的属性已标出。
    注意:所有的配置的标签必须按照以下顺序进行编写!
    在这里插入图片描述

2. 参数配置

2.1 environments:

  可以配置多个环境,但是对于一个模块来说,只可以使用一个环境。你可以在不同的场景使用不同的环境配置,比如测试时使用一种,实际部署时采用另一个。
  每个环境都有自己的id属性。通过在environments标签的default属性中选定使用的环境。子标签有:

  • transactionManager : 指定事务的类型,默认使用JDBC。
    在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

    • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
    • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
  • DataSource:指定连接数据库的方式,可以选择连接池和单次连接。有三种内建的数据源类型,也就是 type="[UNPOOLED|POOLED|JNDI]")。

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
  
  <!--为测试配置环境-->
  <environment id="test">
    <transactionManager type="MANAGE">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

2.2 properties:

  为了方便我们和java项目中的配置文件进行 配合使用。**即将mybatis-config.xml中需要用户手写的参数,以配置文件的方式传递。**默认是db.properties.
  通常我们在resource目录下创建db.properties文件:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456

通过resource属性引入该配置文件:

<configuration>
	<!--引入配置文件,也可以在增加子标签,推荐使用外部文件-->
    <properties resource="db.properties"></properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value= "${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

2.3 类型别名——typeAliases

  通过对全限定名进行别名化处理,简化在mapper.xml配置文件中的对全限定名使用。通常有两种方式使用:

  • 使用typeAlias子标签对单个全限定名进行别名化;
  • 扫描整个包下的类,在没有使用注解的情况下,会将包下的java Bean自动转化成对应的小写字母的别名,User---->user。
  • 并且java类中有一些默认的别名,即我们经常使用的,他们不区分大小写。这也就是为什么我们之前使用map的时候,小写大写都可以。具体内容查看官方文档。要注意的是:如果你直接写int类型,其实这是一个别名,指向的是Integer类型,如果想使用基本数据类型的话,格式为_int
    <typeAliases>
        <typeAlias type="com.yindarui.POJO.User" alias="user"></typeAlias>
        <package name="com.yindarui.POJO"/>
    </typeAliases>

  使用包名的方式会使得无法修改想要的别名,我们可以在java源文件中使用注解来修改

@Alias("user")
public class User{}

2.4 settings

  这是mybatis最关键的参数设置,通过它我们可以开启和关闭许多功能。具体的内容可以去官方文档查看。

    <settings>
        <!--开启缓存-->
        <setting name="cacheEnabled" value="true"/>
        <!--开启懒加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="multipleResultSetsEnabled" value="true"/>
        <setting name="useColumnLabel" value="true"/>
        <setting name="useGeneratedKeys" value="false"/>
        <setting name="autoMappingBehavior" value="PARTIAL"/>
        <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
        <setting name="defaultExecutorType" value="SIMPLE"/>
        <setting name="defaultStatementTimeout" value="25"/>
        <setting name="defaultFetchSize" value="100"/>
        <setting name="safeRowBoundsEnabled" value="false"/>
        <!--自动转化驼峰命名-->
        <setting name="mapUnderscoreToCamelCase" value="false"/>
        <setting name="localCacheScope" value="SESSION"/>
        <!--处理数据库中的null-->
        <setting name="jdbcTypeForNull" value="OTHER"/>
        <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
    </settings>

2.5 映射器(mappers)

  MapperRepository:注册我们编写的Mapper.xml文件。注意,使用后两者就必须确保java源文件名和xml文件的文件名相同,并且还要在同一包下。

  1. 使用url来注册mapper:
    <mappers>
        <mapper resource="com/yindarui/Dao/user/UserMapper.xml"></mapper>
    </mappers>
  1. 使用class文件来注册:
    <mappers>
<!--        <mapper resource="com/yindarui/Dao/user/UserMapper.xml"></mapper>-->
        <mapper class="com.yindarui.Dao.user.UserMapper"></mapper>
    </mappers>
  1. 使用包扫描:
    <mappers>
<!--        <mapper resource="com/yindarui/Dao/user/UserMapper.xml"></mapper>-->
<!--        <mapper class="com.yindarui.Dao.user.UserMapper"></mapper>-->
        <package name="com.yindarui.Dao"/>
    </mappers>

2.6 作用域和生命周期

  涉及到管理并发的问题。生命周期主要是涉及到一些在使用mybatis的过程中的对象实例的创建方式。

1. SqlSessionFactoryBuilder

  为了得到一个SqlSessionFactory,从配置文件加载出来。一经使用就可以丢弃。通常设置为局部变量

2. SqlSessionFactory

  用于创建许多SqlSession,因为用户会源源不断地创建SqlSession并进行数据库操作,所以,这个实例就必须一致存在,以持续提供服务。可以这样理解,工厂提供服务,在整个项目运行的过程中,工厂都不应该关闭。
 &emsp所以,这个实例的最佳域为全局变量。我们可以使用单例模式来保证只有一份。

3. SqlSession

  理解为和数据库建立了一个连接。这个连接会一直保持,直到程序调用close方法。在之前的配置文件中我们也提到,我们可以设置dataSource 为 POOLED。这样我们可以指定最大连接数和活动连接数。所以,整个过程相当于,sqlSession并不是在我们调用时才实例化,而是mybatis一开始就实例化了多个SqlSession,我们从他的池子中拿出使用。
  所以该实例的作用域应该在类似于一个http请求的作用域中。即每次的http请求会导致一个SqlSession被拿出,并提供给用户使用。图片来自狂神说mybatis学习视频
在这里插入图片描述

3. Mapper.xml文件

  mybatis中的映射文件相对比较简单。常用的几个顶级元素包括:
在这里插入图片描述

3.1 resultMap

  这个元素是sql标签的属性,用于将sql返回的结果集和java中操作的集合进行映射是用户指定的一种映射规则。resultMap是 MyBatis 中最重要最强大的元素。通常适用于:

  • sql数据库中字段和java bean中字段名不一致,导致返回结果无法匹配;
  • sql返回结果过于复杂(多表联查),需要特殊处理。

字段名不一致

javabean:

// 注意,这里的password修改成了pwd,而数据库中的字段仍然是password
public class User {
    private int id;
    private String name;
    private String pwd;

Test:

    @Test
    public void UserSelectTest() {
        SqlSession sqlSession = MyBatisUtils.getSQLSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        for (User item : userList) {
            System.out.println(item.getName()+"的密码是:"+item.getPwd());
        }

        sqlSession.close();
    }

结果:
在这里插入图片描述
  其实原理就是mybatis对结果集合javabean的字段进行映射,如果有就赋值,如果没有找到对应则无法赋值。

3.2 使用resultMap

  首先利用resultMap标签指明变量之间的映射,接着在sql语句标签中调用对应的 resultMap规则即可。你只需要指定名称不匹配的字段即可,剩下的mybatis会自动匹配。

<?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="com.yindarui.Dao.user.UserMapper">
    <resultMap id="getUserInfo" type="user">
<!--        <id property="id" column="id" />-->
<!--        <result property="username" column="username"/>-->
        <result property="pwd" column="password"/>
    </resultMap>
    <select id="getUserList" resultMap="getUserInfo">
        select * from user
    </select>
</mapper>

结果:
在这里插入图片描述

4 Mybatis的日志

  日志作为一个系统里记录各种操作和事件的文件,其作用非常巨大。我们通常使用的是在控制台输出的方式观察日志。

4.1 日志工厂

  Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

使用log4j

  Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

导入log4j的包:
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
编写配置文件

建议直接在网上找现成的,然后按照自己的需求选择其中的一部分功能即可。

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
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.Threshold=DEBUG
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
在代码中使用log4j
public class UserTest {
//    logger的生命周期是很长的,因为在整个操作的过程都需要他
    private static Logger logger = Logger.getLogger(UserTest.class);

    @Test
    public void log4jTest() {
        logger.info("hello world!!");
    }
    @Test
    public  void log4jTest2() {
        SqlSession sqlSession = MyBatisUtils.getSQLSession();
        logger.info("SqlSession被使用");
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        for (User item : userList) {
            logger.info(item.getName()+", 您的密码为:"+item.getPwd());
        }
        sqlSession.close();
        logger.info("sqlSession关闭");
    }

}

结果:
在这里插入图片描述

5. 注解开发

5.1 面向接口编程

  为了实现代码的更好的解耦。关于接口的理解(来自于百度百科):

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离
  • 接口的本身反映了系统设计人员对系统的抽象理解
  • 接口应有两类:
    • 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
    • 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
  • 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。

5.2 使用注解开发

  使用步骤极其简单,但是对于复杂的sql的使用效果并不好,比如,上文提到的字段名和javabean属性名不一致时,就需要通过在sql中起别名的方式来解决,无法使用resultMap。注解开发底层使用了反射机制和动态代理设计模式。
步骤如下:

  • 在接口的方法上使用注解编写sql:
    @Select("select * from user")
    public List<User> getUserList();
    
  • 在mybatis-config.xml文件中绑定接口:
    <mappers>
        <mapper class="com.yindarui.Dao.user.UserMapper"/>
    </mappers>
    
  • 测试,操作不变。

5.3 使用@Param注解

  该注解可以理解为向mybatis注册了这个参数,这样在调用函数时就可以使用#{}的方式来调用这个参数。#{}可以防止sql注入。使用时请注意:

  • 对于基本数据类型或者String类型,需要加上该注解;
  • 其他的引用类型不需要加该注解也可以使用#{}的方式调用;
  • 如果只有一个基本参数类型的参数,可以不用该注解。

5.4 使用注解编写crud操作

在接口源文件中编写注解:

    @Select("select * from user")
    public List<User> getUserList();
//单个参数
    @Select("select * from user where id = #{id}")
    public User getUserById(@Param("id") int id);
//    多个参数
    @Update("update user set password=#{password}, name=#{name} where id=#{id}")
    public int updateUser(@Param("id") int id, @Param("name") String name,@Param("password") String password);
    
    @Insert("insert into user(id, name, password) values(#{id},#{name},#{password})")
    public int insertUser(User user);

测试:

    @Test
    public void test1() {
        SqlSession sqlSession = MyBatisUtils.getSQLSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User userById = mapper.getUserById(1);
        mapper.updateUser(1,"lisi", "123456");
        mapper.insertUser(new User(3,"wamgwu", "233333"));
        mapper.insertUser(new User(4,"zhangliu", "233333"));
        mapper.insertUser(new User(5,"yindarui", "233333"));
        List<User> userList = mapper.getUserList();
        sqlSession.close();
    }

5.5 Lombok

  lombok是一个IEDA的插件,主要是简化开发人员对实体类的操作,避免了POJO中实体类的复杂且冗余度高的代码编写。但是**,不支持构造方法的重载,我们可以手动重载**。

使用步骤

  1. 下载lombok的插件
  2. 导入jar包
  3. 使用注解开发。

注解内容:

  1. @Getter and @Setter
  2. @FieldNameConstants
  3. @ToString
  4. @EqualsAndHashCode
  5. @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
  6. @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog,@Flogger, @CustomLog
  7. @Data
  8. @Builder
  9. @SuperBuilder
  10. @Singular
  11. @Delegate
  12. @Value
  13. @Accessors
  14. @Wither
  15. @With
  16. @SneakyThrows
  17. @val
  18. @var
  19. experimental @var
  20. @UtilityClass
@Data

  自动生成有参、无参构造,getter、setter方法,toString方法,hashCode方法,canEquals、equals方法。
在这里插入图片描述  你也可以使用单个注解(比如@Getter and @Setter )来生成指定的方法。
在这里插入图片描述

6. 复杂的数据环境——多表联查

多表联查主要涉及两种关系:

  • 多对一:从多个记录出发查询,且多条记录中的某些字段是别的表中的内容,这时候后,我们必须从别的表中读取出某些字段信息并作为返回值。即从某些记录的某些属性出发,会查询出另一个记录。此时我们需要使用association标签
  • 一对多:从一条记录出发,这一条记录中的某个信息关联了别的表中的内容。此时,我们需要从别的表中读取多个记录并返回。即,依照某一记录中的某些属性,会查询出一个记录的集合。此时需要使用collection标签
      其实本质上,还是解决返回字段和属性名不一致的问题。只不过并不只是属性名不一致,而是存在一对多和多个对一个的情况。
    这一部分会涉及到许多属性,感觉官网说的不太好理解,我总结一下,这些属性是理解resultMap的关键。
  • column:字段。对应数据库中查询的内容;
  • property:属性。对应javabean中的属性;
  • id:唯一标识一个resultMap的标记;
  • type:用于指定resultMap匹配的java类名;
  • ofType:指定collection中的泛型类型;
  • JavaType:指定association中的关联的类型名。

6.1 测试

  基于学生和老师的关系,多个学生对应一个老师,一个老师管理多个学生。
测试表的关系如下:

mysql> desc student;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(10)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20) | YES  |     | NULL    |                |
| tid   | int(10)     | NO   | MUL | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> desc teacher;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(10)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

多对一测试

  此时如果我们希望查询出学生的老师姓名就需要多表联查。从sql的角度出发,根据外键tid关联teacher表中的id字段,查询出name即可。此时返回的字段是学生编号、姓名和老师姓名
  使用mybatis查询时,我们返回的字段和实体类是不对应的。所以我们还要使用resultMap属性来进行映射。但是我们是基于一个表中的一个字段来联查另一个表,此时需要使用association标签
  其实这部分的难点就在于,学生的Teacher是一个对象。在理解sql语句含义的基础上,其实就我们利用resultMap来指定对应的java类,并用字段值填充实例对象的属性值。
实体类:
Student.java

public class Student {
    private int id;
    private String name;
    // 返回的是teacher对象
    private Teacher teacher;
}

Teacher.java

public class Teacher {
    private int id;
    private String name;
}

mapper.xml文件:

<?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="com.yindarui.Dao.student.StudentMapper">
<!--多表联查的sql语句-->
    <select id="getStudentTeacherInfo" resultType="student" resultMap="StudentResultMap">
        select s.id as sid,
               s.name as sname,
               t.id as ttid,
               t.name as tname
        from student s
            left join teacher t
                on s.tid = t.id;
    </select>
<!--编写结果集映射规则-->
    <resultMap id="StudentResultMap" type="student">
        <id column="sid" property="id"/>
        <result column="sname" property="name"/>
        <association property="teacher" javaType="teacher">
            <id column="ttid" property="id"/>
            <result column="tname" property="name"/>
        </association>
    </resultMap>
</mapper>

查询结果:
在这里插入图片描述

一对多测试

  其实和上面的思路一致,只不过是需要获取一个集合。我们使用collection标签来做。核心思想就是我指定并匹配另一个java类型,并用字段值填充实例对象的属性值
Student.java

public class Student {
    private int id;
    private String name;
    private Teacher teacher;
}

Teacher.java

public class Teacher {
    private int id;
    private String name;
    private List<Student> students;
}

TeacherMapper.xml

<?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="com.yindarui.Dao.teacher.TeacherMapper">
    <select id="getTeacherInfoById" resultMap="studentResultMap">
        select t.id as tid,
               t.name as tname,
               s.id as sid,
               s.name as sname
               from teacher t
                   left join student s
                       on t.id = s.tid
    </select>
    <resultMap id="studentResultMap" type="teacher">
        <id column="tid" property="id"/>
        <result column="tname" property="name"/>
        <collection property="students" ofType="student">
            <id column="sid" property="id"/>
            <result column="sname" property="name"/>
        </collection>
    </resultMap>
</mapper>

测试方法:

    @Test
    public void TeachTest01() {
        SqlSession sqlSession = com.yindarui.Utils.MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacherInfoById = mapper.getTeacherInfoById(1);
        sqlSession.close();
    }

结果:
在这里插入图片描述

7. SQL片段

  有些时候我们可以把一些sql的片段提取出来作为公用,提高代码的复用性。操作步骤:

  • 使用sql标签提取公共的部分
  • 在需要使用的位置使用include属性引用。

8. 动态SQL

  根据不同的条件自动拼接不同的sql语句。动态SQL运行我们在sql的角度来执行一些逻辑相关的操作
MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  1. if
  2. choose (when, otherwise)
  3. trim (where, set)
  4. foreach

8.1 if语句

  即为我们提供条件判断的操作。测试内容为拼接name字段作为查询条件。

8.2 测试

接口:

  public List<Teacher> getTeacherByCondi(Map<String, Object> map);

mapper.xml文件:

    <select id="getTeacherByCondi" parameterType="map" resultType="Teacher" >
        select * from teacher
        where 1 = 1
        <if test="name != null">
            and name = #{name}
        </if>
    </select>

测试方法1:存在sql拼接

    @Test
    public void TeacherTestForIf() {
        SqlSession sqlSession = com.yindarui.Utils.MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Map<String, Object> map = new HashMap();
        map.put("name", "yindarui");
        List<Teacher> teacherByCondi = mapper.getTeacherByCondi(map);
        sqlSession.close();
    }

结果:
在这里插入图片描述
测试方法2:name没有指定

    @Test
    public void TeacherTestForIf() {
        SqlSession sqlSession = com.yindarui.Utils.MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Map<String, Object> map = new HashMap();
//        map.put("name", "yindarui");
        List<Teacher> teacherByCondi = mapper.getTeacherByCondi(map);
        sqlSession.close();
    }

在这里插入图片描述

8.3 choose和when——switch case

  和if类似,但是只能选择一种条件返回。而if标签可以满足多个条件。测试内容为从name和tid字段选择一个作为查询条件。
接口:

 public List<Student> getStudentList(Map<String, Object> map);

mapper.xml:

<?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="com.yindarui.Dao.student.StudentMapper">
    <select id="getStudentList" resultType="student">
        select * from student
        where 1 = 1
        <choose>
            <when test="name != null">
                and name = #{name}
            </when>
            <when test="tid != null">
                and tid = #{tid}
            </when>
        </choose>
    </select>
</mapper>

测试方法:

   @Test
    public void stuTestForChoose() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("name", "zhangsan");
        map.put("tid", 2);
        List<Student> studentList = mapper.getStudentList(map);

        sqlSession.close();
    }

结果:
在这里插入图片描述

8.4 where

  使用where标签可以将我们在where前添加的1=1条件给去掉。如果直接不加的话,会导致语句的拼接错误。

    <select id="getStudentList" resultType="student">
        select * from student
        <where>
            <choose>
                <when test="name != null">
                    and name = #{name}
                </when>
                <when test="tid != null">
                    and tid = #{tid}
                </when>
            </choose>
        </where>
    </select>

8.5 trim

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

8.6 set

用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

8.7 foreach

这部分建议阅读官方文档,已经写的很详细了。不再做说明。

9 缓存

9.1 一级缓存

  将查询的数据缓存在内存中,不删除,下次请求相同的信息就直接返回给用户。可以缓解高并发的带来的时延问题。对于经常查询且不经常修改的数据,可以存入缓存。
  在mybatis中,缓存分成两层,一级缓存会默认开启,这个缓存被称为本地缓存。他只会存在在一次会话。在用户获取一个SqlSession的过程中发挥作用。即在SqlSession的建立到close方法的调用期间,这个缓存都是发挥作用的。

9.2 二级缓存

  一级缓存是基于SqlSession的。二级缓存是基于namespace的,即在一个mapper中生效(一个mapper可以有很多方法)。如果当前会话被关闭(SqlSession),此时一级缓存就没了,但是他的结果会被存入二级缓存
  二级缓存的开启需:
*在核心配置文件中开启全局缓存:

<setting name="cacheEnabled" value="true"/>
  • 在mapper.xml中添加:
<cache />

其缓存的规则是:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。别的语句的结果没有缓存的意义。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
      同时,缓存还具备一些属性可以设置
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

  注意:缓存的对象是只读的,这意味着不会对缓存中的数据进行数据同步操作。所以,一旦磁盘中的数据发生了修改,缓存中对应的内容就必须丢弃,否则会发生脏读的现象。当缓存满了就会清除缓存。
可用的清除策略有:

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

9.3 缓存机制

在这里插入图片描述

9.4 自定义缓存

  mybatis提供了用户自定义缓存的功能。只需要实现org.apache.ibatis.cache.Cache 接口,就可以自己定义一更加高级的缓存机制。在mapper.xml中添加:

<cache type="com.domain.something.MyCustomCache"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值