Mybatis学习系列关联查询

Posted 仍是少年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis学习系列关联查询相关的知识,希望对你有一定的参考价值。

前面几节的示例基本都是一些单边查询,实际项目中,经常用到关联表的查询,比如一对一,一对多等情况。在Java实体对象中,一对一和一对多可是使用包装对象解决,属性使用List或者Set来实现,在mybatis中一对一和一对多可是使用association或者collection标签来配合实现。

在MyBatis中有两种方式实现关联查询:

1. 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。首先,然让我们来查看这个元素的属性。所有的你都会看到,它和普通的只由 select 和resultMap 属性的结果映射不同

2. 嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型

一对一查询

示例:商品表products 和 商品分类category  一个商品对应一种分类,所以products->category是一对一的关系

1.嵌套结果实现(resultMap):

通过resultMap将查询结果中商品信息映射到Product对象中,在Product类中添加属性category,将关联结果映射到Product.category属性上。

    <!-- 一对一查询 关联查询 使用resultMap映射结果集 -->
    <select id="oneToOneTestMap" parameterType="int" resultMap="productInfoMap">
        select p.*,c.id
        categoryId,c.name categoryName,c.remark categoryRemark
        from products p
        join category c
        on p.categoryId= c.Id
        where p.id
        =#{value}
    </select>
    <resultMap id="productInfoMap" type="com.sl.po.Product">
        <id column="id" property="Id" />
        <result column="name" property="Name" />
        <result column="description" property="Description" />
        <result column="unitprice" property="UnitPrice" />
        <result column="imageUrl" property="ImageUrl" />
        <result column="isnew" property="IsNew" />
        <result column="citycode" property="cityCode" />
      
   <!-- association:用于映射关联查询单个对象的信息
      property:要将关联查询的分类信息映射到属性category上
   -->
        <association property="category" javaType="com.sl.po.Category">
            <id column="categoryId" property="Id" />
            <result column="categoryName" property="Name" />
            <result column="categoryRemark" property="Remark" />
        </association>
    </resultMap>

注:也可以使用resultType配置实现,需要定义一个包装类含有product和category两个对象的属性即可。

2.嵌套查询实现

    <!-- 关联嵌套查询  -->
    <select id="oneToOneTestAssociationSelect" parameterType="int" resultMap="productInfoMap2">
        select p.*
        from products p
        where p.id =#{value}
    </select>
    <resultMap id="productInfoMap2" type="com.sl.po.Product" >
        <id column="id" property="Id" />
        <result column="name" property="Name" />
        <result column="description" property="Description" />
        <result column="unitprice" property="UnitPrice" />
        <result column="imageUrl" property="ImageUrl" />
        <result column="isnew" property="IsNew" />
        <result column="citycode" property="cityCode" />
        <!-- column:传递子查询参数,如果要处理符复合主键,使用column= "{prop1=col1,prop2=col2}"  -->
        <!-- select:子查询语句id -->
        <association property="category" javaType="com.sl.po.Category" column="categoryId" select="selectCategoryInfo">
        </association>
    </resultMap>
    <!-- 子查询 -->
    <select id="selectCategoryInfo" parameterType="int" resultType="com.sl.po.Category">
        select * from category where id=#{id}
    </select>

使用resultType、resultMap和嵌套查询对比分析:

resultType:使用resultType只需要将查询结果集中的列名与定义的pojo属性一一对应即可完成映射,缺点:resultType无法将查询结果映射到包装类的pojo属性中

resultMap:需要额外定义resultMap,在resultMap中将结果集列名与pojo对象属性一一配置。

嵌套查询:需要定义多个查询,上面示例定义两个查询,一个用于查询products表,一个用于查询category表。由于使用的是嵌套查询,当操作大型数据集合和列表时将会带来频繁操作数据库问题。即执行一条sql获取结果集(oneToOneTestAssociationSelect),根据该结果集,循环执行嵌套查询获取具体信息(selectCategoryInfo),与上面的嵌套结果查询相比,这种情况显然有明显不足。

3. 测试代码

定义Product和Category实体,添加Mapper接口

技术分享图片
package com.sl.po;

import java.math.BigDecimal;

public class Product {
    private int Id;
    private String Name;
    private String Description;
    private BigDecimal UnitPrice;
    private String ImageUrl;
    private Boolean IsNew;
    private String cityCode;
    private int categoryId;
    
    private Category category;
    

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    public int getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(int categoryId) {
        this.categoryId = categoryId;
    }

    public String getCityCode() {
        return cityCode;
    }

    public void setCityCode(String cityCode) {
        this.cityCode = cityCode;
    }

    public int getId() {
        return Id;
    }

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

    public String getName() {
        return Name;
    }

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

    public String getDescription() {
        return Description;
    }

    public void setDescription(String description) {
        this.Description = description;
    }

    public BigDecimal getUnitPrice() {
        return UnitPrice;
    }

    public void setUnitPrice(BigDecimal unitprice) {
        this.UnitPrice = unitprice;
    }

    public String getImageUrl() {
        return Name;
    }

    public void setImageUrl(String imageurl) {
        this.ImageUrl = imageurl;
    }

    public boolean getIsNew() {
        return IsNew;
    }

    public void setIsNew(boolean isnew) {
        this.IsNew = isnew;
    }

    @Override
    public String toString() {
        return "Product [Id=" + Id + ", Name=" + Name + ", Description=" + Description + ", UnitPrice=" + UnitPrice
                + ", ImageUrl=" + ImageUrl + ", IsNew=" + IsNew + ", cityCode=" + cityCode + ", categoryId="
                + categoryId + ", category=" + category + "]";
    }

    
}
View Code
技术分享图片
package com.sl.po;

import java.util.List;

public class Category {
    private int Id;
    private String Name;
    private String Remark;
    
    private List<Product> productList;
    
    
    public int getId() {
        return Id;
    }
    public List<Product> getProductList() {
        return productList;
    }
    public void setProductList(List<Product> productList) {
        this.productList = productList;
    }
    public void setId(int id) {
        Id = id;
    }
    public String getName() {
        return Name;
    }
    public void setName(String name) {
        Name = name;
    }

    public String getRemark() {
        return Remark;
    }
    public void setRemark(String remark) {
        Remark = remark;
    }
    
    @Override
    public String toString() {
        return "Category [Id=" + Id + ", Name=" + Name + ", Remark=" + Remark + ", productList=" + productList + "]";
    }
}
View Code
技术分享图片View Code
技术分享图片
// 一对一 使用resultType映射结果集
    // @Test
    public void testSelectProduct() {

        // 获取mapper接口的代理对象
        UnitMapper unitMapper = session.getMapper(UnitMapper.class);

        ProductDetailInfo detailInfo = unitMapper.oneToOneTest(1);

        System.out.println(detailInfo);

        // 关闭会话
        session.close();
    }

    // 一对一 使用resultMap映射结果集
    // @Test
    public void testSelectProduct2() {

        // 获取mapper接口的代理对象
        UnitMapper unitMapper = session.getMapper(UnitMapper.class);

        Product product = unitMapper.oneToOneTestMap(2);

        System.out.println(product);

        System.out.println(product.getCategory().toString());

        // 关闭会话
        session.close();
    }

//嵌套查询
    //一对一
    
    //@Test
    public void testSelectProductTest() {

        // 获取mapper接口的代理对象
        UnitMapper unitMapper = session.getMapper(UnitMapper.class);

        Product product = unitMapper.oneToOneTestAssociationSelect(2);

        System.out.println(product);

        System.out.println(product.getCategory().toString());

        // 关闭会话
        session.close();
    }
View Code

一对多查询

将上面一对一的示例倒过来看,一种类别下有多个商品,所以category->products是一对多的关系

mybatis中可以通过使用resultMap的collection标签将关联查询的多条记录映射到一个list集合属性中。

1.嵌套结果实现

<!-- 一对多映射 -->
    <select id="oneToManyTest" resultMap="categoryInfo">
        select c.id cid,c.`name`
        cname,c.remark, p.*
        from category c
        join products p
        on p.categoryId= c.Id
        where c.id= #{cid}
    </select>
    <resultMap type="com.sl.po.Category" id="categoryInfo">
        <id column="cid" property="id" />
        <result column="cname" property="name" />
        <result column="remark" property="remark" />
        <!-- collection标签,一对多映射,关联当前分类下产品信息   property映射集合结果,ofType结果集类型 -->
        <collection property="productList" ofType="com.sl.po.Product">
            <id property="id" column="id" javaType="int" jdbcType="INTEGER" />
            <result column="name" property="Name" />
            <result column="description" property="Description" />
            <result column="unitprice" property="UnitPrice" />
            <result column="imageUrl" property="ImageUrl" />
            <result column="isnew" property="IsNew" />
            <result column="citycode" property="cityCode" />
        </collection>
    </resultMap>

2.嵌套查询实现

<!-- 集合嵌套查询 -->
    <select id="oneToManyTestCollectionSelect" resultMap="categoryInfo2">
        select *
        from category c
        where c.id= #{id}
    </select>
    <resultMap id="categoryInfo2" type="com.sl.po.Category">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="remark" property="remark" />
        <!--collection 映射一对多结果集    column:传递嵌套查询参数    select:嵌套查询id-->
        <collection property="productList" ofType="com.sl.po.Product"  column="id"  select="selectProductByCategoryId">
        </collection>
    </resultMap>
    <!-- 嵌套子查询 -->
    <select id="selectProductByCategoryId" resultType="com.sl.po.Product">
        select *
        from products 
        where categoryId= #{id}
    </select>

测试代码:

定义Product和Category实体同上,添加Mapper接口

技术分享图片
public interface UnitMapper {

    Category oneToManyTest(int cId);

    Product oneToOneTestAssociationSelect(int id);
    
        //嵌套查询中的子查询也需要定义接口
    Category selectCategoryInfo(int id);
    
}
View Code
技术分享图片
//一对多
@Test
    public void oneToManyTest() {

        UnitMapper unitMapper = session.getMapper(UnitMapper.class);

        Category catrgoryInfo = unitMapper.oneToManyTest(1);

        System.out.println(catrgoryInfo);

        if (catrgoryInfo.getProductList().size() > 0) {
            for (Product pro : catrgoryInfo.getProductList()) {
                System.out.println(pro);
            }
        }

        // 关闭会话
        session.close();

    }
        
//嵌套查询 一对多
@Test
    public void testoneToManyTestCollectionSelect() {

        // 获取mapper接口的代理对象
        UnitMapper unitMapper = session.getMapper(UnitMapper.class);

        Category category = unitMapper.oneToManyTestCollectionSelect(1);

        System.out.println(category);

        if (category.getProductList().size() > 0) {
            for (Product pro : category.getProductList()) {
                System.out.println(pro);
            }
        }
        // 关闭会话
        session.close();
    }
View Code

多对多查询

示例:一个订单包含多个商品,一个商品也可以对应多个订单,这个示例查询稍微扩展一下,增加用户信息,一个用户对应多个订单

实体对象定义:定义User实体,增加orders属性,用于映射当前用户的订单; 定义Order对象,增加orderItems属性,映射当前订单有哪些内容(产品);定义OrderItem实体,增加product属性,映射具体产品信息

<!-- 多对多映射 查询用户信息及对应订单信息,订单详情 -->
    <!-- select * from orders o join `user` u on o.userId = u.id join orderItem 
        i on o.Id = i.orderid join products p on i.productid = p.Id -->
    <select id="manyToManyTest" resultMap="userAndOrderInfo">
        select u.*,
        o.id oid,
        o.createtime ocreatetime,
        o.userid ouserid,
        o.amount oamount,
        o.remark
        oremark,
        i.id iid,
        i.orderid iorderid,
        i.productid iproductid,
        i.createtime icreatetime,
        i.number inumber,
        i.price iprice,

        p.id pid,
        p.`name` pname,
        p.Description pdescription

        from orders o
        join `user` u on
        o.userId = u.id
        join orderItem i on o.Id = i.orderid
        join products p on
        i.productid = p.Id
        where u.id=#{id}
    </select>
    <resultMap type="com.sl.po.User" id="userAndOrderInfo">
        <id column="id" property="id" />
        <result column="sex" property="sex" />
        <result column="birthday" property="birthday" />
        <result column="address" property="address" />
        <result column="username" property="userName" />
        <!-- 映射用户对应的订单信息,一个用户可以有多个订单 -->
        <collection property="orders" ofType="com.sl.po.Order">
            <id column="oid" property="id" />
            <result column="ocreatetime" property="createTime" />
            <result column="ouserid" property="userId" />
            <result column="oamount" property="amount" />
            <result column="oremark" property="remark" />
            <!-- 订单对应的商品信息,一个订单可以有多个商品  -->
            <collection property="orderItems" ofType="com.sl.po.OrderItem">
                <id column="iid" property="id" />
                <result column="iorderid" property="orderId" />
                <result column="iproductid" property="productId" />
                <result column="icreatetime" property="createTime" />
                <result column="inumber" property="number" />
                <result column="iprice" property="price" />
                <!-- 映射商品信息 (OrderItem与商品product 一一对应)-->
                <association property="product" javaType="com.sl.po.Product">
                    <id column="pid" property="Id" />
                    <result column="pname" property="Name" />
                    <result column="pdescription" property="Description" />
                </association>
            </collection>
        </collection>
    </resultMap>

测试代码

技术分享图片
package com.sl.po;

import java.sql.Date;
import java.util.List;

public class User {
    
    private int id;
    private String sex;
    private Date birthday;
    private String address;
    private String userName;
    
    //映射当前用户订单列表
    private List<Order> orders;
    
    
    public List<Order> getOrders() {
        return orders;
    }
    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    
    @Override
    public String toString() {
        return "User [id=" + id + ", sex=" + sex + ", birthday=" + birthday + ", address=" + address + ", userName="
                + userName + ", orders=" + orders + "]";
    }
    
    
    
}
View Code
技术分享图片
package com.sl.po;

import java.sql.Date;
import java.util.List;

public class Order {
    
    private int id;
    private int userId;
    private Date createTime;
    private int amount;
    private String remark;
    
    private User user;
    //映射订单内容(产品信息)
    private List<OrderItem> orderItems;
    
    
    
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public int getAmount() {
        return amount;
    }
    public void setAmount(int amount) {
        this.amount = amount;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public List<OrderItem> getOrderItems() {
        return orderItems;
    }
    public void setOrderItems(List<OrderItem> orderItems) {
        this.orderItems = orderItems;
    }
    
}
View Code
技术分享图片
package com.sl.po;

public class OrderItem {
    
    private int id;
    private int orderId;
    private int productId;
    private int createTime;
    private int number;
    private int price;

    //订单项对应的具体产品
    private Product product;
    
    
    public Product getProduct() {
        return product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }

    public int getId() {
        return id;
    }

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

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    public int getProductId() {
        return productId;
    }

    public void setProductId(int productId) {
        this.productId = productId;
    }

    public int getCreateTime() {
        return createTime;
    }

    public void setCreateTime(int createTime) {
        this.createTime = createTime;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

}
View Code
技术分享图片
package com.sl.mapper;

import java.util.List;

import com.sl.po.Category;
import com.sl.po.Product;
import com.sl.po.ProductDetailInfo;
import com.sl.po.User;

public interface UnitMapper {

    User manyToManyTest(int id);
}
View Code
技术分享图片
//多对多
    @Test
    public void manyToManyTest() {

        UnitMapper unitMapper = session.getMapper(UnitMapper.class);

        User userOrder = unitMapper.manyToManyTest(1);

        System.out.println(userOrder);
        
        // 关闭会话
        session.close();

    }
View Code

标签即属性说明

Association标签: 作用是可以将关联查询信息映射到一个pojo对象中

collection标签: 作用是可以将关联查询信息映射到一个集合中

Association和collection标签常用到的属性:

Property属性: 指定当前association标签内容映射到pojo对象中哪个属性。

javaType:映射属性的类型

typeHandler:类型处理器,使用这个属性,你可以覆盖默认的 typeHandler 类型处理器。 这个属性值是类的完全限定名或者是一个类型处理器的实现, 或者是类型别名

column:sql结果集列名,用在嵌套查询时传递参数,要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。

select:嵌套查询映射语句的ID

fetchType:可选的。有效值为 lazy和eager。 如果使用了,它将取代全局配置参数lazyLoadingEnabled

columnPrefix:将含有指定前缀的结果集映射到当前标签下 例如:<association property="productInfo" columnPrefix="p_" /> 将结果集中以“p_”开头的列映射到productInfo属性上

 

以上是关于Mybatis学习系列关联查询的主要内容,如果未能解决你的问题,请参考以下文章

mybatis基础系列——关联查询延迟加载一级缓存与二级缓存

myBatis系列之四:关联数据的查询

Mybatis学习——一对多关联表查询

mybatis系列-10-一对一查询

MyBatis:学习笔记——关联查询

MyBatis关联查询,表字段相同,resultMap映射问题的解决办法