SpringBoot使用Mybatis注解进行一对多和多对多查询

SpringBoot使用Mybatis注解进行一对多和多对多查询

GitHub的完整示例项目地址springboot-data

一、模拟的业务查询

系统中的用户user都有唯一对应的地址信息address,每个用户可以有多量车car,类似如下结构

|-- user
    |-- address
    |-- carList
        |-- car1
        |-- car2

二、对应的实体类如下

@Data
public class AddressPO {

    private Long id;

    /**
     * 省份
     */
    private String province;

    /**
     * 城市
     */
    private String city;

    /**
     * 街道
     */
    private String street;
}

@Data
public class CarPO {

    private Long id;

    /**
     * 颜色
     */
    private String color;

    /**
     * 品牌
     */
    private String name;

    private Long userId;
}

@Data
public class UserPO extends AbstractPO {

    private Long id;

    private String username;

    private String password;

    private Integer age;

    private GenderEnum gender;

    /**
     * 地址信息,和用户是一对一的关系
     */
    private AddressPO address;

    /**
     * 地址id
     */
    private Long addressId;

    /**
     * 用户拥有的车,和用户是一对多的关系
     */
    private List<CarPO> cars;

}

三、对应的建表语句和模拟数据如下

CREATE TABLE IF NOT EXISTS `user`
(
    `id`          int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
    `username`    varchar(20) NOT NULL UNIQUE COMMENT '用户名',
    `password`    varchar(50) NOT NULL COMMENT '密码',
    `age`         int(2)      NOT NULL COMMENT '年龄',
    `gender`      varchar(10) NOT NULL COMMENT '性别',
    `address_id`  int(11)              DEFAULT NULL COMMENT '地址',
    `creater`     varchar(20)          DEFAULT NULL COMMENT '创建人',
    `modifier`    varchar(20)          DEFAULT NULL COMMENT '更新人',
    `create_time` datetime    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `modify_time` datetime    NOT NUll DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
    PRIMARY KEY (`id`),
    KEY `index_gender` (`gender`) USING BTREE COMMENT '性别'
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_bin;

CREATE TABLE IF NOT EXISTS `address`
(
    `id`       int(11) NOT NULL AUTO_INCREMENT,
    `province` varchar(50) DEFAULT NULL,
    `city`     varchar(50) DEFAULT NULL,
    `street`   varchar(50) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_bin;

CREATE TABLE IF NOT EXISTS `car`
(
    `id`      int(11) NOT NULL AUTO_INCREMENT,
    `color`   varchar(50) DEFAULT NULL,
    `name`    varchar(50) DEFAULT NULL,
    `user_id` int(11)     DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_bin;

INSERT INTO `user`(username, password, age, gender, address_id)
VALUES ('KimZing', '123456', '25', 'MAN', 1), ('kim', '123456', '25', 'MAN', 2);

INSERT INTO `address`
VALUES ('1', '北京', '北京', '王府井'),
       ('2', '天津', '天津', '周良'),
       ('3', '安徽', '宿州', '涌桥'),
       ('4', '广东', '广州', '顺德');

INSERT INTO
    `car`
VALUES
('1', 'green', '路虎', '1'),
('2', 'white', '奔驰', '2'),
('3', 'blue', '玛莎拉蒂', '1'),
('4', 'yellow', '兰博基尼', '2');

四、@One一对一映射

以获取用户的唯一地址为例,首先我们定义一个根据地址id查询地址的查询方法

@Mapper
public interface AddressRepository {
    /**
     * 根据地址id查询地址
     */
    @Select("SELECT * FROM `address` WHERE id = #{id}")
    AddressPO findAddressById(Long id);
}

然后我们定义一个根据用户id查询用户的方法

@Mapper
public interface MySqlUserRepository {
   
    @Select("SELECT * FROM `user` WHERE id = #{id}")
    UserPO find(Long id);

}

这个时候我们查询出来的user对象中的address属性是空的,和address并没有任何关联。
那么我们要把user中的addressId传递给AddressRepository的查询地址的方法,
然后把查询出的地址对象address赋值给user的address属性,那么我们怎么做呢?

@Mapper
public interface MySqlUserRepository {

    @Select("SELECT * FROM `user` WHERE id = #{id}")
    @Results({
            @Result(property = "address", column = "address_id",
                    one = @One(select = "com.kimzing.data.repository.AddressRepository.findAddressById"))
    })
    UserPO find(Long id);

}

我们要使用@Resutl注解对返回的结果进行配置,

  • property = “address”, 表示要将返回的查询结果赋值给user的address属性
  • column = “address_id” 是指将user表中的address_id作为com.kimzing.data.repository.AddressRepository.findAddressById的查询参数
  • one 表示这是一个一对一的查询
  • @One(select = "方法全路径) 表示我们调用的方法

五、@Many一对多查询

以获取用户拥有的所有车car为例,首先我们定义一个根据用户id查询车的查询方法

@Mapper
public interface CarRepository {
    /**
     * 根据用户id查询所有的车
     */
    @Select("SELECT * FROM `car` WHERE user_id = #{userId}")
    List<Car> findCarsByUserId(Long userId);
}

然后我们定义一个根据用户id查询用户的方法

@Mapper
public interface MySqlUserRepository {
   
    @Select("SELECT * FROM `user` WHERE id = #{id}")
    UserPO find(Long id);

}

这个时候我们查询出来的user对象中的List属性是空的,和car的查询方法并没有任何关联。
那么我们要把user中的用户id传递给CarRepository的查询车的方法,
然后把查询出的集合对象List赋值给user的cars属性,那么我们怎么做呢?(和获取地址是有些类似的)

package com.kimzing.data.repository.impl;

import com.kimzing.data.domain.po.UserPO;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * 数据存储.
 *
 * @author KimZing - kimzing@163.com
 * @since 2020/1/31 13:12
 */
@Mapper
public interface MySqlUserRepository {

    @Select("SELECT * FROM `user` WHERE id = #{id}")
    @Results({
            @Result(property = "address", column = "address_id",
                    one = @One(select = "com.kimzing.data.repository.AddressRepository.findAddressById")),
            @Result(property = "cars", column = "id",
                    many = @Many(select = "com.kimzing.data.repository.CarRepository.findCarsByUserId"))
    }),
    // 对userId进行赋值
    @Result(property = "id", column = "id")
    UserPO find(Long id);


}

我们要使用@Resutl注解对返回的结果进行配置,

  • property = “cars”, 表示要将返回的查询结果赋值给user的cars属性
  • column = “id” 是指将user表中的用户主键id作为com.kimzing.data.repository.CarRepository.findCarsByUserId的查询参数
  • many 表示这是一个一对多的查询
  • @Many(select = "方法全路径) 表示我们调用的方法, 方法参数userId就是上面column指定的列值

六、@One @Many的总结

首先我们统一下概念:查询Address或Car的方法,接下来统称为User的附属查询。

共同点:

  • 无论是一对一还是一对多,都是通过附属查询来实现的,我们需要定义这个附属查询方法。
  • 在主查询方法中通过@One、@Many指定附属查询方法的全路径。
  • 都通过column来传递参数给附属方法。

不同点:

  • 一对一,那么附属方法返回的是一个单独的对象
  • 一对多,那么附属方法返回的是一个对象集合
  • 36
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 32
    评论
在Spring Boot中使用MyBatis实现一对多关系,可以通过在实体类中定义关联关系,以及在Mapper.xml文件中编写SQL语句来实现。以下是一个简单的示例: 假设我们有两个实体类:Order和OrderItem,一个订单可以包含多个订单项。Order类中包含一个List<OrderItem>类型的属性,表示该订单包含的所有订单项。我们可以在Order类中定义一个方法,使用MyBatis的@Select注解查询该订单的所有订单项: ```java public class Order { private Long id; private String orderNo; private List<OrderItem> orderItems; // getter和setter方法省略 @Select("SELECT * FROM order_item WHERE order_id = #{id}") public List<OrderItem> getOrderItems() { return orderItems; } } ``` 在Mapper.xml文件中,我们可以编写一个查询订单及其所有订单项的SQL语句: ```xml <select id="getOrderById" resultType="Order"> SELECT * FROM orders WHERE id = #{id}; <collection property="orderItems" ofType="OrderItem"> SELECT * FROM order_item WHERE order_id = #{id} </collection> </select> ``` 在这个SQL语句中,我们使用MyBatis的collection元素来定义订单项的集合属性。当MyBatis执行这个SQL语句时,它会自动将查询结果映射到Order对象中,并将所有查询到的订单项添加到Order对象的orderItems属性中。 在Spring Boot中使用MyBatis实现一对多关系,需要在pom.xml文件中添加MyBatisMyBatis-Spring的依赖,以及配置MyBatis的SqlSessionFactory和MapperScannerConfigurer。具体的配置方法可以参考官方文档或其他教程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值