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