Mybatis复杂映射


XML方式:

1. 一对一

假如我有一个需求,两张表,用户表和订单表。从订单的角度出发,一个订单只能有一个用户

image-20201109221253438

user.id和orders.id关联。

正确的SQL应该这样写
SELECT * FROM USER u,orders o WHERE u.id=o.uid

image-20201109221530198

此时的结果既有订单信息,又有用户信息。我们把结果不论封装到User中还是Order中都是不行的。我们可以在Order实体类中加入User

//Order.java
public class Order {
    private Integer id;
    private String orderTime;
    private Double total;
    //表示该订单属于哪个用户
    private User user;
	//省略getSet、toString
}

//User.java
public class User {
    private Integer id;
    private String username;
	//省略getSet、toString
}

Mapper:

package com.dxh.dao;
import com.dxh.pojo.Order;
import java.util.List;

public interface OrderMapper {
    public List<Order> findOrderAndUser();
}

XML

<mapper namespace="com.dxh.dao.OrderMapper">
    <resultMap id="orderMap" type="com.dxh.pojo.Order">
        <result property="id" column="id"></result>
        <result property="orderTime" column="orderTime"></result>
        <result property="total" column="total"></result>
        <association property="user" javaType="com.dxh.pojo.User">
            <result property="id" column="uid"></result>
            <result property="username" column="username"></result>
        </association>
    </resultMap>
    <select id="findOrderAndUser" resultMap="orderMap">
        SELECT * FROM  orders o,USER u  WHERE u.id=o.uid
    </select>
</mapper>
  • <select id="findOrderAndUser" resultMap="orderMap">
    resultMap表示:手动配置实体属性与表字段的映射关系,值填写<resultMap>标签中的id

  • <resultMap id="orderMap" type="com.dxh.pojo.Order">
    id与select标签中的resultMap的值一致。

  • <result property="id" column="id"></result>
    表示实体属性id与数据库返回的字段id对应

  • <association property="user" javaType="com.dxh.pojo.User">
    property 就是Order实体中的user这个属性

    javaType就是其类型

  • <result property="id" column="uid"></result>

    image-20201109223957187


    我们可以在数据库的返回结果中看到,因为orders表和user表中都有id这个字段,id又是其外键,而orders表中的uid其实就是user的id,因此我们填写uid

我们测试一下:

    @Test
    public void test3() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Order> orderAndUser = mapper.findOrderAndUser();
        for (Order order : orderAndUser) {
            System.out.println(order);
        }
    }

结果:

Order{id=1,orderTime='2020-11-09',total=100.0,user=User{id=1,username='lucy'}}
Order{id=2,total=200.0,username='lucy'}}
Order{id=3,total=300.0,user=User{id=2,username='李四'}}

2. 一对多

假设我们有一个需求,同样是两张表userorders从用户的角度出发,一个用户可以拥有多个订单这也就是一对多关系,我们要查询出所有用户的信息,以及每个用户的订单信息。

image-20201109221253438

正确的SQL应该是:

SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM user u LEFT JOIN orders o on u.id=o.uid

我们在User实体类中增加订单的信息。

public class User {
    private Integer id;
    private String username;
	//该用户具有的订单信息
    private List<Order> orderList;
	
    //getset、toString方法省略
}

UserMapper.java

    //查询出所有用户和其订单信息
    public List<User> findAllUser();

UserMapper.xml

    <resultMap id="userMap" type="com.dxh.pojo.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>

        <collection property="orderList" ofType="com.dxh.pojo.Order">
            <result property="id" column="oid"></result>
            <result property="orderTime" column="orderTime"></result>
            <result property="total" column="total"></result>
        </collection>
    </resultMap>

    <select id="findAllUser" resultMap="userMap">
        SELECT u.*,o.uid FROM user u LEFT JOIN orders o on u.id=o.uid
    </select>

我们来看一下与一对一不同的地方:

  • <id property="id" column="id"></id> 表示主键
  • <collection property="orderList" ofType="com.dxh.pojo.Order">
    • <collection>标签,我们想要配置User实体中的orderList,而orderList属性是一个集合,所以使用<collection>
    • property="orderList" 表示User实体中的orderList的属性名
    • ofType="com.dxh.pojo.Order" 表示property中那个属性名所对应集合的泛型

我们来测试一下:

    @Test
    public void test4() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUser = mapper.findAllUser();
        for (User user : allUser) {
            System.out.println(user);
        }
    }
User{id=1,username='lucy',orderList=[Order{id=1,user=null},Order{id=2,user=null}]}
User{id=2,username='李四',orderList=[Order{id=3,user=null}]}
User{id=3,username='zhaowu',orderList=[]}

结果正确!

3. 多对多

一个用户有多个角色,一个角色也可以被多个用户使用。这个结构是这样的:

image-20201109231505275

我们有个需求,查询出用户,并查询出该用户所拥有的角色

对应的SQL语句:

SELECT
	* 
FROM
	USER u
	LEFT JOIN user_role ur ON u.id = ur.user_id
	LEFT JOIN role r ON r.id = ur.role_id

结果:

image-20201109231959380

多对多其实就是两个一对多,我们在编写代码时都是差不多的。

User.java和Role.java

//User.java
public class User {
    private Integer id;
    private String username;
    //当前用户具备哪些角色
    private List<Role> roleList;
    private List<Order> orderList;
	//getSet、toString方法省略
}
//Role.java
public class Role {
    private Integer id;
    private String roleName;
	//getSet、toString方法省略
}

UserMapper.java

//查询出所有用户和其角色信息
public List<User> findAllUserAndRole();

UserMapper.xml

    <resultMap id="userRoleMap" type="com.dxh.pojo.User">
        <id property="id" column="user_id"></id>
        <result property="username" column="username"></result>

        <collection property="roleList" ofType="com.dxh.pojo.Role">
            <result property="id" column="role_id"></result>
            <result property="username" column="username"></result>
        </collection>
    </resultMap>
    <select id="findAllUserAndRole" resultMap="userRoleMap">
        SELECT
            *
        FROM
            USER u
            LEFT JOIN user_role ur ON u.id = ur.user_id
            LEFT JOIN role r ON r.id = ur.role_id
    </select>

很简单,和一对多一样。但是有一点要注意:

image-20201109233646895

所以我们这里使用userid和roleid

.... 
<id property="id" column="user_id"></id>
....
<result property="id" column="role_id"></result>

测试一下:

    @Test
    public void test5() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUser = mapper.findAllUserAndRole();
        for (User user : allUser) {
            System.out.println(user);
        }
    }
User{id=1,roleList=[Role{id=2,roleName='CFO'}]}
User{id=2,roleName='CFO'},Role{id=3,roleName='COO'}]}
User{id=3,roleList=[Role{id=1,roleName='CEO'}]}

结果正确!

注解方式:

image-20201109235347337

我们在使用xml方式的时候会使用到很多标签,那么换成注解该如何使用呢?

xml 注解
@Results
@Result
@One
@Many

以上,不同的标签对应不同的注解

4. 注解方式 一对一

image-20201109221253438

OrderMapper.java

package com.dxh.dao;

import com.dxh.pojo.Order;
import com.dxh.pojo.User;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface OrderMapper {
    @Results({
            @Result(property = "id",column = "id"),@Result(property = "orderTime",column = "orderTime"),@Result(property = "total",column = "total"),@Result(property = "user",column = "uid",javaType = User.class,one = @One(select = "com.dxh.dao.UserMapper.findById")
            )
    })
    @Select("select * from orders")
    public List<Order> findOrderAndUserAnnotation();
}

UserMapper.java:

    //根据id查询用户 注解使用
    @Select("select * from user where id=#{id}")
    public User findById(Integer id);

我们刚才图中说了,不同的标签对应不同的注解。这里很好理解,那么为什么只写了@Select("select * from orders")以及@Result(property = "user",column = "uid" 为什么这个column 写了uid呢?

我们来分析一下:

//第一步通过select标记查询出Order的所有字段:id、orderTime、total、uid
//id、orderTime、total很好理解
@Result(property = "user",one = @One(select = "com.dxh.dao.UserMapper.findById") )
//这里表示的是把 uid作为参数,传入到statementId=com.dxh.dao.UserMapper.findById 这个的查询语句中。
//所以@One(select = "这里传入将要查询的sql的statementId"),statementId=namespace.方法名组成。

image-20201110003116367

先执行select * from orders 再执行select * from user where id=#{id} ,而#{id}的值就是 "uid"的值、

我们来测试一下结果:

    @Test
    public void test6() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Order> orderAndUserAnnotation = mapper.findOrderAndUserAnnotation();
        for (Order order : orderAndUserAnnotation) {
            System.out.println(order);
        }
    }
Order{id=1,username='李四'}}

5. 注解方式 一对多

image-20201109221253438

我们现在再来通过注解的方式来进行一对多查询:

orders 表中包含了uid也就是用户id(user.id)按照一对一的思路分析我们可以得到两条sql:

select * from user
select * from orders where uid=#{userId}
//先查询出用户,再根据用户的id进行查询订单

UserMapper.java

    //查询出所有用户和其订单信息 注解使用
    @Results({
            @Result(property ="id",@Result(property ="username",column = "username"),@Result(property ="orderList",column = "id",javaType = List.class,many = @Many(select ="com.dxh.dao.OrderMapper.findOrdersByUesrId" ))
    })
    @Select("select * from user")
    public List<User> findAllUserAnnotation();

OrderMapper.java

    @Select("select * from orders where uid = #{uid}")
    public List<Order> findOrdersByUesrId(Integer uid);

大致都和一对一一样的,因为一对多 order是一个list,因此javaType = List.class。 同时我们使用了many = @Many

测试一下:

    @Test
    public void test7() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUserAnnotation = mapper.findAllUserAnnotation();
        for (User user : allUserAnnotation) {
            System.out.println(user);
        }
    }
User{id=1,total=100.0},}]}
User{id=2,total=300.0}]}
User{id=3,orderList=[]}

结果正确!

6. 注解方式 多对多

image-20201109231505275

同理,我们可以分析出两条sql

SELECT * from user 
select * from user_role ur LEFT JOIN role r on ur.role_id=r.id where ur.user_id=#{userId}
//先通过第一条sql得到用户id,再通过用户id得到角色信息

UserMapper.java

    //查询出所有用户,以及他们的角色
    @Select("SELECT * from user ")
    @Results({
            @Result(property ="id",@Result(property = "roleList",many = @Many(select = "com.dxh.dao.RoleMapper.findRoleById"))
    })
    public List<User> findAllUserAndRoleAnnotation();

新建RoleMapper.java

package com.dxh.dao;

import com.dxh.pojo.Role;
import org.apache.ibatis.annotations.Select;
import java.util.List;

public interface RoleMapper {
    @Select("select * from user_role ur,role r where ur.role_id=r.id and ur.user_id=#{userId}")
    public List<Role> findRoleById(Integer userId);
}

结果:

User{id=1,Role{id=2,roleName='CFO'}],orderList=null}
User{id=2,roleName='COO'}],orderList=null}
User{id=3,roleName='CEO'}],orderList=null}

正确!

相关文章

1.pom.xml引入依赖 &lt;dependency&gt; &lt;gro...
&lt;?xml version=&quot;1.0&quot; encoding=&a...
准备工作 ① 创建数据库&amp;数据表 ## 创建数据库 CREA...
MyBatis逆向工程是指根据数据库表结构自动生成对应的实体类、...
MyBatis获取参数值的两种方式:${}和#{} ${}的本质就是字符串...
resultMap作用是处理数据表中字段与java实体类中属性的映射关...