Skip to content

Commit

Permalink
Merge pull request #49 from vnobo/dev
Browse files Browse the repository at this point in the history
✨ feat(`BaseEvent.java`): Add detailed JavaDoc comments and improve event handling documentation. ♻️ refactor(`UserEvent.java`): Remove redundant `Optional` checks and simplify event methods. ✨ feat(`BaseEvent.java`): Add detailed JavaDoc comments and improve event handling documentation.
♻️ refactor(`UserEvent.java`): Remove redundant `Optional` checks and simplify event methods.

✨ feat(`GroupAuthoritiesRepository`): Update JavaDoc comments for clarity and consistency. ♻️ refactor(`UsersRepository`): Remove unused `changePassword` method and related imports. ✨ feat(`GroupAuthoritiesRepository`): Update JavaDoc comments for clarity and consistency.
♻️ refactor(`UsersRepository`): Remove unused `changePassword` method and related imports.

✨ feat(`users.component.html`): Replace index with `item.id` in user table. ♻️ refactor(`TenantMembersService.java`): Rename `onUserEvent` to `onUserDeletedEvent` and update condition. ♻️ refactor(`UsersService.java`): Add `@Transactional` and refactor event handling in `delete` method. ♻️ refactor(`BaseEvent.java`): Simplify event handling and add `ResolvableType` support. ♻️ refactor(`UserAuthoritiesService.java`): Rename `onUserEvent` to `onUserDeletedEvent` and update condition. ✨ feat(`user.types.ts`): Add `id` field to `User` interface. ♻️ refactor(`GroupMembersService.java`): Rename `onUserEvent` to `onUserDeletedEvent` and update condition. ♻️ refactor(`LoggerEvent.java`): Remove redundant methods and simplify event handling. ✨ feat(`users.component.html`): Replace index with `item.id` in user table.
♻️ refactor(`TenantMembersService.java`): Rename `onUserEvent` to `onUserDeletedEvent` and update condition.
♻️ refactor(`UsersService.java`): Add `@Transactional` and refactor event handling in `delete` method.
♻️ refactor(`BaseEvent.java`): Simplify event handling and add `ResolvableType` support.
♻️ refactor(`UserAuthoritiesService.java`): Rename `onUserEvent` to `onUserDeletedEvent` and update condition.
✨ feat(`user.types.ts`): Add `id` field to `User` interface.
♻️ refactor(`GroupMembersService.java`): Rename `onUserEvent` to `onUserDeletedEvent` and update condition.
♻️ refactor(`LoggerEvent.java`): Remove redundant methods and simplify event handling.

✨ feat(`TenantMembersService`): Add event listener for user deletion to clear tenant member cache. ✨ feat(`UsersService`): Refactor delete method to publish user deletion event and clear cache. ✨ feat(`TenantMembersRepository`): Add method to delete tenant members by user code. ✨ feat(`UserAuthoritiesService`): Add event listener for user deletion to clear user authorities cache. ✨ feat(`UserAuthoritiesRepository`): Add method to delete user authorities by user code. ✨ feat(`GroupMembersRepository`): Add method to delete group members by user code. ✨ feat(`GroupMembersService`): Add event listener for user deletion to clear group member cache. ♻️ refactor(`User`): Mark account-related fields as read-only. ♻️ refactor(`UserRes`): Simplify response structure and remove unused overrides. ✨ feat(`UserEvent`): Refactor event handling with explicit insert, save, and delete methods. ✨ feat(`TenantMembersService`): Add event listener for user deletion to clear tenant member cache.
✨ feat(`UsersService`): Refactor delete method to publish user deletion event and clear cache.
✨ feat(`TenantMembersRepository`): Add method to delete tenant members by user code.
✨ feat(`UserAuthoritiesService`): Add event listener for user deletion to clear user authorities cache.
✨ feat(`UserAuthoritiesRepository`): Add method to delete user authorities by user code.
✨ feat(`GroupMembersRepository`): Add method to delete group members by user code.
✨ feat(`GroupMembersService`): Add event listener for user deletion to clear group member cache.
♻️ refactor(`User`): Mark account-related fields as read-only.
♻️ refactor(`UserRes`): Simplify response structure and remove unused overrides.
✨ feat(`UserEvent`): Refactor event handling with explicit insert, save, and delete methods.

✨ feat(`LoggerEvent.java`): Add new `LoggerEvent` class for handling logger events. ✨ feat(`LoggerEvent.java`): Add new `LoggerEvent` class for handling logger events.

✨ feat(`LoggersService`): Refactor event handling for logger operations. ✨ feat(`LoggerFilter`): Update event publishing to use `LoggerEvent`. ✨ feat(`LoggersService`): Refactor event handling for logger operations.
✨ feat(`LoggerFilter`): Update event publishing to use `LoggerEvent`.

✨ feat(`BaseEvent.java`): Add new `BaseEvent` class for handling entity events. ♻️ refactor(`ContextUtils.java`): Update `eventPublisher` method to accept `BaseEvent` type. 🔧 refactor(`AbstractEntity.java`): Reorder fields and add `@JsonIgnore` annotations for search and query methods. ✨ feat(`BaseEvent.java`): Add new `BaseEvent` class for handling entity events.
♻️ refactor(`ContextUtils.java`): Update `eventPublisher` method to accept `BaseEvent` type.
🔧 refactor(`AbstractEntity.java`): Reorder fields and add `@JsonIgnore` annotations for search and query methods.
  • Loading branch information
vnobo authored Feb 25, 2025
2 parents a04c505 + 2c5820a commit ecb4dd6
Show file tree
Hide file tree
Showing 60 changed files with 1,256 additions and 935 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**
!**/src/test/**
*.log

### STS ###
.apt_generated
Expand Down
8 changes: 7 additions & 1 deletion boot/platform/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive")

implementation 'org.postgresql:r2dbc-postgresql'
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
implementation("org.postgresql:r2dbc-postgresql")

runtimeOnly 'org.postgresql:postgresql'
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-database-postgresql'
implementation 'org.springframework:spring-jdbc'

testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'io.projectreactor:reactor-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* This class serves as the primary class to launch the Spring Boot application.
* It is annotated with `@SpringBootApplication`, which is a convenience annotation
* that includes `@Configuration`, `@EnableAutoConfiguration`, and `@ComponentScan`.
* These annotations together configure the application context, enable auto-configuration
* These annotations together configure the application context, enable autoconfiguration
* of Spring features, and scan for Spring components in the package where this class is located
* and its sub-packages.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package com.plate.boot.commons.base;

import com.plate.boot.commons.utils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import com.plate.boot.commons.utils.ContextUtils;
import com.plate.boot.commons.utils.DatabaseUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.data.relational.core.query.Query;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.util.ObjectUtils;
import org.springframework.util.unit.DataSize;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;


/**
Expand All @@ -34,29 +38,46 @@
* <p>Subclasses of AbstractDatabase can extend this functionality to provide specific database
* interactions tailored to their application's needs.
*/
public abstract class AbstractDatabase extends AbstractService {

@Log4j2
public abstract class AbstractCache implements InitializingBean {
/**
* The R2dbcEntityTemplate instance used for executing reactive database operations.
* This template facilitates interaction with the database, including from execution,
* entity conversion, and transaction management specifically tailored for R2DBC (Reactive Relational Database Connectivity).
* Cache instance used to store and retrieve data within the service.
* Initialized through the method with a cache name defaulting to the class name concatenated with ".cache".
* This cache is cleared upon initialization and is essential for providing temporary storage that accelerates data access.
*/
protected R2dbcEntityTemplate entityTemplate;
protected Cache cache;

/**
* Represents a client for interacting with the database, facilitating operations such as
* querying, updating, and managing data within the database. This field is initialized
* and configured typically during the setup or initialization phase of the hosting class,
* enabling seamless database access throughout the application.
* Invoked by the containing {@code BeanFactory} after it has set all bean properties
* and satisfied all dependencies for this bean. This method allows the bean instance
* to perform initialization only possible when all bean properties have been set
* and to throw an exception in the event of misconfiguration.
*
* <p>This implementation initializes the cache associated with this component,
* using the bean's class name concatenated with ".cache" as the cache identifier.
*/
protected DatabaseClient databaseClient;
@Override
public void afterPropertiesSet() {
this.cache = initializingCache(this.getClass().getName().concat(".cache"));
}

/**
* The R2DBC Converter instance used for converting between R2DBC Data types and domain-specific objects.
* This converter plays a crucial role in mapping from results to entity classes and vice versa,
* facilitating seamless interaction with the R2DBC database through the R2dbcEntityTemplate.
* Initializes the cache with the specified name.
* If the cache manager is set, it attempts to retrieve the cache from the manager;
* otherwise, it creates a new ConcurrentMapCache with the given name.
* After initialization, the cache is cleared and a debug log message is generated indicating
* the cache's class name and name.
*
* @param cacheName The name of the cache to initialize.
*/
protected R2dbcConverter r2dbcConverter;
public Cache initializingCache(String cacheName) {
var cache = Optional.ofNullable(ContextUtils.CACHE_MANAGER).map(manager -> manager.getCache(cacheName))
.orElse(new ConcurrentMapCache(cacheName));
cache.clear();
log.debug("Initializing provider [{}] cache names: {}",
cache.getNativeCache().getClass().getSimpleName(), cache.getName());
return cache;
}

/**
* Executes a database from with caching functionality.
Expand All @@ -70,9 +91,8 @@ public abstract class AbstractDatabase extends AbstractService {
* @return A {@link Flux} emitting the from results, potentially from cache if previously stored.
*/
protected <T> Flux<T> queryWithCache(Object key, Query query, Class<T> entityClass) {
Flux<T> source = this.entityTemplate.select(query, entityClass);
source = source.flatMapSequential(BeanUtils::serializeUserAuditor);
return queryWithCache(key, source).cache();
return queryWithCache(key, DatabaseUtils.query(query, entityClass))
.timeout(Duration.ofSeconds(10)).replay(3).refCount();
}

/**
Expand All @@ -90,13 +110,8 @@ protected <T> Flux<T> queryWithCache(Object key, Query query, Class<T> entityCla
*/
protected <T> Flux<T> queryWithCache(Object key, String sql,
Map<String, Object> bindParams, Class<T> entityClass) {
var executeSpec = this.databaseClient.sql(() -> sql);
executeSpec = executeSpec.bindValues(bindParams);
Flux<T> source = executeSpec
.map((row, rowMetadata) -> this.r2dbcConverter.read(entityClass, row, rowMetadata))
.all();
source = source.flatMapSequential(BeanUtils::serializeUserAuditor);
return queryWithCache(key, source).cache();
return queryWithCache(key, DatabaseUtils.query(sql, bindParams, entityClass))
.timeout(Duration.ofSeconds(10)).replay(3).refCount();
}

/**
Expand All @@ -116,7 +131,7 @@ protected <T> Flux<T> queryWithCache(Object key, Flux<T> sourceFlux) {
if (ObjectUtils.isEmpty(cacheData)) {
var sourceData = new ArrayList<T>();
return sourceFlux.doOnNext(sourceData::add)
.doAfterTerminate(() -> BeanUtils.cachePut(this.cache, cacheKey, sourceData));
.doAfterTerminate(() -> this.cachePut(cacheKey, sourceData));
}
return Flux.fromIterable(cacheData);
}
Expand All @@ -134,8 +149,7 @@ protected <T> Flux<T> queryWithCache(Object key, Flux<T> sourceFlux) {
* @return A {@link Mono} emitting the count of entities as a {@link Long}, potentially from cache.
*/
protected <T> Mono<Long> countWithCache(Object key, Query query, Class<T> entityClass) {
Mono<Long> source = this.entityTemplate.count(query, entityClass);
return countWithCache(key, source).cache();
return countWithCache(key, DatabaseUtils.count(query, entityClass)).timeout(Duration.ofSeconds(10));
}

/**
Expand All @@ -148,10 +162,7 @@ protected <T> Mono<Long> countWithCache(Object key, Query query, Class<T> entity
* @return A Mono emitting the count result, potentially fetched from cache or computed from the database.
*/
protected Mono<Long> countWithCache(Object key, String sql, Map<String, Object> bindParams) {
var executeSpec = this.databaseClient.sql(() -> sql);
executeSpec = executeSpec.bindValues(bindParams);
Mono<Long> source = executeSpec.mapValue(Long.class).first();
return countWithCache(key, source).cache();
return countWithCache(key, DatabaseUtils.count(sql, bindParams)).timeout(Duration.ofSeconds(10));
}

/**
Expand All @@ -171,35 +182,24 @@ protected Mono<Long> countWithCache(Object key, String sql, Map<String, Object>
protected Mono<Long> countWithCache(Object key, Mono<Long> sourceMono) {
String cacheKey = key + ":count";
Long cacheCount = this.cache.get(cacheKey, () -> null);
Mono<Long> source = sourceMono.doOnNext(count -> BeanUtils.cachePut(this.cache, cacheKey, count));
Mono<Long> source = sourceMono.doOnNext(count -> this.cachePut(cacheKey, count));
return Mono.justOrEmpty(cacheCount).switchIfEmpty(Mono.defer(() -> source));
}

/**
* Sets the R2dbcEntityTemplate instance to be used by this class for database operations.
* Inserts an object into the specified cache if its size does not exceed the maximum allowed in-memory size.
*
* @param entityTemplate The R2dbcEntityTemplate instance to inject.
* @param cacheKey The key under which the object will be stored in the cache.
* @param obj The object to be cached. Its size will be evaluated against the maximum allowed size.
* If the object exceeds the limit, it will not be cached and a warning will be logged.
*/
@Autowired
public void setEntityTemplate(R2dbcEntityTemplate entityTemplate) {
this.entityTemplate = entityTemplate;
}

/**
* Invoked by the containing {@code BeanFactory} after it has set all bean properties
* and satisfied all dependencies for this bean. This method allows the bean instance
* to perform initialization only possible when all bean properties have been set
* and to throw an exception in the event of misconfiguration.
*
* <p>In this implementation, the method sets up the {@code databaseClient} and
* {@code r2dbcConverter} by extracting them from the injected {@code entityTemplate}.
* It calls the superclass's {@code afterPropertiesSet} first to ensure any
* necessary setup in the parent class is also executed.
*/
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
this.databaseClient = this.entityTemplate.getDatabaseClient();
this.r2dbcConverter = this.entityTemplate.getConverter();
protected void cachePut(String cacheKey, Object obj) {
DataSize objectSize = DatabaseUtils.getBeanSize(obj);
if (objectSize.toBytes() > DatabaseUtils.MAX_IN_MEMORY_SIZE.toBytes()) {
log.warn("Object size is too large, Max memory size is {}, Object size is {}.",
DatabaseUtils.MAX_IN_MEMORY_SIZE, objectSize);
return;
}
this.cache.put(cacheKey, obj);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ public abstract class AbstractEntity<T> implements BaseEntity<T> {
@InsertOnlyProperty
protected UserAuditor creator;

/**
* Data entity create time, timestamp column
*/
@CreatedDate
@InsertOnlyProperty
protected LocalDateTime createdTime;

/**
* Data entity update operator
* use User.class code property
Expand All @@ -75,12 +82,6 @@ public abstract class AbstractEntity<T> implements BaseEntity<T> {
@LastModifiedDate
protected LocalDateTime updatedTime;

/**
* Data entity create time, timestamp column
*/
@CreatedDate
@InsertOnlyProperty
protected LocalDateTime createdTime;

/**
* Support from for json column
Expand All @@ -97,11 +98,17 @@ public abstract class AbstractEntity<T> implements BaseEntity<T> {
/**
* Support security code for sensitive data
*/
@JsonIgnore
@Transient
protected String securityCode;

@JsonIgnore
public T getId() {
return id;
public String getSearch() {
return search;
}

@JsonIgnore
public Map<String, Object> getQuery() {
return query;
}
}

This file was deleted.

Loading

0 comments on commit ecb4dd6

Please sign in to comment.