MyBatis-Flex-多表联查(自动映射)

本文介绍了MyBatis-Flex框架的QueryWrapper功能,包括基础查询、AS映射、多表联查、高级映射以及处理重名字段的方法,展示了如何提高SQL编写效率和数据查询的灵活性。
摘要由CSDN通过智能技术生成

 简介:

MyBatis-Flex 是一个优雅的 MyBatis 增强框架,它非常轻量、同时拥有极高的性能与灵活性。我们可以轻松的使用 Mybaits-Flex 链接任何数据库,其内置的 "QueryWrapper " → 亮点,帮助我们极大的减少了 SQL 编写的工作的同时,减少出错的可能性。

官网:

MyBatis-Flex - MyBatis-Flex 官方网站icon-default.png?t=N7T8https://mybatis-flex.com/

数据脱敏上文以及说到如果有感兴趣的小伙伴请移步到上一篇

mybatis-flex 详解-CSDN博客

多表联查

初始对比:

为了方便熟悉,我们和mybatis-plus的查询做一个对比,更加容易理解:

MyBatis-Flex:

QueryWrapper query = new QueryWrapper()
    .select(
        ACCOUNT.ID,
        ACCOUNT.USER_NAME,
        max(ACCOUNT.BIRTHDAY),
        avg(ACCOUNT.SEX).as("sex_avg")
    );
List<Employee> employees = employeeMapper.selectListByQuery(query);

MyBatis-Plus:

QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
queryWrapper
    .select(
        "id"
        ,"user_name"
        ,"max(birthday)"
        ,"avg(birthday) as sex_avg"
    );
List<Employee> employees = employeeMapper.selectList(queryWrapper);

在多表联查前面我们先尝试一个基础查询(基础映射):

@Table(value = "tb_account")
public class Account {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    private int age;

    //getter setter
}

Account.java 与表 tb_account 是字段和属性是一一对应关系的。此时,我们在查询数据的时候,可以通过 AccountMapper 方法直接查询,例如:

QueryWrapper qw = new QueryWrapper();
qw.select(ACCOUNT.ALL_COLUMNS)
    .where(ACCOUNT.ID.ge(100));

List<Account> accounts = accountMapper.selectListByQuery(qw);

或者使用如下的链式查询,都可以直接得到 List<Account> 结果:accounts

QueryChain.of(accountMapper)
    .select(ACCOUNT.ALL_COLUMNS)
    .where(ACCOUNT.ID.ge(100))
    .list();

AS 映射

假设我们在 Account.java 中多定义了一些其他属性,如下所示:

java

@Table(value = "tb_account")
public class Account {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    private int age;

    //最大年龄
    private int maxAge;

    //平均年龄
    private int avgAge;

    //getter setter
}

那么,我们在查询的时候,就可以通过 as 进行映射关联,查询代码如下:

java

QueryChain.of(accountMapper)
    .select(
        ACCOUNT.ALL_COLUMNS,
        max(ACCOUNT.AGE).as("maxAge"),
        avg(ACCOUNT.AGE).as("avgAge")
    ).where(ACCOUNT.ID.ge(100))
    .groupBy(ACCOUNT.AGE)
    .list();

或者:

java

QueryChain.of(accountMapper)
    .select(
        ACCOUNT.ALL_COLUMNS,
        max(ACCOUNT.AGE).as("max_age"),
        avg(ACCOUNT.AGE).as("avg_age")
    ).where(ACCOUNT.ID.ge(100))
    .groupBy(ACCOUNT.AGE)
    .list();

或者使用 lambda:

java

QueryChain.of(accountMapper)
    .select(
        ACCOUNT.ALL_COLUMNS,
        max(ACCOUNT.AGE).as(Account::getMaxAge),
        avg(ACCOUNT.AGE).as(Account::getAvgAge)
    ).where(ACCOUNT.ID.ge(100))
    .groupBy(Account::getAge)
    .list();

以上代码执行的 SQL 如下:

sql

select tb_account.*
     , max(tb_account.age) as maxAge
     , avg(tb_account.age) as avgAge
where tb_account.id >= 100
group by tb_account.age

多表映射

假设我们定义了一个 BootVo.java,其中包含了图书的基本信息,也包含了图书归属的用户信息,例如:

java

public class BookVo {

    //图书的基本字段
    private Long id;
    private Long accountId;
    private String title;
    private String content;

    //用户表的字段
    private String userName;
    private int userAge;
}

此时,我们再进行 left join 多表查询时,代码如下:

java

List<BookVo> bookVos = QueryChain.of(bookMapper)
    .select(
        BOOK.ALL_COLUMNS, //图书的所有字段
        ACCOUNT.USER_NAME, //用户表的 user_name 字段
        ACCOUNT.AGE.as("userAge") //用户表的 age 字段, as "userAge"
    ).from(BOOK)
    .leftJoin(ACCOUNT).on(BOOK.ACCOUNT_ID.eq(ACCOUNT.ID))
    .where(ACCOUNT.ID.ge(100))
    .listAs(BookVo.class);

或者,我们也可以直接在 BookVo 中,定义 Account 对象,例如:

java

public class BookVo {

    //图书的基本字段
    private Long id;
    private Long accountId;
    private String title;
    private String content;

    //用户
    private Account account;
}

查询代码如下:

java

List<BookVo> bookVos = QueryChain.of(bookMapper)
    .select(
        BOOK.DEFAULT_COLUMNS,
        ACCOUNT.DEFAULT_COLUMNS,
     )
    .from(BOOK)
    .leftJoin(ACCOUNT).on(BOOK.ACCOUNT_ID.eq(ACCOUNT.ID))
    .where(ACCOUNT.ID.ge(100))
    .listAs(BookVo.class);

高级映射

在以上的表结构中,一个账户可以有多本图书,那么我们假设定义的 AccountVo.java 的结构如下:

java

public class AccountVO {

    private Long id;
    private String userName;
    private int age;

    //账户拥有的 图书列表
    private List<Book> books;
}

java

List<AccountVO> bookVos = QueryChain.of(accountMapper)
    .select() // 不传入参数等同于 SQL 的 select *
    .from(ACCOUNT)
    .leftJoin(BOOK).on(ACCOUNT.ID.eq(BOOK.ACCOUNT_ID))
    .where(ACCOUNT.ID.ge(100))
    .listAs(AccountVO.class);

亦或者指定查询参数:

java

List<AccountVO> bookVos = QueryChain.of(accountMapper)
    .select(
        ACCOUNT.ID,
        ACCOUNT.USER_NAME,
        ACCOUNT.AGE,
        BOOK.TITLE,
        BOOK.CONTENT,
     )
    .from(ACCOUNT)
    .leftJoin(BOOK).on(ACCOUNT.ID.eq(BOOK.ACCOUNT_ID))
    .where(ACCOUNT.ID.ge(100))
    .listAs(AccountVO.class);

高级映射的场景中,我们还可以通过注解 @RelationManyToOne 进行查询, 详情请点击 这里

重名映射

在很多类型嵌套的场景下,可能会出现字段名定义重复的情况,例如:

java

@TableRef(Account.class)
public class AccountVO {

    private Long id;
    private String name;
    private int age;

    //账户拥有的 图书列表
    private List<Book> book;
}

java

public class Book {
    private Long id;
    private Long accountId;
    private String name;
}

在以上的嵌套定义中, AccountVO 以及 Book 都包含了 id 和 name 的定义,假设我们查询的方法如下:

java

List<AccountVO> bookVos = QueryChain.of(accountMapper)
    .select(
        ACCOUNT.ID,
        ACCOUNT.NAME,
        ACCOUNT.AGE,
        BOOK.ID,
        BOOK.NAME,
     )
    .from(ACCOUNT)
    .leftJoin(BOOK).on(ACCOUNT.ID.eq(BOOK.ACCOUNT_ID))
    .where(ACCOUNT.ID.ge(100))
    .listAs(AccountVO.class);

其执行的 SQL 如下:

sql

select tb_account.id   as tb_account$id,
       tb_account.name as tb_account$name,
       tb_account.age,
       tb_book.id      as tb_book$id,  -- Flex 发现有重名时,会自动添加上 as 别名
       tb_book.name    as tb_book$name -- Flex 发现有重名时,会自动添加上 as 别名
from tb_account
         left join tb_book on tb_account.id = tb_book.account_id
where tb_account.id >= 100

此时,查询的数据可以正常映射到 AccountVO 类。

注意事项

  • 在查询 VO 类当中有重名字段时,需要给 VO 类标记 @TableRef 注解,指定其对应的实体类,以正确添加别名。
  • 在 QueryWrapper 的 select(...) 中,MyBatis-Flex 在 多表查询 的情况下,且有相同的字段名时,MyBatis-Flex 内部会主动帮助用户添加上 as 别名,默认为:表名$字段名

错误的情况:

若我们修改查询代码如下:

sql

List<AccountVO> bookVos = QueryChain.of(accountMapper)
    .select()
    .from(ACCOUNT)
    .leftJoin(BOOK).on(ACCOUNT.ID.eq(BOOK.ACCOUNT_ID))
    .where(ACCOUNT.ID.ge(100))
    .listAs(AccountVO.class);

那么,其执行的 SQL 如下:

sql

select * from tb_account
left join tb_book on tb_account.id = tb_book.account_id
where tb_account.id >= 100

此时,查询的结果集中,会有多个 id 和 name 列,程序无法知道 id 和 name 对应的应该是 AccountVO 的还是 Book 的,因此,可能会出现数据错误赋值的情况。

所以,若程序中出现包裹对象有重名属性的情况时,QueryWrapper 的 select(...) 方法必须传入具体的字段才能保证数据正常赋值。

如下的代码也是没问题的:

java

List<AccountVO> bookVos = QueryChain.of(accountMapper)
    .select(
        ACCOUNT.DEFAULT_COLUMNS,
        BOOK.DEFAULT_COLUMNS
     )
    .from(ACCOUNT)
    .leftJoin(BOOK).on(ACCOUNT.ID.eq(BOOK.ACCOUNT_ID))
    .where(ACCOUNT.ID.ge(100))
    .listAs(AccountVO.class);

@ColumnAlias 注解:

@ColumnAlias 注解的作用是用于定义在 entity 查询时,默认的 SQL 别名名称,可以取代自动生成的别名,例如:

java

public class Book {

    @ColumnAlias("bookId")
    private Long id;

    private Long accountId;

    @ColumnAlias("bookName")
    private String name;
}

那么,假设我们的查询代码如下:

java

List<AccountVO> bookVos = QueryChain.of(accountMapper)
    .select(
        ACCOUNT.ID,
        ACCOUNT.NAME,
        ACCOUNT.AGE,
        BOOK.ID,
        BOOK.NAME,
     )
    .from(ACCOUNT)
    .leftJoin(BOOK).on(ACCOUNT.ID.eq(BOOK.ACCOUNT_ID))
    .where(ACCOUNT.ID.ge(100))
    .listAs(AccountVO.class);

其执行的 SQL 为:

sql

select tb_account.id, tb_account.name, tb_account.age,
    tb_book.id as bookId, -- @ColumnAlias("bookId")
    tb_book.name as bookName  -- @ColumnAlias("bookName")
from tb_account
left join tb_book on tb_account.id = tb_book.account_id
where tb_account.id >= 100

此时,数据也是可以正常映射。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Mybatis-Plus 中进行多表联查时,可以使用 `@TableField` 注解指定要查询的字段,并在实体类中定义对应的属性。 假设我们有两张表:`user` 表和 `order` 表,它们的关系是一对多,一个用户可以拥有多个订单。现在我们需要查询用户的信息和他的订单总数,然后将订单总数更新到用户表中的一个字段中。 首先,定义一个实体类 `User`,并在其中添加一个属性 `orderCount`,用来存储订单总数: ``` @Data public class User { private Long id; private String name; private Integer age; @TableField(exist = false) private Integer orderCount; // 订单总数 } ``` 其中,`@TableField(exist = false)` 表示该属性不是数据库表中的字段。 然后,使用 Mybatis-Plus 提供的 `Wrapper` 类进行多表联查,并计算订单总数: ``` QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.select("user.*, COUNT(order.id) as order_count") .eq("user.id", userId) .leftJoin("order", "user.id=order.user_id") .groupBy("user.id"); User user = userMapper.selectOne(wrapper); if (user != null) { user.setOrderCount(orderCount); userMapper.updateById(user); } ``` 在 `wrapper.select()` 方法中,我们使用了 SQL 的聚合函数 `COUNT()` 来计算订单总数,并使用 `as` 关键字给计算结果取了一个别名 `order_count`。然后,在 `wrapper.leftJoin()` 方法中,指定了要联查的表和联查条件。最后,在 `wrapper.groupBy()` 方法中,指定了分组条件。 查询出用户信息后,将订单总数设置到 `User` 对象中,并使用 `userMapper.updateById()` 方法将修改后的数据更新到数据库中。 以上就是使用 Mybatis-Plus 进行多表联查并修改字段的方法。注意,这里的 `orderCount` 属性不是数据库表中的字段,因此需要使用 `@TableField(exist = false)` 注解标识。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值