Mybatis_高级查询

一对一查询

在实际开发中,经常会遇到一对一查询,一对多查询等。这里我们先来看一对一查询。
例如:每本书都有一个作者,作者都有自己的属性,根据这个,我来定义两个实体类:

public class MyBook {
    private Integer id;
    private String name;
    private Author author;

    @Override
    public String toString() {
        return "MyBook{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author=" + author +
                '}';
    }
}



public class Author {
    private Integer id;
    private String name;
    private Integer age;


    @Override
    public String toString() {
        return "Author{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
 }

创建对应的两张表:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for author
-- ----------------------------
DROP TABLE IF EXISTS `author`;
CREATE TABLE `author`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of author
-- ----------------------------
INSERT INTO `author` VALUES (1, '张三', 35);
INSERT INTO `author` VALUES (2, '李四', 25);

SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for book
-- ----------------------------
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `aid` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of book
-- ----------------------------
INSERT INTO `book` VALUES (1, '《法外狂徒传》', 1);
INSERT INTO `book` VALUES (2, '《神墓》', 2);

SET FOREIGN_KEY_CHECKS = 1;

添加成功后,我们新建一个 BookMapper:

    // 查询所有Mybook
    List<MyBook> listMybook();

AuthoreMapper.xml:

<?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.java.lang.mapper.AuthorMapper">
    <resultMap id="AuthorMapper" type="com.java.lang.pojo.Author">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
    </resultMap>
</mapper>

我希望查出来Mybook 的同时,也能查出来它的 Author。再定义一个 BookMapper.xml ,内容如下:

<?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.java.lang.mapper.MyBookMapper">

    <!--抽离出来-->
    <resultMap id="BaseMybook" type="com.java.lang.pojo.MyBook">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
    </resultMap>

    <resultMap id="MyBook" type="com.java.lang.pojo.MyBook" extends="BaseMybook"> <!--继承baseMybook-->
        <association property="author" javaType="com.java.lang.pojo.Author" columnPrefix="author_"
                     resultMap="com.java.lang.mapper.AuthorMapper.AuthorMapper">  <!--columnPrefix 前缀--> <!--resultMap 直接引用上面的定义的Author-->
        </association>
    </resultMap>

    <select id="listMybook" resultMap="MyBook">
        SELECT B.ID, B.NAME, A.ID AS AUTHOR_ID, A.AGE AS AUTHOR_AGE, A.`NAME` AS AUTHOR_NAME
        FROM `BOOK` AS B,
             AUTHOR AS A
        WHERE A.ID = B.AID;
    </select>


</mapper>

在这个查询 SQL 中,首先应该做好一对一查询,然后,返回值一定要定义成 resultMap,注意,这里千万不能写错。然后,在 resultMap 中,来定义查询结果的映射关系。

其中,association 节点用来描述一对一的关系。这个节点中的内容,和 resultMap 一样,也是 id,result 等,在这个节点中,我们还可以继续描述一对一。

由于在实际项目中,每次返回的数据类型可能都会有差异,这就需要定义多个 resultMap,而这多个 resultMap 中,又有一部份属性是相同的,所以,我们可以将相同的部分抽出来,做成一个公共的模板,然后被其他 resultMap 继承.

懒加载

上面这种加载方式,是一次性的读取到所有数据。然后在 resultMap 中做映射。如果一对一的属性使用不是很频繁,可能偶尔用一下,这种情况下,我们也可以启用懒加载。
懒加载,就是先查询 book,查询 book 的过程中,不去查询 author,当用户第一次调用了 book 中的 author 属性后,再去查询 author。

    <!--懒加载-->
    <!--执行顺序 先去select book ,如过需要用到author的时候再去调用 com.java.lang.mapper.AuthorMapper.getAuthorById
    这个方法 把select book 里的aid 当成 getAuthorById 这个方法所需要的id 传进去-->
    <resultMap id="MyBookById" type="com.java.lang.pojo.MyBook" extends="BaseMybook">
        <association property="author" javaType="com.java.lang.pojo.Author"
                     select="com.java.lang.mapper.AuthorMapper.getAuthorById" column="{id=aid}"
                     fetchType="lazy"/>
    </resultMap>

    <select id="getMybookById" resultMap="MyBookById">
        select *
        from book
        where id = #{id};
    </select>
    @Test
    public void getBookById() {
        MyBookMapper myBookMapper = sqlsession.getMapper(MyBookMapper.class);
        MyBook myBook = myBookMapper.getMybookById(1L);
        // System.out.println(myBook.getName()); /*这个时候是不会去查询author的*/
        System.out.println(myBook.getAuthor());
    }

一对多查询

一对多查询,也是一个非常典型的使用场景。比如用户和角色的关系,一个用户可以具备多个角色。

首先准备三个表:

/*
Navicat MySQL Data Transfer

Source Server         : localhost
Source Server Version : 50717
Source Host           : localhost:3306
Source Database       : security

Target Server Type    : MYSQL
Target Server Version : 50717
File Encoding         : 65001

Date: 2018-07-28 15:26:51
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `nameZh` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'dba', '数据库管理员');
INSERT INTO `role` VALUES ('2', 'admin', '系统管理员');
INSERT INTO `role` VALUES ('3', 'user', '用户');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `enabled` tinyint(1) DEFAULT NULL,
  `locked` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'root', '2a10RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');
INSERT INTO `user` VALUES ('2', 'admin', '2a10RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');
INSERT INTO `user` VALUES ('3', 'sang', '2a10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) DEFAULT NULL,
  `rid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1', '1');
INSERT INTO `user_role` VALUES ('2', '1', '2');
INSERT INTO `user_role` VALUES ('3', '2', '2');
INSERT INTO `user_role` VALUES ('4', '3', '3');
SET FOREIGN_KEY_CHECKS=1;


这三个表中,有用户表,角色表以及用户角色关联表,其中用户角色关联表用来描述用户和角色之间的关系,他们是一对多的关系。

然后,根据这三个表,创建两个实体类:

package com.java.lang.pojo;

import java.util.List;

public class User {
    private Integer id;
    private String username;
    private String password;
    private List<Role> roles;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", roles=" + roles +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}



package com.java.lang.pojo;

public class Role {
    private Integer id;
    private String name;
    private String nameZh;

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", nameZh='" + nameZh + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameZh() {
        return nameZh;
    }

    public void setNameZh(String nameZh) {
        this.nameZh = nameZh;
    }
}


接下来定义一个接口 以及Mapper :

    List<User> getAllUserWithRole();
    <resultMap id="BaseMapUser" type="com.java.lang.pojo.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
    </resultMap>

    <resultMap id="WithRoleMap" type="com.java.lang.pojo.User" extends="BaseMapUser">
        <collection property="roles" ofType="com.java.lang.pojo.Role" columnPrefix="role_"
                    resultMap="com.java.lang.mapper.RoleMapper.BaseRoleMap">
        </collection>
    </resultMap>

    <select id="getAllUserWithRole" resultMap="WithRoleMap">
        SELECT u.*, r.id as role_id, r.name as role_name, r.nameZh as role_nameZh
        FROM `user` u
                 LEFT JOIN user_role ur ON u.id = ur.uid
                 LEFT JOIN role r ON r.id = ur.rid
    </select>

在这里插入图片描述
懒加载:

    <resultMap id="WithRoleMap2" type="com.java.lang.pojo.User" extends="BaseMapUser">
        <collection property="roles" ofType="com.java.lang.pojo.Role" fetchType="lazy"
                    select="com.java.lang.mapper.RoleMapper.getRoleById" column="{id=id}">
        </collection>
    </resultMap>
    
    <select id="getAllUserWithRole2" resultMap="WithRoleMap2">
        select *
        from user
    </select>

    <resultMap id="BaseRoleMap" type="com.java.lang.pojo.Role">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="nameZh" column="nameZh"/>
    </resultMap>

    <select id="getRoleById" resultMap="BaseRoleMap">
        SELECT *
        FROM `role` r,
             user_role ur
        WHERE ur.rid = r.id
          and ur.uid = #{id}
    </select>

一级缓存

mybatis一级缓存是mybatis提供的一种缓存机制,即在同一会话中,连续调用同一mapper中的同一查询方法,mybatis会自动将第一次查询的结果集缓存,后续的同一查询不再调用数据库查询,大大提升了效率。一级缓存是默认开启的,不需要任何配置,也不可关闭。
在这里插入图片描述

二级缓存

需要手动开启

在mybatis-config.xml 中开启

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

使用 :
在mapper 文件中写一个<cache /> 即可开启缓存
mapper 文件中的所有select语句都会被缓存下来,如果缓存满了,默认使用LRU淘汰算法,也可以自己配置FIFLO先进先出等。。,insert into / update / delete 语句会更新缓存
在这里插入图片描述

内存分页

内存分页 : 原理,先把数据都加载到内存中,再进行分页,如果数据内容很大,那就没有意义。

    // 内存分页
    List<Book> lsitBookByPage(RowBounds rowBounds);
    <select id="lsitBookByPage" resultMap="bookMap">
        select *
        from t_book;
    </select>
    @Test
    public void listBookByPage() {
        BookMapper bookMapper = sqlsession.getMapper(BookMapper.class);
        List<Book> books = bookMapper.lsitBookByPage(new RowBounds(5, 5));
        for (Book book : books) {
            System.out.println(book);
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值