MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatement、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

1.Map和模糊查询

 <!--使用Map-->
    <update id="updateUser1" parameterType="Map">
        update mybatis.user set name=#{oneName},pwd=#{onePwd}  where id=#{oneId};
    </update>

 <!--模糊查询-->
    <select id="getUserLike" resultType="User">
        select *from mybatis.user where name like "%"#{value}"%"
    </select>
 //改(使用Map)
    @Test
    public void updateUser1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("oneId",4);
        map.put("oneName","Mike");
        map.put("onePwd","110110");
        mapper.updateUser1(map);

        sqlSession.commit();
        sqlSession.close();
    }
    //模糊查询
    @Test
    public void getUserLike(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = mapper.getUserLike("李");
        for (User user : list) {
            System.out.println(user);
        }

        sqlSession.close();
    }

2.ResultMap 结果集映射

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你做一些 JDBC 不支持的事情。实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。

 <!--结果集映射-->
    <resultMap id="UserMap" type="User">
        <!--column:数据库中的字段  property:实体类中的属性-->
        <result column="pwd" property="password"/>
    </resultMap>

    <select id="getUserLike" resultMap="UserMap">
        select * from mybatis.user where name like "%"#{value}"%"
    </select>

3.日志工厂

1629692883411.png

  • SLF4J
  • LOG4J [*]
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING [*]
  • NO_LOGGING

STDOUT_LOGGING :标准日志输出

<settings>
     <!--标准日志工厂-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

LOG4J :

  1. 导入依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 配置
<settings>
    <!--log4j-->
    <setting name="logImpl" value="LOG4J"/>
</settings>
  1. 测试

    public class UserDaoTest {
    
        static Logger logger= Logger.getLogger(UserDaoTest.class);
    
        @Test
        public void getUserLike(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            logger.info("测试,进入getUserLike方法成功!");
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> list = mapper.getUserLike("李");
            for (User user : list) {
                System.out.println(user);
            }
            sqlSession.close();
        }
    }
    

4.分页

方式一:

接口:

//分页
List<User> getUserByLimit(Map<String,Integer> map);

mapper:

<!--分页1-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
    select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

测试:

@Test
public void getUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",1);
    map.put("pageSize",2);

    List<User> userList = mapper.getUserByLimit(map);
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

方式二:

接口:

//RowBounds实现分页
List<User> getUserByRowBounds();

mapper:

<!--分页2-->
<select id="getUserByRowBounds" resultMap="UserMap">
    select * from mybatis.user
</select>

测试:

@Test
public void getUserByRowBounds(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //RowBounds实现
    RowBounds rowBounds = new RowBounds(0,3);
    List<User> userList = sqlSession.selectList("com.jiutian.dao.UserMapper.getUserByRowBounds", null, rowBounds);
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

5.注解

接口

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

核心配置文件中绑定接口

<mappers>
    <mapper class="com.jiutian.dao.UserMapper"/>
</mappers>

@Param()注解

  • 基本数据类型或String,要加上
  • 引用数据类型不用
  • 如果只有一个基本数据类型,建议加上
  • 在SQL中引用的就是@Param()中设定的属性名

6.Lombok插件使用

1.下载插件

2.导入jar包

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
</dependencies>

3.pojo类使用

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;

}

7.多对一处理

1.pojo类

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

2.接口

public interface StudentMapper {
     //查询所有学生及其老师
    List<Student> getStudent();
    List<Student> getStudent2();
}

3.Mapper文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jiutian.dao.StudentMapper">
    
    <!--方式一:按照查询嵌套处理-->
    <select id="getStudent" resultMap="StudentTeacher">
        select * from student
    </select>
    <resultMap id="StudentTeacher" type="Student">
         <!--复杂的属性,单独处理: 对象:association   集合:collection-->
        <association property="teacher" column="tid" javaType="Teacher" select="selectTeacherById"/>
    </resultMap>
    <select id="selectTeacherById" resultType="Teacher">
        select *from teacher where id=#{tid}
    </select>

    
    <!--方式二:按照结果嵌套处理-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select a.id sid,a.name sname,a.tid tid,b.name tname
        from student a,teacher b
        where a.tid=b.id
    </select>
    <resultMap id="StudentTeacher2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>
</mapper>

8.一对多处理

1.pojo类

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

2.接口

public interface TeacherMapper {
    //根据指定id查询老师及其所有学生
    List<Teacher> getTeacher(@Param("id") int id);
    List<Teacher> getTeacher2(@Param("id") int id);
}

3.Mapper文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jiutian.dao.TeacherMapper">
    
    <!--方式一:按照查询嵌套处理-->
    <select id="getTeacher" resultMap="TeacherStudent">
        select * from teacher where id=#{id}
    </select>
    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="id"/>
        <!--复杂的属性,单独处理: 对象:association   集合:collection-->
        <collection property="students" column="id" javaType="ArrayList" ofType="Student"  select="selectStudentByTeacherId"/>
    </resultMap>
    <select id="selectStudentByTeacherId" resultType="Student">
        select * from student where tid=#{id}
    </select>

    
    <!--方式二:按照结果嵌套处理-->
    <select id="getTeacher2" resultMap="TeacherStudent2">
        select b.id tid,b.name tname,a.id sid,a.name sname
        from student a,teacher b
        where a.tid=b.id
    </select>
    <resultMap id="TeacherStudent2" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" javaType="ArrayList" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
</mapper>

小结

  1. 关联 - association [多对一]
  2. 集合- collection [一对多]
  3. javaType:用来指定实体类中属性的类型
  4. ofType:用来指定映射到List,集合中的pojo类型,泛型中的约束类型

9.动态SQL

pojo类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

接口

public interface BlogMapper {
	//插入数据
	int addBlog(Blog blog);
}

Mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jiutian.dao.BlogMapper">
    <insert id="addBlog" parameterType="Blog">
        insert into blog (id, title, author, create_time, views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>
</mapper>

自动生成id(主键)工具类

public class IdUtils {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    @Test
    public void test(){
        System.out.println(IdUtils.getId());
    }
}

测试1

@Test
public void addBlog(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

    Blog blog = new Blog();
    blog.setId(IdUtils.getId());
    blog.setTitle("Mybatis如此简单");
    blog.setAuthor("jiutian");
    blog.setCreateTime(new Date());
    blog.setViews(9999);

    mapper.addBlog(blog);

    blog.setId(IdUtils.getId());
    blog.setTitle("Java如此简单");
    mapper.addBlog(blog);

    blog.setId(IdUtils.getId());
    blog.setTitle("Spring如此简单");
    mapper.addBlog(blog);

    blog.setId(IdUtils.getId());
    blog.setTitle("微服务如此简单");
    mapper.addBlog(blog);
    sqlSession.close();
}

if,sql,choose标签使用

//查询博客(if,sql)
List<Blog> queryBlogByIf(Map map);

//查询博客(choose,when)
List<Blog> queryBlogByChoose(Map map);

<sql id="ifTitleAuthor">
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>
<select id="queryBlogByIf" parameterType="map" resultType="Blog">
    select * from mybatis.blog
    <where>
        <include refid="ifTitleAuthor"></include>
    </where>
</select>

<select id="queryBlogByChoose" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <choose>
            <when test="title != null">
                and title = #{title}
            </when>
            <when test="title != null">
                and author = #{author}
            </when>
            <otherwise>
                and views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

测试2

@Test
public void queryBlogByIf(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("title","Java如此简单");
    map.put("author","jiutian");
    List<Blog> blogs = mapper.queryBlogByIf(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

@Test
public void queryBlogByChoose(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("title","Mybatis如此简单");
    //map.put("author","jiutian");
    map.put("views",9999);
    List<Blog> blogs = mapper.queryBlogByChoose(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

set标签使用

//更新博客 (set)
int updateBlog(Map map);

<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author},
        </if>
    </set>
    where id = #{id}
</update>

测试3

@Test
public void updateBlog() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("id","1");
    map.put("title","Java如此简单2");
    map.put("author","jiutian2");
    mapper.updateBlog(map);
    sqlSession.close();
}

foreach标签使用

//查询第1,2,3号博客
List<Blog> queryBlogByForeach(Map map);

//查询第1,2,3号博客
List<Blog> queryBlogByForeach2(Map map);

<!-- select * from blog where id=1 or id=2 or id=3 -->
<select id="queryBlogByForeach" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="" close="" separator="or">
            id = #{id}
        </foreach>
    </where>
</select>

<!-- select * from blog where id in (1,2,3) -->
<select id="queryBlogByForeach2" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="id in (" close=")" separator=",">
            #{id}
        </foreach>
    </where>
</select>

测试4

@Test
public void queryBlogByForeach(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    ArrayList<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    map.put("ids",ids);
    List<Blog> blogs = mapper.queryBlogByForeach(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

@Test
public void queryBlogByForeach2(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    ArrayList<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    map.put("ids",ids);
    List<Blog> blogs = mapper.queryBlogByForeach2(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

小结:

  1. choose-when相当于switch-case,并且匹配到一个会"break"结束。
  2. where会根据情况所需自动前置“WHERE” ,自动去除开头的“AND” 或 “OR”【不能增加】。
  3. set会根据情况所需自动前置“set” ,自动去除结尾额外的逗号(在使用条件语句给列赋值时引入的)【不能增加】。
  4. sql配合include使用,可以抽取公共的部分,方便复用。
  5. foreach可用于对集合进行遍历,collection代表传的集合,item相当于一个循环变量;open,close,separator指定开头与结尾的字符串以及集合项迭代之间的分隔符。并且foreach不会错误地添加多余的分隔符。

10.缓存

MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。

默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:

<cache/>

一级缓存

表示SqlSession级别的缓存,默认开启。每次查询的时候会开启一个会话,此会话相当于一次连接,关闭之后自动失效。

测试

@Test
public void queryUserById(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);

    //mapper.updateUser(new User(2,"okok","121212"));   //增删改会刷新一级缓存
    //sqlSession.clearCache();      //手动清除缓存
    System.out.println("=============================");
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    System.out.println(user==user2);
    sqlSession.close();
}

二级缓存

二级缓存是全局范围的缓存,SqlSession关闭之后才会生效。

核心配置文件:

<!--默认是true,可以显示地开启全局缓存-->
<setting name="cacheEnabled" value="true"/>

Mapper.xml文件:

<!--在当前Mapper.xml使用二级缓存,readOnly默认是false,实体类需要序列化-->
<cache/>

测试

@Test
public void queryUserById(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);
    sqlSession.close();

    System.out.println("=============================");

    SqlSession sqlSession2 = MybatisUtils.getSqlSession();
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    User user2 = mapper2.queryUserById(1);
    System.out.println(user2);

    System.out.println(user==user2);
    
    sqlSession2.close();
}

第三方缓存

  1. 导入jar包

    <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
    <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.2.1</version>
    </dependency>
    
    
  2. 在resources目录下加入ehcache.xml

  3. Mapper.xml文件

    <!--第三方缓存-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    
    

小结

  1. 二级缓存生效的时候,是在一级缓存sqlSession关闭的时候
  2. 在开启了二级缓存的情况下,先查询二级缓存,再查询一级缓存
  3. readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false

Q.E.D.


以无限为有限,以无法为有法