|
@@ -0,0 +1,250 @@
|
|
|
+package com.chelvc.framework.database.support;
|
|
|
+
|
|
|
+import java.io.Serializable;
|
|
|
+import java.sql.SQLException;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.function.Supplier;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
|
+import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.chelvc.framework.common.util.ObjectUtils;
|
|
|
+import com.chelvc.framework.common.util.StringUtils;
|
|
|
+import com.chelvc.framework.common.util.TreeUtils;
|
|
|
+import com.chelvc.framework.database.context.DatabaseContextHolder;
|
|
|
+import com.chelvc.framework.database.entity.TreeEntity;
|
|
|
+import lombok.NonNull;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 树形模型业务增强处理默认实现
|
|
|
+ *
|
|
|
+ * @author Woody
|
|
|
+ * @date 2024/4/28
|
|
|
+ */
|
|
|
+public class DefaultTreeService<M extends BaseMapper<T>, T extends TreeEntity<? extends Serializable, T>>
|
|
|
+ extends ServiceImpl<M, T> implements TreeService<T> {
|
|
|
+ /**
|
|
|
+ * 序号替换SQL
|
|
|
+ */
|
|
|
+ private static final String SEQUENCE_REPLACE_SQL =
|
|
|
+ "UPDATE `%s` SET `sequence` = REPLACE(`sequence`, '%s', '%s') WHERE `sequence` LIKE '%s'";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化树形节点序号
|
|
|
+ *
|
|
|
+ * @param entity 树形对象实例
|
|
|
+ */
|
|
|
+ private void initializeSequence(T entity) {
|
|
|
+ T parent = ObjectUtils.ifNull(entity.getParentId(), this::getById);
|
|
|
+ if (parent == null || StringUtils.isEmpty(parent.getSequence())) {
|
|
|
+ entity.setSequence(entity.getId() + StringUtils.HORIZONTAL);
|
|
|
+ } else {
|
|
|
+ entity.setSequence(parent.getSequence() + entity.getId() + StringUtils.HORIZONTAL);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化树形节点序号
|
|
|
+ *
|
|
|
+ * @param entities 树形对象实例集合
|
|
|
+ */
|
|
|
+ private void initializeSequence(Collection<T> entities) {
|
|
|
+ List<T> parents = ObjectUtils.ifEmpty(
|
|
|
+ entities.stream().map(TreeEntity::getParentId).filter(Objects::nonNull).collect(Collectors.toSet()),
|
|
|
+ this::listByIds
|
|
|
+ );
|
|
|
+ if (ObjectUtils.isEmpty(parents)) {
|
|
|
+ entities.forEach(entity -> entity.setSequence(entity.getId() + StringUtils.HORIZONTAL));
|
|
|
+ } else {
|
|
|
+ Map<Serializable, String> sequences =
|
|
|
+ parents.stream().collect(Collectors.toMap(TreeEntity::getId, TreeEntity::getSequence));
|
|
|
+ entities.forEach(entity -> {
|
|
|
+ String sequence = sequences.get(entity.getParentId());
|
|
|
+ if (StringUtils.isEmpty(sequence)) {
|
|
|
+ entity.setSequence(entity.getId() + StringUtils.HORIZONTAL);
|
|
|
+ } else {
|
|
|
+ entity.setSequence(sequence + entity.getId() + StringUtils.HORIZONTAL);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建序号替换SQL
|
|
|
+ *
|
|
|
+ * @param original 原始序号
|
|
|
+ * @param current 当前序号
|
|
|
+ * @return SQL语句
|
|
|
+ */
|
|
|
+ private String buildSequenceReplaceSql(String original, String current) {
|
|
|
+ String table = TableInfoHelper.getTableInfo(this.getEntityClass()).getTableName();
|
|
|
+ return String.format(SEQUENCE_REPLACE_SQL, table, original, current, original + "%");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建树查询对象
|
|
|
+ *
|
|
|
+ * @return Lambda查询包装器实例
|
|
|
+ */
|
|
|
+ protected LambdaQueryChainWrapper<T> treeQuery() {
|
|
|
+ return this.lambdaQuery();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T tree(@NonNull Serializable id) {
|
|
|
+ List<T> trees = TreeUtils.assemble(this.families(id));
|
|
|
+ return ObjectUtils.isEmpty(trees) ? null : trees.get(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<T> trees() {
|
|
|
+ List<T> nodes = this.treeQuery().list();
|
|
|
+ return TreeUtils.assemble(nodes);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<T> trees(@NonNull Collection<? extends Serializable> ids) {
|
|
|
+ if (ObjectUtils.isEmpty(ids)) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ List<T> nodes = this.treeQuery().in(TreeEntity::getId, ids).list();
|
|
|
+ return TreeUtils.assemble(nodes);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<T> children(@NonNull Serializable id) {
|
|
|
+ return this.treeQuery().eq(TreeEntity::getParentId, id).list();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<T> children(@NonNull Collection<? extends Serializable> ids) {
|
|
|
+ if (ObjectUtils.isEmpty(ids)) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ return this.treeQuery().in(TreeEntity::getParentId, ids).list();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<T> families(@NonNull Serializable id) {
|
|
|
+ T root = this.treeQuery().eq(TreeEntity::getId, id).one();
|
|
|
+ if (root == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ } else if (StringUtils.isEmpty(root.getSequence())) {
|
|
|
+ return Collections.singletonList(root);
|
|
|
+ }
|
|
|
+ return this.treeQuery().likeRight(TreeEntity::getSequence, root.getSequence()).list();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<T> families(@NonNull Collection<? extends Serializable> ids) {
|
|
|
+ if (ObjectUtils.isEmpty(ids)) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ List<T> nodes = this.treeQuery().in(TreeEntity::getId, ids).list();
|
|
|
+ if (ObjectUtils.isEmpty(nodes)) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ Set<String> sequences = nodes.stream().map(TreeEntity::getSequence).filter(StringUtils::notEmpty)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+ if (ObjectUtils.notEmpty(sequences)) {
|
|
|
+ List<T> children = this.treeQuery().and(query -> {
|
|
|
+ // 重置查询实体对象,否则使用实体对象父类或接口函数查询会出现异常
|
|
|
+ query.setEntityClass(getEntityClass());
|
|
|
+
|
|
|
+ // 设置多个Like或查询条件
|
|
|
+ sequences.forEach(sequence -> query.or().likeRight(TreeEntity::getSequence, sequence));
|
|
|
+ }).list();
|
|
|
+ nodes.addAll(children);
|
|
|
+ }
|
|
|
+ return nodes;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean save(@NonNull T entity) {
|
|
|
+ return DatabaseContextHolder.transactional((Supplier<Boolean>) () -> {
|
|
|
+ boolean success = super.save(entity);
|
|
|
+ if (success) {
|
|
|
+ // 更新树形节点序号
|
|
|
+ this.initializeSequence(entity);
|
|
|
+ super.updateById(entity);
|
|
|
+ }
|
|
|
+ return success;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean saveBatch(@NonNull Collection<T> entities, int batchSize) {
|
|
|
+ if (ObjectUtils.isEmpty(entities)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return DatabaseContextHolder.transactional((Supplier<Boolean>) () -> {
|
|
|
+ boolean success = super.saveBatch(entities, batchSize);
|
|
|
+ if (success) {
|
|
|
+ // 更新树形节点序号
|
|
|
+ this.initializeSequence(entities);
|
|
|
+ super.updateBatchById(entities, batchSize);
|
|
|
+ }
|
|
|
+ return success;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean updateById(@NonNull T entity) {
|
|
|
+ return DatabaseContextHolder.transactional((Supplier<Boolean>) () -> {
|
|
|
+ // 获取原始序号
|
|
|
+ T original = ObjectUtils.ifNull(entity.getId(), this::getById);
|
|
|
+ String sequence = ObjectUtils.ifNull(original, TreeEntity::getSequence);
|
|
|
+
|
|
|
+ // 更新树形节点序号
|
|
|
+ this.initializeSequence(entity);
|
|
|
+ boolean success = super.updateById(entity);
|
|
|
+ if (success && !Objects.equals(sequence, entity.getSequence())) {
|
|
|
+ String sql = this.buildSequenceReplaceSql(sequence, entity.getSequence());
|
|
|
+ try {
|
|
|
+ DatabaseContextHolder.execute(sql);
|
|
|
+ } catch (SQLException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return success;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean updateBatchById(@NonNull Collection<T> entities, int batchSize) {
|
|
|
+ if (ObjectUtils.isEmpty(entities)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return DatabaseContextHolder.transactional((Supplier<Boolean>) () -> {
|
|
|
+ List<T> originals = this.listByIds(entities.stream().map(TreeEntity::getId).collect(Collectors.toSet()));
|
|
|
+ Map<Serializable, String> sequences =
|
|
|
+ originals.stream().collect(Collectors.toMap(TreeEntity::getId, TreeEntity::getSequence));
|
|
|
+ this.initializeSequence(entities);
|
|
|
+ boolean success = super.updateBatchById(entities, batchSize);
|
|
|
+ if (success) {
|
|
|
+ List<String> sqls = entities.stream().map(entity -> {
|
|
|
+ String sequence = sequences.get(entity.getId());
|
|
|
+ if (Objects.equals(sequence, entity.getSequence())) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return this.buildSequenceReplaceSql(sequence, entity.getSequence());
|
|
|
+ }).filter(Objects::nonNull).collect(Collectors.toList());
|
|
|
+ if (ObjectUtils.notEmpty(sqls)) {
|
|
|
+ try {
|
|
|
+ DatabaseContextHolder.execute(sqls);
|
|
|
+ } catch (SQLException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return success;
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|