SpringDataJPA多表联合查询

相信大家都用过jpa,jpa继承CrudRepository和PagingAndSortingRepository接口之后,在简单的单表查询中,不管是使用自带的findAll(),saveAll等方法还是利用jpa的方法命名规范进行jpql查询,jpa使用起来快的一撇。然而在进行复杂查询时,需要继承JpaSpecificationExecutor接口 利用Specification 进行复杂查询,还需要定义好表之间的映射关系。今天就介绍下jpa的多表查询与条件构造。


一、Jpa配置和构建基础表

pom

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

配置

spring:
	jpa:
	    show-sql: true
	    hibernate:
	      # 自动创建、更新、验证数据库表结构
	      ddl-auto: update

这里有3个表,用户(JpaUser),角色(JpaRole),权限(JpaPermission)。
一个用户有一个角色,1个角色可以分配给多个用户,用户对角色就是多对一。假设一个角色绑定1个权限。角色对权限就是一对一。
JpaUser

@Data
@Entity
@Table(name = "jpa_user")
@org.hibernate.annotations.Table(appliesTo = "jpa_user", comment = "用户表")
public class JpaUser {

    @Id
    @GeneratedValue
    @ApiParam("主键ID")
    @Column(name = "id", length = 20)
    private Long id;

    @ApiParam("用户名")
    @Column(name = "user_name", columnDefinition = "VARCHAR(255) NOT NULL COMMENT '用户名'")
    private String userName;

    @ApiParam("昵称")
    @Column(name = "nick_name", columnDefinition = "VARCHAR(255) NOT NULL COMMENT '昵称'")
    private String nickName;

    @ApiParam("岗位")
    @Column(name = "position")
    private String position;

    @ApiParam("年龄")
    @Column(name = "age")
    private Integer age;

    @Column(name = "longitude", precision = 10, scale = 7)
    private BigDecimal longitude;

    @Column(name = "latitude", precision = 10, scale = 7)
    private BigDecimal latitude;

    /**
     * @ManyToOne 用户:角色 多个用户对应一个角色,当我们创建表结构时,应在多的一方去维护表关系,也就是说,应将@ManyToOne注解加在用户表中,并且设置为懒加载。
     * @JsonBackReference 生成json时该属性排除
     */
    @ManyToOne
    @JoinColumn(name = "role_id")
    private JpaRole role;
}

JpaRole

@Data
@Entity
@Table(name = "jpa_role")
@org.hibernate.annotations.Table(appliesTo = "jpa_role", comment = "角色表")
public class JpaRole {

    @Id
    @GeneratedValue
    @ApiParam("主键ID")
    @Column(name = "id")
    private Long id;

    @ApiParam("角色名")
    @Column(name = "role_name", columnDefinition = "VARCHAR(64) NOT NULL COMMENT '角色名'")
    private String roleName;

    @JsonBackReference
    @OneToMany(targetEntity = JpaUser.class, mappedBy = "role", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
    private List<JpaUser> jpaUsers;

    /**
     * 一对一的关系 在其中一个表维护就好了
     */
    @OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    @JoinColumn(name = "permission_id")
    private JpaPermission permission;

}

JpaPermission

@Data
@Entity
@Table(name = "jpa_permission")
@org.hibernate.annotations.Table(appliesTo = "jpa_permission", comment = "权限表")
public class JpaPermission {

    @Id
    @GeneratedValue
    @ApiParam("主键ID")
    @Column(name = "id", length = 20)
    protected Long id;

    @ApiParam("权限名称")
    @Column(name = "permission_name", columnDefinition = "VARCHAR(64) NOT NULL COMMENT '权限名称'")
    private String permissionName;

}

二、条件查询构造器

定义一个条件查询动态构造器。
JpaUserSpecification

public class JpaUserSpecification implements Specification<JpaUser> {

    private JpaUserQueryVo param;

    public JpaUserSpecification(JpaUserQueryVo jpaUserQueryVo) {
        this.param = jpaUserQueryVo;
    }

    @Override
    public Specification<JpaUser> and(Specification<JpaUser> other) {
        return null;
    }

    @Override
    public Specification<JpaUser> or(Specification<JpaUser> other) {
        return null;
    }

    @Override
    public Predicate toPredicate(Root<JpaUser> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        List<Predicate> predicates = new ArrayList<Predicate>();
        /**
         * 2张表
         */
        if (StringUtils.isNotEmpty(param.getRoleName())) {
            Join<JpaRole, JpaUser> join = root.join("role", JoinType.LEFT);
            predicates.add(criteriaBuilder.equal(join.get("roleName"), param.getRoleName()));
        }
        /**
         * 跨表查询,3个表
         * 需要通过权限名称查出对应的用户,JpaUser.role.permission = param.getPermissionName()
         * jointype.LEFT主要是说"role"这个属性是在哪个表中,这里是在JpaUser中。 
         * 此处debug就可以发现,执行第一行之后,join中的Model就变成了JpaRole。(图贴在后面) 
         * 此处是要通过权限名称查出对应的用户。所以先root.join("role", JoinType.LEFT)拿到JpaRole,然后再join.get("permission")拿到JpaPermission ,然后再匹配它的属性permissionName
         * 这里就是get出相应的属性,一直到你得到想要的属性为止。 
         */
        if (StringUtils.isNotEmpty(param.getPermissionName())) {
            Join<JpaPermission, JpaUser> join = root.join("role", JoinType.LEFT);
            predicates.add(criteriaBuilder.equal(join.get("permission").get("permissionName"), param.getPermissionName()));
        }
        /**
         * in查询
         */
        if (StringUtils.isNotEmpty(param.getIds())) {
            CriteriaBuilder.In in = criteriaBuilder.in(root.get("id").as(Long.class));
            List<String> ids = Arrays.asList(param.getIds().split(","));
            ids.forEach(s -> in.value(Long.parseLong(s)));
            predicates.add(criteriaBuilder.and(in));
        }
        /**
         * like查询
         */
        if (StringUtils.isNotEmpty(param.getUserName())) {
            predicates.add(criteriaBuilder.like(root.get("userName"), "%" + param.getUserName() + "%"));
        }
        /**
         * 全等 = 查询
         */
        if (StringUtils.isNotEmpty(param.getNickName())) {
            predicates.add(criteriaBuilder.equal(root.get("nickName"), param.getNickName()));
        }
        /**
         * 大于小于等等
         */
        if (param.getAge() != null) {
            predicates.add(criteriaBuilder.le(root.get("age"), param.getAge()));
            predicates.add(criteriaBuilder.gt(root.get("age"), param.getAge() - 10));
        }
        return predicates.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
    }
}

JpaUserQueryVo 是接收各种查询参数的Entity。此处就不贴它的代码了。
在这里插入图片描述


三、调用查询构造器

此处就省略controller层代码,只看service层代码。

@Override
public Page<JpaUser> tables(JpaUserQueryVo userQueryVo) {
    PageRequest pageRequest = PageRequest.of(0, 10);
    Specification<JpaUser> specification = new JpaUserSpecification(userQueryVo);
    return jpaJpaUserRepository.findAll(specification, pageRequest);
}

就这样就ok了。JpaUserSpecification会根据JpaUserQueryVo 里的参数自动构建查询。
来看一下效果。
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Data JPA可以通过使用@Query注解和JPQL语句来实现多联合查询。在JPQL语句中,可以使用JOIN关键字来连接多个,并使用ON子句来指定连接条件。例如: @Query("SELECT u.username, p.title FROM User u JOIN u.posts p ON u.id = p.userId") List<Object[]> findUserAndPost(); 这个例子中,User和Post是两个实体类,它们之间通过userId属性建立了关联关系。通过JOIN关键字和ON子句,我们可以将两个连接起来,并查询出用户名和文章标题。最终返回的是一个Object数组,包含了查询结果中的两个字段。 除了使用@Query注解,Spring Data JPA还提供了一些方法命名规则,可以自动生成查询语句。例如,如果我们在UserRepository中定义一个方法: List<User> findByPostsTitle(String title); Spring Data JPA会自动根据方法名生成查询语句,查询所有标题为指定值的文章所属的用户。这个方法名中的“Posts”是User实体类中的一个属性,它示用户发的所有文章。通过这种方式,我们可以方便地进行多联合查询,而不需要手动编写复杂的SQL语句。 ### 回答2: Spring Data JPA是Spring Framework的一个模块,它提供了Spring和JPA之间的集成,并支持快速开发具有CRUD功能的应用程序。在多联合查询中,Spring Data JPA可以使用JPQL或原生SQL语句来查询多个,并通过实体类的关系来建立之间的关联。下面将具体介绍如何使用Spring Data JPA进行多联合查询。 1. 使用JPQL进行多联合查询 使用JPQL进行多联合查询比使用原生SQL语句更方便,因为可以直接使用实体类和属性名来代替名和列名。 例如,我们有两个实体类:Customer和Order,它们之间是多对一的关系,即一个客户可以下多个订单。我们可以使用以下JPQL查询语句来查询所有客户及其对应的订单: ``` @Query("SELECT c, o FROM Customer c LEFT JOIN c.orders o") List<Object[]> findCustomerWithOrder(); ``` 这里使用了LEFT JOIN关键字来连接两个,LEFT JOIN示左连接,即返回左(这里是Customer)中所有数据和右(这里是Order)中匹配的数据。在SELECT语句中,我们选择了两个实体类,分别用c和o来代它们,并将它们放在一个数组中返回。这样就可以查询到所有客户及其对应的订单。 2. 使用原生SQL语句进行多联合查询 如果使用JPQL不能满足需求,也可以使用原生SQL语句来进行多联合查询。这时需要使用EntityManager来执行SQL语句。 例如,我们有两个:Customer和Order,它们之间的关系同上。我们可以使用以下SQL语句来查询所有客户及其对应的订单: ``` SELECT c.*, o.* FROM customer c LEFT JOIN orders o ON c.id = o.customer_id ``` 在EntityManager中,可以使用createNativeQuery方法来创建原生SQL语句的查询对象,以及setParameter方法来设置查询参数(如果有的话)。例如: ``` String sql = "SELECT c.*, o.* FROM customer c LEFT JOIN orders o ON c.id = o.customer_id"; Query query = entityManager.createNativeQuery(sql); List<Object[]> resultList = query.getResultList(); ``` 这里使用了Query.getResultList方法来返回结果集,它返回的是一个包含多个数组的列,每个数组对应一行查询结果。数组中的元素按照SELECT语句的顺序排列。 总结 Spring Data JPA提供了方便的方法来进行多联合查询,可以使用JPQL或原生SQL语句来查询多个,并通过实体类之间的关系建立之间的关联。使用JPQL可以更方便地查询,而使用原生SQL语句可以更灵活地满足各种查询需求。 ### 回答3: Spring Data JPA 是Spring Data项目的一部分,它通过JPA规范提供了ORM解决方案。在关系型数据库中,一个数据库通常由多张组成,而在开发过程中,我们经常需要对这些进行查询、修改、删除等操作,如果我们需要进行多查询,该如何实现呢? Spring Data JPA 提供了多种方式实现多联合查询,下面我们对这些方式进行一一介绍。 1.通过JPA的关联查询实现多联合 JPA提供了两种形式的关联查询:内部关联查询和左外关联查询,这两种关联查询可以满足大部分复杂查询的需求。 内部关联查询:通过@Table注解中@TableJoinColumn属性或@JoinColumn注解,将两个实体类之间的关联关系定义在Java类中。定义完关联关系后,可以通过JPQL或Spring Data JPA提供的函数查询方法实现联合查询。 左外关联查询:Spring Data JPA还提供了@Query注解的方式,开发者可以自行编写JPQL或SQL语句实现多联合查询,并通过@Query注解进行绑定。 2.通过Criteria API实现多联合 Criteria API是JPA提供的查询语言的一种形式,它允许开发人员通过程序生成查询语句,而不必编写具体的SQL或JPQL语句。使用Criteria API时,只需要将实体类的基本属性与关联属性设定好之后,生成一个CriteriaBuilder对象,就可以构建复杂的查询语句。 3.通过Spring Data JPA提供的命名查询实现多联合 Spring Data JPA提供了命名查询的方式,命名查询是通过在Repository中定义方法的名称和方法的输入参数来实现的。命名查询是一个声明式的查询,开发者可以指定所需的查询字符串和返回值。 总而言之,Spring Data JPA提供了多种方式实现多联合查询,并且具有简洁、高效、易维护等特点,开发者可以根据需求选择最合适的方式来实现多联合查询

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值