diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/controller/DetailController.java b/src/main/java/com/lanyuanxiaoyao/service/template/controller/DetailController.java index 59b3f6e..b51060c 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/controller/DetailController.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/controller/DetailController.java @@ -2,8 +2,28 @@ package com.lanyuanxiaoyao.service.template.controller; import com.lanyuanxiaoyao.service.template.controller.response.GlobalResponse; +/** + * 详情控制器接口,用于定义统一的获取实体详情的接口规范 + * + *

+ * 前端传入的JSON格式示例: + *

+ * GET /detail/1
+ * 
+ *

+ * + * @param 详情实体类型 + * @author lanyuanxiaoyao + */ public interface DetailController { String DETAIL = "/detail/{id}"; + /** + * 根据ID获取实体详情 + * + * @param id 实体ID + * @return GlobalResponse 返回实体详情 + * @throws Exception 查询过程中可能抛出的异常 + */ GlobalResponse detail(Long id) throws Exception; -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/controller/ListController.java b/src/main/java/com/lanyuanxiaoyao/service/template/controller/ListController.java index e303dfc..bd2e9ad 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/controller/ListController.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/controller/ListController.java @@ -2,10 +2,77 @@ package com.lanyuanxiaoyao.service.template.controller; import com.lanyuanxiaoyao.service.template.controller.response.GlobalCrudResponse; +/** + * 列表控制器接口,用于定义统一的获取实体列表的接口规范 + * + *

+ * 前端传入的JSON格式示例: + *

+ * {
+ *   "query": {
+ *     "equal": {
+ *       "status": "ACTIVE"
+ *     },
+ *     "like": {
+ *       "name": "关键字"
+ *     }
+ *   },
+ *   "sort": [
+ *     {
+ *       "column": "createTime",
+ *       "direction": "DESC"
+ *     }
+ *   ],
+ *   "page": {
+ *     "index": 0,
+ *     "size": 10
+ *   }
+ * }
+ * 
+ *

+ * + *

+ * 支持的查询条件说明: + *

    + *
  • nullEqual: 指定字段值为null的条件列表
  • + *
  • notNullEqual: 指定字段值不为null的条件列表
  • + *
  • empty: 指定字段值为空的条件列表(如空字符串、空集合等)
  • + *
  • notEmpty: 指定字段值不为空的条件列表
  • + *
  • equal: 指定字段值相等的条件映射(字段名 -> 值)
  • + *
  • notEqual: 指定字段值不相等的条件映射(字段名 -> 值)
  • + *
  • like: 指定字段模糊匹配的条件映射(字段名 -> 匹配值)
  • + *
  • notLike: 指定字段不模糊匹配的条件映射(字段名 -> 匹配值)
  • + *
  • great: 指定字段大于条件的映射(字段名 -> 值)
  • + *
  • less: 指定字段小于条件的映射(字段名 -> 值)
  • + *
  • greatEqual: 指定字段大于等于条件的映射(字段名 -> 值)
  • + *
  • lessEqual: 指定字段小于等于条件的映射(字段名 -> 值)
  • + *
  • in: 指定字段值在指定范围内的条件映射(字段名 -> 值列表)
  • + *
  • notIn: 指定字段值不在指定范围内的条件映射(字段名 -> 值列表)
  • + *
  • between: 指定字段值在指定区间内的条件映射(字段名 -> 区间范围)
  • + *
  • notBetween: 指定字段值不在指定区间内的条件映射(字段名 -> 区间范围)
  • + *
+ *

+ * + * @param 列表项的实体类型 + * @author lanyuanxiaoyao + */ public interface ListController { String LIST = "/list"; + /** + * 获取所有实体列表 + * + * @return GlobalCrudResponse 返回实体列表 + * @throws Exception 查询过程中可能抛出的异常 + */ GlobalCrudResponse list() throws Exception; + /** + * 根据查询条件获取实体列表 + * + * @param query 查询条件对象 + * @return GlobalCrudResponse 返回符合条件的实体列表 + * @throws Exception 查询过程中可能抛出的异常 + */ GlobalCrudResponse list(Query query) throws Exception; -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/controller/Query.java b/src/main/java/com/lanyuanxiaoyao/service/template/controller/Query.java index 2fbc78d..a013dad 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/controller/Query.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/controller/Query.java @@ -6,62 +6,218 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +/** + * 查询条件封装类,用于构建复杂的查询条件 + * 包含查询条件、排序条件和分页条件 + * + *

前端传入的JSON格式示例:

+ *
+ * {
+ *   "query": {
+ *     "equal": {
+ *       "name": "张三"
+ *     },
+ *     "like": {
+ *       "address": "%北京%"
+ *     },
+ *     "greatEqual": {
+ *       "age": 18
+ *     },
+ *     "less": {
+ *       "age": 60
+ *     },
+ *     "between": {
+ *       "salary": {
+ *         "start": 5000,
+ *         "end": 10000
+ *       }
+ *     }
+ *   },
+ *   "sort": [
+ *     {
+ *       "column": "createTime",
+ *       "direction": "DESC"
+ *     }
+ *   ],
+ *   "page": {
+ *     "index": 0,
+ *     "size": 10
+ *   }
+ * }
+ * 
+ * + *

查询条件说明:

+ *
    + *
  • nullEqual: 字段值为null的条件
  • + *
  • notNullEqual: 字段值不为null的条件
  • + *
  • empty: 字段值为空的条件
  • + *
  • notEmpty: 字段值不为空的条件
  • + *
  • equal: 字段值相等的条件
  • + *
  • notEqual: 字段值不相等的条件
  • + *
  • like: 字段值模糊匹配的条件
  • + *
  • notLike: 字段值不模糊匹配的条件
  • + *
  • great: 字段值大于的条件
  • + *
  • less: 字段值小于的条件
  • + *
  • greatEqual: 字段值大于等于的条件
  • + *
  • lessEqual: 字段值小于等于的条件
  • + *
  • in: 字段值在指定范围内的条件
  • + *
  • notIn: 字段值不在指定范围内的条件
  • + *
  • between: 字段值在指定区间内的条件
  • + *
  • notBetween: 字段值不在指定区间内的条件
  • + *
+ */ @Setter @Getter @ToString public class Query { + /** + * 查询条件 + */ private Queryable query; + /** + * 排序条件列表 + */ private List sort; + /** + * 分页条件 + */ private Pageable page; + /** + * 可查询条件类,封装各种查询条件 + */ @Setter @Getter @ToString public static class Queryable { + /** + * 指定字段值为null的条件列表 + */ private List nullEqual; + /** + * 指定字段值不为null的条件列表 + */ private List notNullEqual; + /** + * 指定字段值为空的条件列表(如空字符串、空集合等) + */ private List empty; + /** + * 指定字段值不为空的条件列表 + */ private List notEmpty; + /** + * 指定字段值相等的条件映射(字段名 -> 值) + */ private Map equal; + /** + * 指定字段值不相等的条件映射(字段名 -> 值) + */ private Map notEqual; + /** + * 指定字段模糊匹配的条件映射(字段名 -> 匹配值) + */ private Map like; + /** + * 指定字段不模糊匹配的条件映射(字段名 -> 匹配值) + */ private Map notLike; + /** + * 指定字段大于条件的映射(字段名 -> 值) + */ private Map great; + /** + * 指定字段小于条件的映射(字段名 -> 值) + */ private Map less; + /** + * 指定字段大于等于条件的映射(字段名 -> 值) + */ private Map greatEqual; + /** + * 指定字段小于等于条件的映射(字段名 -> 值) + */ private Map lessEqual; + /** + * 指定字段值在指定范围内的条件映射(字段名 -> 值列表) + */ private Map> in; + /** + * 指定字段值不在指定范围内的条件映射(字段名 -> 值列表) + */ private Map> notIn; + /** + * 指定字段值在指定区间内的条件映射(字段名 -> 区间范围) + */ private Map between; + /** + * 指定字段值不在指定区间内的条件映射(字段名 -> 区间范围) + */ private Map notBetween; + /** + * 区间范围类,用于表示起始值和结束值 + */ @Setter @Getter @ToString public static class Between { + /** + * 起始值 + */ private Object start; + /** + * 结束值 + */ private Object end; } } + /** + * 可排序条件类,用于指定排序字段和排序方向 + */ @Setter @Getter @ToString public static class Sortable { + /** + * 排序字段名 + */ private String column; + /** + * 排序方向 + */ private Direction direction; + /** + * 排序方向枚举 + */ public enum Direction { + /** + * 升序排列 + */ ASC, + /** + * 降序排列 + */ DESC, } } + /** + * 可分页条件类,用于指定分页参数 + */ @Setter @Getter @ToString public static class Pageable { + /** + * 页码索引(从0开始) + */ private Integer index; + /** + * 每页大小 + */ private Integer size; } -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/controller/RemoveController.java b/src/main/java/com/lanyuanxiaoyao/service/template/controller/RemoveController.java index 4a2b2e9..a8c5289 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/controller/RemoveController.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/controller/RemoveController.java @@ -2,8 +2,27 @@ package com.lanyuanxiaoyao.service.template.controller; import com.lanyuanxiaoyao.service.template.controller.response.GlobalResponse; +/** + * 删除控制器接口,用于定义统一的删除实体对象的接口规范 + * + *

+ * 前端传入的JSON格式示例: + *

+ * DELETE /remove/1
+ * 
+ *

+ * + * @author lanyuanxiaoyao + */ public interface RemoveController { String REMOVE = "/remove/{id}"; + /** + * 根据ID删除实体对象 + * + * @param id 需要删除的实体ID + * @return GlobalResponse 返回删除结果 + * @throws Exception 删除过程中可能抛出的异常 + */ GlobalResponse remove(Long id) throws Exception; -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/controller/SaveController.java b/src/main/java/com/lanyuanxiaoyao/service/template/controller/SaveController.java index c75e362..4f5e454 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/controller/SaveController.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/controller/SaveController.java @@ -2,8 +2,31 @@ package com.lanyuanxiaoyao.service.template.controller; import com.lanyuanxiaoyao.service.template.controller.response.GlobalResponse; +/** + * 保存控制器接口,用于定义统一的保存实体对象的接口规范 + * + *

+ * 前端传入的JSON格式示例: + *

+ * {
+ *   // 实体对象的具体字段
+ *   "name": "示例名称",
+ *   "description": "示例描述"
+ * }
+ * 
+ *

+ * + * @author lanyuanxiaoyao + */ public interface SaveController { String SAVE = "/save"; + /** + * 保存实体对象 + * + * @param item 需要保存的实体对象 + * @return GlobalResponse 返回保存后的实体ID + * @throws Exception 保存过程中可能抛出的异常 + */ GlobalResponse save(SAVE_ITEM item) throws Exception; -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/controller/SimpleControllerSupport.java b/src/main/java/com/lanyuanxiaoyao/service/template/controller/SimpleControllerSupport.java index 3c92fe1..5863fa6 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/controller/SimpleControllerSupport.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/controller/SimpleControllerSupport.java @@ -13,14 +13,106 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +/** + * 简单控制器支持类,提供基础的CRUD操作实现 + *

+ * 该类实现了基本的增删改查功能,通过泛型支持不同类型的数据转换。 + * 子类需要实现对应的Mapper函数来完成实体类与传输对象之间的转换。 + *

+ * + *

+ * 前端传入的JSON格式示例: + *

+ * // 保存实体
+ * POST /save
+ * {
+ *   // 保存项的具体字段
+ *   "name": "示例名称",
+ *   "description": "示例描述"
+ * }
+ *
+ * // 查询列表(无条件)
+ * GET /list
+ *
+ * // 查询列表(带条件)
+ * POST /list
+ * {
+ *   "query": {
+ *     "equal": {
+ *       "status": "ACTIVE"
+ *     },
+ *     "like": {
+ *       "name": "关键字"
+ *     }
+ *   },
+ *   "sort": [
+ *     {
+ *       "column": "createTime",
+ *       "direction": "DESC"
+ *     }
+ *   ],
+ *   "page": {
+ *     "index": 0,
+ *     "size": 10
+ *   }
+ * }
+ *
+ * // 获取详情
+ * GET /detail/1
+ *
+ * // 删除实体
+ * GET /remove/1
+ * 
+ *

+ * + *

+ * 支持的查询条件说明: + *

    + *
  • nullEqual: 指定字段值为null的条件列表
  • + *
  • notNullEqual: 指定字段值不为null的条件列表
  • + *
  • empty: 指定字段值为空的条件列表(如空字符串、空集合等)
  • + *
  • notEmpty: 指定字段值不为空的条件列表
  • + *
  • equal: 指定字段值相等的条件映射(字段名 -> 值)
  • + *
  • notEqual: 指定字段值不相等的条件映射(字段名 -> 值)
  • + *
  • like: 指定字段模糊匹配的条件映射(字段名 -> 匹配值)
  • + *
  • notLike: 指定字段不模糊匹配的条件映射(字段名 -> 匹配值)
  • + *
  • great: 指定字段大于条件的映射(字段名 -> 值)
  • + *
  • less: 指定字段小于条件的映射(字段名 -> 值)
  • + *
  • greatEqual: 指定字段大于等于条件的映射(字段名 -> 值)
  • + *
  • lessEqual: 指定字段小于等于条件的映射(字段名 -> 值)
  • + *
  • in: 指定字段值在指定范围内的条件映射(字段名 -> 值列表)
  • + *
  • notIn: 指定字段值不在指定范围内的条件映射(字段名 -> 值列表)
  • + *
  • between: 指定字段值在指定区间内的条件映射(字段名 -> 区间范围)
  • + *
  • notBetween: 指定字段值不在指定区间内的条件映射(字段名 -> 区间范围)
  • + *
+ *

+ * + * @param 实体类型,必须继承SimpleEntity + * @param 保存项类型 + * @param 列表项类型 + * @param 详情项类型 + * @author lanyuanxiaoyao + */ @Slf4j public abstract class SimpleControllerSupport implements SimpleController { protected final SimpleServiceSupport service; + /** + * 构造函数 + * + * @param service 简单服务支持类实例 + */ public SimpleControllerSupport(SimpleServiceSupport service) { this.service = service; } + /** + * 保存实体对象 + * + * @param item 需要保存的项 + * @return GlobalResponse 返回保存后的实体ID + * @throws Exception 保存过程中可能抛出的异常 + */ @PostMapping(SAVE) @Override public GlobalResponse save(@RequestBody SAVE_ITEM item) throws Exception { @@ -28,6 +120,12 @@ public abstract class SimpleControllerSupport 返回实体列表 + * @throws Exception 查询过程中可能抛出的异常 + */ @GetMapping(LIST) @Override public GlobalCrudResponse list() throws Exception { @@ -48,6 +146,13 @@ public abstract class SimpleControllerSupport 返回符合条件的实体列表 + * @throws Exception 查询过程中可能抛出的异常 + */ @PostMapping(LIST) @Override public GlobalCrudResponse list(@RequestBody Query query) throws Exception { @@ -70,6 +175,13 @@ public abstract class SimpleControllerSupport 返回实体详情 + * @throws Exception 查询过程中可能抛出的异常 + */ @GetMapping(DETAIL) @Override public GlobalResponse detail(@PathVariable("id") Long id) throws Exception { @@ -77,6 +189,13 @@ public abstract class SimpleControllerSupport 返回删除结果 + * @throws Exception 删除过程中可能抛出的异常 + */ @GetMapping(REMOVE) @Override public GlobalResponse remove(@PathVariable("id") Long id) throws Exception { @@ -84,9 +203,24 @@ public abstract class SimpleControllerSupport 保存项到实体的转换函数 + */ protected abstract Function saveItemMapper(); + /** + * 列表项映射器,将实体对象转换为列表项 + * + * @return Function 实体到列表项的转换函数 + */ protected abstract Function listItemMapper(); + /** + * 详情项映射器,将实体对象转换为详情项 + * + * @return Function 实体到详情项的转换函数 + */ protected abstract Function detailItemMapper(); -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/entity/IdOnlyEntity.java b/src/main/java/com/lanyuanxiaoyao/service/template/entity/IdOnlyEntity.java index 020657b..e6a594e 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/entity/IdOnlyEntity.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/entity/IdOnlyEntity.java @@ -10,14 +10,40 @@ import lombok.ToString; import org.hibernate.annotations.Comment; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +/** + * 仅包含ID的基础实体类 + *

+ * 该类作为所有实体类的基类,仅提供ID字段,使用Snowflake算法生成唯一标识。 + * 通过继承此类,实体类可以自动获得唯一ID字段以及相关的JPA注解配置。 + *

+ * + *

+ * 该类使用了以下注解: + *

    + *
  • @MappedSuperclass: 标识该类为JPA映射的超类,其属性会被映射到子类的数据库表字段中
  • + *
  • @EntityListeners(AuditingEntityListener.class): 启用Spring Data JPA的审计功能
  • + *
  • @Id: 标识id字段为主键
  • + *
  • @SnowflakeId: 使用Snowflake算法生成唯一ID
  • + *
  • @Comment: 为数据库字段添加注释
  • + *
+ *

+ * + * @author lanyuanxiaoyao + */ @Getter @Setter @ToString @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public class IdOnlyEntity { + /** + * 实体唯一标识符 + *

+ * 使用Snowflake算法生成的Long类型ID,保证全局唯一性。 + *

+ */ @Id @SnowflakeId @Comment("记录唯一标记") private Long id; -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/entity/SimpleEntity.java b/src/main/java/com/lanyuanxiaoyao/service/template/entity/SimpleEntity.java index 0a31a44..e895bb7 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/entity/SimpleEntity.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/entity/SimpleEntity.java @@ -11,16 +11,49 @@ import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +/** + * 简单实体类,包含基础字段 + *

+ * 该类继承自IdOnlyEntity,除了具备唯一ID外,还提供了创建时间和修改时间字段。 + * 通过Spring Data JPA的审计功能自动维护时间字段,适用于大多数业务实体场景。 + *

+ * + *

+ * 该类使用了以下注解: + *

    + *
  • @MappedSuperclass: 标识该类为JPA映射的超类,其属性会被映射到子类的数据库表字段中
  • + *
  • @EntityListeners(AuditingEntityListener.class): 启用Spring Data JPA的审计功能
  • + *
  • @CreatedDate: 自动设置实体创建时间
  • + *
  • @LastModifiedDate: 自动更新实体最后修改时间
  • + *
  • @Comment: 为数据库字段添加注释
  • + *
+ *

+ * + * @author lanyuanxiaoyao + */ @Getter @Setter @ToString(callSuper = true) @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public class SimpleEntity extends IdOnlyEntity { + /** + * 记录创建时间 + *

+ * 由Spring Data JPA自动维护,当实体首次持久化时设置该字段的值。 + *

+ */ @CreatedDate @Comment("记录创建时间") private LocalDateTime createdTime; + + /** + * 记录更新时间 + *

+ * 由Spring Data JPA自动维护,当实体每次更新时刷新该字段的值。 + *

+ */ @LastModifiedDate @Comment("记录更新时间") private LocalDateTime modifiedTime; -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/repository/SimpleRepository.java b/src/main/java/com/lanyuanxiaoyao/service/template/repository/SimpleRepository.java index 3d5eed2..230549d 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/repository/SimpleRepository.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/repository/SimpleRepository.java @@ -6,6 +6,100 @@ import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.query.QueryByExampleExecutor; +/** + * 简单仓库接口,整合多种数据访问功能 + *

+ * 该接口继承了多个Spring Data JPA和扩展功能接口,为数据访问层提供丰富的查询和操作能力。 + * 通过继承此接口,可以快速实现常见的数据访问功能,包括基本CRUD操作、复杂条件查询、 + * 示例查询和QueryDSL查询等。 + *

+ * + *

+ * 继承的接口功能说明: + *

    + *
  • + * FenixJpaRepository: 扩展的JPA仓库接口,提供基本的CRUD操作、分页和排序功能。 + *

    常用方法示例:

    + *
    + *     // 保存实体
    + *     User user = new User("张三", "zhangsan@example.com");
    + *     User savedUser = repository.save(user);
    + *
    + *     // 根据ID查找
    + *     Optional user = repository.findById(1L);
    + *
    + *     // 查找所有
    + *     List users = repository.findAll();
    + *
    + *     // 分页查询
    + *     Pageable pageable = PageRequest.of(0, 10, Sort.by("createdTime").descending());
    + *     Page userPages = repository.findAll(pageable);
    + *
    + *     // 根据ID删除
    + *     repository.deleteById(1L);
    + *     
    + *
  • + *
  • + * FenixJpaSpecificationExecutor: Fenix扩展的JPA规范执行器,支持通过Specification构建复杂查询条件。 + *

    常用方法示例:

    + *
    + *     // 使用Specification构建复杂查询
    + *     Specification spec = (root, query, criteriaBuilder) -> {
    + *         List predicates = new ArrayList<>();
    + *         predicates.add(criteriaBuilder.equal(root.get("status"), "ACTIVE"));
    + *         predicates.add(criteriaBuilder.like(root.get("name"), "%张三%"));
    + *         return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
    + *     };
    + *     List users = repository.findAll(spec);
    + *
    + *     // 分页查询
    + *     Page userPages = repository.findAll(spec, pageable);
    + *     
    + *
  • + *
  • + * QueryByExampleExecutor: 示例查询执行器,通过示例对象进行查询。 + *

    常用方法示例:

    + *
    + *     // 使用示例对象查询
    + *     User exampleUser = new User();
    + *     exampleUser.setName("张三");
    + *     exampleUser.setStatus("ACTIVE");
    + *     Example example = Example.of(exampleUser);
    + *     List users = repository.findAll(example);
    + *
    + *     // 使用匹配器增强查询
    + *     ExampleMatcher matcher = ExampleMatcher.matching()
    + *         .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.startsWith())
    + *         .withIgnorePaths("createdTime");
    + *     Example exampleWithMatcher = Example.of(exampleUser, matcher);
    + *     List matchedUsers = repository.findAll(exampleWithMatcher);
    + *     
    + *
  • + *
  • + * QuerydslPredicateExecutor: QueryDSL查询执行器,支持类型安全的查询构建。 + *

    常用方法示例:

    + *
    + *     // 使用QueryDSL构建类型安全查询
    + *     QUser qUser = QUser.user;
    + *     Iterable users = repository.findAll(
    + *         qUser.status.eq("ACTIVE")
    + *         .and(qUser.name.like("%张三%"))
    + *     );
    + *
    + *     // 排序和分页
    + *     Iterable sortedUsers = repository.findAll(
    + *         qUser.status.eq("ACTIVE"),
    + *         qUser.createdTime.desc()
    + *     );
    + *     
    + *
  • + *
+ *

+ * + * @param 实体类型 + * @param 实体ID类型 + * @author lanyuanxiaoyao + */ @NoRepositoryBean public interface SimpleRepository extends FenixJpaRepository, FenixJpaSpecificationExecutor, QueryByExampleExecutor, QuerydslPredicateExecutor { -} +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java b/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java index 5803e82..a1b3ae3 100644 --- a/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java +++ b/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java @@ -20,16 +20,95 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; +/** + * 简单服务支持类,提供基础的业务逻辑实现 + *

+ * 该类实现了SimpleService接口,提供实体的增删改查等基本操作。 + * 通过继承此类,可以快速实现常见的业务逻辑功能,包括: + *

    + *
  • 实体的保存和更新
  • + *
  • 实体的条件查询和分页查询
  • + *
  • 实体的详情查询(多种方式)
  • + *
  • 实体的删除操作
  • + *
+ *

+ * + *

+ * 查询条件说明: + *

    + *
  • nullEqual: 指定字段值为null的条件列表
  • + *
  • notNullEqual: 指定字段值不为null的条件列表
  • + *
  • empty: 指定字段值为空的条件列表(如空字符串、空集合等)
  • + *
  • notEmpty: 指定字段值不为空的条件列表
  • + *
  • equal: 指定字段值相等的条件映射(字段名 -> 值)
  • + *
  • notEqual: 指定字段值不相等的条件映射(字段名 -> 值)
  • + *
  • like: 指定字段模糊匹配的条件映射(字段名 -> 匹配值)
  • + *
  • notLike: 指定字段不模糊匹配的条件映射(字段名 -> 匹配值)
  • + *
  • great: 指定字段大于条件的映射(字段名 -> 值)
  • + *
  • less: 指定字段小于条件的映射(字段名 -> 值)
  • + *
  • greatEqual: 指定字段大于等于条件的映射(字段名 -> 值)
  • + *
  • lessEqual: 指定字段小于等于条件的映射(字段名 -> 值)
  • + *
  • in: 指定字段值在指定范围内的条件映射(字段名 -> 值列表)
  • + *
  • notIn: 指定字段值不在指定范围内的条件映射(字段名 -> 值列表)
  • + *
  • between: 指定字段值在指定区间内的条件映射(字段名 -> 区间范围)
  • + *
  • notBetween: 指定字段值不在指定区间内的条件映射(字段名 -> 区间范围)
  • + *
+ *

+ * + *

+ * 前端传入的JSON格式示例: + *

+ * {
+ *   "query": {
+ *     "equal": {
+ *       "status": "ACTIVE"
+ *     },
+ *     "like": {
+ *       "name": "关键字"
+ *     }
+ *   },
+ *   "sort": [
+ *     {
+ *       "column": "createdTime",
+ *       "direction": "DESC"
+ *     }
+ *   ],
+ *   "page": {
+ *     "index": 1,
+ *     "size": 10
+ *   }
+ * }
+ * 
+ *

+ * + * @param 实体类型,必须继承SimpleEntity + * @author lanyuanxiaoyao + */ @Slf4j public abstract class SimpleServiceSupport implements SimpleService { private static final Integer DEFAULT_PAGE_INDEX = 1; private static final Integer DEFAULT_PAGE_SIZE = 10; protected final SimpleRepository repository; + /** + * 构造函数 + * + * @param repository 简单仓库实例 + */ public SimpleServiceSupport(SimpleRepository repository) { this.repository = repository; } + /** + * 保存实体对象 + *

+ * 使用saveOrUpdateByNotNullProperties方法保存实体,只更新非空字段。 + * 该方法具有事务性,遇到任何异常都会回滚。 + *

+ * + * @param entity 需要保存的实体对象 + * @return Long 返回保存后的实体ID + */ @Transactional(rollbackOn = Throwable.class) @Override public Long save(ENTITY entity) { @@ -37,6 +116,14 @@ public abstract class SimpleServiceSupport implemen return entity.getId(); } + /** + * 统计符合条件的实体数量 + *

+ * 根据[listPredicate](file:///Users/lanyuanxiaoyao/Project/IdeaProjects/spring-boot-service-template/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java#L261-L263)方法构建的条件统计实体数量。 + *

+ * + * @return Long 返回符合条件的实体数量 + */ @Override public Long count() { return repository.count( @@ -50,6 +137,14 @@ public abstract class SimpleServiceSupport implemen ); } + /** + * 获取所有符合条件的实体列表 + *

+ * 根据[listPredicate](file:///Users/lanyuanxiaoyao/Project/IdeaProjects/spring-boot-service-template/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java#L261-L263)方法构建的条件查询所有实体。 + *

+ * + * @return List 返回符合条件的实体列表 + */ @Override public List list() { return repository.findAll( @@ -63,6 +158,15 @@ public abstract class SimpleServiceSupport implemen ); } + /** + * 根据ID集合获取实体列表 + *

+ * 根据提供的ID集合查询对应的实体列表,并结合[listPredicate](file:///Users/lanyuanxiaoyao/Project/IdeaProjects/spring-boot-service-template/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleServiceSupport.java#L261-L263)方法构建的条件。 + *

+ * + * @param ids ID集合 + * @return List 返回ID集合对应的实体列表 + */ @Override public List list(Set ids) { return repository.findAll( @@ -74,6 +178,19 @@ public abstract class SimpleServiceSupport implemen ); } + /** + * 解析字段路径 + *

+ * 支持多级字段路径解析,使用"/"分隔多级字段。 + * 例如: "user/name" 表示实体的user属性的name字段。 + *

+ * + * @param root JPA Criteria查询根节点 + * @param column 字段路径字符串 + * @param 字段类型 + * @return Path 返回字段路径对象 + * @throws IllegalArgumentException 当字段路径为空时抛出 + */ private Path column(Root root, String column) { if (ObjectHelper.isEmpty(column)) { throw new IllegalArgumentException("Column cannot be blank"); @@ -86,6 +203,19 @@ public abstract class SimpleServiceSupport implemen return path; } + /** + * 处理字段值 + *

+ * 对于枚举类型字段,将字符串值转换为对应的枚举值。 + * 其他类型直接返回原值。 + *

+ * + * @param column 字段路径 + * @param value 字段值 + * @param 字段类型 + * @return Object 处理后的字段值 + * @throws IllegalArgumentException 当枚举类型字段的值不是字符串时抛出 + */ @SuppressWarnings({"unchecked", "rawtypes"}) private Object value(Path column, Object value) { var javaType = column.getJavaType(); @@ -100,6 +230,19 @@ public abstract class SimpleServiceSupport implemen return value; } + /** + * 构建查询条件谓词列表 + *

+ * 根据Query.Queryable对象构建JPA Criteria查询的谓词列表。 + * 支持多种查询条件类型,包括相等、不等、模糊匹配、范围查询等。 + *

+ * + * @param queryable 查询条件对象 + * @param root JPA Criteria查询根节点 + * @param query JPA Criteria查询对象 + * @param builder JPA Criteria构建器 + * @return List 返回构建的谓词列表 + */ @SuppressWarnings("unchecked") protected List queryPredicates(Query.Queryable queryable, Root root, CriteriaQuery query, CriteriaBuilder builder) { var predicates = new ArrayList(); @@ -211,51 +354,122 @@ public abstract class SimpleServiceSupport implemen return predicates; } + /** + * 检查字段类型是否可比较 + * + * @param path 字段路径 + * @param column 字段名称 + * @throws NotComparableException 当字段类型不可比较时抛出 + */ private void checkComparable(Path path, String column) { if (!ObjectHelper.isComparable(path.getJavaType())) { throw new NotComparableException(column); } } + /** + * 检查值是否可比较 + * + * @param value 值对象 + * @param column 字段名称 + * @throws NotComparableException 当值不可比较时抛出 + */ private void checkComparable(Object value, String column) { if (!ObjectHelper.isComparable(value)) { throw new NotComparableException(column); } } + /** + * 检查区间值是否可比较 + * + * @param value 区间对象 + * @param column 字段名称 + * @throws NotComparableException 当区间值不可比较时抛出 + */ private void checkComparable(Query.Queryable.Between value, String column) { checkComparable(value.getStart(), column); checkComparable(value.getEnd(), column); } + /** + * 检查字段类型是否为集合 + * + * @param path 字段路径 + * @param column 字段名称 + * @throws NotCollectionException 当字段类型不是集合时抛出 + */ private void checkCollection(Path path, String column) { if (!ObjectHelper.isCollection(path.getJavaType())) { throw new NotCollectionException(column); } } + /** + * 检查值是否为集合 + * + * @param value 值对象 + * @param column 字段名称 + * @throws NotCollectionException 当值不是集合时抛出 + */ private void checkCollection(Object value, String column) { if (!ObjectHelper.isCollection(value)) { throw new NotCollectionException(column); } } + /** + * 检查字段类型是否为字符串 + * + * @param path 字段路径 + * @param column 字段名称 + * @throws NotStringException 当字段类型不是字符串时抛出 + */ private void checkString(Path path, String column) { if (!ObjectHelper.isString(path.getJavaType())) { throw new NotStringException(column); } } + /** + * 检查值是否为字符串 + * + * @param value 值对象 + * @param column 字段名称 + * @throws NotStringException 当值不是字符串时抛出 + */ private void checkString(Object value, String column) { if (!ObjectHelper.isString(value)) { throw new NotStringException(column); } } + /** + * 构建列表查询条件 + *

+ * 子类可以重写此方法以添加特定的查询条件。 + * 默认返回空列表,表示不添加额外条件。 + *

+ * + * @param root JPA Criteria查询根节点 + * @param query JPA Criteria查询对象 + * @param builder JPA Criteria构建器 + * @return List 返回查询条件谓词列表 + */ protected List listPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder) { return new ArrayList<>(0); } + /** + * 根据查询条件分页获取实体列表 + *

+ * 支持复杂的查询条件和分页功能。 + * 默认分页参数:第1页,每页10条记录,按创建时间降序排列。 + *

+ * + * @param listQuery 查询条件对象 + * @return Page 返回分页查询结果 + */ @Override public Page list(Query listQuery) { var pageRequest = PageRequest.of(DEFAULT_PAGE_INDEX - 1, DEFAULT_PAGE_SIZE, Sort.by("createdTime").descending()); @@ -276,6 +490,15 @@ public abstract class SimpleServiceSupport implemen ); } + /** + * 根据ID获取实体详情(Optional包装) + *

+ * 如果ID为空则返回空Optional,否则根据ID查询实体。 + *

+ * + * @param id 实体ID + * @return Optional 返回实体详情的Optional包装 + */ @Override public Optional detailOptional(Long id) { if (ObjectHelper.isNull(id)) { @@ -290,21 +513,58 @@ public abstract class SimpleServiceSupport implemen ); } + /** + * 根据ID获取实体详情 + *

+ * 如果实体不存在则返回null。 + *

+ * + * @param id 实体ID + * @return ENTITY 返回实体详情,不存在时返回null + */ @Override public ENTITY detail(Long id) { return detailOrNull(id); } + /** + * 根据ID获取实体详情,不存在时抛出异常 + *

+ * 如果实体不存在则抛出IdNotFoundException异常。 + *

+ * + * @param id 实体ID + * @return ENTITY 返回实体详情 + * @throws IdNotFoundException 当实体不存在时抛出 + */ @Override public ENTITY detailOrThrow(Long id) { return detailOptional(id).orElseThrow(() -> new IdNotFoundException(id)); } + /** + * 根据ID获取实体详情,不存在时返回null + *

+ * 与[detail](file:///Users/lanyuanxiaoyao/Project/IdeaProjects/spring-boot-service-template/src/main/java/com/lanyuanxiaoyao/service/template/service/SimpleService.java#L21-L21)方法功能相同。 + *

+ * + * @param id 实体ID + * @return ENTITY 返回实体详情,不存在时返回null + */ @Override public ENTITY detailOrNull(Long id) { return detailOptional(id).orElse(null); } + /** + * 根据ID删除实体 + *

+ * 具有事务性,遇到任何异常都会回滚。 + * 如果ID为空则不执行任何操作。 + *

+ * + * @param id 实体ID + */ @Transactional(rollbackOn = Throwable.class) @Override public void remove(Long id) { @@ -313,31 +573,78 @@ public abstract class SimpleServiceSupport implemen } } + /** + * ID未找到异常 + *

+ * 当根据ID查询实体但实体不存在时抛出此异常。 + *

+ */ public static final class IdNotFoundException extends RuntimeException { + /** + * 构造函数(无参) + */ public IdNotFoundException() { super("资源不存在"); } + /** + * 构造函数(带ID参数) + * + * @param id 实体ID + */ public IdNotFoundException(Long id) { super("ID为 %d 的资源不存在".formatted(id)); } } + /** + * 不可比较异常 + *

+ * 当尝试对不可比较的字段或值执行比较操作时抛出此异常。 + *

+ */ public static final class NotComparableException extends RuntimeException { + /** + * 构造函数 + * + * @param variable 变量名称 + */ public NotComparableException(String variable) { super("变量 %s 不能比较".formatted(variable)); } } + /** + * 非集合异常 + *

+ * 当尝试对非集合类型的字段或值执行集合操作时抛出此异常。 + *

+ */ public static final class NotCollectionException extends RuntimeException { + /** + * 构造函数 + * + * @param variable 变量名称 + */ public NotCollectionException(String variable) { super("变量 %s 不是集合".formatted(variable)); } } + /** + * 非字符串异常 + *

+ * 当尝试对非字符串类型的字段或值执行字符串操作时抛出此异常。 + *

+ */ public static final class NotStringException extends RuntimeException { + /** + * 构造函数 + * + * @param variable 变量名称 + */ public NotStringException(String variable) { super("变量 %s 不是字符串".formatted(variable)); } } -} +} \ No newline at end of file