当前位置: 首页 > 工具软件 > mybatis-jpa > 使用案例 >

Spring boot data jpa +mybatis-plus 多表联合查询带分页,最终实现

柴瀚昂
2023-12-01

Spring boot data jpa +mybatis-plus 多表联合查询带分页,最终实现

经过两天的测试和摸索,记录一下,所关联的知识点,这里不赘述。直接展示最终代码。

实体类代码

Company.java

   @Id
    @TableId
    private Long id = SnowFlakeUtil.nextId();
    @CreatedBy
    @Column(columnDefinition = "varchar(100) comment '创建者'")
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    @Column(columnDefinition = "varchar(255)  comment '名称'")
    private String name;
    @Column(columnDefinition = "varchar(80) comment '联系人'")
    private String connectPeople;
    @Column(columnDefinition = "varchar(20) comment '联系电话'")
    private String connectPhone;

	private Long accountId;

Account.java

	@Id
    @TableId
    private Long id = SnowFlakeUtil.nextId();
   @Column(columnDefinition = "decimal(10,2) not null default 0.00 comment '余额'")
    private BigDecimal yuEr;
    @Column(columnDefinition = "decimal(10,2) not null default 0.00  comment '受权金额'")
    private BigDecimal limitMoney;
    @Column(columnDefinition = "decimal(10,2) not null default 0.00 comment '总充值金额'")
    private BigDecimal totalIn;
    @Column(columnDefinition = "decimal(10,2) not null default 0.00 comment '总扣税金额'")
    private BigDecimal totalOut;
    @Column(columnDefinition = "varchar(150)  comment '备注'")
    private String remark;

User.java

	@Id
    @TableId
    private Long id = SnowFlakeUtil.nextId();
    @CreatedBy
    @Column(columnDefinition = "varchar(100) comment '创建者'")
    @Pattern(regexp = NameUtil.regMobile, message = "11位手机号格式不正确")
    private String mobile;

    @ApiModelProperty(value = "邮箱")
    @Pattern(regexp = NameUtil.regEmail, message = "邮箱格式不正确")
    private String email;

    @ApiModelProperty(value = "所属公司")
    private Long companyId;

视图对像

companyVo.java

@Data
@NoArgsConstructor
public class CompanyVo extends Company {
    private BigDecimal yuEr;
    private BigDecimal limitMoney;
    private BigDecimal totalIn;
    private BigDecimal totalOut;

    public CompanyVo(Company company, Account account) {
        this.yuEr = account.getYuEr();
        this.totalIn = account.getTotalIn();
        this.totalOut = account.getTotalOut();
        this.limitMoney = account.getLimitMoney();

        this.setId(company.getId());
        this.setName(company.getName());
        this.setAbbreviation(company.getAbbreviation());
        this.setNameFirstCase(company.getNameFirstCase());
        this.setConnectPeople(company.getConnectPeople());
        this.setConnectPhone(company.getConnectPhone());
        this.setIsCoalSource(company.getIsCoalSource());
    }
}

userVo.java

public class UserVo extends User{
	//公司名称
    private String companyName;

	//只列出关键属性 。。。

}
  • userVo.java 中的 companyName 属性和 companyVo中的 createUserName 两个属性,如果不考虑数据冗余,可以添加到
    user.java 和Company.java 中。在查询时也少了一些关联查询。但是这两属性对传递依赖于其主键,不满足数据设计 第三范式,会导致更新异常。
    -公司名称,和用户名称通常都是唯一的,基本不会改变。基它的属性是随时可以改变的
  • 其它属性请看代码

Jpa 的实现

companyDao.java


    @Query(value = "select new com.example.demo.vo.CompanyVo(c,a) " +
            "from Company c left join TaxesAccount a on a.companyId = c.id " +
            "where c.id > 0 and c.name is not null ")
    Page<CompanyVo> findVoPage(Pageable page);

关键

  • //@query 查询Vo
    //1. 如果是 native 查询 返回的类型,要用List<Map<String,Object>>
    // 1.1 不支持动态的条件
    // 1.2 如果要分页 自行计算 limit 和其它数据
    //2. 如果不是 native
    // 2.1 需要新建一个关联类 VO
    // 2.2 不支持动态的条件
    // 2.3 如果要分页 需要传递一个 Pageable
    // 2.3 需要注意 Where 书写。c.name is not null 我多余加上的,因为 c.id > 0 这一个条件 打印出的的构思被解析成 company.id =? 不知道为什么

mybatis-plus 实现

CompanyMapper.java

  //AbstractWrapper 只能用 QueryWrapper 
    @Select("select c.*,a.total_in,a.total_out,a.yu_er from company c, taxes_account a  ${ew.customSqlSegment} " +
            "and  c.account_id = a.id")
    IPage<CompanyVo> listPage(Page page, @Param(Constants.WRAPPER) AbstractWrapper wrapper);

关键

  • //联合分页查询 需要注意以下的内容
    
/**
 * @Select
 * 联合分页查询
 * 1.新建立一个关联的实体类
 * 2.实体类必须有一个无参数的构造方法
 * 3.@select 是原生 sql;主要注意Field的名称,
 * 4.两张表不能写left join 之类的 只能是 from table t1 ,table2 t2 ,在sql 的条件后面加上 On 的条件
 * 如 and  c.account_id = a.id" 不是 on  c.account_id = a.id"
 * 5.要分页,必须传一个 Page的参数
 * 6.要动态条件 必须传一个 Wrapper ;如 listPage(Page page, @Param(Constants.WRAPPER) LambdaQueryWrapper wrapper);\
 * 7. 动态条件的需要要用的列需要指明
 * 8. 关联类,也需要建立一个Mapper extends BaseMapper<关联类> 不然
 *  can not find lambda cache for this property [XXX] of entity [com.XXX.XXX]
 *  9.如果 Column 'limit_money' in where clause is ambiguous 不能用 LombadWrapper **这里会引发 字段不明确的异常**
 */

CompanyServiceImpl.java


 @Autowired
 CompanyMapper companyMapper;

   @Override
    public IPage<CompanyVo> listSelectWrapperPage() {

        QueryWrapper<Object> wr = Wrappers.query();
        wr.like(true, "name", "天天");
        wr.or(w -> {
            w.like(true, "abbreviation", "天天").ge(true, "a.limit_money", 0.0);
        });
        wr.select("c.*", "name", "a.yu_er");//@select 后无效 除非 ${ew.sqlSelect}
        Page<CompanyVo> p = new Page(1, 2);

        return companyVoMapper.listPage(p, wr);
    }

关键

  • wr.select(“c.id”, “name”, “a.yu_er”);//@select 后无效 除非 ${ew.sqlSelect}见后面,定义查询的列的Sql写法
  • a.limit_money ,“a.yu_er” 相信大家都看出,为什么 AbstractWrapper 只能用 QueryWrapper

定义查询的列的Sql写法

   //c.*,a.total_in,a.total_out,a.yu_er 换成  ${ew.sqlSelect} 
    @Select("select ${ew.sqlSelect} from company c, taxes_account a  ${ew.customSqlSegment} " +
            "and  c.account_id = a.id")
    IPage<CompanyVo> listPage(Page page, @Param(Constants.WRAPPER) AbstractWrapper wrapper);
    

总结

  • 推荐使用Mybatis-plus 来操作数据,jpa 只用来动态生成数据表。
  • mybatis-plus 可以做到动态列和动态条件,而Vo里面不需要一个带参数的构造方法,参见 CompanyVo.java。
  • 一路填坑,最终与这样的方法解决,如果你有其它方法,评论留言一起讨论。
 类似资料: