zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Spring学习笔记(三十一)——SpringBoot JPA优雅高效的工具:QueryHelp

2023-06-13 09:11:07 时间

背景

说一下我目前对数据持久层的看法:在后端开发中,接口开发是一个后端必备的技能,但是如何才能优雅,高效的去完成这个拧螺丝的CRUD呢?一般我们会使用MyBatis作为持久层,但是这个需要自己配置XML写映射和SQL语句,或者自己写映射注解和SQL语句,所以就诞生了MyBatis-plus,这个有效的减少繁琐的xml等一些的依赖配置,但是目前好像只适合用于单表的操作,多表操作还是需要自己手动做map映射,一个个手写SQL,相对还是挺麻烦的,不过手动配置也有它的好处,那就是自己操作的空间更大,操作也可以按自己思路去灵活多变,但缺点就是太麻烦了。然后一般我们也会使用Sping-Data-jpa去作为持久层就是开发,这个就相当于解放了双手,真的是大部分的数据操作都是可以无配置实现,并且做多表操作,级联操作也很方便,如果有需要自己写Sql配置的,也可以使用@Query注解方便的进行配置,可以说,jpa的开发效率确实很高,但缺点就是操作相对没有那么灵活,如果数据量庞大,还是需要自己去手动配置,有可能就会被硬性要求使用MyBatis。还有就是Sping-Data-jpa可以很方便的集成其他的一些数据工具,比如ElasticSearch等。

有下面的一个问题: 如果需要一个接口,可以根据传进来的不确定的多个参数动态的,条件的查询数据,这个需要怎么处理呢? 其实这个问题在之前的做项目中也有遇到过,处理方法是使用MyBatis的配置语句动态进行拼接,有一种if-else的感觉,当时做这个需求还是挺麻烦的。

所以需要一个更加优雅。更加高效的处理方法:我最近在项目中遇到过一个工具:QueryHelp。

QueryHelp介绍

QueryHelp其实只是一个工具类,这个工具类中对在真实开发中常用的查询方法进行了封装,在操作持节层的时候也是基于Spring-Data-Jpa的,只需要配置一个序列化的QueryCriteria 作为本次数据操作的条件,进行简单的配置,即可以做到一个接口可以进行多条件,模糊,精准,甚至多表连接后的数据进行条件查询。 先看一下效果吧:

这有一个非常精简的查询接口,但这个查询接口却具有很大的魔力

  1. 这是正常的查询全部。
  1. 这是多条件模糊查询
  1. 这是分页查询

当然参数既可以条件也可以加分页。

QueryHelp的使用步骤

在本次的项目demo中,使用到了上篇文章的Mapstruct,和上上篇文章的Jdk8新特性。

0. 目录结构如下

1. 建表

-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES (1, '软件部');
INSERT INTO `dept` VALUES (2, '产品部');
INSERT INTO `dept` VALUES (3, '测试部');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
  `did` int(11) NULL DEFAULT NULL,
  `createtime` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP,
  `updatetime` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@baomidou.com', 1, '2021-08-17 09:45:31', '2021-09-02 14:49:48');
INSERT INTO `user` VALUES (3, 'Tom', 28, 'test3@baomidou.com', 3, '2021-08-17 09:45:31', '2021-09-02 14:50:17');
INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@baomidou.com', 2, '2021-08-17 09:45:31', '2021-09-02 14:49:48');
INSERT INTO `user` VALUES (5, 'Billie', 24, 'test5@qq.com', 2, '2021-08-17 09:45:31', '2021-09-02 14:49:49');
INSERT INTO `user` VALUES (13, '如我西沉', 21, '12055@qq.com', 1, '2021-08-17 10:04:54', '2021-09-02 14:49:53');
INSERT INTO `user` VALUES (14, 'Nick', 21, '12055@qq.com', 1, '2021-08-19 18:19:00', '2021-09-02 14:49:51');

2. 导入坐标依赖

<properties>
        <java.version>1.8</java.version>
        <mapstruct.version>1.3.1.Final</mapstruct.version>
        <hutool.version>5.3.4</hutool.version>
        <commons-pool2.version>2.5.0</commons-pool2.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>3
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.2</version>
        </dependency>
        <!--工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <!-- spring boot 缓存 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!--Spring boot Redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!--spring boot 集成redis所需common-pool2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>${commons-pool2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!--mapStruct依赖-->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        <!-- Apache的BeanUtils依赖 -->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.3</version>
        </dependency>
    </dependencies>

3. 创建工具类注解(重点)

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by tao.
 * Date: 2021/9/2 10:59
 * 描述:  条件查询注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {

    //基本对象的属性名
    String propName() default "";

    //查询方式
    Type type() default Type.EQUAL;

    /**
     * 连接查询的属性名,如User类中的dept
     */
    String joinName() default "";

    /**
     * 默认左连接
     */
    Join join() default Join.LEFT;

    /**
     * 多字段模糊搜索,仅支持String类型字段,多个用逗号隔开, 如@Query(blurry = "email,username")
     */
    String blurry() default "";

    enum Type {
        //相等
        EQUAL
        //大于等于
        , GREATER_THAN
        //小于等于
        , LESS_THAN
        //中模糊查询
        , INNER_LIKE
        //左模糊查询
        , LEFT_LIKE
        //右模糊查询
        , RIGHT_LIKE
        //小于
        , LESS_THAN_NQ
        //包含
        , IN
        // 不等于
        , NOT_EQUAL
        // between
        , BETWEEN
        // 不为空
        , NOT_NULL
        // 为空
        , IS_NULL
    }

    /**
     * @author tao
     * 适用于简单连接查询,复杂的请自定义该注解,或者使用sql查询
     */
    enum Join {
        LEFT, RIGHT, INNER
    }
}

4. 创建QueryHelp工具类和PageUtil工具类(重点)

  • QueryHelp
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.kt.mapstructdemo.annotation.Query;
import lombok.extern.slf4j.Slf4j;
import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.util.*;
/**
 * Created by tao.
 * Date: 2021/9/2 11:15
 * 描述:
 */
@Slf4j
@SuppressWarnings({"unchecked", "all"})
public class QueryHelp {
    public static <R, Q> Predicate getPredicate(Root<R> root, Q query, CriteriaBuilder cb) {
        List<Predicate> list = new ArrayList<>();
        if (query == null) {
            return cb.and(list.toArray(new Predicate[0]));
        }
        try {
            List<Field> fields = getAllFields(query.getClass(), new ArrayList<>());
            for (Field field : fields) {
                boolean accessible = field.isAccessible();
                // 设置对象的访问权限,保证对private的属性的访
                field.setAccessible(true);
                Query q = field.getAnnotation(Query.class);
                if (q != null) {
                    String propName = q.propName();
                    String joinName = q.joinName();
                    String blurry = q.blurry();
                    String attributeName = isBlank(propName) ? field.getName() : propName;
                    Class<?> fieldType = field.getType();
                    Object val = field.get(query);
                    if (ObjectUtil.isNull(val) || "".equals(val)) {
                        continue;
                    }
                    Join join = null;
                    // 模糊多字段
                    if (ObjectUtil.isNotEmpty(blurry)) {
                        String[] blurrys = blurry.split(",");
                        List<Predicate> orPredicate = new ArrayList<>();
                        for (String s : blurrys) {
                            orPredicate.add(cb.like(root.get(s)
                                    .as(String.class), "%" + val.toString() + "%"));
                        }
                        Predicate[] p = new Predicate[orPredicate.size()];
                        list.add(cb.or(orPredicate.toArray(p)));
                        continue;
                    }
                    if (ObjectUtil.isNotEmpty(joinName)) {
                        String[] joinNames = joinName.split(">");
                        for (String name : joinNames) {
                            switch (q.join()) {
                                case LEFT:
                                    if (ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)) {
                                        join = join.join(name, JoinType.LEFT);
                                    } else {
                                        join = root.join(name, JoinType.LEFT);
                                    }
                                    break;
                                case RIGHT:
                                    if (ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)) {
                                        join = join.join(name, JoinType.RIGHT);
                                    } else {
                                        join = root.join(name, JoinType.RIGHT);
                                    }
                                    break;
                                case INNER:
                                    if (ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)) {
                                        join = join.join(name, JoinType.INNER);
                                    } else {
                                        join = root.join(name, JoinType.INNER);
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    switch (q.type()) {
                        case EQUAL:
                            list.add(cb.equal(getExpression(attributeName, join, root)
                                    .as((Class<? extends Comparable>) fieldType), val));
                            break;
                        case GREATER_THAN:
                            list.add(cb.greaterThanOrEqualTo(getExpression(attributeName, join, root)
                                    .as((Class<? extends Comparable>) fieldType), (Comparable) val));
                            break;
                        case LESS_THAN:
                            list.add(cb.lessThanOrEqualTo(getExpression(attributeName, join, root)
                                    .as((Class<? extends Comparable>) fieldType), (Comparable) val));
                            break;
                        case LESS_THAN_NQ:
                            list.add(cb.lessThan(getExpression(attributeName, join, root)
                                    .as((Class<? extends Comparable>) fieldType), (Comparable) val));
                            break;
                        case INNER_LIKE:
                            list.add(cb.like(getExpression(attributeName, join, root)
                                    .as(String.class), "%" + val.toString() + "%"));
                            break;
                        case LEFT_LIKE:
                            list.add(cb.like(getExpression(attributeName, join, root)
                                    .as(String.class), "%" + val.toString()));
                            break;
                        case RIGHT_LIKE:
                            list.add(cb.like(getExpression(attributeName, join, root)
                                    .as(String.class), val.toString() + "%"));
                            break;
                        case IN:
                            if (CollUtil.isNotEmpty((Collection<Long>) val)) {
                                list.add(getExpression(attributeName, join, root).in((Collection<Long>) val));
                            }
                            break;
                        case NOT_EQUAL:
                            list.add(cb.notEqual(getExpression(attributeName, join, root), val));
                            break;
                        case NOT_NULL:
                            list.add(cb.isNotNull(getExpression(attributeName, join, root)));
                            break;
                        case IS_NULL:
                            list.add(cb.isNull(getExpression(attributeName, join, root)));
                            break;
                        case BETWEEN:
                            List<Object> between = new ArrayList<>((List<Object>) val);
                            list.add(cb.between(getExpression(attributeName, join, root).as((Class<? extends Comparable>) between.get(0).getClass()),
                                    (Comparable) between.get(0), (Comparable) between.get(1)));
                            break;
                        default:
                            break;
                    }
                }
                field.setAccessible(accessible);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        int size = list.size();
        return cb.and(list.toArray(new Predicate[size]));
    }

    @SuppressWarnings("unchecked")
    private static <T, R> Expression<T> getExpression(String attributeName, Join join, Root<R> root) {
        if (ObjectUtil.isNotEmpty(join)) {
            return join.get(attributeName);
        } else {
            return root.get(attributeName);
        }
    }

    private static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(cs.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    public static List<Field> getAllFields(Class clazz, List<Field> fields) {
        if (clazz != null) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            getAllFields(clazz.getSuperclass(), fields);
        }
        return fields;
    }
}
  • PageUtil
import org.springframework.data.domain.Page;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
 * Created by tao.
 * Date: 2021/9/2 11:13
 * 描述:  分页工具
 */
public class PageUtil extends cn.hutool.core.util.PageUtil {
    /**
     * List 分页
     */
    public static List toPage(int page, int size, List list) {
        int fromIndex = page * size;
        int toIndex = page * size + size;
        if (fromIndex > list.size()) {
            return new ArrayList();
        } else if (toIndex >= list.size()) {
            return list.subList(fromIndex, list.size());
        } else {
            return list.subList(fromIndex, toIndex);
        }
    }

    /**
     * Page 数据处理,预防redis反序列化报错
     */
    public static Map<String, Object> toPage(Page page) {
        Map<String, Object> map = new LinkedHashMap<>(2);
        map.put("content", page.getContent());
        map.put("totalElements", page.getTotalElements());
        return map;
    }

    /**
     * 自定义分页
     */
    public static Map<String, Object> toPage(Object object, Object totalElements) {
        Map<String, Object> map = new LinkedHashMap<>(2);
        map.put("content", object);
        map.put("totalElements", totalElements);
        return map;
    }
}

5. 配置文件配置MySql

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ceshi?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456

6. 配置两个实体类做一对多关联

User.java

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

/**
 * Created by tao.
 * Date: 2021/9/2 9:32
 * 描述:
 */
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Data
@Table(name = "user")
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "age")
    private Integer age;

    @Column(name = "email")
    private String email;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "did")
    private Dept dept;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Column(name = "createtime")
    private Date createtime;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Column(name = "updatetime")
    private Date updatetime;

    public User(String name, Integer age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

Dept.java

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by tao.
 * Date: 2021/9/2 14:51
 * 描述:
 */
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Data
@Table(name = "dept")
public class Dept implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "dept", fetch = FetchType.EAGER)
    @JsonIgnore
    private List<User> users = new ArrayList<>();
}

7. 配置Dao层

import cn.kt.mapstructdemo.domin.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
 * Created by tao.
 * Date: 2021/9/2 10:31
 * 描述:
 */
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

8. 配置Mapstruct

新建BaseMapper

import java.util.List;

/**
 * Created by tao
 * Date: 2021/9/2 9:32
 * 描述:
 */
public interface BaseMapper<D, E> {

    /**
     * DTO转Entity
     * @param dto /
     * @return /
     */
    E toEntity(D dto);

    /**
     * Entity转DTO
     * @param entity /
     * @return /
     */
    D toDto(E entity);

    /**
     * DTO集合转Entity集合
     * @param dtoList /
     * @return /
     */
    List <E> toEntity(List<D> dtoList);

    /**
     * Entity集合转DTO集合
     * @param entityList /
     * @return /
     */
    List <D> toDto(List<E> entityList);
}

新建UserMapper.java

import cn.kt.mapstructdemo.base.BaseMapper;
import cn.kt.mapstructdemo.domin.User;
import cn.kt.mapstructdemo.service.dto.UserDto;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;

/**
 * Created by tao.
 * Date: 2021/9/2 11:04
 * 描述:
 */
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface UserMapper extends BaseMapper<UserDto, User> {
}

9. 配置查询条件QueryCriteria(重点)

新建 UserQueryCriteria.java

import cn.kt.mapstructdemo.annotation.Query;
import lombok.Data;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by tao.
 * Date: 2021/9/2 10:56
 * 描述:
 */
@Data
public class UserQueryCriteria implements Serializable {
    /*条件查询*/
    @Query
    private Long id;

    /*模糊查询*/
    @Query(type = Query.Type.INNER_LIKE)
    private String name;

    /*多字段模糊查询*/
    @Query(blurry = "name,email")
    private String blurry;

    /*根据多表连接后的数据进行条件查询*/
    @Query(propName = "id", type = Query.Type.IN, joinName = "dept")
    private Set<Long> dids = new HashSet<>();
}

这个QueryCriteria的配置注解可以参考上面示例,也可以查看注解类Query,里面有很清楚详细的注释。

10. 配置Service层

UserService.java

import cn.kt.mapstructdemo.domin.User;
import cn.kt.mapstructdemo.service.dto.UserDto;
import cn.kt.mapstructdemo.service.queryCriteria.UserQueryCriteria;
import org.springframework.data.domain.Pageable;

import java.util.Set;

/**
 * Created by tao.
 * Date: 2021/9/2 10:50
 * 描述:
 */
public interface UserService {
    /**
     * 查询全部
     *
     * @return /
     */
    UserDto findByAll();

    /**
     * 分页查询
     *
     * @return /
     */
    Object findByAll(UserQueryCriteria criteria, Pageable pageable);

    /**
     * 根据ID查询
     *
     * @param id ID
     * @return /
     */
    UserDto findById(long id);

    /**
     * 新增用户
     *
     * @param user /
     */
    void create(User user);

    /**
     * 编辑用户
     *
     * @param user /
     */
    void update(User user);

    /**
     * 删除用户
     *
     * @param ids /
     */
    void delete(Set<Long> ids);

    /**
     * 根据用户名查询
     *
     * @param userName /
     * @return /
     */
    UserDto findByName(String userName);
}

UserServiceImpl.java

import cn.kt.mapstructdemo.domin.User;
import cn.kt.mapstructdemo.repository.UserRepository;
import cn.kt.mapstructdemo.service.UserService;
import cn.kt.mapstructdemo.service.dto.UserDto;
import cn.kt.mapstructdemo.service.mapstruct.UserMapper;
import cn.kt.mapstructdemo.service.queryCriteria.UserQueryCriteria;
import cn.kt.mapstructdemo.utils.PageUtil;
import cn.kt.mapstructdemo.utils.QueryHelp;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.Set;

/**
 * Created by tao.
 * Date: 2021/9/2 10:54
 * 描述:
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;
    private final UserMapper userMapper;

    @Override
    public Object findByAll(UserQueryCriteria criteria, Pageable pageable) {
        Page<User> page = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(userMapper::toDto));
    }

    @Override
    public UserDto findByAll() {
        return null;
    }

    @Override
    public UserDto findById(long id) {
        return null;
    }

    @Override
    public void create(User user) {

    }

    @Override
    public void update(User user) {

    }

    @Override
    public void delete(Set<Long> ids) {

    }

    @Override
    public UserDto findByName(String userName) {
        return null;
    }
}

11. 最后配置Controller层

UserController.java

import cn.kt.mapstructdemo.service.UserService;
import cn.kt.mapstructdemo.service.queryCriteria.UserQueryCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by tao.
 * Date: 2021/9/2 13:15
 * 描述:
 */
@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;
    /*
     * 用户查询(分页)
     */
    @GetMapping
    public ResponseEntity<Object> query(UserQueryCriteria criteria, Pageable pageable) {
        return new ResponseEntity<>(userService.findByAll(criteria, pageable), HttpStatus.OK);
    }
}

12. 启动项目

启动项目就可以操作这个接口了:http://localhost:8080/api/users

小结

这个项目是自己从大项目中抽取出来的一些好用的工具,是完全自己总结、归纳的,这个期间包含了学习 Jdk8 新特性,总结对象拷贝,学习MapStruct,和学习QueryHelp。不过这些也只是开发中一些好用的工具吧,也可能就是因为这些优雅的代码,便捷的封装技术,和一些框架源码,才让大佬成为大佬的吧。路漫漫其修远兮啊,加油吧,,,

源码下载

链接:https://pan.baidu.com/s/1RL25QwUGzVKWObZlMowH3w 提取码:m13f