spring-data-jpa 仅从数据库中获取具有多个筛选条件的选定列Sping Boot 2 JPA

ozxc1zmp  于 2022-11-10  发布在  Spring
关注(0)|答案(2)|浏览(149)

我正在尝试创建一个spring Boot 2 web应用程序,它将根据传递给它的过滤标准从数据库中获取数据,但只会获取某些列。
下面是我的员工类:

@Entity
@Table("table=emplooyee")
class Employee{ 
         @column("name="fname")
         String fname;
         @column("name="lname")
         String lname;
         @column("name="phoneNo")
         String phoneNo;
         @column("name="address")
         String address;
     }

在我的实体和数据库中还有25个这样的字段。
从前端,用户应能够选择过滤标准,例如:fname,lname,phoneNo,address等。他可以指定任何组合,如fname和phoneNo,或lname和address,也可以不指定任何我必须在其中执行select * 的内容。在某种程度上,我希望有多个过滤条件。我希望这些过滤器作为前端的请求参数出现。
我的存储库是:

public interface EmployeeRepository extends JpaRepository<Employee,Long>, JpaSpecificationExecutor<Employee>{

}

到目前为止,我已经研究了规格,这是相当酷。
所以我创建了一个规范,

import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

public class EmployeeSpecs {
    public static Specification<Employee> hasFname(String fname){
        return new Specification<Employee>() {
            @Override
            public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(root.get("fname"),fname);
            }
        };
    }
    public static Specification<Employee> hasLname(String lname){
        return new Specification<Employee>() {
            @Override
            public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(root.get("lname"), lname);
            }
        };
    }
    public static Specification<Employee> hasAddress(String address){
        return new Specification<Employee>() {
            @Override
            public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(root.get("address"), address);
            }
        };
    }
    public static Specification<Employee> hasPhone(String phone){
        return new Specification<Employee>() {
            @Override
            public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(root.get("phone"), phone);
            }
        };
    }
}

现在,从我的服务来说,我打算做的是:

this.employeeRepository.findAll(EmployeeSpecs.hasFName(requestParameterFname).and(EmployeeSpecs.hasLName(requestParameterLname))).forEach(e->list.add(e));

然而,这将获取我的数据库中的所有25列。我的前端应用程序有6个页面,每个页面都需要显示不同的列,但需要这些规范的组合作为where子句。
我试着研究投影的概念,但发现目前SpringBoot不支持带投影的规范。
是否有办法只获取选定的列并具有多个筛选条件?是否有办法动态地将传递的请求参数追加到查询中并只获取相关字段?
我是否应该创建单独的实体,以便每次只从存储库中获取这些字段,然后为每个字段创建一个新的规范?这样会不会创建太多不必要的实体和规范文件?
我可以想到的另一种方法是,我必须手动提取这些列。这听起来很愚蠢,因为我已经知道我需要做一个'select column1, column2, column3 from db where condition1 = true and condition2= true',但我仍然在做一个select *
有没有人能告诉我在这种情况下最好的方法是什么?什么看起来是实现这一点的最简单的方法?我应该手动编写一个查询,就像一个本地查询吗?
简而言之,我想要的是:
1.多个筛选条件-任何可能的组合,即要传递到sql select语句的“where”子句的多个条件。
1.仅限选定的列,而不是所有列-但不同的使用情形需要不同的列。

guykilcj

guykilcj1#

Spring Data没有任何特殊的特性,所以你需要create a custom method,在这里你把Specification中的Predicate和一个选择列表结合起来。
自定义方法可能类似于:

Employee findBySpecAndColumns(Specification spec, List<String> columns) {

    // create select list as described here, but from the list of columns or whatever you use to specify which columns you want to select: https://www.objectdb.com/java/jpa/query/jpql/select#SELECT_in_Criteria_Queries

    // use spec.toPredicate(...) to create the where clause

    // execute the query.

    // transform the result to the form you need/want.
}

另请参阅:如何使用条件API指定选取清单。
我想知道这是否值得。我希望选择25列数据显示在一个页面上,这与从同一个表中选择4列数据没有太大区别。

juzqafwq

juzqafwq2#

您可以使用GraphQLQueryDSL
使用queryDSL的示例

QMenuItemRelation entity = new QMenuItemRelation("entity");
    QMenuItem menuItem = new QMenuItem("menuItem");
    QMenuItemRelationPrice menuItemRelationPrice = new QMenuItemRelationPrice("menuItemRelationPrice");
    return queryFactory.select(Projections.constructor(
                    MenuItemScalesExportDTO.class,
                    entity.menuItem.id,
                    entity.menuItem.name,
                    entity.menuItem.barcode,
                    entity.menuItem.unitType,
                    menuItemRelationPrice.price))
            .from(entity)
            .where(entity.active.eq(true), entity.menu.id.eq(menuId), entity.menuItem.usedByScales.eq(true))
            .leftJoin(entity.menuItem, menuItem)
            .leftJoin(menuItemRelationPrice).on(entity.eq(menuItemRelationPrice.menuItemRelation))
            .orderBy(entity.id.desc())
            .fetch();

如果您希望使用getter/setter而不是构造函数进行Map,也可以使用Projections.bean。
数据传输对象

public class MenuItemScalesExportDTO implements Serializable {
private UUID id;
private String name;
private String code;
private String unit;
private List<PriceDTO> price;
private BigDecimal unitPrice;

public MenuItemScalesExportDTO(UUID id, String name, String code, String unit, List<PriceDTO> price) {
    this.id = id;
    this.name = name;
    this.code = code;
    this.unit = unit;
    this.price = price;
}

相关问题