diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/JdbcSqlExecutorConfiguration.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/JdbcSqlExecutorConfiguration.java index f7ebe67bd..44c1c4082 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/JdbcSqlExecutorConfiguration.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/JdbcSqlExecutorConfiguration.java @@ -20,14 +20,14 @@ public class JdbcSqlExecutorConfiguration { @Bean @ConditionalOnMissingBean - public SyncSqlExecutor syncSqlExecutor() { - return new DefaultJdbcExecutor(); + public SyncSqlExecutor syncSqlExecutor(DataSource dataSource) { + return new DefaultJdbcExecutor(dataSource); } @Bean @ConditionalOnMissingBean - public ReactiveSqlExecutor reactiveSqlExecutor() { - return new DefaultJdbcReactiveExecutor(); + public ReactiveSqlExecutor reactiveSqlExecutor(DataSource dataSource) { + return new DefaultJdbcReactiveExecutor(dataSource); } } \ No newline at end of file diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListener.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListener.java index 8dae712ff..dd17ca854 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListener.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListener.java @@ -16,7 +16,6 @@ import org.hswebframework.ezorm.rdb.mapping.events.ReactiveResultHolder; import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; import org.hswebframework.ezorm.rdb.operator.builder.fragments.NativeSql; -import org.hswebframework.ezorm.rdb.operator.dml.update.UpdateOperator; import org.hswebframework.web.api.crud.entity.Entity; import org.hswebframework.web.bean.FastBeanCopier; import org.hswebframework.web.event.AsyncEvent; @@ -72,72 +71,118 @@ public void onEvent(EventType type, EventContext context) { Class entityType; if (mapping == null || - !Entity.class.isAssignableFrom(entityType = (Class) mapping.getEntityType()) || - !listenerConfigure.isEnabled(entityType)) { + !Entity.class.isAssignableFrom(entityType = (Class) mapping.getEntityType()) || + !listenerConfigure.isEnabled(entityType)) { return; } + // 查询之前 if (type == MappingEventTypes.select_before) { handleQueryBefore(mapping, context); } - if (type == MappingEventTypes.insert_before) { + // 查询包装列 + else if (type == MappingEventTypes.select_wrapper_column) { + + } + // 查询包装对象完成 + else if (type == MappingEventTypes.select_wrapper_done) { + + } + // 查询完成 + else if (type == MappingEventTypes.select_done) { + + } + // insert + else if (type == MappingEventTypes.insert_before) { boolean single = context.get(MappingContextKeys.type).map("single"::equals).orElse(false); if (single) { handleSingleOperation(mapping.getEntityType(), - EntityEventType.create, - context, - EntityPrepareCreateEvent::new, - EntityBeforeCreateEvent::new, - EntityCreatedEvent::new); + EntityEventType.create, + context, + EntityPrepareCreateEvent::new, + EntityBeforeCreateEvent::new, + EntityCreatedEvent::new); } else { handleBatchOperation(mapping.getEntityType(), - EntityEventType.create, - context, - EntityPrepareCreateEvent::new, - EntityBeforeCreateEvent::new, - EntityCreatedEvent::new); + EntityEventType.create, + context, + EntityPrepareCreateEvent::new, + EntityBeforeCreateEvent::new, + EntityCreatedEvent::new); + } + } else if (type == MappingEventTypes.insert_after) { + boolean single = context.get(MappingContextKeys.type).map("single"::equals).orElse(false); + if (single) { + handleSingleOperationAfter(mapping.getEntityType(), + EntityEventType.create, + context, + EntityCreatedEvent::new); + } else { + handleBatchOperationAfter(mapping.getEntityType(), + EntityEventType.create, + context, + EntityCreatedEvent::new); } } - if (type == MappingEventTypes.save_before) { + // save + else if (type == MappingEventTypes.save_before) { boolean single = context.get(MappingContextKeys.type).map("single"::equals).orElse(false); if (single) { handleSingleOperation(mapping.getEntityType(), - EntityEventType.save, - context, - - EntityPrepareSaveEvent::new, - EntityBeforeSaveEvent::new, - EntitySavedEvent::new); + EntityEventType.save, + context, + EntityPrepareSaveEvent::new, + EntityBeforeSaveEvent::new, + EntitySavedEvent::new); } else { handleBatchOperation(mapping.getEntityType(), - EntityEventType.save, - context, - EntityPrepareSaveEvent::new, - EntityBeforeSaveEvent::new, - EntitySavedEvent::new); + EntityEventType.save, + context, + EntityPrepareSaveEvent::new, + EntityBeforeSaveEvent::new, + EntitySavedEvent::new); + } + } else if (type == MappingEventTypes.save_after) { + boolean single = context.get(MappingContextKeys.type).map("single"::equals).orElse(false); + if (single) { + handleSingleOperationAfter(mapping.getEntityType(), + EntityEventType.save, + context, + EntitySavedEvent::new); + } else { + handleBatchOperationAfter(mapping.getEntityType(), + EntityEventType.save, + context, + EntitySavedEvent::new); } } - if (type == MappingEventTypes.update_before) { + // update + else if (type == MappingEventTypes.update_before) { handleUpdateBefore(context); + } else if (type == MappingEventTypes.update_after) { + handleUpdateAfter(context); } - if (type == MappingEventTypes.delete_before) { + // delete + else if (type == MappingEventTypes.delete_before) { handleDeleteBefore(entityType, context); + } else if (type == MappingEventTypes.delete_after) { + handleDeleteAfter(context); } } protected void handleQueryBefore(EntityColumnMapping mapping, EventContext context) { context.get(MappingContextKeys.reactiveResultHolder) - .ifPresent(holder -> { - context.get(MappingContextKeys.queryOaram) - .ifPresent(queryParam -> { - EntityBeforeQueryEvent event = new EntityBeforeQueryEvent<>(queryParam, mapping.getEntityType()); - eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, event, mapping.getEntityType())); - holder - .before( - event.getAsync() - ); - }); - }); + .ifPresent(holder -> { + context.get(MappingContextKeys.queryOaram) + .ifPresent(queryParam -> { + EntityBeforeQueryEvent event = new EntityBeforeQueryEvent<>(queryParam, mapping.getEntityType()); + eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, event, mapping.getEntityType())); + holder + .before( + event.getAsync() + ); + }); + }); } protected List createAfterData(List olds, @@ -145,12 +190,12 @@ protected List createAfterData(List olds, List newValues = new ArrayList<>(olds.size()); EntityColumnMapping mapping = context - .get(MappingContextKeys.columnMapping) - .orElseThrow(UnsupportedOperationException::new); + .get(MappingContextKeys.columnMapping) + .orElseThrow(UnsupportedOperationException::new); Map columns = context - .get(MappingContextKeys.updateColumnInstance) - .orElse(Collections.emptyMap()); + .get(MappingContextKeys.updateColumnInstance) + .orElse(Collections.emptyMap()); for (Object old : olds) { Map oldMap = null; @@ -171,17 +216,17 @@ protected List createAfterData(List olds, //原生sql if (value instanceof NativeSql) { value = expressionInvoker == null ? null : expressionInvoker.invoke( - ((NativeSql) value), - mapping, - oldMap == null ? oldMap = createFullMapping(old, mapping) : oldMap); + ((NativeSql) value), + mapping, + oldMap == null ? oldMap = createFullMapping(old, mapping) : oldMap); if (value == null) { continue; } } GlobalConfig - .getPropertyOperator() - .setProperty(data, column.getAlias(), value); + .getPropertyOperator() + .setProperty(data, column.getAlias(), value); } newValues.add(data); @@ -207,33 +252,33 @@ protected Mono sendUpdateEvent(List before, Function3, List, Class, AsyncEvent> mapper) { return publishEvent(this, - type, - () -> mapper.apply(before, after, type), - eventPublisher::publishEvent); + type, + () -> mapper.apply(before, after, type), + eventPublisher::publishEvent); } protected Mono sendDeleteEvent(List olds, Class type, BiFunction, Class, AsyncEvent> eventBuilder) { return publishEvent(this, - type, - () -> eventBuilder.apply(olds, type), - eventPublisher::publishEvent); + type, + () -> eventBuilder.apply(olds, type), + eventPublisher::publishEvent); } // 回填修改后的字段到准备更新的数据中 // 用于实现通过事件来修改即将被修改的数据 protected void prepareUpdateInstance(List before, List after, EventContext ctx) { Map instance = ctx - .get(MappingContextKeys.updateColumnInstance) - .orElse(null); + .get(MappingContextKeys.updateColumnInstance) + .orElse(null); if (before.size() != 1 || after.size() != 1 || instance == null) { //不支持一次性更新多条数据时设置. return; } EntityColumnMapping mapping = ctx - .get(MappingContextKeys.columnMapping) - .orElseThrow(UnsupportedOperationException::new); + .get(MappingContextKeys.columnMapping) + .orElseThrow(UnsupportedOperationException::new); Object afterEntity = after.get(0); Object beforeEntity = before.get(0); @@ -258,7 +303,7 @@ protected void prepareUpdateInstance(List before, List after, Ev if (origin == null) { //值相同忽略更新,可能是事件并没有修改这个字段. if (Objects.equals(beforeMap.get(column.getAlias()), entry.getValue()) || - Objects.equals(beforeMap.get(column.getName()), entry.getValue())) { + Objects.equals(beforeMap.get(column.getName()), entry.getValue())) { continue; } } @@ -272,8 +317,8 @@ protected void prepareUpdateInstance(List before, List after, Ev } DSLUpdate operator = ctx - .get(ContextKeys.>source()) - .orElse(null); + .get(ContextKeys.>source()) + .orElse(null); if (operator != null && MapUtils.isNotEmpty(copy)) { for (Map.Entry entry : copy.entrySet()) { @@ -288,11 +333,49 @@ protected void prepareUpdateInstance(List before, List after, Ev } + // 阻塞式更新 + protected void handleUpdateAfter(EventContext context) { + Object repo = context.get(MappingContextKeys.repository).orElse(null); + if (repo instanceof SyncRepository) { + List before = context.get(readyToUpdateBeforeContextKey).orElse(null); + List after = context.get(readyToUpdateAfterContextKey).orElse(null); + if (before == null || after == null) { + return; + } + EntityColumnMapping mapping = context + .get(MappingContextKeys.columnMapping) + .orElseThrow(UnsupportedOperationException::new); + Class entityType = (Class) mapping.getEntityType(); + if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.after)) { + block(sendUpdateEvent(before, after, entityType, EntityModifyEvent::new)); + } + } + } + + // 阻塞式删除 + protected void handleDeleteAfter(EventContext context) { + Object repo = context.get(MappingContextKeys.repository).orElse(null); + if (repo instanceof SyncRepository) { + List deleted = context.get(readyToDeleteContextKey).orElse(null); + if (deleted == null) { + return; + } + EntityColumnMapping mapping = context + .get(MappingContextKeys.columnMapping) + .orElseThrow(UnsupportedOperationException::new); + Class entityType = (Class) mapping.getEntityType(); + if (isEnabled(entityType, EntityEventType.delete, EntityEventPhase.after)) { + block(sendDeleteEvent(deleted, entityType, EntityDeletedEvent::new)); + } + } + } + + protected void handleUpdateBefore(DSLUpdate update, EventContext context) { Object repo = context.get(MappingContextKeys.repository).orElse(null); EntityColumnMapping mapping = context - .get(MappingContextKeys.columnMapping) - .orElseThrow(UnsupportedOperationException::new); + .get(MappingContextKeys.columnMapping) + .orElseThrow(UnsupportedOperationException::new); Class entityType = (Class) mapping.getEntityType(); if (repo instanceof ReactiveRepository) { ReactiveResultHolder holder = context.get(MappingContextKeys.reactiveResultHolder).orElse(null); @@ -300,38 +383,38 @@ protected void handleUpdateBefore(DSLUpdate update, EventContext context) AtomicReference, List>> updated = new AtomicReference<>(); //prepare if (isEnabled(entityType, - EntityEventType.modify, - EntityEventPhase.prepare, - EntityEventPhase.before, - EntityEventPhase.after)) { + EntityEventType.modify, + EntityEventPhase.prepare, + EntityEventPhase.before, + EntityEventPhase.after)) { holder.before( - this.doAsyncEvent(() -> ((ReactiveRepository) repo) - .createQuery() - .setParam(update.toQueryParam()) - .fetch() - .collectList() - .flatMap((list) -> { - //没有数据被修改则不触发事件 - if (list.isEmpty()) { - return Mono.empty(); - } - List after = createAfterData(list, context); - updated.set(Tuples.of(list, after)); - context.set(readyToUpdateBeforeContextKey, list); - context.set(readyToUpdateAfterContextKey, after); - EntityPrepareModifyEvent event = new EntityPrepareModifyEvent(list, after, entityType); - - return sendUpdateEvent(list, - after, - entityType, - (_list, _after, _type) -> event) - .then(Mono.fromRunnable(() -> { - if (event.hasListener()) { - prepareUpdateInstance(list, after, context); + this.doAsyncEvent(() -> ((ReactiveRepository) repo) + .createQuery() + .setParam(update.toQueryParam()) + .fetch() + .collectList() + .flatMap((list) -> { + //没有数据被修改则不触发事件 + if (list.isEmpty()) { + return Mono.empty(); } - })); - - }).then()) + List after = createAfterData(list, context); + updated.set(Tuples.of(list, after)); + context.set(readyToUpdateBeforeContextKey, list); + context.set(readyToUpdateAfterContextKey, after); + EntityPrepareModifyEvent event = new EntityPrepareModifyEvent(list, after, entityType); + + return sendUpdateEvent(list, + after, + entityType, + (_list, _after, _type) -> event) + .then(Mono.fromRunnable(() -> { + if (event.hasListener()) { + prepareUpdateInstance(list, after, context); + } + })); + + }).then()) ); } //before @@ -340,9 +423,9 @@ protected void handleUpdateBefore(DSLUpdate update, EventContext context) Tuple2, List> _tmp = updated.get(); if (_tmp != null) { return sendUpdateEvent(_tmp.getT1(), - _tmp.getT2(), - entityType, - EntityBeforeModifyEvent::new); + _tmp.getT2(), + entityType, + EntityBeforeModifyEvent::new); } return Mono.empty(); })); @@ -351,101 +434,162 @@ protected void handleUpdateBefore(DSLUpdate update, EventContext context) //after if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.after)) { holder.after(v -> this - .doAsyncEvent(() -> { - Tuple2, List> _tmp = updated.getAndSet(null); - if (_tmp != null) { - return sendUpdateEvent(_tmp.getT1(), - _tmp.getT2(), - entityType, - EntityModifyEvent::new); - } - return Mono.empty(); - })); + .doAsyncEvent(() -> { + Tuple2, List> _tmp = updated.getAndSet(null); + if (_tmp != null) { + return sendUpdateEvent(_tmp.getT1(), + _tmp.getT2(), + entityType, + EntityModifyEvent::new); + } + return Mono.empty(); + })); } } } else if (repo instanceof SyncRepository) { - if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.before)) { + if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.prepare, EntityEventPhase.before, EntityEventPhase.after)) { QueryParam param = update.toQueryParam(); SyncRepository syncRepository = ((SyncRepository) repo); - List list = syncRepository.createQuery() - .setParam(param) - .fetch(); - if (list.isEmpty()) { + List before = syncRepository.createQuery().setParam(param).fetch(); + if (before.isEmpty()) { return; } - sendUpdateEvent(list, - createAfterData(list, context), - (Class) mapping.getEntityType(), - EntityBeforeModifyEvent::new) - .block(); + List after = createAfterData(before, context); + context.set(readyToUpdateBeforeContextKey, before); + context.set(readyToUpdateAfterContextKey, after); + + // prepare + if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.prepare)) { + EntityPrepareModifyEvent event = new EntityPrepareModifyEvent(before, after, entityType); + block( + sendUpdateEvent(before, + after, + entityType, + (_list, _after, _type) -> event) + ); + + prepareUpdateInstance(before, after, context); + } + // before + if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.before)) { + block(sendUpdateEvent(before, + after, + entityType, + EntityBeforeModifyEvent::new)); + } + } } } protected void handleUpdateBefore(EventContext context) { - context.>get(ContextKeys.source()) - .ifPresent(dslUpdate -> { - handleUpdateBefore(dslUpdate, context); - }); + DSLUpdate update = context.>get(ContextKeys.source()).orElse(null); + if (update != null) { + handleUpdateBefore(update, context); + } } protected void handleDeleteBefore(Class entityType, EventContext context) { EntityColumnMapping mapping = context - .get(MappingContextKeys.columnMapping) - .orElseThrow(UnsupportedOperationException::new); + .get(MappingContextKeys.columnMapping) + .orElseThrow(UnsupportedOperationException::new); context.get(ContextKeys.source()) - .ifPresent(dslUpdate -> { - Object repo = context.get(MappingContextKeys.repository).orElse(null); - if (repo instanceof ReactiveRepository) { - context.get(MappingContextKeys.reactiveResultHolder) - .ifPresent(holder -> { - AtomicReference> deleted = new AtomicReference<>(); - if (isEnabled(entityType, EntityEventType.delete, EntityEventPhase.before, EntityEventPhase.after)) { - holder.before( - this.doAsyncEvent(() -> ((ReactiveRepository) repo) - .createQuery() - .setParam(dslUpdate.toQueryParam()) - .fetch() - .collectList() - .doOnNext(list -> { - context.set(readyToDeleteContextKey, list); - }) - .filter(CollectionUtils::isNotEmpty) - .flatMap(list -> { - deleted.set(list); - return this - .sendDeleteEvent(list, (Class) mapping.getEntityType(), EntityBeforeDeleteEvent::new); - }) - ) - ); - } - if (isEnabled(entityType, EntityEventType.delete, EntityEventPhase.after)) { - holder.after(v -> this - .doAsyncEvent(() -> { - List _tmp = deleted.getAndSet(null); - if (CollectionUtils.isNotEmpty(_tmp)) { - return sendDeleteEvent(_tmp, (Class) mapping.getEntityType(), EntityDeletedEvent::new); - } - return Mono.empty(); - })); - } - - }); - } else if (repo instanceof SyncRepository) { - QueryParam param = dslUpdate.toQueryParam(); - SyncRepository syncRepository = ((SyncRepository) repo); - List list = syncRepository.createQuery() - .setParam(param) - .fetch(); - this.sendDeleteEvent(list, (Class) mapping.getEntityType(), EntityBeforeDeleteEvent::new) - .block(); - } - }); + .ifPresent(dslUpdate -> { + Object repo = context.get(MappingContextKeys.repository).orElse(null); + if (repo instanceof ReactiveRepository) { + context.get(MappingContextKeys.reactiveResultHolder) + .ifPresent(holder -> { + AtomicReference> deleted = new AtomicReference<>(); + if (isEnabled(entityType, EntityEventType.delete, EntityEventPhase.before, EntityEventPhase.after)) { + holder.before( + this.doAsyncEvent(() -> ((ReactiveRepository) repo) + .createQuery() + .setParam(dslUpdate.toQueryParam()) + .fetch() + .collectList() + .doOnNext(list -> { + context.set(readyToDeleteContextKey, list); + }) + .filter(CollectionUtils::isNotEmpty) + .flatMap(list -> { + deleted.set(list); + return this + .sendDeleteEvent(list, (Class) mapping.getEntityType(), EntityBeforeDeleteEvent::new); + }) + ) + ); + } + if (isEnabled(entityType, EntityEventType.delete, EntityEventPhase.after)) { + holder.after(v -> this + .doAsyncEvent(() -> { + List _tmp = deleted.getAndSet(null); + if (CollectionUtils.isNotEmpty(_tmp)) { + return sendDeleteEvent(_tmp, (Class) mapping.getEntityType(), EntityDeletedEvent::new); + } + return Mono.empty(); + })); + } + + }); + } else if (repo instanceof SyncRepository) { + QueryParam param = dslUpdate.toQueryParam(); + SyncRepository syncRepository = ((SyncRepository) repo); + List list = syncRepository.createQuery() + .setParam(param) + .fetch(); + context.set(readyToDeleteContextKey, list); + block(this.sendDeleteEvent(list, (Class) mapping.getEntityType(), EntityBeforeDeleteEvent::new)); + } + }); } - protected void handleUpdateAfter(EventContext context) { + protected void handleSingleOperationAfter(Class clazz, + EntityEventType entityEventType, + EventContext context, + BiFunction, Class, AsyncEvent> after) { + Object repo = context.get(MappingContextKeys.repository).orElse(null); + if (repo instanceof SyncRepository) { + Entity lst = context + .get(MappingContextKeys.instance) + .filter(Entity.class::isInstance) + .map(Entity.class::cast) + .orElse(null); + if (lst == null) { + return; + } + if (isEnabled(clazz, entityEventType, EntityEventPhase.after)) { + AsyncEvent afterEvent = after.apply(Collections.singletonList(lst), clazz); + block(publishEvent(this, + clazz, + () -> afterEvent, + eventPublisher::publishEvent)); + } + } + } + + protected void handleBatchOperationAfter(Class clazz, + EntityEventType entityEventType, + EventContext context, + BiFunction, Class, AsyncEvent> after) { + Object repo = context.get(MappingContextKeys.repository).orElse(null); + if (repo instanceof SyncRepository) { + List lst = context.get(MappingContextKeys.instance) + .filter(List.class::isInstance) + .map(List.class::cast) + .orElse(null); + if (lst == null) { + return; + } + if (isEnabled(clazz, entityEventType, EntityEventPhase.after)) { + AsyncEvent afterEvent = after.apply(lst, clazz); + block(publishEvent(this, + clazz, + () -> afterEvent, + eventPublisher::publishEvent)); + } + } } protected void handleBatchOperation(Class clazz, @@ -456,9 +600,9 @@ protected void handleBatchOperation(Class clazz, BiFunction, Class, AsyncEvent> after) { List lst = context.get(MappingContextKeys.instance) - .filter(List.class::isInstance) - .map(List.class::cast) - .orElse(null); + .filter(List.class::isInstance) + .map(List.class::cast) + .orElse(null); if (lst == null) { return; } @@ -467,47 +611,59 @@ protected void handleBatchOperation(Class clazz, AsyncEvent afterEvent = after.apply(lst, clazz); AsyncEvent beforeEvent = execute.apply(lst, clazz); Object repo = context.get(MappingContextKeys.repository).orElse(null); + // 响应式 if (repo instanceof ReactiveRepository) { Optional resultHolder = context.get(MappingContextKeys.reactiveResultHolder); if (resultHolder.isPresent()) { ReactiveResultHolder holder = resultHolder.get(); if (null != prepareEvent && isEnabled(clazz, entityEventType, EntityEventPhase.prepare)) { holder.before( - this.doAsyncEvent(() -> { - return publishEvent(this, - clazz, - () -> prepareEvent, - eventPublisher::publishEvent); - }) + this.doAsyncEvent(() -> { + return publishEvent(this, + clazz, + () -> prepareEvent, + eventPublisher::publishEvent); + }) ); } if (null != beforeEvent && isEnabled(clazz, entityEventType, EntityEventPhase.before)) { holder.invoke( - this.doAsyncEvent(() -> { - return publishEvent(this, - clazz, - () -> beforeEvent, - eventPublisher::publishEvent); - }) + this.doAsyncEvent(() -> { + return publishEvent(this, + clazz, + () -> beforeEvent, + eventPublisher::publishEvent); + }) ); } if (null != afterEvent && isEnabled(clazz, entityEventType, EntityEventPhase.after)) { holder.after(v -> { return this.doAsyncEvent(() -> { return publishEvent(this, - clazz, - () -> afterEvent, - eventPublisher::publishEvent); + clazz, + () -> afterEvent, + eventPublisher::publishEvent); }); }); } return; } + } else { + if (isEnabled(clazz, entityEventType, EntityEventPhase.prepare)) { + block(publishEvent(this, + clazz, + () -> prepareEvent, + eventPublisher::publishEvent)) ; + } + + if (isEnabled(clazz, entityEventType, EntityEventPhase.before)) { + block(publishEvent(this, + clazz, + () -> beforeEvent, + eventPublisher::publishEvent)); + } } - eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, afterEvent, clazz)); - //block非响应式的支持 - afterEvent.getAsync().block(); } boolean isEnabled(Class clazz, EntityEventType entityEventType, EntityEventPhase... phase) { @@ -525,63 +681,88 @@ protected void handleSingleOperation(Class clazz, BiFunction, Class, AsyncEvent> before, BiFunction, Class, AsyncEvent> execute, BiFunction, Class, AsyncEvent> after) { - context.get(MappingContextKeys.instance) - .filter(Entity.class::isInstance) - .map(Entity.class::cast) - .ifPresent(entity -> { - AsyncEvent prepareEvent = before.apply(Collections.singletonList(entity), clazz); - AsyncEvent beforeEvent = execute.apply(Collections.singletonList(entity), clazz); - AsyncEvent afterEvent = after.apply(Collections.singletonList(entity), clazz); - - Object repo = context.get(MappingContextKeys.repository).orElse(null); - if (repo instanceof ReactiveRepository) { - Optional resultHolder = context.get(MappingContextKeys.reactiveResultHolder); - if (resultHolder.isPresent()) { - ReactiveResultHolder holder = resultHolder.get(); - if (null != prepareEvent && isEnabled(clazz, entityEventType, EntityEventPhase.prepare)) { - holder.before( - this.doAsyncEvent(() -> { - return publishEvent(this, - clazz, - () -> prepareEvent, - eventPublisher::publishEvent); - }) - ); - } - - if (null != beforeEvent && isEnabled(clazz, entityEventType, EntityEventPhase.before)) { - holder.invoke( - this.doAsyncEvent(() -> { - return publishEvent(this, - clazz, - () -> beforeEvent, - eventPublisher::publishEvent); - }) - ); - } - if (null != afterEvent && isEnabled(clazz, entityEventType, EntityEventPhase.after)) { - holder.after(v -> { - return this.doAsyncEvent(() -> { - return publishEvent(this, - clazz, - () -> afterEvent, - eventPublisher::publishEvent); - }); - }); - } - return; - } - } - eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, afterEvent, clazz)); - //block非响应式的支持 - afterEvent.getAsync().block(); - }); + Entity entity = context.get(MappingContextKeys.instance) + .filter(Entity.class::isInstance) + .map(Entity.class::cast).orElse(null); + if (entity == null) { + return; + } + + AsyncEvent prepareEvent = before.apply(Collections.singletonList(entity), clazz); + AsyncEvent beforeEvent = execute.apply(Collections.singletonList(entity), clazz); + AsyncEvent afterEvent = after.apply(Collections.singletonList(entity), clazz); + + Object repo = context.get(MappingContextKeys.repository).orElse(null); + + // 响应式 + if (repo instanceof ReactiveRepository) { + Optional resultHolder = context.get(MappingContextKeys.reactiveResultHolder); + if (resultHolder.isPresent()) { + ReactiveResultHolder holder = resultHolder.get(); + if (null != prepareEvent && isEnabled(clazz, entityEventType, EntityEventPhase.prepare)) { + holder.before( + this.doAsyncEvent(() -> { + return publishEvent(this, + clazz, + () -> prepareEvent, + eventPublisher::publishEvent); + }) + ); + } + + if (null != beforeEvent && isEnabled(clazz, entityEventType, EntityEventPhase.before)) { + holder.invoke( + this.doAsyncEvent(() -> { + return publishEvent(this, + clazz, + () -> beforeEvent, + eventPublisher::publishEvent); + }) + ); + } + if (null != afterEvent && isEnabled(clazz, entityEventType, EntityEventPhase.after)) { + holder.after(v -> { + return this.doAsyncEvent(() -> { + return publishEvent(this, + clazz, + () -> afterEvent, + eventPublisher::publishEvent); + }); + }); + } + return; + } + } else { + + // 非响应式 + if (isEnabled(clazz, entityEventType, EntityEventPhase.prepare)) { + block( + publishEvent(this, + clazz, + () -> prepareEvent, + eventPublisher::publishEvent) + ); + } + + if (isEnabled(clazz, entityEventType, EntityEventPhase.before)) { + block( + publishEvent(this, + clazz, + () -> beforeEvent, + eventPublisher::publishEvent) + ); + } + } } protected Mono doAsyncEvent(Supplier> eventSupplier) { return EntityEventHelper.tryFireEvent(eventSupplier); } + private void block(Mono mono) { + mono.block(); + } + @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE - 100; diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/ValidateEventListener.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/ValidateEventListener.java index 4d52c1c14..25572f1f9 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/ValidateEventListener.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/ValidateEventListener.java @@ -34,13 +34,13 @@ public void onEvent(EventType type, EventContext context) { if (resultHolder.isPresent()) { resultHolder - .ifPresent(holder -> holder - .invoke(LocaleUtils - .doInReactive(() -> { - tryValidate(type, context); - return null; - }) - )); + .ifPresent(holder -> holder + .invoke(LocaleUtils + .doInReactive(() -> { + tryValidate(type, context); + return null; + }) + )); } else { tryValidate(type, context); } @@ -48,7 +48,8 @@ public void onEvent(EventType type, EventContext context) { @SuppressWarnings("all") public void tryValidate(EventType type, EventContext context) { - if (type == MappingEventTypes.insert_before || type == MappingEventTypes.save_before) { + if (type == MappingEventTypes.insert_before + || type == MappingEventTypes.save_before) { boolean single = context.get(MappingContextKeys.type).map("single"::equals).orElse(false); if (single) { @@ -60,10 +61,11 @@ public void tryValidate(EventType type, EventContext context) { context.get(MappingContextKeys.instance) .filter(List.class::isInstance) .map(List.class::cast) - .ifPresent(lst -> lst.stream() - .filter(Entity.class::isInstance) - .map(Entity.class::cast) - .forEach(e -> ((Entity) e).tryValidate(CreateGroup.class)) + .ifPresent(lst -> lst + .stream() + .filter(Entity.class::isInstance) + .map(Entity.class::cast) + .forEach(e -> ((Entity) e).tryValidate(CreateGroup.class)) ); } diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/CrudService.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/CrudService.java index 97477f941..fea2ed262 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/CrudService.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/CrudService.java @@ -1,5 +1,6 @@ package org.hswebframework.web.crud.service; +import lombok.SneakyThrows; import org.apache.commons.collections4.CollectionUtils; import org.hswebframework.ezorm.core.param.QueryParam; import org.hswebframework.ezorm.rdb.mapping.SyncDelete; @@ -12,6 +13,7 @@ import org.hswebframework.web.api.crud.entity.TransactionManagers; import org.springframework.transaction.annotation.Transactional; +import java.sql.SQLException; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -32,13 +34,15 @@ default SyncDelete createDelete() { return getRepository().createDelete(); } - @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) + @Transactional( readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default Optional findById(K id) { return getRepository() .findById(id); } @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default List findById(Collection id) { if (CollectionUtils.isEmpty(id)) { return Collections.emptyList(); @@ -48,63 +52,70 @@ default List findById(Collection id) { .findById(id); } - @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager) + @Transactional(rollbackFor = Throwable.class,transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default SaveResult save(Collection entityArr) { return getRepository() .save(entityArr); } - @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager) + @Transactional(rollbackFor = Throwable.class,transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default int insert(Collection entityArr) { return getRepository() .insertBatch(entityArr); } - @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager) - default void insert(E entityArr) { - getRepository() - .insert(entityArr); + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.jdbcTransactionManager) + default void insert(E entityArr){ + getRepository().insert(entityArr); } - @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager) + @Transactional(rollbackFor = Throwable.class,transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default int updateById(K id, E entityArr) { - return getRepository() - .updateById(id, entityArr); + return getRepository().updateById(id, entityArr); } - @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager) + @Transactional(rollbackFor = Throwable.class,transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default SaveResult save(E entity) { return getRepository() .save(Collections.singletonList(entity)); } - @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager) + @Transactional(rollbackFor = Throwable.class,transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default SaveResult save(List entities) { return getRepository() .save(entities); } - @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager) + @Transactional(rollbackFor = Throwable.class,transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default int deleteById(Collection idArr) { return getRepository().deleteById(idArr); } - @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager) + @Transactional(rollbackFor = Throwable.class,transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default int deleteById(K idArr) { return deleteById(Collections.singletonList(idArr)); } @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default List query(QueryParamEntity queryParam) { return createQuery().setParam(queryParam).fetch(); } @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default PagerResult queryPager(QueryParamEntity param) { int count = param.getTotal() == null ? count(param) : param.getTotal(); if (count == 0) { - return PagerResult.empty(); + return PagerResult.of(0,Collections.emptyList(),param); } param.rePaging(count); @@ -112,6 +123,7 @@ default PagerResult queryPager(QueryParamEntity param) { } @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) + @SneakyThrows default int count(QueryParam param) { return getRepository() .createQuery() diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/EnableCacheReactiveCrudService.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/EnableCacheReactiveCrudService.java index c996595c5..6fc7206d1 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/EnableCacheReactiveCrudService.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/EnableCacheReactiveCrudService.java @@ -34,48 +34,55 @@ default Mono findById(Mono publisher) { } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono updateById(K id, E data) { return updateById(id, Mono.just(data)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono updateById(K id, Mono entityPublisher) { return registerClearCache(Collections.singleton("id:" + id)) .then(ReactiveCrudService.super.updateById(id, entityPublisher)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono save(Collection collection) { return registerClearCache() .then(ReactiveCrudService.super.save(collection)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono save(E data) { return registerClearCache() .then(ReactiveCrudService.super.save(data)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono save(Publisher entityPublisher) { return registerClearCache() .then(ReactiveCrudService.super.save(entityPublisher)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insert(E data) { return registerClearCache() .then(ReactiveCrudService.super.insert(data)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insert(Publisher entityPublisher) { return registerClearCache() .then(ReactiveCrudService.super.insert(entityPublisher)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insertBatch(Publisher> entityPublisher) { return registerClearCache() .then(ReactiveCrudService.super.insertBatch(entityPublisher)); @@ -106,12 +113,13 @@ public Mono afterCommit() { @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono deleteById(K id) { return deleteById(Mono.just(id)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono deleteById(Publisher idPublisher) { Flux cache = Flux.from(idPublisher).cache(); return cache diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/GenericCrudService.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/GenericCrudService.java index 5b519de3a..bea845d7c 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/GenericCrudService.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/GenericCrudService.java @@ -1,7 +1,9 @@ package org.hswebframework.web.crud.service; import org.hswebframework.ezorm.rdb.mapping.SyncRepository; +import org.hswebframework.web.api.crud.entity.TransactionManagers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; public abstract class GenericCrudService implements CrudService { diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java index cec6e0ce8..ce2efce8d 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java @@ -90,13 +90,13 @@ default ReactiveDelete createDelete() { } - @Transactional(readOnly = true, transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono findById(K id) { return getRepository() .findById(id); } - @Transactional(readOnly = true, transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Flux findById(Collection publisher) { return getRepository() .findById(publisher); @@ -114,61 +114,61 @@ default Flux findById(Flux publisher) { .findById(publisher); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono save(Publisher entityPublisher) { return getRepository() .save(entityPublisher); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono save(E data) { return getRepository() .save(data); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono save(Collection collection) { return getRepository() .save(collection); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono updateById(K id, Mono entityPublisher) { return getRepository() .updateById(id, entityPublisher); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono updateById(K id, E data) { return getRepository() .updateById(id, Mono.just(data)); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insertBatch(Publisher> entityPublisher) { return getRepository() .insertBatch(entityPublisher); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insert(Publisher entityPublisher) { return getRepository() .insert(entityPublisher); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insert(E data) { return getRepository() .insert(Mono.just(data)); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono deleteById(Publisher idPublisher) { return getRepository() .deleteById(idPublisher); } - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono deleteById(K id) { return getRepository() .deleteById(Mono.just(id)); diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortEntityService.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortEntityService.java index 97bd0187a..ec9619f04 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortEntityService.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortEntityService.java @@ -174,23 +174,23 @@ default Flux queryIncludeChildren(QueryParamEntity queryParam) { } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insert(Publisher entityPublisher) { return insertBatch(Flux.from(entityPublisher).collectList()); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insert(E data) { return this.insertBatch(Flux.just(Collections.singletonList(data))); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono insertBatch(Publisher> entityPublisher) { return this .getRepository() - .insertBatch(new TreeSortServiceHelper<>(this) + .insertBatch(new ReactiveTreeSortServiceHelper<>(this) .prepare(Flux.from(entityPublisher) .flatMapIterable(Function.identity())) // .doOnNext(e -> e.tryValidate(CreateGroup.class)) @@ -201,105 +201,11 @@ default int getBufferSize() { return 200; } - @Deprecated - default Mono applyTreeProperty(E ele) { - if (StringUtils.hasText(ele.getPath()) || - ObjectUtils.isEmpty(ele.getParentId())) { - return Mono.just(ele); - } - - return this.checkCyclicDependency(ele.getId(), ele) - .then(this.findById(ele.getParentId()) - .doOnNext(parent -> ele.setPath(parent.getPath() + "-" + RandomUtil.randomChar(4)))) - .thenReturn(ele); - } - - @Deprecated - //校验是否有循环依赖,修改父节点为自己的子节点? - default Mono checkCyclicDependency(K id, E ele) { - if (ObjectUtils.isEmpty(id)) { - return Mono.empty(); - } - return this - .queryIncludeChildren(Collections.singletonList(id)) - .doOnNext(e -> { - if (Objects.equals(ele.getParentId(), e.getId())) { - throw new ValidationException.NoStackTrace("parentId", "error.tree_entity_cyclic_dependency"); - } - }) - .then(Mono.just(ele)); - } - - @Deprecated - default Mono> checkParentId(Collection source) { - - Set idSet = source - .stream() - .map(TreeSupportEntity::getId) - .filter(e -> !ObjectUtils.isEmpty(e)) - .collect(Collectors.toSet()); - - if (idSet.isEmpty()) { - return Mono.just(source); - } - - Set readyToCheck = source - .stream() - .map(TreeSupportEntity::getParentId) - .filter(e -> !ObjectUtils.isEmpty(e) && !idSet.contains(e)) - .collect(Collectors.toSet()); - - if (readyToCheck.isEmpty()) { - return Mono.just(source); - } - - return this - .createQuery() - .select("id") - .in("id", readyToCheck) - .fetch() - .doOnNext(e -> readyToCheck.remove(e.getId())) - .then(Mono.fromSupplier(() -> { - if (!readyToCheck.isEmpty()) { - throw new ValidationException( - "error.tree_entity_parent_id_not_exist", - Collections.singletonList( - new ValidationException.Detail( - "parentId", - "error.tree_entity_parent_id_not_exist", - readyToCheck)) - ); - } - return source; - })); - - } - - @Deprecated - //重构子节点的path - default void refactorChildPath(K id, Function> childGetter, String path, Consumer pathAccepter) { - - Collection children = childGetter.apply(id); - if (CollectionUtils.isEmpty(children)) { - return; - } - for (E child : children) { - if (ObjectUtils.isEmpty(path)) { - child.setPath(RandomUtil.randomChar(4)); - } else { - child.setPath(path + "-" + RandomUtil.randomChar(4)); - } - pathAccepter.accept(child); - this.refactorChildPath(child.getId(), childGetter, child.getPath(), pathAccepter); - } - - } - @Override @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) default Mono save(Publisher entityPublisher) { - return new TreeSortServiceHelper<>(this) + return new ReactiveTreeSortServiceHelper<>(this) .prepare(Flux.from(entityPublisher)) // .doOnNext(e -> e.tryValidate(CreateGroup.class)) .buffer(getBufferSize()) @@ -310,7 +216,7 @@ default Mono save(Publisher entityPublisher) { @Deprecated default Flux tryRefactorPath(Flux stream) { - return new TreeSortServiceHelper<>(this).prepare(stream); + return new ReactiveTreeSortServiceHelper<>(this).prepare(stream); } @Override diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortServiceHelper.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortServiceHelper.java new file mode 100644 index 000000000..02faae4aa --- /dev/null +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortServiceHelper.java @@ -0,0 +1,41 @@ +package org.hswebframework.web.crud.service; + +import org.hswebframework.web.api.crud.entity.TreeSortSupportEntity; +import org.hswebframework.web.id.IDGenerator; +import reactor.core.publisher.Flux; + +import java.util.*; + +public class ReactiveTreeSortServiceHelper, PK> extends TreeSortServiceHelper { + + private final ReactiveTreeSortEntityService service; + + public ReactiveTreeSortServiceHelper(ReactiveTreeSortEntityService service) { + this.service = service; + } + + @Override + protected IDGenerator getIdGenerator() { + return service.getIDGenerator(); + } + + @Override + protected void applyChildren(E parent, List children) { + service.setChildren(parent, children); + } + + @Override + protected boolean isRootNode(E node) { + return service.isRootNode(node); + } + + @Override + protected Flux queryIncludeChildren(Collection idList) { + return service.queryIncludeChildren(idList); + } + + @Override + protected Flux queryById(Collection idList) { + return service.findById(idList); + } +} diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/SyncTreeSortServiceHelper.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/SyncTreeSortServiceHelper.java new file mode 100644 index 000000000..57cba64fe --- /dev/null +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/SyncTreeSortServiceHelper.java @@ -0,0 +1,52 @@ +package org.hswebframework.web.crud.service; + +import org.hswebframework.web.api.crud.entity.TreeSortSupportEntity; +import org.hswebframework.web.id.IDGenerator; +import reactor.core.publisher.Flux; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class SyncTreeSortServiceHelper, PK> extends TreeSortServiceHelper { + + private final TreeSortEntityService service; + + public SyncTreeSortServiceHelper(TreeSortEntityService service) { + this.service = service; + } + + @Override + protected IDGenerator getIdGenerator() { + return service.getIDGenerator(); + } + + @Override + protected void applyChildren(E parent, List children) { + service.setChildren(parent, children); + } + + @Override + protected boolean isRootNode(E node) { + return service.isRootNode(node); + } + + public List prepare(Collection source) { + return super + .prepare(Flux.fromIterable(source)) + .toStream() + .collect(Collectors.toList()); + } + + @Override + @SuppressWarnings("all") + protected Flux queryIncludeChildren(Collection idList) { + return Flux.fromIterable(service.queryIncludeChildren(idList)); + } + + @Override + @SuppressWarnings("all") + protected Flux queryById(Collection idList) { + return Flux.fromIterable(service.findById(idList)); + } +} diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortEntityService.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortEntityService.java index c35ffdd5f..5c565c50a 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortEntityService.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortEntityService.java @@ -25,124 +25,98 @@ * @see GenericReactiveTreeSupportCrudService */ public interface TreeSortEntityService, K> - extends CrudService { + extends CrudService { @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) default List queryResultToTree(QueryParamEntity paramEntity) { return TreeSupportEntity - .list2tree(query(paramEntity), - this::setChildren, - this::createRootNodePredicate); + .list2tree(query(paramEntity), + this::setChildren, + this::createRootNodePredicate); } @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) default List queryIncludeChildrenTree(QueryParamEntity paramEntity) { return TreeSupportEntity - .list2tree(queryIncludeChildren(paramEntity), - this::setChildren, - this::createRootNodePredicate); + .list2tree(queryIncludeChildren(paramEntity), + this::setChildren, + this::createRootNodePredicate); } @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) default List queryIncludeChildren(Collection idList) { return findById(idList) - .stream() - .flatMap(e -> createQuery() - .where() - .like$("path", e.getPath()) - .fetch() - .stream()) - .collect(Collectors.toList()); + .stream() + .flatMap(e -> createQuery() + .where() + .like$("path", e.getPath()) + .fetch() + .stream()) + .collect(Collectors.toList()); } @Transactional(readOnly = true, transactionManager = TransactionManagers.jdbcTransactionManager) default List queryIncludeChildren(QueryParamEntity queryParam) { return query(queryParam) - .stream() - .flatMap(e -> createQuery() - .where() - .like$("path", e.getPath()) - .fetch() - .stream()) - .collect(Collectors.toList()); + .stream() + .flatMap(e -> createQuery() + .where() + .like$("path", e.getPath()) + .fetch() + .stream()) + .collect(Collectors.toList()); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.jdbcTransactionManager) default void insert(E entityPublisher) { insert(Collections.singletonList(entityPublisher)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.jdbcTransactionManager) default int insert(Collection entityPublisher) { - return this - .getRepository() - .insertBatch(entityPublisher - .stream() - .flatMap(this::applyTreeProperty) - .flatMap(e -> TreeSupportEntity - .expandTree2List(e, getIDGenerator()) - .stream()) - .collect(Collectors.toList()) - ); - } - - default Stream applyTreeProperty(E ele) { - if (StringUtils.hasText(ele.getPath()) || - ObjectUtils.isEmpty(ele.getParentId())) { - return Stream.of(ele); - } - - this.checkCyclicDependency(ele.getId(), ele); - this.findById(ele.getParentId()) - .ifPresent(parent -> ele.setPath(parent.getPath() + "-" + RandomUtil.randomChar(4))); - return Stream.of(ele); - } - - //校验是否有循环依赖,修改父节点为自己的子节点? - default void checkCyclicDependency(K id, E ele) { - if (ObjectUtils.isEmpty(id)) { - return; - } - for (E e : this.queryIncludeChildren(Collections.singletonList(id))) { - if (Objects.equals(ele.getParentId(), e.getId())) { - throw new IllegalArgumentException("不能修改父节点为自己或者自己的子节点"); - } - } - + return new SyncTreeSortServiceHelper<>(this) + .prepare(Flux.fromIterable(entityPublisher)) + .buffer(getBufferSize()) + .map(this.getRepository()::insertBatch) + .reduce(Math::addExact) + .blockOptional() + .orElse(0); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.jdbcTransactionManager) default SaveResult save(List entities) { - return this.getRepository() - .save(entities - .stream() - .flatMap(this::applyTreeProperty) - //把树结构平铺 - .flatMap(e -> TreeSupportEntity - .expandTree2List(e, getIDGenerator()) - .stream()) - .collect(Collectors.toList()) - ); + return new SyncTreeSortServiceHelper<>(this) + .prepare(Flux.fromIterable(entities)) + .buffer(getBufferSize()) + .map(this.getRepository()::save) + .reduce(SaveResult::merge) + .blockOptional() + .orElse(SaveResult.of(0,0)); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.jdbcTransactionManager) default int updateById(K id, E entity) { entity.setId(id); return this.save(entity).getTotal(); } @Override + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.jdbcTransactionManager) default int deleteById(Collection idPublisher) { List dataList = findById(idPublisher); return dataList - .stream() - .map(e -> createDelete() - .where() - .like$(e::getPath) - .execute()) - .mapToInt(Integer::intValue) - .sum(); + .stream() + .map(e -> createDelete() + .where() + .like$(e::getPath) + .execute()) + .mapToInt(Integer::intValue) + .sum(); } IDGenerator getIDGenerator(); @@ -153,6 +127,10 @@ default List getChildren(E entity) { return entity.getChildren(); } + default int getBufferSize() { + return 200; + } + default Predicate createRootNodePredicate(TreeSupportEntity.TreeHelper helper) { return node -> { if (isRootNode(node)) { diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortServiceHelper.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortServiceHelper.java index 0da90387b..4050cd442 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortServiceHelper.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortServiceHelper.java @@ -6,6 +6,7 @@ import org.hswebframework.web.api.crud.entity.TreeSortSupportEntity; import org.hswebframework.web.api.crud.entity.TreeSupportEntity; import org.hswebframework.web.exception.ValidationException; +import org.hswebframework.web.id.IDGenerator; import org.springframework.util.ObjectUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -16,54 +17,59 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -public class TreeSortServiceHelper, PK> { +public abstract class TreeSortServiceHelper, PK> { //包含子节点的数据 - private Map allData; + protected Map allData; - private Map oldData; + protected Map oldData; - private Map thisTime; + protected Map thisTime; - private Map readyToSave; + protected Map readyToSave; - private final Map> childrenMapping = new LinkedHashMap<>(); + protected final Map> childrenMapping = new LinkedHashMap<>(); - private final ReactiveTreeSortEntityService service; + protected abstract IDGenerator getIdGenerator(); - TreeSortServiceHelper(ReactiveTreeSortEntityService service) { - this.service = service; - } + protected abstract void applyChildren(E parent, List children); + + protected abstract boolean isRootNode(E node); + + protected abstract Flux queryIncludeChildren(Collection idList); + + protected abstract Flux queryById(Collection idList); - Flux prepare(Flux source) { + + public Flux prepare(Flux source) { Flux cache = source - .flatMapIterable(e -> TreeSupportEntity.expandTree2List(e, service.getIDGenerator())) - .collectList() - .flatMapIterable(list -> { - - Map map = list - .stream() - .filter(e -> e.getId() != null) - .collect(Collectors.toMap( - TreeSupportEntity::getId, - Function.identity(), - (a, b) -> a - )); - //重新组装树结构 - TreeSupportEntity.list2tree(list, - service::setChildren, - (Predicate) e -> service.isRootNode(e) || map.get(e.getParentId()) == null); - - return list; - }) - .cache(); + .flatMapIterable(e -> TreeSupportEntity.expandTree2List(e, getIdGenerator())) + .collectList() + .flatMapIterable(list -> { + + Map map = list + .stream() + .filter(e -> e.getId() != null) + .collect(Collectors.toMap( + TreeSupportEntity::getId, + Function.identity(), + (a, b) -> a + )); + //重新组装树结构 + TreeSupportEntity.list2tree(list, + this::applyChildren, + (Predicate) e -> isRootNode(e) || map.get(e.getParentId()) == null); + + return list; + }) + .cache(); return init(cache) - .then(Mono.defer(this::checkParentId)) - .then(Mono.fromRunnable(this::checkCyclicDependency)) - .then(Mono.fromRunnable(this::refactorPath)) - .thenMany(Flux.defer(() -> Flux.fromIterable(readyToSave.values()))) - .doOnNext(this::refactor); + .then(Mono.defer(this::checkParentId)) + .then(Mono.fromRunnable(this::checkCyclicDependency)) + .then(Mono.fromRunnable(this::refactorPath)) + .thenMany(Flux.defer(() -> Flux.fromIterable(readyToSave.values()))) + .doOnNext(this::refactor); } private Mono init(Flux source) { @@ -73,49 +79,49 @@ private Mono init(Flux source) { readyToSave = new LinkedHashMap<>(); Mono> allDataFetcher = - source - .mapNotNull(e -> { - - if (e.getId() != null) { - thisTime.put(e.getId(), e); - } - - return e.getId(); - }) - .collect(Collectors.toSet()) - .flatMap(list -> service - .queryIncludeChildren(list) - .collectMap(TreeSupportEntity::getId, Function.identity())); + source + .mapNotNull(e -> { + + if (e.getId() != null) { + thisTime.put(e.getId(), e); + } + + return e.getId(); + }) + .collect(Collectors.toSet()) + .flatMap(list -> + queryIncludeChildren(list) + .collectMap(TreeSupportEntity::getId, Function.identity())); return allDataFetcher - .doOnNext(includeChildren -> { - //旧的数据 - for (E value : thisTime.values()) { - E old = includeChildren.get(value.getId()); - if (null != old) { - this.oldData.put(value.getId(), old); - } + .doOnNext(includeChildren -> { + //旧的数据 + for (E value : thisTime.values()) { + E old = includeChildren.get(value.getId()); + if (null != old) { + this.oldData.put(value.getId(), old); } + } - readyToSave.putAll(thisTime); + readyToSave.putAll(thisTime); - allData.putAll(includeChildren); - allData.putAll(this.thisTime); - initChildren(); + allData.putAll(includeChildren); + allData.putAll(this.thisTime); + initChildren(); - }) - .then(); + }) + .then(); } private void initChildren() { childrenMapping.clear(); for (E value : allData.values()) { - if (service.isRootNode(value) || value.getId() == null) { + if (isRootNode(value) || value.getId() == null) { continue; } childrenMapping - .computeIfAbsent(value.getParentId(), ignore -> new LinkedHashMap<>()) - .put(value.getId(), value); + .computeIfAbsent(value.getParentId(), ignore -> new LinkedHashMap<>()) + .put(value.getId(), value); } } @@ -144,43 +150,40 @@ private Mono checkParentId() { } Set readyToCheck = thisTime - .values() - .stream() - .map(TreeSupportEntity::getParentId) - .filter(e -> !ObjectUtils.isEmpty(e) && !allData.containsKey(e)) - .collect(Collectors.toSet()); + .values() + .stream() + .map(TreeSupportEntity::getParentId) + .filter(e -> !ObjectUtils.isEmpty(e) && !allData.containsKey(e)) + .collect(Collectors.toSet()); if (readyToCheck.isEmpty()) { return Mono.empty(); } - return service - .createQuery() - .in("id", readyToCheck) - .fetch() - .doOnNext(e -> { - allData.put(e.getId(), e); - readyToCheck.remove(e.getId()); - }) - .then(Mono.fromRunnable(() -> { - if (!readyToCheck.isEmpty()) { - throw new ValidationException( + return queryById(readyToCheck) + .doOnNext(e -> { + allData.put(e.getId(), e); + readyToCheck.remove(e.getId()); + }) + .then(Mono.fromRunnable(() -> { + if (!readyToCheck.isEmpty()) { + throw new ValidationException( + "error.tree_entity_parent_id_not_exist", + Collections.singletonList( + new ValidationException.Detail( + "parentId", "error.tree_entity_parent_id_not_exist", - Collections.singletonList( - new ValidationException.Detail( - "parentId", - "error.tree_entity_parent_id_not_exist", - readyToCheck)) - ); - } - initChildren(); - })); + readyToCheck)) + ); + } + initChildren(); + })); } private void refactorPath() { Function> childGetter - = id -> childrenMapping - .getOrDefault(id, Collections.emptyMap()) - .values(); + = id -> childrenMapping + .getOrDefault(id, Collections.emptyMap()) + .values(); for (E data : thisTime.values()) { E old = data.getId() == null ? null : oldData.get(data.getId()); @@ -200,7 +203,7 @@ private void refactorPath() { }; //变更到了顶级节点 - if (service.isRootNode(data)) { + if (isRootNode(data)) { data.setPath(RandomUtil.randomChar(4)); this.refactorChildPath(old.getId(), data.getPath(), childConsumer); //重新保存所有子节点 @@ -240,11 +243,11 @@ else if (parentId != null) { private void putChildToReadyToSave(Function> childGetter, E data) { childGetter - .apply(data.getId()) - .forEach(e -> { - readyToSave.put(e.getId(), e); - putChildToReadyToSave(childGetter, e); - }); + .apply(data.getId()) + .forEach(e -> { + readyToSave.put(e.getId(), e); + putChildToReadyToSave(childGetter, e); + }); } private void refactor(E e) { diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultJdbcExecutor.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultJdbcExecutor.java index abfb9eb58..bd6608c18 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultJdbcExecutor.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultJdbcExecutor.java @@ -1,5 +1,6 @@ package org.hswebframework.web.crud.sql; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.hswebframework.ezorm.rdb.executor.SqlRequest; import org.hswebframework.ezorm.rdb.executor.jdbc.JdbcSyncSqlExecutor; @@ -7,7 +8,10 @@ import org.hswebframework.web.api.crud.entity.TransactionManagers; import org.hswebframework.web.datasource.DataSourceHolder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.jdbc.datasource.DataSourceUtils; +import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; +import org.springframework.jdbc.support.SQLExceptionTranslator; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -25,6 +29,13 @@ public class DefaultJdbcExecutor extends JdbcSyncSqlExecutor { @Autowired private DataSource dataSource; + public DefaultJdbcExecutor() { + } + + public DefaultJdbcExecutor(DataSource dataSource) { + this.dataSource = dataSource; + } + protected String getDatasourceId() { return DataSourceHolder.switcher().datasource().current().orElse("default"); } @@ -33,8 +44,8 @@ protected String getDatasourceId() { public Connection getConnection(SqlRequest sqlRequest) { DataSource dataSource = DataSourceHolder.isDynamicDataSourceReady() ? - DataSourceHolder.currentDataSource().getNative() : - this.dataSource; + DataSourceHolder.currentDataSource().getNative() : + this.dataSource; Connection connection = DataSourceUtils.getConnection(dataSource); boolean isConnectionTransactional = DataSourceUtils.isConnectionTransactional(connection, dataSource); if (log.isDebugEnabled()) { @@ -50,8 +61,8 @@ public void releaseConnection(Connection connection, SqlRequest sqlRequest) { } try { DataSource dataSource = DataSourceHolder.isDynamicDataSourceReady() ? - DataSourceHolder.currentDataSource().getNative() : - this.dataSource; + DataSourceHolder.currentDataSource().getNative() : + this.dataSource; DataSourceUtils.doReleaseConnection(connection, dataSource); } catch (SQLException e) { log.error(e.getMessage(), e); diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultJdbcReactiveExecutor.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultJdbcReactiveExecutor.java index 7e05edfdd..2e7a17ad9 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultJdbcReactiveExecutor.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultJdbcReactiveExecutor.java @@ -24,14 +24,23 @@ public class DefaultJdbcReactiveExecutor extends JdbcReactiveSqlExecutor { @Autowired private DataSource dataSource; + @Deprecated + public DefaultJdbcReactiveExecutor() { + + } + + public DefaultJdbcReactiveExecutor(DataSource dataSource) { + this.dataSource = dataSource; + } + protected String getDatasourceId() { return DataSourceHolder.switcher().datasource().current().orElse("default"); } private Tuple2 getDataSourceAndConnection() { DataSource dataSource = DataSourceHolder.isDynamicDataSourceReady() ? - DataSourceHolder.currentDataSource().getNative() : - this.dataSource; + DataSourceHolder.currentDataSource().getNative() : + this.dataSource; Connection connection = DataSourceUtils.getConnection(dataSource); boolean isConnectionTransactional = DataSourceUtils.isConnectionTransactional(connection, dataSource); if (log.isDebugEnabled()) { @@ -44,56 +53,56 @@ private Tuple2 getDataSourceAndConnection() { @Override public Mono getConnection() { return Mono - .using( - this::getDataSourceAndConnection - , - tp2 -> Mono.just(tp2.getT2()), - tp2 -> DataSourceUtils.releaseConnection(tp2.getT2(), tp2.getT1()), - false - ); + .using( + this::getDataSourceAndConnection + , + tp2 -> Mono.just(tp2.getT2()), + tp2 -> DataSourceUtils.releaseConnection(tp2.getT2(), tp2.getT1()), + false + ); } @Override protected Flux doInConnection(Function> handler) { return Flux .using(this::getDataSourceAndConnection, - tp2 -> handler.apply(tp2.getT2()), - tp2 -> DataSourceUtils.releaseConnection(tp2.getT2(), tp2.getT1()) + tp2 -> handler.apply(tp2.getT2()), + tp2 -> DataSourceUtils.releaseConnection(tp2.getT2(), tp2.getT1()) ); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager,readOnly = true) + @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager, readOnly = true) public Flux select(String sql, ResultWrapper wrapper) { - return super.select(sql,wrapper); + return super.select(sql, wrapper); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager,rollbackFor = Throwable.class) + @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager, rollbackFor = Throwable.class) public Mono update(Publisher request) { return super.update(request); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager,rollbackFor = Throwable.class) + @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager, rollbackFor = Throwable.class) public Mono update(String sql, Object... args) { - return super.update(sql,args); + return super.update(sql, args); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager,rollbackFor = Throwable.class) + @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager, rollbackFor = Throwable.class) public Mono update(SqlRequest request) { return super.update(request); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager,rollbackFor = Throwable.class) + @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager, rollbackFor = Throwable.class) public Mono execute(Publisher request) { return super.execute(request); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager,rollbackFor = Throwable.class) + @Transactional(transactionManager = TransactionManagers.jdbcTransactionManager, rollbackFor = Throwable.class) public Mono execute(SqlRequest request) { return super.execute(request); } diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultR2dbcExecutor.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultR2dbcExecutor.java index d7f7a1047..616b6f2e5 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultR2dbcExecutor.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/sql/DefaultR2dbcExecutor.java @@ -146,31 +146,31 @@ protected void releaseConnection(SignalType type, Connection connection) { } @Override - @Transactional(propagation = Propagation.REQUIRES_NEW, transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW, transactionManager = TransactionManagers.reactiveTransactionManager) public Mono execute(SqlRequest request) { return super.execute(request); } @Override - @Transactional(propagation = Propagation.REQUIRES_NEW, transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW, transactionManager = TransactionManagers.reactiveTransactionManager) public Mono execute(Publisher request) { return super.execute(request); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) public Mono update(Publisher request) { return super.update(request); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) public Mono update(SqlRequest request) { return super.update(request); } @Override - @Transactional(transactionManager = TransactionManagers.reactiveTransactionManager) + @Transactional(rollbackFor = Throwable.class, transactionManager = TransactionManagers.reactiveTransactionManager) public Mono update(String sql, Object... args) { return super.update(sql, args); } diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonErrorControllerAdvice.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonErrorControllerAdvice.java index 43052d28e..bd97d0ef7 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonErrorControllerAdvice.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonErrorControllerAdvice.java @@ -13,6 +13,8 @@ import org.hswebframework.web.i18n.LocaleUtils; import org.hswebframework.web.logger.ReactiveLogger; import org.springframework.core.annotation.Order; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DuplicateKeyException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.TransactionException; @@ -176,7 +178,7 @@ private Mono>> handleBindingRes @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public Mono> handleException(jakarta.validation.ValidationException e) { - return Mono.just(ResponseMessage.error(400, CodeConstants.Error.illegal_argument, e.getMessage())); + return Mono.just(ResponseMessage.error(400, CodeConstants.Error.illegal_argument, e.getLocalizedMessage())); } @ExceptionHandler @@ -196,7 +198,7 @@ public Mono> handleException(RuntimeException e) { log.warn(e.getLocalizedMessage(), e); return LocaleUtils .resolveMessageReactive("error.internal_server_error") - .map(msg -> ResponseMessage.error(500, "internal_server_error", msg)); + .map(msg -> ResponseMessage.error(500, CodeConstants.Error.internal_server_error, msg)); } @ExceptionHandler @@ -206,7 +208,7 @@ public Mono> handleException(NullPointerException e) { return LocaleUtils .resolveMessageReactive("error.internal_server_error") - .map(msg -> ResponseMessage.error(500, "internal_server_error", msg)); + .map(msg -> ResponseMessage.error(500, CodeConstants.Error.internal_server_error, msg)); } @ExceptionHandler @@ -292,4 +294,21 @@ public Mono> handleException(I18nSupportException e) { .map(msg -> ResponseMessage.error(400, e.getI18nCode(), msg)); } + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Mono> handleException(DataAccessException e) { + return LocaleUtils + .resolveMessageReactive("error.data_access_failed") + .map(msg -> ResponseMessage.error(400, "data_access_failed", msg)); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Mono> handleException(DuplicateKeyException e) { + return LocaleUtils + .resolveMessageReactive("error.duplicate_key") + .map(msg -> ResponseMessage.error(400, "duplicate_key", msg)); + } + + } diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebMvcErrorControllerAdvice.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebMvcErrorControllerAdvice.java index 3feb580ee..2f6de6895 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebMvcErrorControllerAdvice.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebMvcErrorControllerAdvice.java @@ -13,7 +13,10 @@ import org.hswebframework.web.i18n.LocaleUtils; import org.hswebframework.web.logger.ReactiveLogger; import org.springframework.core.annotation.Order; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DuplicateKeyException; import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -28,6 +31,7 @@ import reactor.core.publisher.Mono; import jakarta.validation.ConstraintViolationException; + import java.util.List; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; @@ -39,7 +43,7 @@ public class CommonWebMvcErrorControllerAdvice { private String resolveMessage(Throwable e) { if (e instanceof I18nSupportException) { - return LocaleUtils.resolveMessage(((I18nSupportException) e).getI18nCode()); + return LocaleUtils.resolveMessage(((I18nSupportException) e).getI18nCode(),((I18nSupportException) e).getArgs()); } return e.getMessage() == null ? null : LocaleUtils.resolveMessage(e.getMessage()); } @@ -63,8 +67,8 @@ public ResponseMessage handleException(UnsupportedOperationException e) @ResponseStatus(HttpStatus.UNAUTHORIZED) public ResponseMessage handleException(UnAuthorizedException e) { return ResponseMessage - .error(401, CodeConstants.Error.unauthorized, resolveMessage(e)) - .result(e.getState()); + .error(401, CodeConstants.Error.unauthorized, resolveMessage(e)) + .result(e.getState()); } @@ -85,9 +89,8 @@ public ResponseMessage handleException(NotFoundException e) { public ResponseMessage> handleException(ValidationException e) { return ResponseMessage - .>error(400, CodeConstants.Error.illegal_argument, resolveMessage(e)) - .result(e.getDetails()) - ; + .>error(400, CodeConstants.Error.illegal_argument, resolveMessage(e)) + .result(e.getDetails()); } @ExceptionHandler @@ -100,24 +103,24 @@ public ResponseMessage> handleException(Constra @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseMessage> handleException(BindException e) { return handleException(new ValidationException(e.getMessage(), e - .getBindingResult().getAllErrors() - .stream() - .filter(FieldError.class::isInstance) - .map(FieldError.class::cast) - .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(), null)) - .collect(Collectors.toList()))); + .getBindingResult().getAllErrors() + .stream() + .filter(FieldError.class::isInstance) + .map(FieldError.class::cast) + .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(), null)) + .collect(Collectors.toList()))); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseMessage> handleException(WebExchangeBindException e) { return handleException(new ValidationException(e.getMessage(), e - .getBindingResult().getAllErrors() - .stream() - .filter(FieldError.class::isInstance) - .map(FieldError.class::cast) - .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(), null)) - .collect(Collectors.toList()))); + .getBindingResult().getAllErrors() + .stream() + .filter(FieldError.class::isInstance) + .map(FieldError.class::cast) + .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(), null)) + .collect(Collectors.toList()))); } @@ -125,18 +128,18 @@ public ResponseMessage> handleException(WebExch @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseMessage> handleException(MethodArgumentNotValidException e) { return handleException(new ValidationException(e.getMessage(), e - .getBindingResult().getAllErrors() - .stream() - .filter(FieldError.class::isInstance) - .map(FieldError.class::cast) - .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(), null)) - .collect(Collectors.toList()))); + .getBindingResult().getAllErrors() + .stream() + .filter(FieldError.class::isInstance) + .map(FieldError.class::cast) + .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(), null)) + .collect(Collectors.toList()))); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseMessage handleException(jakarta.validation.ValidationException e) { - return ResponseMessage.error(400, CodeConstants.Error.illegal_argument, e.getMessage()); + return ResponseMessage.error(400, CodeConstants.Error.illegal_argument, e.getLocalizedMessage()); } @ExceptionHandler @@ -150,7 +153,18 @@ public ResponseMessage handleException(TimeoutException e) { @Order public ResponseMessage handleException(RuntimeException e) { log.warn(e.getLocalizedMessage(), e); - return ResponseMessage.error(resolveMessage(e)); + return ResponseMessage.error(CodeConstants.Error.internal_server_error, + LocaleUtils.resolveMessage("error.internal_server_error")); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + @Order + public ResponseMessage handleException(HttpMessageNotReadableException e) { + return ResponseMessage + .error(400, + "missing_request_body", + LocaleUtils.resolveMessage("error.missing_request_body")); } @ExceptionHandler @@ -182,8 +196,8 @@ public ResponseMessage handleException(UnsupportedMediaTypeStatusExcepti log.warn(e.getLocalizedMessage(), e); return ResponseMessage - .error(415, "unsupported_media_type", LocaleUtils.resolveMessage("error.unsupported_media_type")) - .result(e.getSupportedMediaTypes()); + .error(415, "unsupported_media_type", LocaleUtils.resolveMessage("error.unsupported_media_type")) + .result(e.getSupportedMediaTypes()); } @ExceptionHandler @@ -192,9 +206,9 @@ public ResponseMessage handleException(NotAcceptableStatusException e) { log.warn(e.getLocalizedMessage(), e); return ResponseMessage - .error(406, "not_acceptable_media_type", LocaleUtils - .resolveMessage("error.not_acceptable_media_type")) - .result(e.getSupportedMediaTypes()); + .error(406, "not_acceptable_media_type", LocaleUtils + .resolveMessage("error.not_acceptable_media_type")) + .result(e.getSupportedMediaTypes()); } @ExceptionHandler @@ -203,8 +217,8 @@ public ResponseMessage handleException(MethodNotAllowedException e) { log.warn(e.getLocalizedMessage(), e); return ResponseMessage - .error(406, "method_not_allowed", LocaleUtils.resolveMessage("error.method_not_allowed")) - .result(e.getSupportedMethods()); + .error(406, "method_not_allowed", LocaleUtils.resolveMessage("error.method_not_allowed")) + .result(e.getSupportedMethods()); } @@ -220,7 +234,7 @@ public ResponseMessage> handleException(ServerW } while (exception != null && exception != e); if (exception == null) { - return ResponseMessage.error(400, CodeConstants.Error.illegal_argument, e.getMessage()); + return ResponseMessage.error(400, CodeConstants.Error.illegal_argument, e.getMessage()); } return ResponseMessage.error(400, CodeConstants.Error.illegal_argument, resolveMessage(exception)); } @@ -231,4 +245,20 @@ public ResponseMessage handleException(I18nSupportException e) { return ResponseMessage.error(400, e.getI18nCode(), resolveMessage(e)); } + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseMessage handleException(DataAccessException e){ + return ResponseMessage.error(400, + "data_access_failed", + LocaleUtils.resolveMessage("error.data_access_failed")); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseMessage handleException(DuplicateKeyException e){ + return ResponseMessage.error(400, + "duplicate_key", + LocaleUtils.resolveMessage("error.duplicate_key")); + } + } diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/TreeServiceQueryController.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/TreeServiceQueryController.java new file mode 100644 index 000000000..dfee37e92 --- /dev/null +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/TreeServiceQueryController.java @@ -0,0 +1,67 @@ +package org.hswebframework.web.crud.web; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import org.hswebframework.web.api.crud.entity.QueryOperation; +import org.hswebframework.web.api.crud.entity.QueryParamEntity; +import org.hswebframework.web.api.crud.entity.TreeSortSupportEntity; +import org.hswebframework.web.authorization.annotation.Authorize; +import org.hswebframework.web.authorization.annotation.QueryAction; +import org.hswebframework.web.crud.service.ReactiveTreeSortEntityService; +import org.hswebframework.web.crud.service.TreeSortEntityService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.List; + +public interface TreeServiceQueryController, K> { + + @Authorize(ignore = true) + TreeSortEntityService getService(); + + @GetMapping("/_query/tree") + @QueryAction + @QueryOperation(summary = "使用GET动态查询并返回树形结构") + default List findAllTree(@Parameter(hidden = true) QueryParamEntity param) { + return getService().queryResultToTree(param); + } + + @GetMapping("/_query/_children") + @QueryAction + @QueryOperation(summary = "使用GET动态查询并返回子节点数据") + default List findAllChildren(@Parameter(hidden = true) QueryParamEntity param) { + return getService().queryIncludeChildren(param); + } + + @GetMapping("/_query/_children/tree") + @QueryAction + @QueryOperation(summary = "使用GET动态查询并返回子节点树形结构数据") + default List findAllChildrenTree(@Parameter(hidden = true) QueryParamEntity param) { + return getService().queryIncludeChildrenTree(param); + } + + @PostMapping("/_query/tree") + @QueryAction + @Operation(summary = "使用POST动态查询并返回树形结构") + default List findAllTreePost(@RequestBody QueryParamEntity param) { + return getService().queryResultToTree(param); + } + + @PostMapping("/_query/_children") + @QueryAction + @Operation(summary = "使用POST动态查询并返回子节点数据") + default List findAllChildrenPost(@RequestBody QueryParamEntity param) { + return getService().queryIncludeChildren(param); + } + + @PostMapping("/_query/_children/tree") + @QueryAction + @Operation(summary = "使用POST动态查询并返回子节点树形结构数据") + default List findAllChildrenTreePost(@RequestBody QueryParamEntity param) { + return getService().queryIncludeChildrenTree(param); + } + +} diff --git a/hsweb-commons/hsweb-commons-crud/src/main/resources/i18n/commons/messages_en.properties b/hsweb-commons/hsweb-commons-crud/src/main/resources/i18n/commons/messages_en.properties index ca9a3d786..f5293f979 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/resources/i18n/commons/messages_en.properties +++ b/hsweb-commons/hsweb-commons-crud/src/main/resources/i18n/commons/messages_en.properties @@ -8,4 +8,7 @@ error.tree_entity_cyclic_dependency=Cannot modify parent node as oneself or one' error.tree_entity_parent_id_not_exist=Parent node does not exist or has been deleted error.resource_not_found=Resource not found error.data.find.not_found=Data not found -error.sql.prepare.failed.IndexOutOfBoundsException=Execute SQL failed, try check config: `easyorm.dialect`. \ No newline at end of file +error.sql.prepare.failed.IndexOutOfBoundsException=Execute SQL failed, try check config: `easyorm.dialect`. +error.missing_request_body=Required request body is missing +error.duplicate_key=Duplicate Data +error.data_access_failed=Data Access Failed \ No newline at end of file diff --git a/hsweb-commons/hsweb-commons-crud/src/main/resources/i18n/commons/messages_zh.properties b/hsweb-commons/hsweb-commons-crud/src/main/resources/i18n/commons/messages_zh.properties index 28b2e85ae..c0ee2ebd4 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/resources/i18n/commons/messages_zh.properties +++ b/hsweb-commons/hsweb-commons-crud/src/main/resources/i18n/commons/messages_zh.properties @@ -7,4 +7,7 @@ error.internal_server_error=\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF error.tree_entity_cyclic_dependency=\u4E0D\u80FD\u4FEE\u6539\u7236\u8282\u70B9\u4E3A\u81EA\u5DF1\u6216\u8005\u81EA\u5DF1\u7684\u5B50\u8282\u70B9 error.tree_entity_parent_id_not_exist=\u7236\u8282\u70B9\u4E0D\u5B58\u5728\u6216\u5DF2\u88AB\u5220\u9664 error.data.find.not_found=\u6570\u636E\u4E0D\u5B58\u5728 -error.sql.prepare.failed.IndexOutOfBoundsException=SQL\u6267\u884C\u5931\u8D25,\u8BF7\u5C1D\u8BD5\u68C0\u67E5`easyorm.dialect`\u914D\u7F6E. \ No newline at end of file +error.sql.prepare.failed.IndexOutOfBoundsException=SQL\u6267\u884C\u5931\u8D25,\u8BF7\u5C1D\u8BD5\u68C0\u67E5`easyorm.dialect`\u914D\u7F6E. +error.missing_request_body=\u8BF7\u6C42\u4F53\u7F3A\u5931 +error.duplicate_key=\u5DF2\u5B58\u5728\u91CD\u590D\u7684\u6570\u636E +error.data_access_failed=\u8BBF\u95EE\u6570\u636E\u5931\u8D25 \ No newline at end of file diff --git a/hsweb-core/src/main/java/org/hswebframework/web/CodeConstants.java b/hsweb-core/src/main/java/org/hswebframework/web/CodeConstants.java index 388b5df05..310c32cd2 100644 --- a/hsweb-core/src/main/java/org/hswebframework/web/CodeConstants.java +++ b/hsweb-core/src/main/java/org/hswebframework/web/CodeConstants.java @@ -12,6 +12,8 @@ interface Error { String unauthorized = "unauthorized"; String not_found="not_found"; + + String internal_server_error="internal_server_error"; } } diff --git a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/resources/i18n/authentication-default/messages_en.properties b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/resources/i18n/authentication-default/messages_en.properties index b2f495f82..65ff7fc0b 100644 --- a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/resources/i18n/authentication-default/messages_en.properties +++ b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/resources/i18n/authentication-default/messages_en.properties @@ -1,3 +1,2 @@ -error.duplicate_key=Duplicate Data error.user_already_exists=User already exists error.user_not_found=The user does not exist or the id does not meet the rule \ No newline at end of file diff --git a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/resources/i18n/authentication-default/messages_zh.properties b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/resources/i18n/authentication-default/messages_zh.properties index 43724899f..84e18030c 100644 --- a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/resources/i18n/authentication-default/messages_zh.properties +++ b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/resources/i18n/authentication-default/messages_zh.properties @@ -1,3 +1,2 @@ -error.duplicate_key=已存在重复的数据 -error.user_already_exists=用户已存在 -error.user_not_found=用户不存在或ID不符合规则:[{0}] \ No newline at end of file +error.user_already_exists=\u7528\u6237\u5DF2\u5B58\u5728 +error.user_not_found=\u7528\u6237\u4E0D\u5B58\u5728\u6216ID\u4E0D\u7B26\u5408\u89C4\u5219:[{0}] \ No newline at end of file