【SSM开发框架】Mybatis复杂映射

笔记输出来源:拉勾教育Java就业急训营
如有侵权,私信立删

修改时间:2020年2月18日
作者:pp_x
邮箱:[email protected]

Mybatis高级查询

ResultMap

  • resultType:如果实体的属性名与表中字段名一致,将查询结果自动封装到实体类中
  • resultMap:如果实体的属性名与表中字段名不一致,可以使用ResutlMap实现手动封装到实体类中
  • 属性:
    • id:唯一标识,调用的时候填写
    • type:封装后的实体类型
 <!--    id:  标签的唯一标识
            type:封装后的实体类型
    -->
    <resultMap id="userResultMap" type="com.lagou.domain.User">
        <!--        手动配置映射关系-->
        <!--        id:用来配置主键-->
        <id property="id" column="id"></id>

        <!--        result:用来配置普通字段-->
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
    </resultMap>

多条件查询

需求:根据id和username查询user表

方式一

  • 使用 #{arg0}-#{argn} 或者#{param1}-#{paramn} 获取参数
<!--    多条件查询方式一-->
    <select id="findByIdAndUsername" resultMap="userResultMap">
        <!-- select * from user where id = #{arg0} and username = #{arg1} -->
        select * from user where id = #{param1} and username = #{param2}
    </select>

方式二

  • 使用注解,引入 @Param()注解获取参数
  • 接口类
/**
     * 多条件查询方式二
     * @param id
     * @param username
     * @return
     */
    public List<User> findByIdAndUsername2(@Param("id") int id, @Param("username") String username);
  • 配置文件
  <!--    多条件查询方式二 -->
    <select id="findByIdAndUsername2" resultMap="userResultMap">
        <!--   对应接口中@param注解中的值-->
        select * from user where id = #{id} and username = #{username}
    </select>

方式三

  • 使用pojo对象传递参数
  • 此方式#{}中的变量必须对应属性名
  • 接口类

    /**
     * 多条件查询方式三
     * @return
     */
    public List<User> findByIdAndUsername3(User user);
  • 配置文件
 <!--    多条件查询方式三 -->
    <select id="findByIdAndUsername3" resultMap="userResultMap" parameterType="user">

        select * from user where id = #{id} and username = #{username}
    </select>

模糊查询

需求:根据username模糊查询user表

方式一

  • xml配置文件
<!--    模糊查询方式一-->
    <select id="findByUsername1" resultMap="userResultMap" parameterType="String">
        <!--
        parameterType是基本数据类型或者String的时候,#{}里面的值可以随便写
        #{}在mybatis中是占位符,引用参数值时会自动添加单引号
        -->
        select * from user where username like #{username}
    </select>
  • 测试类
 /**
     * 模糊查询方式一
     */
    @Test
    public void test5() throws IOException {
    
    
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //当前返回的是基于UserMapper所产生的代理对象 底层:JDK动态代理  实际类型 proxy
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.findByUsername1("%liuxiao%");
        for (User user:users
        ) {
    
    
            System.out.println(user);
        }
    }

方式二

  • xml配置
 <!--    模糊查询方式二-->
    <select id="findByUsername2" resultMap="userResultMap" parameterType="String">
        <!--
        parameterType是基本数据类型或者String的时候,${}里面的值只能写value
        ${}时sql原样拼接,不会自动添加单引号
        -->
        select * from user where username like '${value}'
    </select>
  • 测试
/**
     * 模糊查询方式二
     */
    @Test
    public void test6() throws IOException {
    
    
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //当前返回的是基于UserMapper所产生的代理对象 底层:JDK动态代理  实际类型 proxy
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.findByUsername2("%liuxiao%");
        for (User user:users
        ) {
    
    
            System.out.println(user);
        }
    }

Mybatis映射文件

返回主键

  • 应用场景
    向数据库插入一条记录后,希望能立即拿到这条记录在数据库中的主键值。

useGeneratedKeys

  • useGeneratedKeys:声明返回主键
  • keyProperty:把返回主键的值封装到实体中的哪个属性上
  • 只能在支持主键自增的数据库中使用Oracle不行
<!--    添加用户以及获取返回主键方式一-->
    <!--
        useGeneratedKeys:声明返回主键
        keyProperty:把返回主键的值封装到实体中的哪个属性上
        只能在支持主键自增的数据库中使用 Oracle不行
    -->
    <insert id="saveUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>

selectKey

  • selectKey:适用范围更广,支持所有类型的数据库
  • order="AFTER":在sql语句执行后 执行此语句
  • keyColumn:指定主键对应的列名
  • keyProperty:指定将主键封装到实体的哪个属性
  • resultType:指定返回的主键类型
 <!--    添加用户以及获取返回主键方式二-->
    <!--
       selectKey:适用范围更广,支持所有类型的数据库
       order="AFTER" 在sql语句执行后 执行此语句
       keyColumn:指定主键对应的列名
       keyProperty:指定将主键封装到实体的哪个属性
       resultType:指定返回的主键类型
    -->
    <insert id="saveUser2" parameterType="user">
        <selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">
            select last_insert_id()
        </selectKey>
        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>

动态sql

  • 应用场景
    当我们要根据不同的条件,来执行不同的sql语句的时候,需要用到动态sql。

if 标签

  • test里写表达式
  • where标签可以在合适的时候删除和添加and连接词
<!--    动态sql之if:多条件查询-->
    <select id="findByIdAndUsernameIf" parameterType="user" resultType="user">
        select * from user
        <!--
        test里写表达式
        where标签相当于 where 1=1
        -->
        <where>
            <if test="id!=null">
                and id = #{id}
            </if>
            <if test="username!=null">
                and username = #{username}
            </if>
        </where>

    </select>

set标签

  • <set>:在更新的时候自动添加set关键字 还会去掉最后一个条件的逗号
 <!--    动态sql之set:动态跟新-->
    <update id="updateIf" parameterType="user">
        update user
        <!--
        <set>:在更新的时候自动添加set关键字 还会去掉最后一个条件的逗号
         -->
        <set>
            <if test="username!=null">
                username=#{username},
            </if>
            <if test="birthday!=null">
                birthday=#{birthday},
            </if>
            <if test="sex!=null">
                sex=#{sex},
            </if>
            <if test="address!=null">
                address=#{address},
            </if>
        </set>
        where id=#{id}
    </update>

foreach标签

  • collection:代表要遍历的集合元素 通常写collection或者list
  • open: 代表语句的开始部分
  • close:代表语句的结束部分
  • item:代表遍历集合中的每个元素生成的变量名 和#{}中的值保持一致
 <!--    动态sql之foreach:多值查询-->
    <select id="findByList" parameterType="list" resultType="user">
        <include refid="selectUser"></include>
        <where>
            <!-- foreach的属性:
                    collection:代表要遍历的集合元素 通常写collection或者list
                    open:代表语句的开始部分
                    close:代表语句的结束部分
                    item:代表遍历集合中的每个元素生成的变量名 和#{}中的值保持一致
                    separator:分隔符
            -->
            <foreach collection="collection" open="id in(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

<!--    动态sql之foreach:多值查询  数组-->
    <select id="findByArray" parameterType="int" resultType="user">
        <include refid="selectUser"></include>
        <where>
            <foreach collection="array" open="id in(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

sql片段


 <!--    重复sql片段抽取-->
    <sql id="selectUser">
        select * from user
    </sql>

<include refid="selectUser"></include>

Mybatis核心配置文件

plugins标签

以分页助手插件为例

  • 导入插件入通用PageHelper坐标
    在这里插入图片描述
  • 在mybatis核心配置文件中配置PageHelper插件
<!--    分页助手插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
<!--            dialect:指定方言-->
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>
  • 分页相关的参数
/**
     * 核心配置文件 plugin标签:pageHelper
     *
     */
    @Test
    public void test13() throws IOException {
    
    
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //当前返回的是基于UserMapper所产生的代理对象 底层:JDK动态代理  实际类型 proxy
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        //设置分页参数  必须写在调用mapper方法的上面
        //参数一:当前页
        //参数二:每页显示的条数
        PageHelper.startPage(1,2);
        List<User> list = mapper.findAllResultMap();
        for (User user:list
             ) {
    
    
            System.out.println(user);
        }
        //获取分页的其他参数
        PageInfo<User> userPageInfo = new PageInfo<User>(list);
        System.out.println("总条数:"+userPageInfo.getTotal());
        System.out.println("总页数:"+userPageInfo.getPages());
        System.out.println("是否是第一页:"+userPageInfo.isIsFirstPage());
        sqlSession.close();
    }
}

Mybatis多表查询

一对一

  • 查询语句
    SELECT * FROM orders o LEFT JOIN USER u ON o.uid=u.id;

代码实现

  • 实体类:需要增加一个用户对象字段
public class Orders {
    
    

    private Integer id;
    private String ordertime;
    private Double total;
    private Integer uid;

    // 表示当前订单属于那个用户 association
    private User user;

    @Override
    public String toString() {
    
    
        return "Orders{" +
                "id=" + id +
                ", ordertime='" + ordertime + '\'' +
                ", total=" + total +
                ", uid=" + uid +
                ", user=" + user +
                '}';
    }

    public Integer getId() {
    
    
        return id;
    }

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

    public String getOrdertime() {
    
    
        return ordertime;
    }

    public void setOrdertime(String ordertime) {
    
    
        this.ordertime = ordertime;
    }

    public Double getTotal() {
    
    
        return total;
    }

    public void setTotal(Double total) {
    
    
        this.total = total;
    }

    public Integer getUid() {
    
    
        return uid;
    }

    public void setUid(Integer uid) {
    
    
        this.uid = uid;
    }
}

  • xml配置
  • association: 在进行一对一关联查询配置时,使用association标签进行关联
    • property="user":要封装实体的属性名
    • javaType="com.lagou.domain.User":要封装的实体的属性类型
 <!--一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息-->

    <resultMap id="orderMap" type="com.lagou.domain.Orders">
        <id property="id" column="id"/>
        <result property="ordertime" column="ordertime"/>
        <result property="total" column="total"/>
        <result property="uid" column="uid"/>


        <!--
            association : 在进行一对一关联查询配置时,使用association标签进行关联
                property="user" :要封装实体的属性名
                javaType="com.lagou.domain.User" 要封装的实体的属性类型
        -->
        <association property="user" javaType="com.lagou.domain.User">
            <id property="id" column="uid"></id>
            <result property="username" column="username"></result>
            <result property="birthday" column="birthday"></result>
            <result property="sex" column="sex"></result>
            <result property="address" column="address"></result>

        </association>
    </resultMap>

    <select id="findAllWithUser" resultMap="orderMap">
        SELECT * FROM orders o LEFT JOIN USER u ON o.uid = u.id
    </select>

一对多

  • 查询语句:要起别名,因为oid对应的uid有空值
    SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM orders o RIGHT JOIN USER u ON o.uid = u.id

代码实现

  • 实体类需要增加一个List属性对应订单列表
public class User {
    
     
	private Integer id; 
	private String username; 
	private Date birthday; 
	private String sex; 
	private String address; 
	// 代表当前用户具备的订单列表 
	private List<Order> orderList;
}
  • xml映射文件
 <!--一对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息-->

    <resultMap id="userMap" type="com.lagou.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>


        <!--
            collection : 一对多使用collection标签进行关联
        -->
        <collection property="ordersList" ofType="com.lagou.domain.Orders">
            <id property="id" column="oid"></id>
            <result property="ordertime" column="ordertime"/>
            <result property="total" column="total"/>
            <result property="uid" column="uid"/>
        </collection>

    </resultMap>


    <select id="findAllWithOrder"  resultMap="userMap">
       SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM orders o RIGHT JOIN USER u ON o.uid = u.id
    </select>

多对多

  • 多对多即两个一对多,除了sql查询语句有区别,xml映射即使两个角度的一对多
  • 查询语句
SELECT * 
	FROM 
		USER u -- 用户表 
	LEFT JOIN user_role ur -- 左外连接中间表 
		ON u.`id` = ur.`uid` 
	LEFT JOIN role r -- 左外连接角色表 
		ON ur.`rid` = r.`id` ;

代码实现

  • 实体类:增加一个List字段存储对应的角色列表
public class User {
    
     
	private Integer id; 
	private String username; 
	private Date birthday; 
	private String sex; 
	private String address; 
	// 代表当前用户关联的角色列表 
	private List<Role> roleList; 
}
  • xml 映射
 <!--多对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息-->

    <resultMap id="userRoleMap" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

        <collection property="roleList" ofType="role">
            <id column="rid" property="id"></id>
            <result column="rolename" property="rolename"></result>
            <result column="roleDesc" property="roleDesc"></result>
         </collection>

    </resultMap>

    <select id="findAllWithRole" resultMap="userRoleMap">
        SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur ON ur.userid = u.id
		     LEFT JOIN sys_role r ON ur.roleid = r.id
    </select>

小结

  • 多对一(一对一)配置:使用<resultMap>+<association>做配置
  • 一对多配置:使用<resultMap>+<collection>做配置
  • 多对多配置:使用<resultMap>+<collection>做配置

Mybatis嵌套查询

一对一

  • 查询语句
-- 先查询订单 
SELECT * FROM orders; 
-- 再根据订单uid外键,查询用户 
SELECT * FROM `user` WHERE id = #{订单的uid};

代码实现

  • association标签中的select属性填命名空间.statment
  • order映射
 <!--一对一嵌套查询-->


    <resultMap id="orderMap2" type="com.lagou.domain.Orders">
        <id property="id" column="id"/>
        <result property="ordertime" column="ordertime"/>
        <result property="total" column="total"/>
        <result property="uid" column="uid"/>

        <!--问题:1.怎么去执行第二条sql , 2.如何执行第二条sql的时候,把uid作为参数进行传递-->
        <association property="user" javaType="com.lagou.domain.User"
                     select="com.lagou.mapper.UserMapper.findById" column="uid"/>



    </resultMap>


    <select id="findAllWithUser2" resultMap="orderMap2">
        SELECT * FROM orders
    </select>
  • user映射
 <!--根据id查询用户
        useCache="true" 代表当前这个statement是使用二级缓存
    -->
    <select id="findById" resultType="com.lagou.domain.User" parameterType="int" useCache="true">
        SELECT * FROM user WHERE id = #{id}
    </select>

一对多

  • 查询语句
-- 先查询用户 
SELECT * FROM `user`; 
-- 再根据用户id主键,查询订单列表 
SELECT * FROM orders where uid = #{用户id};

代码实现

  • user映射
 <!--一对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息-->

    <resultMap id="userOrderMap" type="com.lagou.domain.User">
        <id property="id" column="id"/>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>


        <!--fetchType="lazy" : 延迟加载策略
            fetchType="eager": 立即加载策略
        -->
        <collection property="ordersList" ofType="com.lagou.domain.Orders" column="id"
                    select="com.lagou.mapper.OrderMapper.findByUid" fetchType="lazy" ></collection>

    </resultMap>

    <select id="findAllWithOrder2" resultMap="userOrderMap">
        SELECT * FROM USER
    </select>
  • order映射

    <select id="findByUid" parameterType="int" resultType="com.lagou.domain.Orders">
        SELECT * FROM orders WHERE uid = #{uid}
    </select>

多对多

  • 查询语句
-- 先查询用户 SELECT * FROM `user`; 
-- 再根据用户id主键,查询角色列表 
SELECT * FROM role r 
INNER JOIN user_role ur ON r.`id` = ur.`rid` 
WHERE ur.`uid` = #{用户id};

代码实现

  • user映射
 <resultMap id="userRoleMap2" type="com.lagou.domain.User">
        <id property="id" column="id"/>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

        <collection property="roleList" ofType="com.lagou.domain.Role" column="id" select="com.lagou.mapper.RoleMapper.findByUid"></collection>
    </resultMap>

    <!--多对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息-->
    <select id="findAllWithRole2" resultMap="userRoleMap2">
        SELECT * FROM USER
    </select>
  • role映射文件
<select id="findByUid" resultType="com.lagou.domain.Role" parameterType="int">
        SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON ur.roleid = r.id
			WHERE ur.userid = #{uid}
    </select>

小结

  • 一对一配置:使用<resultMap>+<association>做配置,通过column条件,执行select查询
  • 一对多配置:使用<resultMap>+<collection>做配置,通过column条件,执行select查询
  • 多对多配置:使用<resultMap>+<collection>做配置,通过column条件,执行select查询
  • 优点:简化多表查询操作
  • 缺点:执行多次sql语句,浪费数据库性能

猜你喜欢

转载自blog.csdn.net/weixin_46303867/article/details/113852441