基本功能

This commit is contained in:
zhou-hao
2019-10-09 18:43:56 +08:00
parent b6033ba794
commit 063fc321e6
102 changed files with 1057 additions and 3604 deletions

View File

@@ -1,11 +0,0 @@
# 通用功能模块
实现通用CRUD功能,增删改查,直接继承之.
# 目录介绍
1. [hsweb-commons-controller](hsweb-commons-controller):通用springmvc控制器
1. [hsweb-commons-dao](hsweb-commons-dao):通用dao实现
1. [hsweb-commons-entity](hsweb-commons-entity):通用实体类
1. [hsweb-commons-service](hsweb-commons-service):通用服务类
1. [hsweb-commons-utils](hsweb-commons-utils):工具类
# 使用
[如何建一个增删改查功能](create-crud.md)

View File

@@ -1,505 +0,0 @@
# 使用通用CRUD 创建 dao,service,controller...
hsweb 按照功能分模块, 再将controller,service,dao等分为子模块.
以[hsweb-system-menu](../hsweb-system/hsweb-system-menu)为例,创建maven项目模块以及子模块.
## 模块结构
* hsweb-system-menu
- hsweb-system-menu-controller
- hsweb-system-menu-dao
- hsweb-system-menu-dao-api
- hsweb-system-menu-dao-mybatis
- hsweb-system-menu-entity
- hsweb-system-menu-model
- hsweb-system-menu-service
- hsweb-system-menu-service-api
- hsweb-system-menu-service-simple
- hsweb-system-menu-service-cloud
- hsweb-system-menu-starter
[使用idea创建时的常见问题](https://github.com/hs-web/hsweb-framework/issues/31)
## Entity
模块:hsweb-system-menu-entity
hsweb中的entity都为接口并提供了一个默认实现,例如 MenuEntity=>SimpleMenuEntity.
但是并不强制使用此方式创建entity. 可以只有类,不使用接口.
约定:
1. entity应该实现`Entity`接口
2. 有主键的entity应该实现`GenericEntity<PK>`接口
3. entity应该使用`EntityFactory`创建而不是new
4. 树形结构的entity,可以实现`TreeSortSupportEntity<PK>`
注: `PK`=主键
创建一个entity.
1. 引入maven依赖
```xml
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-entity</artifactId>
<version>${hsweb.version}</version>
</dependency>
```
2. 新建接口类:
```java
package org.hswebframework.web.entity.menu;
import org.hswebframework.web.commons.entity.GenericEntity;
import org.hswebframework.web.commons.entity.RecordCreationEntity;
public interface MenuEntity extends GenericEntity<String> {
String getName();
void setName(String remark);
String getUrl();
void setUrl(String url);
}
```
3. 新建默认实现类
```java
package org.hswebframework.web.entity.menu;
import org.hswebframework.web.commons.entity.GenericEntity;
import org.hswebframework.web.commons.entity.RecordCreationEntity;
public class SimpleMenuEntity implements MenuEntity {
private String name;
private String url;
public String getName(){
return this.name;
}
public void setName(String name){
this.name=name;
}
public String getUrl(){
return this.url;
}
public void setUrl(String url){
this.url=url;
}
}
```
注意: 默认实现类一般和接口在同一个包中,并且名称为Simple开头+接口名称.
因为默认的`EntityFactory`按照此约定来创建未指定特殊实现接口实现的实例.详见 [MapperEntityFactory](hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/factory/MapperEntityFactory.java)
## DAO
模块:hsweb-system-menu-dao
hsweb 目前提供了mybatis的通用dao实现,支持动态条件.
常用dao接口:
1. [InsertDao](hsweb-commons-dao/hsweb-commons-dao-api/src/main/java/org/hswebframework/web/dao/InsertDao.java) : 支持insert
2. [DeleteDao](hsweb-commons-dao/hsweb-commons-dao-api/src/main/java/org/hswebframework/web/dao/DeleteDao.java) : 支持根据主键删除
3. [DeleteByEntityDao](hsweb-commons-dao/hsweb-commons-dao-api/src/main/java/org/hswebframework/web/dao/dynamic/DeleteByEntityDao.java) : 支持根据实体删除(动态条件)
4. [QueryByEntityDao](hsweb-commons-dao/hsweb-commons-dao-api/src/main/java/org/hswebframework/web/dao/dynamic/QueryByEntityDao.java) : 支持根据实体查询(动态条件)
5. [UpdateByEntityDao](hsweb-commons-dao/hsweb-commons-dao-api/src/main/java/org/hswebframework/web/dao/dynamic/UpdateByEntityDao.java) : 支持根据实体更新(动态条件)
6. [CrudDao](hsweb-commons-dao/hsweb-commons-dao-api/src/main/java/org/hswebframework/web/dao/CrudDao.java) : 集上述dao于一体
增删改查功能继承 `CrudDao`即可.
1. 新建Dao接口
进入模块: hsweb-system-menu-dao-api 引入依赖
```xml
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-dao-api</artifactId>
<version>${hsweb.version}</version>
</dependency>
```
创建接口:
```java
package org.hswebframework.web.dao.menu;
import org.hswebframework.web.dao.CrudDao;
import org.hswebframework.web.entity.menu.MenuEntity;
public interface MenuDao extends CrudDao<MenuEntity, String> {
}
```
2. mybatis实现.
进入模块: hsweb-system-menu-dao-mybatis引入依赖
```xml
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-dao-mybatis</artifactId>
<version>${hsweb.version}</version>
</dependency>
```
hsweb依然使用xml的方式实现dao,xml建议放到resources目录下如: 'resources/org/hswebframework/web/dao/mybatis/mappers/menu'
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://www.mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.hswebframework.web.dao.menu.MenuDao">
<resultMap id="MenuResultMap" type="org.hswebframework.web.entity.menu.SimpleMenuEntity">
<id property="id" column="u_id" javaType="string" jdbcType="VARCHAR"/>
<result property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
<result property="url" column="url" javaType="String" jdbcType="VARCHAR"/>
</resultMap>
<!--用于动态生成sql所需的配置-->
<sql id="config">
<bind name="resultMapId" value="'MenuResultMap'"/>
<bind name="tableName" value="'s_menu'"/>
</sql>
<insert id="insert" parameterType="org.hswebframework.web.commons.entity.Entity">
<include refid="config"/>
<include refid="BasicMapper.buildInsertSql"/>
</insert>
<delete id="deleteByPk" parameterType="String">
<include refid="config"/>
<include refid="BasicMapper.switcher"/> <!--支持表切换-->
delete from ${_fullTableName} where u_id =#{id}
</delete>
<delete id="delete" parameterType="org.hswebframework.web.commons.entity.Entity">
<include refid="config"/>
<include refid="BasicMapper.buildDeleteSql"/>
</delete>
<update id="update" parameterType="org.hswebframework.web.commons.entity.Entity">
<include refid="config"/>
<include refid="BasicMapper.buildUpdateSql"/>
</update>
<select id="query" parameterType="org.hswebframework.web.commons.entity.Entity" resultMap="MenuResultMap">
<include refid="config"/>
<include refid="BasicMapper.buildSelectSql"/>
</select>
<select id="count" parameterType="org.hswebframework.web.commons.entity.Entity" resultType="int">
<include refid="config"/>
<include refid="BasicMapper.buildTotalSql"/>
</select>
</mapper>
```
注意: 目前动态条件参数仅支持: `QueryParamEntity`,`UpdateParamEntity`,`DeleteParamEntity`
## Service
模块: hsweb-system-menu-service
通用service中,很多实现使用接口(java8的default),以实现多继承
常用通用service接口:
1. [InsertService](hsweb-commons-service/hsweb-commons-service-api/src/main/java/org/hswebframework/web/service/InsertService.java):增
2. [DeleteService](hsweb-commons-service/hsweb-commons-service-api/src/main/java/org/hswebframework/web/service/DeleteService.java):删
3. [UpdateService](hsweb-commons-service/hsweb-commons-service-api/src/main/java/org/hswebframework/web/service/UpdateService.java):改
4. [QueryService](hsweb-commons-service/hsweb-commons-service-api/src/main/java/org/hswebframework/web/service/QueryService.java):查
5. [QueryByEntityService](hsweb-commons-service/hsweb-commons-service-api/src/main/java/org/hswebframework/web/service/QueryByEntityService.java):动态查
6. [CrudService](hsweb-commons-service/hsweb-commons-service-api/src/main/java/org/hswebframework/web/service/CrudService.java): 合以上为一
7. [TreeService](hsweb-commons-service/hsweb-commons-service-api/src/main/java/org/hswebframework/web/service/TreeService.java):树结构(`TreeSupportEntity`)常用操作服务
常用通用service实现:
1. [GenericService](hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/GenericService.java): 通用服务,提供增删改查,dsl方式操作接口.
2. [AbstractService](hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/AbstractService.java):提供验证器等服务类常用操作,实现`CreateEntityService`.
3. [AbstractTreeSortService](hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/AbstractTreeSortService.java):同上,对树形结构操作.实现`TreeService`.
4. [GenericEntityService](hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/GenericEntityService.java): 通用服务,实现对`GenericEntity`的增删改查操作
5. [DefaultDSLDeleteService](hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/DefaultDSLDeleteService.java): dsl方式删除
6. [DefaultDSLQueryService](hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/DefaultDSLQueryService.java): dsl方式查询
7. [DefaultDSLUpdateService](hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/DefaultDSLUpdateService.java): dsl方式更新
DSL方式操作使用[easy-orm](https://github.com/hs-web/hsweb-easy-orm)来构建动态查询参数,[使用方法](hsweb-commons-service/hsweb-commons-service-simple/README.md).
1. 创建service接口
进入模块: hsweb-system-menu-service-api
引入依赖:
```xml
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-service-api</artifactId>
<version>${hsweb.version}</version>
</dependency>
```
创建接口类:
```java
package org.hswebframework.web.service.menu;
import org.hswebframework.web.entity.menu.MenuEntity;
import org.hswebframework.web.service.CrudService;
import java.util.List;
public interface MenuService
//泛型<实体类型,主键类型>
extends CrudService<MenuEntity, String> {
}
```
进入模块:hsweb-system-menu-service-simple
引入依赖:
```xml
<!--上面创建的service接口模块-->
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-system-menu-service-api</artifactId>
<version>${hsweb.version}</version>
</dependency>
<!--通用service实现模块-->
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-service-simple</artifactId>
<version>${hsweb.version}</version>
</dependency>
```
创建实现类
```java
package org.hswebframework.web.service.menu.simple;
import org.hswebframework.web.dao.menu.MenuDao;
import org.hswebframework.web.entity.menu.MenuEntity;
import org.hswebframework.web.id.IDGenerator;
import org.hswebframework.web.service.GenericEntityService;
import org.hswebframework.web.service.menu.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("menuService")
public class SimpleMenuService
//泛型<实体类型,主键类型>
extends GenericEntityService<MenuEntity, String>
implements MenuService {
private MenuDao menuDao;
//ID生成器,通用服务的ID都使用主动ID生成,不使用orm或者数据库自动生成
//可通过自己实现IDGenerator进行自定义生成
@Override
protected IDGenerator<String> getIDGenerator() {
return IDGenerator.MD5;
}
// 实现CrudDao接口的类
@Override
public MenuDao getDao() {
return menuDao;
}
//注入dao实现
@Autowired
public void setMenuDao(MenuDao menuDao) {
this.menuDao = menuDao;
}
}
```
## controller
模块: hsweb-system-menu-controller
常用通用controller接口
1. [CreateController](hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/CreateController.java) : 增
2. [DeleteController](hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/DeleteController.java) : 删
3. [UpdateController](hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/UpdateController.java) : 改
4. [QueryController](hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/QueryController.java) : 查
5. [CrudController](hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/CrudController.java) : 增删改查
泛型: E, PK, Q extends Entity, M => Entity,主键,动态查询实体类,Model.
增改时,使用Model接收参数;查询时,使用Q接受参数,使用Model作为响应.
注意: Model 并不是必须,如果不使用单独的Model,可使用 `SimpleCrudController`. 通用controller使用restful方式提供接口
响应结果统一为`ResponseMessage<T>`. [更多使用方法](hsweb-commons-controller/README.md)
1. 创建Controller
进入模块:hsweb-system-menu-controller
引入依赖:
```xml
<!--只依赖service接口-->
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-system-menu-service-api</artifactId>
<version>${hsweb.version}</version>
</dependency>
<!--通用Controller-->
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-controller</artifactId>
<version>${hsweb.version}</version>
</dependency>
```
创建类:
```java
package org.hswebframework.web.controller.menu;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.Role;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.commons.entity.TreeSupportEntity;
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
import org.hswebframework.web.controller.GenericEntityController;
import org.hswebframework.web.controller.message.ResponseMessage;
import org.hswebframework.web.entity.menu.MenuEntity;
import org.hswebframework.web.logging.AccessLogger;
import org.hswebframework.web.service.menu.MenuGroupService;
import org.hswebframework.web.service.menu.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.hswebframework.web.controller.message.ResponseMessage.ok;
@RestController
@RequestMapping("${hsweb.web.mappings.menu:menu}") //默认/menu
@Authorize(permission = "menu") // menu权限
@Api(value = "menu-manager", description = "系统菜单管理") //swagger
public class MenuController implements
//泛型 <实体,主键,动态查询实体(目前只支持此类型),模型>
//等同 SimpleGenericEntityController<MenuEntity, String, QueryParamEntity>
GenericEntityController<MenuEntity, String, QueryParamEntity, MenuEntity> {
private MenuService menuService;
@Autowired
public void setMenuService(MenuService menuService) {
this.menuService = menuService;
}
@Override
public MenuService getService() {
return menuService;
}
}
```
## starter
模块: hsweb-system-menu-starter
模块整合,自动配置.
1. 引入依赖
```xml
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-system-menu-service-simple</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-system-menu-dao-mybatis</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-system-menu-controller</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-spring-boot-starter</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-tests</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!--其他依赖-->
```
2. 新建文件: `resources/hsweb-starter.js`,此脚本在模块第一次使用或者更新版本的时候被执行
内容如下:
```js
//组件信息
var info = {
groupId: "org.hsweb",
artifactId: "hsweb-system-menu",
version: "3.0",
website: "https://github.com/hs-web/hsweb-framework/tree/master/hsweb-system/hsweb-system-menu",
author: "zh.sqy@qq.com",
comment: "菜单"
};
//版本更新信息
var versions = [
// {
// version: "3.0.1",
// upgrade: function (context) {
// //如果已安装3.0.0准备使用3.0.1,将执行此代码
// java.lang.System.out.println("更新到3.0.2了");
// }
// }
];
var JDBCType = java.sql.JDBCType;
function install(context) {
//当首次使用此模块的时候,执行创建数据库
var database = context.database;
database.createOrAlter("s_menu")
.addColumn().name("u_id").varchar(32).notNull().primaryKey().comment("uid").commit()
.addColumn().name("name").varchar(64).notNull().comment("名称").commit()
.addColumn().name("url").varchar(2048).notNull().comment("url").commit()
//更多字段
//。。。
.comment("系统菜单表").commit()
}
//以下为固定写法,无需改动
dependency.setup(info)
.onInstall(install)
.onUpgrade(function (context) { //更新时执行
var upgrader = context.upgrader;
upgrader.filter(versions)
.upgrade(function (newVer) {
newVer.upgrade(context);
});
})
.onUninstall(function (context) {
//卸载时执行
});
```
3. 自动配置类
目前无需创建自动配置类
4. 单元测试
TODO
在使用的时候,直接依赖starter即可:
```xml
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-system-menu-starter</artifactId>
<version>${project.version}</version>
</dependency>
```
在未来将提供更多的starter,例如dubbo,spring-cloud

View File

@@ -44,6 +44,46 @@
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-datasource-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,34 @@
package org.hswebframework.web.crud.annotation;
import org.hswebframework.web.crud.configuration.EasyormRepositoryRegistrar;
import org.springframework.context.annotation.Import;
import javax.persistence.Table;
import java.lang.annotation.*;
/**
* @see org.hswebframework.ezorm.rdb.mapping.ReactiveRepository
* @see org.hswebframework.ezorm.rdb.mapping.SyncRepository
* @since 4.0.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({EasyormRepositoryRegistrar.class})
public @interface EnableEasyormRepository {
/**
* 实体类包名:
* <pre>
* com.company.project.entity
* </pre>
*/
String[] value();
/**
* @see org.hswebframework.ezorm.rdb.mapping.jpa.JpaEntityTableMetadataParser
*/
Class<? extends Annotation>[] annotation() default Table.class;
}

View File

@@ -0,0 +1,14 @@
package org.hswebframework.web.crud.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ImplementFor {
Class value();
Class idType() default Void.class;
}

View File

@@ -0,0 +1,13 @@
package org.hswebframework.web.crud.annotation;
import java.lang.annotation.*;
/**
* @see org.hswebframework.ezorm.rdb.mapping.ReactiveRepository
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Reactive {
}

View File

@@ -0,0 +1,56 @@
package org.hswebframework.web.crud.configuration;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
import org.springframework.beans.factory.annotation.Autowired;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Slf4j
public class AutoDDLProcessor {
private List<Class> entities = new ArrayList<>();
@Autowired
private DatabaseOperator operator;
@Autowired
private EasyormProperties properties;
@Autowired
private EntityTableMetadataResolver resolver;
private boolean reactive;
public void init() {
if (properties.isAutoDdl()) {
if(reactive){
Flux.fromIterable(entities)
.doOnNext(type -> log.info("auto ddl for {}", type))
.map(resolver::resolve)
.flatMap(meta -> operator.ddl().createOrAlter(meta).commit().reactive())
.onErrorContinue((err, a) -> log.warn(err.getMessage(), err))
.then()
.block();
}else{
for (Class entity : entities) {
log.warn("auto ddl for {}", entity);
try {
operator.ddl()
.createOrAlter(resolver.resolve(entity))
.commit()
.sync();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
}
}
}

View File

@@ -0,0 +1,42 @@
package org.hswebframework.web.crud.configuration;
import org.hswebframework.ezorm.rdb.mapping.parser.EntityTableMetadataParser;
import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata;
import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
public class CompositeEntityTableMetadataResolver implements EntityTableMetadataResolver {
private List<EntityTableMetadataParser> resolvers = new ArrayList<>();
private Map<Class, AtomicReference<RDBTableMetadata>> cache = new ConcurrentHashMap<>();
public void addParser(EntityTableMetadataParser resolver) {
resolvers.add(resolver);
}
@Override
public RDBTableMetadata resolve(Class<?> entityClass) {
return cache.computeIfAbsent(entityClass, type -> new AtomicReference<>(doResolve(type))).get();
}
private RDBTableMetadata doResolve(Class<?> entityClass) {
return resolvers.stream()
.map(resolver -> resolver.parseTableMetadata(entityClass))
.filter(Optional::isPresent)
.map(Optional::get)
.reduce((t1, t2) -> {
for (RDBColumnMetadata column : t1.getColumns()) {
t2.addColumn(column.clone());
}
return t2;
}).orElse(null);
}
}

View File

@@ -0,0 +1,21 @@
package org.hswebframework.web.crud.configuration;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrapper;
import org.hswebframework.ezorm.rdb.mapping.EntityManager;
import org.hswebframework.ezorm.rdb.mapping.wrapper.EntityResultWrapper;
@AllArgsConstructor
public class DefaultEntityResultWrapperFactory implements EntityResultWrapperFactory {
private EntityManager entityManager;
@Override
@SneakyThrows
public <T> ResultWrapper<T, ?> getWrapper(Class<T> tClass) {
return new EntityResultWrapper<>(() -> entityManager.newInstance(tClass),
entityManager.getMapping(tClass));
}
}

View File

@@ -0,0 +1,146 @@
package org.hswebframework.web.crud.configuration;
import io.r2dbc.spi.ConnectionFactory;
import lombok.SneakyThrows;
import org.hswebframework.ezorm.core.meta.Feature;
import org.hswebframework.ezorm.rdb.executor.SyncSqlExecutor;
import org.hswebframework.ezorm.rdb.executor.reactive.ReactiveSqlExecutor;
import org.hswebframework.ezorm.rdb.executor.reactive.ReactiveSyncSqlExecutor;
import org.hswebframework.ezorm.rdb.mapping.EntityColumnMapping;
import org.hswebframework.ezorm.rdb.mapping.EntityManager;
import org.hswebframework.ezorm.rdb.mapping.MappingFeatureType;
import org.hswebframework.ezorm.rdb.mapping.jpa.JpaEntityTableMetadataParser;
import org.hswebframework.ezorm.rdb.mapping.parser.EntityTableMetadataParser;
import org.hswebframework.ezorm.rdb.metadata.RDBDatabaseMetadata;
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
import org.hswebframework.ezorm.rdb.operator.DefaultDatabaseOperator;
import org.hswebframework.web.crud.sql.DefaultJdbcExecutor;
import org.hswebframework.web.crud.annotation.EnableEasyormRepository;
import org.hswebframework.web.crud.entity.factory.EntityFactory;
import org.hswebframework.web.crud.sql.DefaultJdbcReactiveExecutor;
import org.hswebframework.web.crud.sql.DefaultR2dbcExecutor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.List;
@Configuration
@EnableConfigurationProperties(EasyormProperties.class)
@EnableEasyormRepository("org.hswebframework.web.**.entity")
public class EasyOrmConfiguration {
@Autowired
private EasyormProperties properties;
@Configuration
@ConditionalOnBean(DataSource.class)
public static class JdbcSqlExecutorConfiguration {
@Bean
@ConditionalOnMissingBean
public SyncSqlExecutor syncSqlExecutor() {
return new DefaultJdbcExecutor();
}
@Bean
@ConditionalOnMissingBean
public ReactiveSqlExecutor reactiveSqlExecutor() {
return new DefaultJdbcReactiveExecutor();
}
}
@Configuration
@ConditionalOnClass(ConnectionFactory.class)
public static class R2dbcSqlExecutorConfiguration {
@Bean
@ConditionalOnMissingBean
public ReactiveSqlExecutor reactiveSqlExecutor() {
return new DefaultR2dbcExecutor();
}
@Bean
@ConditionalOnMissingBean
public SyncSqlExecutor syncSqlExecutor(ReactiveSqlExecutor reactiveSqlExecutor) {
return ReactiveSyncSqlExecutor.of(reactiveSqlExecutor);
}
}
@Bean
@ConditionalOnMissingBean
public EntityManager entityManager(EntityTableMetadataResolver resolver, EntityFactory entityFactory) {
return new EntityManager() {
@Override
@SneakyThrows
public <E> E newInstance(Class<E> type) {
return entityFactory.newInstance(type);
}
@Override
public EntityColumnMapping getMapping(Class entity) {
return resolver.resolve(entityFactory.getInstanceType(entity))
.getFeature(MappingFeatureType.columnPropertyMapping.createFeatureId(entity))
.map(EntityColumnMapping.class::cast)
.orElse(null);
}
};
}
@Bean
public DefaultEntityResultWrapperFactory defaultEntityResultWrapperFactory(EntityManager entityManager) {
return new DefaultEntityResultWrapperFactory(entityManager);
}
@Bean
@ConditionalOnMissingBean
public EntityTableMetadataResolver entityTableMappingResolver(List<EntityTableMetadataParser> parsers) {
CompositeEntityTableMetadataResolver resolver = new CompositeEntityTableMetadataResolver();
parsers.forEach(resolver::addParser);
return resolver;
}
@Bean
@ConditionalOnMissingBean
public EntityTableMetadataParser jpaEntityTableMetadataParser(DatabaseOperator operator) {
JpaEntityTableMetadataParser parser = new JpaEntityTableMetadataParser();
parser.setDatabaseMetadata(operator.getMetadata());
return parser;
}
@Bean
@ConditionalOnMissingBean
public DatabaseOperator databaseOperator() {
RDBDatabaseMetadata metadata = properties.createDatabaseMetadata();
return DefaultDatabaseOperator.of(metadata);
}
@Bean
public BeanPostProcessor autoRegisterFeature(DatabaseOperator operator) {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Feature) {
operator.getMetadata().addFeature(((Feature) bean));
}
return bean;
}
};
}
}

View File

@@ -0,0 +1,112 @@
package org.hswebframework.web.crud.configuration;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.SneakyThrows;
import org.hswebframework.ezorm.rdb.metadata.RDBDatabaseMetadata;
import org.hswebframework.ezorm.rdb.metadata.RDBSchemaMetadata;
import org.hswebframework.ezorm.rdb.metadata.dialect.Dialect;
import org.hswebframework.ezorm.rdb.supports.h2.H2SchemaMetadata;
import org.hswebframework.ezorm.rdb.supports.mssql.SqlServerSchemaMetadata;
import org.hswebframework.ezorm.rdb.supports.mysql.MysqlSchemaMetadata;
import org.hswebframework.ezorm.rdb.supports.oracle.OracleSchemaMetadata;
import org.hswebframework.ezorm.rdb.supports.posgres.PostgresqlSchemaMetadata;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@ConfigurationProperties(prefix = "easyorm")
@Data
public class EasyormProperties {
private String defaultSchema;
private String[] schemas = {};
private boolean autoDdl = true;
private boolean allowAlter = false;
private DialectEnum dialect = DialectEnum.h2;
private Class<? extends Dialect> dialectType;
private Class<? extends RDBSchemaMetadata> schemaType;
public RDBDatabaseMetadata createDatabaseMetadata() {
RDBDatabaseMetadata metadata = new RDBDatabaseMetadata(createDialect());
Set<String> schemaSet = new HashSet<>(Arrays.asList(schemas));
if (defaultSchema != null) {
schemaSet.add(defaultSchema);
}
schemaSet.stream()
.map(this::createSchema)
.forEach(metadata::addSchema);
metadata.getSchema(defaultSchema)
.ifPresent(metadata::setCurrentSchema);
return metadata;
}
@SneakyThrows
public RDBSchemaMetadata createSchema(String name) {
if (schemaType == null) {
return dialect.createSchema(name);
}
return schemaType.getConstructor(String.class).newInstance(name);
}
@SneakyThrows
public Dialect createDialect() {
if (dialectType == null) {
return dialect.getDialect();
}
return dialectType.newInstance();
}
@Getter
@AllArgsConstructor
public enum DialectEnum {
mysql(Dialect.MYSQL) {
@Override
public RDBSchemaMetadata createSchema(String name) {
return new MysqlSchemaMetadata(name);
}
},
mssql(Dialect.MSSQL) {
@Override
public RDBSchemaMetadata createSchema(String name) {
return new SqlServerSchemaMetadata(name);
}
},
oracle(Dialect.ORACLE) {
@Override
public RDBSchemaMetadata createSchema(String name) {
return new OracleSchemaMetadata(name);
}
},
postgres(Dialect.POSTGRES) {
@Override
public RDBSchemaMetadata createSchema(String name) {
return new PostgresqlSchemaMetadata(name);
}
},
h2(Dialect.H2) {
@Override
public RDBSchemaMetadata createSchema(String name) {
return new H2SchemaMetadata(name);
}
},
;
Dialect dialect;
public abstract RDBSchemaMetadata createSchema(String name);
}
}

View File

@@ -0,0 +1,130 @@
package org.hswebframework.web.crud.configuration;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.ezorm.rdb.mapping.defaults.DefaultReactiveRepository;
import org.hswebframework.ezorm.rdb.mapping.defaults.DefaultSyncRepository;
import org.hswebframework.utils.ClassUtils;
import org.hswebframework.web.crud.annotation.EnableEasyormRepository;
import org.hswebframework.web.crud.annotation.ImplementFor;
import org.hswebframework.web.crud.annotation.Reactive;
import org.hswebframework.web.crud.entity.GenericEntity;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Slf4j
public class EasyormRepositoryRegistrar implements ImportBeanDefinitionRegistrar {
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
@Override
@SneakyThrows
@SuppressWarnings("all")
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> attr = importingClassMetadata.getAnnotationAttributes(EnableEasyormRepository.class.getName());
if (attr == null) {
return;
}
String[] arr = (String[]) attr.get("value");
String path = Arrays.stream(arr)
.map(str -> ResourcePatternResolver
.CLASSPATH_ALL_URL_PREFIX
.concat(str.replace(".", "/")).concat("/**/*.class"))
.collect(Collectors.joining());
Class<Annotation>[] anno = (Class[]) attr.get("annotation");
List<Class> allEntities = new ArrayList<>();
for (Resource resource : resourcePatternResolver.getResources(path)) {
MetadataReader reader = metadataReaderFactory.getMetadataReader(resource);
String className = reader.getClassMetadata().getClassName();
Class entityType = Class.forName(className);
if (Arrays.stream(anno)
.noneMatch(ann -> AnnotationUtils.findAnnotation(entityType, ann) != null)) {
continue;
}
allEntities.add(entityType);
ImplementFor implementFor = AnnotationUtils.findAnnotation(entityType, ImplementFor.class);
Reactive reactive = AnnotationUtils.findAnnotation(entityType, Reactive.class);
Class genericType = Optional.ofNullable(implementFor)
.map(ImplementFor::value)
.orElseGet(() -> {
return Stream.of(entityType.getInterfaces())
.filter(e -> GenericEntity.class.isAssignableFrom(e))
.findFirst()
.orElse(entityType);
});
Class idType = null;
if (implementFor == null || implementFor.idType() == Void.class) {
try {
if (GenericEntity.class.isAssignableFrom(entityType)) {
idType = ClassUtils.getGenericType(entityType);
}
if (idType == null) {
Method getId = org.springframework.util.ClassUtils.getMethod(entityType, "getId");
idType = getId.getReturnType();
}
} catch (Exception e) {
idType = String.class;
}
} else {
idType = implementFor.idType();
}
if(reactive!=null){
log.debug("register ReactiveRepository<{},{}>", genericType.getName(), idType.getSimpleName());
ResolvableType repositoryType = ResolvableType.forClassWithGenerics(DefaultReactiveRepository.class, genericType, idType);
RootBeanDefinition definition = new RootBeanDefinition();
definition.setTargetType(repositoryType);
definition.setBeanClass(ReactiveRepositoryFactoryBean.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.getPropertyValues().add("entityType", entityType);
registry.registerBeanDefinition(entityType.getSimpleName().concat("ReactiveRepository"), definition);
}else {
log.debug("register SyncRepository<{},{}>", genericType.getName(), idType.getSimpleName());
ResolvableType repositoryType = ResolvableType.forClassWithGenerics(DefaultSyncRepository.class, genericType, idType);
RootBeanDefinition definition = new RootBeanDefinition();
definition.setTargetType(repositoryType);
definition.setBeanClass(SyncRepositoryFactoryBean.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.getPropertyValues().add("entityType", entityType);
registry.registerBeanDefinition(entityType.getSimpleName().concat("SyncRepository"), definition);
}
}
RootBeanDefinition definition = new RootBeanDefinition();
definition.setTargetType(AutoDDLProcessor.class);
definition.setBeanClass(AutoDDLProcessor.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.getPropertyValues().add("entities", allEntities);
definition.setInitMethodName("init");
registry.registerBeanDefinition(AutoDDLProcessor.class.getName(), definition);
}
}

View File

@@ -0,0 +1,8 @@
package org.hswebframework.web.crud.configuration;
import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrapper;
public interface EntityResultWrapperFactory {
<T> ResultWrapper<T, ?> getWrapper(Class<T> tClass);
}

View File

@@ -0,0 +1,9 @@
package org.hswebframework.web.crud.configuration;
import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata;
public interface EntityTableMetadataResolver {
RDBTableMetadata resolve(Class<?> entityClass);
}

View File

@@ -0,0 +1,45 @@
package org.hswebframework.web.crud.configuration;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.ezorm.rdb.mapping.defaults.DefaultReactiveRepository;
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
@Getter
@Setter
public class ReactiveRepositoryFactoryBean<E, PK>
implements FactoryBean<ReactiveRepository<E, PK>> {
@Autowired
private DatabaseOperator operator;
@Autowired
private EntityTableMetadataResolver resolver;
private Class<E> entityType;
@Autowired
private EntityResultWrapperFactory wrapperFactory;
@Override
public ReactiveRepository<E, PK> getObject() {
return new DefaultReactiveRepository<>(operator,
resolver.resolve(entityType),
entityType,
wrapperFactory.getWrapper(entityType));
}
@Override
public Class<?> getObjectType() {
return ReactiveRepository.class;
}
@Override
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,46 @@
package org.hswebframework.web.crud.configuration;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.ezorm.rdb.mapping.SyncRepository;
import org.hswebframework.ezorm.rdb.mapping.defaults.DefaultSyncRepository;
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
@Getter
@Setter
public class SyncRepositoryFactoryBean<E, PK>
implements FactoryBean<SyncRepository<E, PK>> {
@Autowired
private DatabaseOperator operator;
@Autowired
private EntityTableMetadataResolver resolver;
private Class<E> entityType;
@Autowired
private EntityResultWrapperFactory wrapperFactory;
@Override
public SyncRepository<E, PK> getObject() {
return new DefaultSyncRepository<>(operator,
resolver.resolve(entityType),
entityType,
wrapperFactory.getWrapper(entityType));
}
@Override
public Class<?> getObjectType() {
return SyncRepository.class;
}
@Override
public boolean isSingleton() {
return true;
}
}

View File

@@ -16,20 +16,34 @@
*
*/
package org.hswebframework.web.dao.dynamic;
package org.hswebframework.web.crud.entity;
import org.hswebframework.web.commons.entity.Entity;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.web.bean.ToString;
import org.springframework.data.annotation.Id;
import javax.persistence.Column;
import java.util.List;
/**
* 根据实体类动态查询可传入特定的实体类进行查询
* 目前支持{@link org.hswebframework.web.commons.entity.param.QueryParamEntity} 动态查询
* @author zhouhao
* @since 3.0
* @since 4.0
*/
public interface QueryByEntityDao<PO> {
List<PO> query(Entity queryEntity);
@Getter
@Setter
public class GenericEntity<PK> implements Entity {
int count(Entity queryEntity);
@Column(length = 32)
@Id
private PK id;
public String toString(String... ignoreProperty) {
return ToString.toString(this, ignoreProperty);
}
@Override
public String toString() {
return ToString.toString(this);
}
}

View File

@@ -16,70 +16,47 @@
*
*/
package org.hswebframework.web.commons.entity;
package org.hswebframework.web.crud.entity;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.ezorm.rdb.mapping.annotation.Comment;
import javax.persistence.Column;
/**
* 支持树形结构排序的实体类要使用树形结构排序功能的实体类直接继承该类
*/
public abstract class SimpleTreeSortSupportEntity<PK> extends SimpleGenericEntity<PK>
@Getter
@Setter
public abstract class GenericTreeSortSupportEntity<PK> extends GenericEntity<PK>
implements TreeSortSupportEntity<PK> {
/**
* 父级类别
*/
@Column(name = "parent_id", length = 32)
@Comment("父级ID")
private PK parentId;
/**
* 树结构编码,用于快速查找, 每一层由4位字符组成,-分割
* 如第一层:0001 第二层:0001-0001 第三层:0001-0001-0001
*/
@Column(name = "path", length = 128)
@Comment("树路径")
private String path;
/**
* 排序索引
*/
@Column(name = "sort_index", precision = 32)
@Comment("排序序号")
private Long sortIndex;
@Column(name = "_level", precision = 32)
@Comment("树层级")
private Integer level;
@Override
public String getPath() {
return path;
}
@Override
public void setPath(String path) {
this.path = path;
}
@Override
public PK getParentId() {
return parentId;
}
@Override
public void setParentId(PK parentId) {
this.parentId = parentId;
}
@Override
public Long getSortIndex() {
return sortIndex;
}
@Override
public Integer getLevel() {
return level;
}
@Override
public void setLevel(Integer level) {
this.level = level;
}
@Override
public void setSortIndex(Long sortIndex) {
this.sortIndex = sortIndex;
}
}

View File

@@ -16,7 +16,7 @@
*
*/
package org.hswebframework.web.commons.entity;
package org.hswebframework.web.crud.entity;
public interface SortSupportEntity extends Comparable<SortSupportEntity>, Entity {

View File

@@ -15,7 +15,7 @@
*
*/
package org.hswebframework.web.commons.entity;
package org.hswebframework.web.crud.entity;
/**
* 支持树形结构排序的实体类要使用树形结构排序功能的实体类直接继承该类

View File

@@ -16,12 +16,12 @@
*
*/
package org.hswebframework.web.commons.entity;
package org.hswebframework.web.crud.entity;
import org.apache.commons.collections.CollectionUtils;
import org.hswebframework.utils.RandomUtil;
import org.hswebframework.web.id.IDGenerator;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.function.*;
@@ -29,13 +29,11 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
@SuppressWarnings("all")
public interface TreeSupportEntity<PK> extends GenericEntity<PK> {
public interface TreeSupportEntity<PK> extends Entity {
String id = "id";
PK getId();
String path = "path";
String parentId = "parentId";
void setId(PK id);
String getPath();

View File

@@ -63,7 +63,7 @@ public class MapperEntityFactory implements EntityFactory, BeanFactory {
public MapperEntityFactory() {
}
public <T> MapperEntityFactory(Map<Class<T>, Mapper> realTypeMapper) {
public MapperEntityFactory(Map<Class<?>, Mapper> realTypeMapper) {
this.realTypeMapper.putAll(realTypeMapper);
}
@@ -119,7 +119,7 @@ public class MapperEntityFactory implements EntityFactory, BeanFactory {
if (iterator.hasNext()) {
realType = (Class<T>) iterator.next().getClass();
}
//尝试使用 Simple类如: package.SimpleUserBean
if (realType == null) {
mapper = defaultMapperFactory.apply(beanClass);
}

View File

@@ -3,7 +3,7 @@ package org.hswebframework.web.crud.service;
import org.hswebframework.ezorm.rdb.mapping.SyncRepository;
import org.springframework.beans.factory.annotation.Autowired;
public class DefaultCrudService<E,K> implements CrudService<E,K> {
public class GenericCrudService<E,K> implements CrudService<E,K> {
@Autowired
private SyncRepository<E, K> repository;

View File

@@ -3,7 +3,7 @@ package org.hswebframework.web.crud.service;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.springframework.beans.factory.annotation.Autowired;
public class DefaultReactiveCrudService<E,K> implements ReactiveCrudService<E,K> {
public class GenericReactiveCrudService<E,K> implements ReactiveCrudService<E,K> {
@Autowired
private ReactiveRepository<E, K> repository;

View File

@@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collection;
import java.util.Collections;
public interface ReactiveCrudService<E, K> {
@@ -40,13 +41,26 @@ public interface ReactiveCrudService<E, K> {
return publisher.flatMap(e -> findById(Mono.just(e)));
}
@Transactional
@Transactional(rollbackFor = Throwable.class)
default Mono<SaveResult> save(Publisher<E> entityPublisher) {
return getRepository()
.save(entityPublisher);
}
@Transactional
@Transactional(rollbackFor = Throwable.class)
default Mono<Integer> insertBatch(Publisher<? extends Collection<E>> entityPublisher) {
return getRepository()
.insertBatch(entityPublisher);
}
@Transactional(rollbackFor = Throwable.class)
default Mono<Integer> insert(Publisher<E> entityPublisher) {
return getRepository()
.insert(entityPublisher);
}
@Transactional(rollbackFor = Throwable.class)
default Mono<Integer> deleteById(Publisher<K> idPublisher) {
return getRepository()
.deleteById(idPublisher);

View File

@@ -0,0 +1,82 @@
package org.hswebframework.web.crud.sql;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.ezorm.rdb.executor.SqlRequest;
import org.hswebframework.ezorm.rdb.executor.jdbc.JdbcSyncSqlExecutor;
import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrapper;
import org.hswebframework.web.datasource.DataSourceHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author zhouhao
*/
@Slf4j
public class DefaultJdbcExecutor extends JdbcSyncSqlExecutor {
@Autowired
private DataSource dataSource;
protected String getDatasourceId() {
return DataSourceHolder.switcher().datasource().current().orElse("default");
}
@Override
public Connection getConnection(SqlRequest sqlRequest) {
DataSource dataSource = DataSourceHolder.isDynamicDataSourceReady() ?
DataSourceHolder.currentDataSource().getNative() :
this.dataSource;
Connection connection = DataSourceUtils.getConnection(dataSource);
boolean isConnectionTransactional = DataSourceUtils.isConnectionTransactional(connection, dataSource);
if (log.isDebugEnabled()) {
log.debug("DataSource ({}) JDBC Connection [{}] will {}be managed by Spring", getDatasourceId(), connection, (isConnectionTransactional ? "" : "not "));
}
return connection;
}
@Override
public void releaseConnection(Connection connection, SqlRequest sqlRequest) {
if (log.isDebugEnabled()) {
log.debug("Releasing DataSource ({}) JDBC Connection [{}]", getDatasourceId(), connection);
}
try {
DataSource dataSource = DataSourceHolder.isDynamicDataSourceReady() ?
DataSourceHolder.currentDataSource().getNative() :
this.dataSource;
DataSourceUtils.doReleaseConnection(connection, dataSource);
} catch (SQLException e) {
log.error(e.getMessage(), e);
try {
connection.close();
} catch (Exception e2) {
log.error(e2.getMessage(), e2);
}
}
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void execute(SqlRequest request) {
super.execute(request);
}
@Transactional(rollbackFor = Throwable.class)
@Override
public int update(SqlRequest request) {
return super.update(request);
}
@Override
@Transactional(readOnly = true)
public <T, R> R select(SqlRequest request, ResultWrapper<T, R> wrapper) {
return super.select(request, wrapper);
}
}

View File

@@ -0,0 +1,35 @@
package org.hswebframework.web.crud.sql;
import org.hswebframework.ezorm.rdb.executor.SqlRequest;
import org.hswebframework.ezorm.rdb.executor.jdbc.JdbcReactiveSqlExecutor;
import org.hswebframework.web.datasource.DataSourceHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceUtils;
import reactor.core.publisher.Mono;
import javax.sql.DataSource;
import java.sql.Connection;
public class DefaultJdbcReactiveExecutor extends JdbcReactiveSqlExecutor {
@Autowired
private DataSource dataSource;
@Override
public Mono<Connection> getConnection(SqlRequest sqlRequest) {
DataSource dataSource = DataSourceHolder.isDynamicDataSourceReady() ?
DataSourceHolder.currentDataSource().getNative() :
this.dataSource;
Connection connection = DataSourceUtils.getConnection(dataSource);
return Mono.just(connection);
}
@Override
public void releaseConnection(Connection connection, SqlRequest sqlRequest) {
DataSource dataSource = DataSourceHolder.isDynamicDataSourceReady() ?
DataSourceHolder.currentDataSource().getNative() :
this.dataSource;
DataSourceUtils.releaseConnection(connection, dataSource);
}
}

View File

@@ -0,0 +1,57 @@
package org.hswebframework.web.crud.sql;
import io.r2dbc.spi.Connection;
import io.r2dbc.spi.ConnectionFactory;
import org.hswebframework.ezorm.rdb.executor.SqlRequest;
import org.hswebframework.ezorm.rdb.executor.reactive.r2dbc.R2dbcReactiveSqlExecutor;
import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrapper;
import org.hswebframework.web.datasource.DataSourceHolder;
import org.hswebframework.web.datasource.R2dbcDataSource;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.SignalType;
public class DefaultR2dbcExecutor extends R2dbcReactiveSqlExecutor {
@Autowired
private ConnectionFactory defaultFactory;
@Override
protected Mono<Connection> getConnection() {
if (DataSourceHolder.isDynamicDataSourceReady()) {
return DataSourceHolder.currentR2dbc()
.flatMap(R2dbcDataSource::getNative)
.flatMap(ConnectionFactoryUtils::getConnection);
} else {
return ConnectionFactoryUtils.getConnection(defaultFactory);
}
}
@Override
protected void releaseConnection(SignalType type, Connection connection) {
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public Mono<Void> execute(Publisher<SqlRequest> request) {
return super.execute(request);
}
@Override
@Transactional
public Mono<Integer> update(Publisher<SqlRequest> request) {
return super.update(request);
}
@Override
@Transactional(readOnly = true)
public <E> Flux<E> select(Publisher<SqlRequest> request, ResultWrapper<E, ?> wrapper) {
return super.select(request, wrapper);
}
}

View File

@@ -0,0 +1,3 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.hswebframework.web.crud.configuration.EasyOrmConfiguration

View File

@@ -0,0 +1,29 @@
package org.hswebframework.web.crud;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.web.crud.entity.TestEntity;
import org.hswebframework.web.crud.service.TestEntityService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class CrudTests {
@Autowired
private TestEntityService service;
@Test
public void test(){
Mono.just(TestEntity.of("test",100))
.as(service::insert)
.as(StepVerifier::create)
.expectNext(1)
.verifyComplete();
}
}

View File

@@ -0,0 +1,17 @@
package org.hswebframework.web.crud;
import org.hswebframework.web.crud.entity.factory.EntityFactory;
import org.hswebframework.web.crud.entity.factory.MapperEntityFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@SpringBootApplication
@Configuration
public class TestApplication {
@Bean
public EntityFactory entityFactory(){
return new MapperEntityFactory();
}
}

View File

@@ -0,0 +1,26 @@
package org.hswebframework.web.crud.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hswebframework.web.crud.annotation.Reactive;
import javax.persistence.Column;
import javax.persistence.Table;
@Getter
@Setter
@Table(name = "s_test")
@Reactive
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class TestEntity extends GenericEntity<String> {
@Column(length = 32)
private String name;
@Column
private Integer age;
}

View File

@@ -0,0 +1,8 @@
package org.hswebframework.web.crud.service;
import org.hswebframework.web.crud.entity.TestEntity;
import org.springframework.stereotype.Service;
@Service
public class TestEntityService extends GenericReactiveCrudService<TestEntity,String> {
}

View File

@@ -0,0 +1,11 @@
logging:
level:
org.hswebframework: debug
org.springframework.transaction: debug
org.springframework.data.r2dbc.connectionfactory: debug
#spring:
# r2dbc:
#
easyorm:
default-schema: PUBLIC
dialect: h2

View File

@@ -1,5 +0,0 @@
# 通用DAO实现
支持动态条件的通用CRUD实现
⚠️注意:目前仅提供了[mybatis](hsweb-commons-dao-mybatis)实现.

View File

@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ /*
~ * Copyright 2019 http://www.hswebframework.org
~ *
~ * Licensed under the Apache License, Version 2.0 (the "License");
~ * you may not use this file except in compliance with the License.
~ * You may obtain a copy of the License at
~ *
~ * http://www.apache.org/licenses/LICENSE-2.0
~ *
~ * Unless required by applicable law or agreed to in writing, software
~ * distributed under the License is distributed on an "AS IS" BASIS,
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ * See the License for the specific language governing permissions and
~ * limitations under the License.
~ */
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hsweb-commons-dao</artifactId>
<groupId>org.hswebframework.web</groupId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hsweb-commons-dao-api</artifactId>
<description>通用增删改查-通用Dao接口模块</description>
<dependencies>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-entity</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-utils</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,44 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.dao;
import org.hswebframework.web.dao.dynamic.UpdateByEntityDao;
import org.hswebframework.web.dao.dynamic.DeleteByEntityDao;
import org.hswebframework.web.dao.dynamic.QueryByEntityDao;
/**
* 通用增删改查DAO接口,定义了增删改查.以及动态条件查询,修改,删除。
*
* @param <E> PO类型
* @param <PK> 主键类型
* @author zhouhao
* @see InsertDao
* @see DeleteDao
* @see DeleteByEntityDao
* @see UpdateByEntityDao
* @see QueryByEntityDao
* @since 3.0
*/
public interface CrudDao<E, PK> extends
InsertDao<E>,
DeleteDao<PK>,
DeleteByEntityDao,
UpdateByEntityDao,
QueryByEntityDao<E> {
}

View File

@@ -1,28 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.dao;
/**
* Dao的总接口用于标识类为Dao
*
* @author zhouhao
* @since 3.0
*/
public interface Dao {
}

View File

@@ -1,36 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.dao;
/**
* 通用删除dao
*
* @param <PK> 主键类型
* @author zhouhao
* @since 3.0
*/
public interface DeleteDao<PK> extends Dao {
/**
* 根据主键删除数据,并返回被删除数据的数量
*
* @param pk 主键
* @return 删除的数据数量, 理论上此返回值应该为0或者1.
*/
int deleteByPk(PK pk);
}

View File

@@ -1,29 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.dao;
/**
* 通用数据插入DAO接口
*
* @author zhouhao
* @since 3.0
*/
public interface InsertDao<E> extends Dao {
void insert(E e);
}

View File

@@ -1,31 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.dao.dynamic;
import org.hswebframework.web.commons.entity.Entity;
/**
* 根据实体类条件进行删除,删除条件根据实体类进行解析。解析方式和{@link QueryByEntityDao#query}一致
*
* @author zhouhao
* @since 3.0
*/
public interface DeleteByEntityDao {
int delete(Entity entity);
}

View File

@@ -1,32 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.dao.dynamic;
import org.hswebframework.web.commons.entity.Entity;
/**
* 根据实体类进行更新,实体类支持动态条件或者普通实体类。
* 动态条件和{@link QueryByEntityDao#query(Entity)} 一致。
*
* @author zhouhao
* @since 3.0
*/
public interface UpdateByEntityDao {
int update(Entity entity);
}

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ /*
~ * Copyright 2019 http://www.hswebframework.org
~ *
~ * Licensed under the Apache License, Version 2.0 (the "License");
~ * you may not use this file except in compliance with the License.
~ * You may obtain a copy of the License at
~ *
~ * http://www.apache.org/licenses/LICENSE-2.0
~ *
~ * Unless required by applicable law or agreed to in writing, software
~ * distributed under the License is distributed on an "AS IS" BASIS,
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ * See the License for the specific language governing permissions and
~ * limitations under the License.
~ */
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hsweb-commons</artifactId>
<groupId>org.hswebframework.web</groupId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<description>通用增删改查-通用Dao模块</description>
<artifactId>hsweb-commons-dao</artifactId>
<packaging>pom</packaging>
<modules>
<module>hsweb-commons-dao-api</module>
<module>hsweb-commons-dao-mybatis</module>
</modules>
<dependencies>
<dependency>
<groupId>org.hswebframework</groupId>
<artifactId>hsweb-easy-orm-rdb</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,49 +0,0 @@
# 通用实体类模块
集成系统通用的实体类,如 树形结构实体,排序实体,创建信息实体
# 常用实体类
| 类名 | 说明 |
| ------------- |:-------------:|
| [`Entity`](src/main/java/org/hswebframework/web/commons/entity/Entity.java) | 实体类的总接口,用来标识为一个实体类 |
| [`GenericEntity`](src/main/java/org/hswebframework/web/commons/entity/GenericEntity.java) | 提供基本属性的实体类 |
| [`RecordCreationEntity`](src/main/java/org/hswebframework/web/commons/entity/RecordCreationEntity.java) | 可记录创建信息的实体类 |
| [`TreeSortSupportEntity`](src/main/java/org/hswebframework/web/commons/entity/TreeSortSupportEntity.java) | 可排序树形结构实体类 |
# 实体类工厂
作用: 为了增加拓展性,各个地方依赖的实体均为接口,实体实例应该调用[EntityFactory](src/main/java/org/hswebframework/web/commons/entity/factory/EntityFactory.java)
进行实例化。如: `UserEntity user=entityFactory.newInstance(UserEntity.class);`
目标: controller,service 不再依赖具体实体实现类。实现类由 dao和springMvc进行提供
默认工厂实现: [MapperEntityFactory](src/main/java/org/hswebframework/web/commons/entity/factory/MapperEntityFactory.java)
该工厂可注册接口和实现类的映射关系,以及提供默认的实现类创建。
默认的实现类创建逻辑为。`Class.forName("Simple"+interfaceName);`
如:`UserEntity user=entityFactory.newInstance(UserEntity.class)`
如果未注册`UserEntity`对应的实现类,则将尝试创建`UserEntity`同包下的`SimpleUserEntity`类实例
注册接口和实现类映射关系:
方式1: 调用 mapperEntityFactory进行注册
```java
@javax.annotation.Resource
private MapperEntityFactory mapperEntityFactory;
@javax.annotation.PostConstruct
public void init(){
mapperEntityFactory.addMapping(UserEntity.class,new Mapper(CustomUserEntity.class,CustomUserEntity::new));
}
```
方式2: application.yml 配置文件描述
```yaml
entity:
mappings:
- source-base-package: org.hswebframework.web.entity.authorization
target-base-package: com.company.authorization
mapping:
UserEntity: CustomUserEntity
```

View File

@@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ /*
~ * Copyright 2019 http://www.hswebframework.org
~ *
~ * Licensed under the Apache License, Version 2.0 (the "License");
~ * you may not use this file except in compliance with the License.
~ * You may obtain a copy of the License at
~ *
~ * http://www.apache.org/licenses/LICENSE-2.0
~ *
~ * Unless required by applicable law or agreed to in writing, software
~ * distributed under the License is distributed on an "AS IS" BASIS,
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ * See the License for the specific language governing permissions and
~ * limitations under the License.
~ */
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hsweb-commons</artifactId>
<groupId>org.hswebframework.web</groupId>
<version>4.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hsweb-commons-entity</artifactId>
<description>通用增删改查-通用实体模块</description>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>hsweb-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.hswebframework</groupId>
<artifactId>hsweb-easy-orm-rdb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hswebframework</groupId>
<artifactId>hsweb-utils</artifactId>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-bean</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,8 +0,0 @@
package org.hswebframework.web.commons.entity;
/**
* @author zhouhao
*/
public interface CloneableEntity extends Entity, Cloneable {
CloneableEntity clone();
}

View File

@@ -1,11 +0,0 @@
package org.hswebframework.web.commons.entity;
/**
* @author zhouhao
* @see DataStatusEnum
*/
public interface DataStatus {
Byte STATUS_ENABLED = 1;
Byte STATUS_DISABLED = 0;
Byte STATUS_LOCKED = -1;
}

View File

@@ -1,19 +0,0 @@
package org.hswebframework.web.commons.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.hswebframework.web.dict.EnumDict;
@AllArgsConstructor
@Getter
public enum DataStatusEnum implements EnumDict<Byte> {
ENABLED((byte) 1, "正常"),
DISABLED((byte) 0, "禁用"),
LOCK((byte) -1, "锁定"),
DELETED((byte) -10, "删除");
private Byte value;
private String text;
}

View File

@@ -1,40 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.commons.entity;
import org.hswebframework.web.commons.bean.Bean;
import org.hswebframework.web.commons.bean.ValidateBean;
import java.io.Serializable;
/**
* 实体总接口,所有实体需实现此接口
*
* @author zhouhao
* @see org.hswebframework.web.commons.entity.factory.EntityFactory
* @see GenericEntity
* @see TreeSupportEntity
* @see TreeSortSupportEntity
* @see Bean
* @since 3.0
*/
public interface Entity extends ValidateBean {
}

View File

@@ -1,90 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.commons.entity;
import org.hswebframework.web.bean.ToString;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 通用实体,提供实体常用属性
*
* @author zhouhao
* @since 3.0
*/
public interface GenericEntity<PK> extends CloneableEntity {
String id = "id";
String properties = "properties";
PK getId();
void setId(PK id);
default String toString(String... ignoreProperty) {
return ToString.toString(this, ignoreProperty);
}
default Map<String, Object> getProperties() {
return null;
}
default void setProperties(Map<String, Object> properties) {
}
@SuppressWarnings("unchecked")
default <T> T getProperty(String propertyName, T defaultValue) {
Map<String, Object> map = getProperties();
if (map == null) {
return null;
}
return (T) map.getOrDefault(propertyName, defaultValue);
}
default <T> T getProperty(String propertyName) {
return getProperty(propertyName, null);
}
default void setProperty(String propertyName, Object value) {
Map<String, Object> map = getProperties();
if (map == null) {
map = new LinkedHashMap<>();
setProperties(map);
}
map.put(propertyName, value);
}
default Map<String, Object> cloneProperties() {
Map<String, Object> target = new LinkedHashMap<>();
Map<String, Object> old = getProperties();
if (old == null || old.isEmpty()) {
return target;
}
old.forEach((k, v) -> {
if (v instanceof CloneableEntity) {
target.put(k, ((CloneableEntity) v).clone());
} else {
target.put(k, v);
}
});
return target;
}
}

View File

@@ -1,19 +0,0 @@
package org.hswebframework.web.commons.entity;
/**
* 逻辑删除
*
* @author zhouhao
* @since 3.0.6
*/
public interface LogicalDeleteEntity {
Boolean getDeleted();
void setDeleted(boolean deleted);
Long getDeleteTime();
void setDeleteTime(Long deleteTime);
}

View File

@@ -1,73 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.commons.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
import java.util.ArrayList;
import java.util.List;
@ApiModel(description = "分页结果")
@Getter
@Setter
public class PagerResult<E> implements Entity {
private static final long serialVersionUID = -6171751136953308027L;
public static <E> PagerResult<E> empty() {
return new PagerResult<>(0, new ArrayList<>());
}
public static <E> PagerResult<E> of(int total, List<E> list) {
return new PagerResult<>(total, list);
}
public static <E> PagerResult<E> of(int total, List<E> list, QueryParamEntity entity) {
PagerResult pagerResult = new PagerResult<>(total, list);
pagerResult.setPageIndex(entity.getThinkPageIndex());
pagerResult.setPageSize(entity.getPageSize());
return pagerResult;
}
@ApiModelProperty("当前页码")
private int pageIndex;
@ApiModelProperty("每页数据数量")
private int pageSize;
@ApiModelProperty("数据总数量")
private int total;
@ApiModelProperty("查询结果")
private List<E> data;
public PagerResult() {
}
public PagerResult(int total, List<E> data) {
this.total = total;
this.data = data;
}
}

View File

@@ -1,23 +0,0 @@
package org.hswebframework.web.commons.entity;
import org.hswebframework.web.HttpParameterConverter;
import java.util.Map;
import java.util.StringJoiner;
/**
* @author zhouhao
* @since 3.0
*/
public interface QueryEntity extends Entity {
/**
* 转为http查询参数
* @return
*/
default String toHttpQueryParamString() {
Map<String, String> result = new HttpParameterConverter(this).convert();
StringJoiner joiner = new StringJoiner("&");
result.forEach((key, value) -> joiner.add(key.concat("=").concat(value)));
return joiner.toString();
}
}

View File

@@ -1,30 +0,0 @@
package org.hswebframework.web.commons.entity;
/**
* 记录创建信息的实体类,包括创建人和创建时间。
* 此实体类与行级权限控制相关联:只能操作自己创建的数据
*
* @author zhouhao
* @since 3.0
*/
public interface RecordCreationEntity extends Entity {
String creatorId = "creatorId";
String createTime = "createTime";
String getCreatorId();
void setCreatorId(String creatorId);
Long getCreateTime();
void setCreateTime(Long createTime);
default void setCreateTimeNow() {
setCreateTime(System.currentTimeMillis());
}
default String getCreatorIdProperty() {
return creatorId;
}
}

View File

@@ -1,29 +0,0 @@
package org.hswebframework.web.commons.entity;
/**
* 记录修改信息的实体类,包括修改人和修改时间。
*
* @author zhouhao
* @since 3.0.6
*/
public interface RecordModifierEntity extends Entity {
String modifierId = "modifierId";
String modifyTime = "modifyTime";
String getModifierId();
void setModifierId(String modifierId);
Long getModifyTime();
void setModifyTime(Long modifyTime);
default void setModifyTimeNow() {
setModifyTime(System.currentTimeMillis());
}
default String getModifierIdProperty() {
return modifierId;
}
}

View File

@@ -1,91 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.commons.entity;
import lombok.SneakyThrows;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author zhouhao
* @since 3.0
*/
public abstract class SimpleGenericEntity<PK> implements GenericEntity<PK> {
private static final long serialVersionUID = 4546315942526096290L;
private PK id;
private Map<String, Object> properties;
@Override
public String toString() {
return toString((String[]) null);
}
@Override
public PK getId() {
return this.id;
}
@Override
public void setId(PK id) {
this.id = id;
}
@Override
public Map<String, Object> getProperties() {
return properties;
}
@Override
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
@Override
@SuppressWarnings("unchecked")
public <T> T getProperty(String propertyName, T defaultValue) {
if (null == properties) {
return defaultValue;
}
return (T) properties.getOrDefault(propertyName, defaultValue);
}
@Override
public <T> T getProperty(String propertyName) {
return getProperty(propertyName, null);
}
@Override
public void setProperty(String propertyName, Object value) {
if (null == properties) {
properties = new LinkedHashMap<>();
}
properties.put(propertyName, value);
}
@Override
@SuppressWarnings("unchecked")
@SneakyThrows
public SimpleGenericEntity<PK> clone() {
return (SimpleGenericEntity) super.clone();
}
}

View File

@@ -1,15 +0,0 @@
package org.hswebframework.web.commons.entity.events;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.Serializable;
@AllArgsConstructor
@Getter
public class EntityCreatedEvent<E> implements Serializable {
private E entity;
private Class<E> entityType;
}

View File

@@ -1,20 +0,0 @@
package org.hswebframework.web.commons.entity.events;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.Serializable;
@AllArgsConstructor
@Getter
public class EntityModifyEvent<E> implements Serializable{
private static final long serialVersionUID = -7158901204884303777L;
private E before;
private E after;
private Class<E> entityType;
}

View File

@@ -1,12 +0,0 @@
package org.hswebframework.web.commons.entity.factory;
import java.util.function.Function;
/**
* 默认的实体映射
*
* @author zhouhao
*/
@FunctionalInterface
public interface DefaultMapperFactory extends Function<Class, MapperEntityFactory.Mapper> {
}

View File

@@ -1,10 +0,0 @@
package org.hswebframework.web.commons.entity.factory;
/**
* 默认的属性复制器
*
* @author zhouhao
*/
@FunctionalInterface
public interface DefaultPropertyCopier extends PropertyCopier<Object, Object> {
}

View File

@@ -1,115 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.commons.entity.factory;
import org.hswebframework.web.commons.entity.Entity;
/**
* 实体工厂接口,系统各个地方使用此接口来创建实体,在实际编码中也应该使用此接口来创建实体,而不是使用new方式来创建
*
* @author zhouhao
* @see Entity
* @see MapperEntityFactory
* @since 3.0
*/
public interface EntityFactory {
/**
* 根据类型创建实例
* <p>
* e.g.
* <pre>
* entityFactory.newInstance(UserEntity.class);
* </pre>
*
* @param entityClass 要创建的class
* @param <T> 类型
* @return 创建结果
*/
<T> T newInstance(Class<T> entityClass);
/**
* 根据类型创建实例,如果类型无法创建,则使用默认类型进行创建
* <p>
* e.g.
* <pre>
* entityFactory.newInstance(UserEntity.class,SimpleUserEntity.class);
* </pre>
*
* @param entityClass 要创建的class
* @param defaultClass 默认class,当{@code entityClass}无法创建时使用此类型进行创建
* @param <T> 类型
* @return 实例
*/
<T> T newInstance(Class<T> entityClass, Class<? extends T> defaultClass);
/**
* 创建实体并设置默认的属性
*
* @param entityClass 实体类型
* @param defaultProperties 默认属性
* @param <S> 默认属性的类型
* @param <T> 实体类型
* @return 创建结果
* @see EntityFactory#copyProperties(Object, Object)
*/
default <S, T> T newInstance(Class<T> entityClass, S defaultProperties) {
return copyProperties(defaultProperties, newInstance(entityClass));
}
/**
* 创建实体并设置默认的属性
*
* @param entityClass 实体类型
* @param defaultClass 默认class
* @param defaultProperties 默认属性
* @param <S> 默认属性的类型
* @param <T> 实体类型
* @return 创建结果
* @see EntityFactory#copyProperties(Object, Object)
*/
default <S, T> T newInstance(Class<T> entityClass, Class<? extends T> defaultClass, S defaultProperties) {
return copyProperties(defaultProperties, newInstance(entityClass, defaultClass));
}
/**
* 根据类型获取实体的真实的实体类型,
* 可通过此方法获取获取已拓展的实体类型,如:<br>
* <code>
* factory.getInstanceType(MyBeanInterface.class);
* </code>
*
* @param entityClass 类型
* @param <T> 泛型
* @return 实体类型
*/
<T> Class<T> getInstanceType(Class<T> entityClass);
/**
* 拷贝对象的属性
*
* @param source 要拷贝到的对象
* @param target 被拷贝的对象
* @param <S> 要拷贝对象的类型
* @param <T> 被拷贝对象的类型
* @return 被拷贝的对象
*/
<S, T> T copyProperties(S source, T target);
}

View File

@@ -1,240 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.commons.entity.factory;
import lombok.SneakyThrows;
import org.hswebframework.web.exception.NotFoundException;
import org.hswebframework.utils.ClassUtils;
import org.hswebframework.web.bean.BeanFactory;
import org.hswebframework.web.bean.FastBeanCopier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Supplier;
/**
* @author zhouhao
* @since 3.0
*/
@SuppressWarnings("unchecked")
public class MapperEntityFactory implements EntityFactory, BeanFactory {
private Map<Class, Mapper> realTypeMapper = new HashMap<>();
private Logger logger = LoggerFactory.getLogger(this.getClass());
private Map<String, PropertyCopier> copierCache = new HashMap<>();
private static final DefaultMapperFactory DEFAULT_MAPPER_FACTORY = clazz -> {
String simpleClassName = clazz.getPackage().getName().concat(".Simple").concat(clazz.getSimpleName());
try {
return defaultMapper(Class.forName(simpleClassName));
} catch (ClassNotFoundException ignore) {
// throw new NotFoundException(e.getMessage());
}
return null;
};
/**
* 默认的属性复制器
*/
private static final DefaultPropertyCopier DEFAULT_PROPERTY_COPIER = FastBeanCopier::copy;
private DefaultMapperFactory defaultMapperFactory = DEFAULT_MAPPER_FACTORY;
private DefaultPropertyCopier defaultPropertyCopier = DEFAULT_PROPERTY_COPIER;
public MapperEntityFactory() {
}
public <T> MapperEntityFactory(Map<Class<T>, Mapper> realTypeMapper) {
this.realTypeMapper.putAll(realTypeMapper);
}
public <T> MapperEntityFactory addMapping(Class<T> target, Mapper<? extends T> mapper) {
realTypeMapper.put(target, mapper);
return this;
}
public MapperEntityFactory addCopier(PropertyCopier copier) {
Class source = ClassUtils.getGenericType(copier.getClass(), 0);
Class target = ClassUtils.getGenericType(copier.getClass(), 1);
if (source == null || source == Object.class) {
throw new UnsupportedOperationException("generic type " + source + " not support");
}
if (target == null || target == Object.class) {
throw new UnsupportedOperationException("generic type " + target + " not support");
}
addCopier(source, target, copier);
return this;
}
public <S, T> MapperEntityFactory addCopier(Class<S> source, Class<T> target, PropertyCopier<S, T> copier) {
copierCache.put(getCopierCacheKey(source, target), copier);
return this;
}
private String getCopierCacheKey(Class source, Class target) {
return source.getName().concat("->").concat(target.getName());
}
@Override
public <S, T> T copyProperties(S source, T target) {
Objects.requireNonNull(source);
Objects.requireNonNull(target);
try {
PropertyCopier<S, T> copier = copierCache.<S, T>get(getCopierCacheKey(source.getClass(), target.getClass()));
if (null != copier) {
return copier.copyProperties(source, target);
}
return (T) defaultPropertyCopier.copyProperties(source, target);
} catch (Exception e) {
logger.warn("copy properties error", e);
}
return target;
}
protected <T> Mapper<T> initCache(Class<T> beanClass) {
Mapper<T> mapper = null;
Class<T> realType = null;
ServiceLoader<T> serviceLoader = ServiceLoader.load(beanClass, this.getClass().getClassLoader());
Iterator<T> iterator = serviceLoader.iterator();
if (iterator.hasNext()) {
realType = (Class<T>) iterator.next().getClass();
}
//尝试使用 Simple类如: package.SimpleUserBean
if (realType == null) {
mapper = defaultMapperFactory.apply(beanClass);
}
if (!Modifier.isInterface(beanClass.getModifiers()) && !Modifier.isAbstract(beanClass.getModifiers())) {
realType = beanClass;
}
if (mapper == null && realType != null) {
if (logger.isDebugEnabled() && realType != beanClass) {
logger.debug("use instance {} for {}", realType, beanClass);
}
mapper = new Mapper<>(realType, new DefaultInstanceGetter(realType));
}
if (mapper != null) {
realTypeMapper.put(beanClass, mapper);
}
return mapper;
}
@Override
public <T> T newInstance(Class<T> beanClass) {
return newInstance(beanClass, null);
}
@Override
public <T> T newInstance(Class<T> beanClass, Class<? extends T> defaultClass) {
if (beanClass == null) {
return null;
}
Mapper<T> mapper = realTypeMapper.get(beanClass);
if (mapper != null) {
return mapper.getInstanceGetter().get();
}
mapper = initCache(beanClass);
if (mapper != null) {
return mapper.getInstanceGetter().get();
}
if (defaultClass != null) {
return newInstance(defaultClass);
}
if (Map.class == beanClass) {
return (T) new HashMap<>();
}
if (List.class == beanClass) {
return (T) new ArrayList<>();
}
if (Set.class == beanClass) {
return (T) new HashSet<>();
}
throw new NotFoundException("can't create instance for " + beanClass);
}
@Override
@SuppressWarnings("unchecked")
public <T> Class<T> getInstanceType(Class<T> beanClass) {
Mapper<T> mapper = realTypeMapper.get(beanClass);
if (null != mapper) {
return mapper.getTarget();
}
mapper = initCache(beanClass);
if (mapper != null) {
return mapper.getTarget();
}
return Modifier.isAbstract(beanClass.getModifiers())
|| Modifier.isInterface(beanClass.getModifiers())
? null : beanClass;
}
public void setDefaultMapperFactory(DefaultMapperFactory defaultMapperFactory) {
Objects.requireNonNull(defaultMapperFactory);
this.defaultMapperFactory = defaultMapperFactory;
}
public void setDefaultPropertyCopier(DefaultPropertyCopier defaultPropertyCopier) {
this.defaultPropertyCopier = defaultPropertyCopier;
}
public static class Mapper<T> {
Class<T> target;
Supplier<T> instanceGetter;
public Mapper(Class<T> target, Supplier<T> instanceGetter) {
this.target = target;
this.instanceGetter = instanceGetter;
}
public Class<T> getTarget() {
return target;
}
public Supplier<T> getInstanceGetter() {
return instanceGetter;
}
}
public static <T> Mapper<T> defaultMapper(Class<T> target) {
return new Mapper<>(target, defaultInstanceGetter(target));
}
public static <T> Supplier<T> defaultInstanceGetter(Class<T> clazz) {
return new DefaultInstanceGetter<>(clazz);
}
static class DefaultInstanceGetter<T> implements Supplier<T> {
Class<T> type;
public DefaultInstanceGetter(Class<T> type) {
this.type = type;
}
@Override
@SneakyThrows
public T get() {
return type.newInstance();
}
}
}

View File

@@ -1,11 +0,0 @@
package org.hswebframework.web.commons.entity.factory;
/**
* 属性复制接口,用于自定义属性复制
*
* @author zhouhao
* @since 3.0
*/
public interface PropertyCopier<S, T> {
T copyProperties(S source, T target);
}

View File

@@ -1,45 +0,0 @@
package org.hswebframework.web.commons.entity.param;
import org.hswebframework.ezorm.core.dsl.Delete;
import org.hswebframework.ezorm.core.dsl.Update;
import org.hswebframework.ezorm.core.param.Param;
import org.hswebframework.web.commons.entity.Entity;
import org.hswebframework.web.commons.entity.QueryEntity;
/**
* 查询参数实体,使用<a href="https://github.com/hs-web/hsweb-easy-orm">easyorm</a>进行动态查询参数构建<br>
* 可通过静态方法创建:<br>
* {@link DeleteParamEntity#build()}<br>
*
* @author zhouhao
* @see Param
* @see Entity
* @since 3.0
*/
public class DeleteParamEntity extends Param implements QueryEntity {
private static final long serialVersionUID = 6120598637420234301L;
/**
* 创建一个无条件的删除条件实体
* 创建后需自行指定条件({@link DeleteParamEntity#where(String, Object)})
* 否则可能无法执行更新(dao实现应该禁止无条件的删除)
*
* @return DeleteParamEntity
*/
public static DeleteParamEntity build() {
return new DeleteParamEntity();
}
/**
* @since 3.0.4
*/
public static Delete<DeleteParamEntity> newDelete() {
return new Delete<>(new DeleteParamEntity());
}
@Override
public String toString() {
return toHttpQueryParamString();
}
}

View File

@@ -1,161 +0,0 @@
package org.hswebframework.web.commons.entity.param;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.ezorm.core.NestConditional;
import org.hswebframework.ezorm.core.dsl.Query;
import org.hswebframework.ezorm.core.param.QueryParam;
import org.hswebframework.ezorm.core.param.Term;
import org.hswebframework.web.commons.entity.Entity;
import org.hswebframework.web.commons.entity.QueryEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* 查询参数实体,使用<a href="https://github.com/hs-web/hsweb-easy-orm">easyorm</a>进行动态查询参数构建<br>
* 可通过静态方法创建:<br>
* {@link QueryParamEntity#empty()}<br>
* {@link QueryParamEntity#single(String, Object)}<br>
* 如:
* <code>
* QueryParamEntity.single("id",id);
* </code>
*
* @author zhouhao
* @see QueryParam
* @see Entity
* @since 3.0
*/
public class QueryParamEntity extends QueryParam implements QueryEntity {
private static final long serialVersionUID = 8097500947924037523L;
@Getter
private String termExpression;
/**
* 创建一个空的查询参数实体,该实体无任何参数.
*
* @return 无条件的参数实体
*/
public static QueryParamEntity empty() {
return new QueryParamEntity();
}
/**
* 创建一个含有单个条件的参数实体,条件默认为is
*
* @param field 参数名称
* @param value 参数值
* @return 单个条件的参数实体
* @see QueryParam#where(String, Object)
*/
public static QueryParamEntity single(String field, Object value) {
return of(field, value);
}
/**
* @see this#single(String, Object)
*/
public static QueryParamEntity of(String field, Object value) {
return empty().where(field, value);
}
/**
* @since 3.0.4
*/
public static <T> Query<T, QueryParamEntity> newQuery() {
return Query.empty(new QueryParamEntity());
}
/**
* @since 3.0.4
*/
public <T> Query<T, QueryParamEntity> toQuery() {
return Query.empty(this);
}
/**
* 将已有的条件包装到一个嵌套的条件里,并返回一个Query对象.例如:
* <pre>
* entity.toNestQuery().and("userId",userId);
* </pre>
* <p>
* 原有条件: name=? or type=?
* <p>
* 执行后条件: (name=? or type=?) and userId=?
*
* @see this#toNestQuery(Consumer)
* @since 3.0.4
*/
public <T> Query<T, QueryParamEntity> toNestQuery() {
return toNestQuery(null);
}
/**
* 将已有的条件包装到一个嵌套的条件里,并返回一个Query对象.例如:
* <pre>
* entity.toNestQuery(query->query.and("userId",userId));
* </pre>
* <p>
* 原有条件: name=? or type=?
* <p>
* 执行后条件: userId=? (name=? or type=?)
*
* @param before 在包装之前执行,将条件包装到已有条件之前
* @since 3.0.4
*/
public <T> Query<T, QueryParamEntity> toNestQuery(Consumer<Query<T, QueryParamEntity>> before) {
List<Term> terms = getTerms();
setTerms(new ArrayList<>());
Query<T, QueryParamEntity> query = toQuery();
if (null != before) {
before.accept(query);
}
return query
.nest()
.each(terms, NestConditional::accept)
.end();
}
/**
* 设置条件表达式,可以通过表达式的方式快速构建查询条件. 表达式是类似sql条件的语法,如:
* <pre>
* name is 测试 and age gte 10
* </pre>
* <pre>
* name is 测试 and (age gt 10 or age lte 90 )
* </pre>
*
* @param termExpression 表达式
* @see 3.0.5
*/
public void setTermExpression(String termExpression) {
this.termExpression = termExpression;
setTerms(TermExpressionParser.parse(termExpression));
}
@Override
public List<Term> getTerms() {
List<Term> terms = super.getTerms();
if (CollectionUtils.isEmpty(terms) && StringUtils.hasText(termExpression)) {
setTerms(terms = TermExpressionParser.parse(termExpression));
}
return terms;
}
@Override
public String toString() {
return toHttpQueryParamString();
}
public QueryParamEntity noPaging() {
setPaging(false);
return this;
}
}

View File

@@ -1,184 +0,0 @@
package org.hswebframework.web.commons.entity.param;
import org.hswebframework.ezorm.core.NestConditional;
import org.hswebframework.ezorm.core.dsl.Query;
import org.hswebframework.ezorm.core.param.Term;
import org.hswebframework.ezorm.core.param.TermType;
import java.util.Arrays;
import java.util.List;
/**
* 动态条件表达式解析器
* name=测试 and age=test
*/
public class TermExpressionParser {
public static List<Term> parse(String expression) {
Query<?, QueryParamEntity> conditional = QueryParamEntity.newQuery();
NestConditional<?> nest = null;
// 字符容器
char[] buf = new char[128];
// 记录词项的长度, Arrays.copyOf使用
byte len = 0;
// 空格数量?
byte spaceLen = 0;
// 当前列
char[] currentColumn = null;
// 当前列对应的值
char[] currentValue = null;
// 当前条件类型 eq btw in ...
String currentTermType = null;
// 当前链接类型 and / or
String currentType = "and";
// 是否是引号, 单引号 / 双引号
byte quotationMarks = 0;
// 表达式字符数组
char[] all = expression.toCharArray();
for (char c : all) {
if (c == '\'' || c == '"') {
if (quotationMarks != 0) {
// 碰到(结束的)单/双引号, 标志归零, 跳过
quotationMarks = 0;
continue;
}
// 碰到(开始的)单/双引号, 做记录, 跳过
quotationMarks++;
continue;
} else if (c == '(') {
nest = (nest == null ?
(currentType.equals("or") ? conditional.orNest() : conditional.nest()) :
(currentType.equals("or") ? nest.orNest() : nest.nest()));
len = 0;
continue;
} else if (c == ')') {
if (nest == null) {
continue;
}
if (null != currentColumn) {
currentValue = Arrays.copyOf(buf, len);
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
currentColumn = null;
currentTermType = null;
}
Object end = nest.end();
nest = end instanceof NestConditional ? ((NestConditional) end) : null;
len = 0;
spaceLen++;
continue;
} else if (c == '=' || c == '>' || c == '<') {
if (currentTermType != null) {
currentTermType += String.valueOf(c);
//spaceLen--;
} else {
currentTermType = String.valueOf(c);
}
if (currentColumn == null) {
currentColumn = Arrays.copyOf(buf, len);
}
spaceLen++;
len = 0;
continue;
} else if (c == ' ') {
if (len == 0) {
continue;
}
if (quotationMarks != 0) {
// 如果当前字符是空格,并且前面迭代时碰到过单/双引号, 不处理并且添加到buf中
buf[len++] = c;
continue;
}
spaceLen++;
if (currentColumn == null && (spaceLen == 1 || spaceLen % 5 == 0)) {
currentColumn = Arrays.copyOf(buf, len);
len = 0;
continue;
}
if (null != currentColumn) {
if (null == currentTermType) {
currentTermType = new String(Arrays.copyOf(buf, len));
len = 0;
continue;
}
currentValue = Arrays.copyOf(buf, len);
if (nest != null) {
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
} else {
conditional.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
}
currentColumn = null;
currentTermType = null;
len = 0;
continue;
} else if (len == 2 || len == 3) {
String type = new String(Arrays.copyOf(buf, len));
if (type.equalsIgnoreCase("or")) {
currentType = "or";
if (nest != null) {
nest.or();
} else {
conditional.or();
}
len = 0;
continue;
} else if (type.equalsIgnoreCase("and")) {
currentType = "and";
if (nest != null) {
nest.and();
} else {
conditional.and();
}
len = 0;
continue;
} else {
currentColumn = Arrays.copyOf(buf, len);
len = 0;
spaceLen++;
}
} else {
currentColumn = Arrays.copyOf(buf, len);
len = 0;
spaceLen++;
}
continue;
}
buf[len++] = c;
}
if (null != currentColumn) {
currentValue = Arrays.copyOf(buf, len);
if (nest != null) {
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
} else {
conditional.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
}
}
return conditional.getParam().getTerms();
}
private static String convertTermType(String termType) {
if (termType == null) {
return TermType.eq;
}
switch (termType) {
case "=":
return TermType.eq;
case ">":
return TermType.gt;
case "<":
return TermType.lt;
case ">=":
return TermType.gte;
case "<=":
return TermType.lte;
default:
return termType;
}
}
}

View File

@@ -1,71 +0,0 @@
package org.hswebframework.web.commons.entity.param;
import org.hswebframework.ezorm.core.dsl.Query;
import org.hswebframework.ezorm.core.dsl.Update;
import org.hswebframework.ezorm.core.param.UpdateParam;
import org.hswebframework.web.commons.entity.Entity;
import org.hswebframework.web.commons.entity.QueryEntity;
/**
* 修改参数实体,使用<a href="https://github.com/hs-web/hsweb-easy-orm">easyorm</a>进行动态参数构建
*
* @author zhouhao
* @see UpdateParam
* @see Entity
* @since 3.0
*/
public class UpdateParamEntity<T> extends UpdateParam<T> implements QueryEntity {
private static final long serialVersionUID = -4074863219482678510L;
public UpdateParamEntity() {
}
public UpdateParamEntity(T data) {
super(data);
}
/**
* 创建一个无任何条件并指定数据的更新参数实体
* 创建后需自行指定条件({@link UpdateParamEntity#where(String, Object)})
* 否则可能无法执行更新(dao实现应该禁止无条件的更新)
*
* @param data 要更新的数据
* @param <T> 数据泛型
* @return 更新参数实体
*/
public static <T> UpdateParamEntity<T> build(T data) {
return new UpdateParamEntity<>(data);
}
/**
* 创建一个单个条件并指定数据的更新参数实体,条件默认为is:
* <br>例如:<br>
* <code>
* // where id = #{id}
* <br>
* UpdateParamBean.build(data,"id",id);
* </code>
*
* @param data 要更新的数据
* @param field 条件名
* @param value 条件值
* @param <T> 数据泛型
* @return 更新参数实体
*/
public static <T> UpdateParamEntity<T> build(T data, String field, Object value) {
return new UpdateParamEntity<>(data).where(field, value);
}
/**
* @since 3.0.4
*/
public static <T> Update<T, UpdateParamEntity<T>> newUpdate() {
return new Update<>(new UpdateParamEntity<>());
}
@Override
public String toString() {
return toHttpQueryParamString();
}
}

View File

@@ -1,18 +0,0 @@
package org.hswebframework.web.commons.entity;
import lombok.*;
import java.util.List;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MenuEntity extends SimpleTreeSortSupportEntity<Integer> {
private static final long serialVersionUID = 5548107788893085691L;
private String name;
private List<MenuEntity> children;
}

View File

@@ -1,66 +0,0 @@
package org.hswebframework.web.commons.entity;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Predicate;
public class TreeSupportEntityTests {
@Test
public void test() {
MenuEntity parent = MenuEntity.builder().build();
parent.setName("menu-1");
parent.setId(1);
parent.setParentId(-1);
MenuEntity m101 = MenuEntity.builder().build();
m101.setName("menu-101");
m101.setId(101);
m101.setParentId(1);
MenuEntity m102 = MenuEntity.builder().build();
m102.setName("menu-102");
m102.setId(102);
m102.setParentId(1);
MenuEntity m10201 = MenuEntity.builder().build();
m10201.setName("menu-10201");
m10201.setId(10201);
m10201.setParentId(102);
//list转为树形结构
List<MenuEntity> tree = TreeSupportEntity
.list2tree(Arrays.asList(parent, m101, m102, m10201), MenuEntity::setChildren, (Predicate<MenuEntity>) menu -> menu.getParentId().equals(-1));
Assert.assertEquals(tree.get(0).getChildren().get(0).getId(), Integer.valueOf(101));
Assert.assertEquals(tree.get(0).getChildren().get(1).getId(), Integer.valueOf(102));
Assert.assertEquals(tree.get(0).getChildren().get(1).getChildren().get(0).getId(), Integer.valueOf(10201));
System.out.println(JSON.toJSONString(tree, SerializerFeature.PrettyFormat));
LongAdder adder=new LongAdder();
TreeSupportEntity.forEach(tree,menu->{
adder.increment();
});
Assert.assertEquals(adder.intValue(),4);
List<MenuEntity> list = new ArrayList<>();
//将树形结构展平为list
TreeSupportEntity.expandTree2List(tree.get(0), list, () -> (int) Math.round(Math.random() * 1000000), MenuEntity::setChildren);
System.out.println(JSON.toJSONString(list, SerializerFeature.PrettyFormat));
Assert.assertEquals(list.size(), 4);
}
}

View File

@@ -1,59 +0,0 @@
package org.hswebframework.web.commons.entity.factory;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import static org.junit.Assert.*;
public class MapperEntityFactoryTests {
@Test
public void testCreateEntity() {
MapperEntityFactory entityFactory = new MapperEntityFactory();
entityFactory.addMapping(TestEntity.class, entityFactory.initCache(NewTestEntity.class));
TestEntity entity = entityFactory.newInstance(TestEntity.class);
Assert.assertEquals(entity.getClass(), NewTestEntity.class);
entity = entityFactory.copyProperties(new HashMap<String, Object>() {
private static final long serialVersionUID = 6458422824954290386L;
{
put("name", "张三");
put("nickName", "小张");
}
}, entity);
Assert.assertEquals(entity.getName(), "张三");
Assert.assertEquals(((NewTestEntity) entity).getNickName(), "小张");
entityFactory.addCopier(new CustomPropertyCopier());
HashMap<String, Object> data = new HashMap<>();
data.put("name", "李四");
data.put("nickName", "小李");
entityFactory.copyProperties(data, entity);
Assert.assertEquals(entity.getName(), "李四");
Assert.assertEquals(((NewTestEntity) entity).getNickName(), "小李");
}
class CustomPropertyCopier implements PropertyCopier<HashMap, NewTestEntity> {
@Override
public NewTestEntity copyProperties(HashMap source, NewTestEntity target) {
target.setName((String) source.get("name"));
target.setNickName((String) source.get("nickName"));
return target;
}
}
}

View File

@@ -1,11 +0,0 @@
package org.hswebframework.web.commons.entity.factory;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class NewTestEntity extends TestEntity {
private static final long serialVersionUID = -8151514416436801617L;
private String nickName;
}

View File

@@ -1,15 +0,0 @@
package org.hswebframework.web.commons.entity.factory;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.web.commons.entity.SimpleGenericEntity;
@Getter
@Setter
public class TestEntity extends SimpleGenericEntity<String> {
private static final long serialVersionUID = 2468328156748007412L;
private String name;
}

View File

@@ -1,81 +0,0 @@
package org.hswebframework.web.commons.entity.param;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.hswebframework.ezorm.core.param.Term;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.*;
public class TermExpressionParserTest {
@Test
public void testSimple() {
String expression = "name=测试 or age=10";
List<Term> terms = TermExpressionParser.parse(expression);
Assert.assertNotNull(terms);
Assert.assertEquals(terms.size(), 2);
Assert.assertEquals(terms.get(0).getColumn(), "name");
Assert.assertEquals(terms.get(0).getValue(), "测试");
Assert.assertEquals(terms.get(1).getColumn(), "age");
Assert.assertEquals(terms.get(1).getValue(), "10");
Assert.assertEquals(terms.get(1).getType(), Term.Type.or);
}
@Test
public void testNest() {
String expression = "name = 测试 and (age > 10 or age <= 20) and test like test2 and (age gt age2 or age btw age3,age4 or (age > 10 or age <= 20))";
System.out.println(expression);
List<Term> terms = TermExpressionParser.parse(expression);
System.out.println(JSON.toJSONString(terms, SerializerFeature.PrettyFormat));
Assert.assertNotNull(terms);
Assert.assertEquals(terms.size(), 4);
Assert.assertEquals(terms.get(1).getTerms().size(),2);
Assert.assertEquals(terms.get(0).getColumn(), "name");
Assert.assertEquals(terms.get(0).getValue(), "测试");
Assert.assertEquals(terms.get(1).getTerms().get(0).getColumn(), "age");
Assert.assertEquals(terms.get(1).getTerms().get(0).getTermType(), "gt");
Assert.assertEquals(terms.get(1).getTerms().get(0).getValue(), "10");
Assert.assertEquals(terms.get(1).getTerms().get(1).getColumn(), "age");
Assert.assertEquals(terms.get(1).getTerms().get(1).getTermType(), "lte");
Assert.assertEquals(terms.get(1).getTerms().get(1).getValue(), "20");
Assert.assertEquals(terms.get(1).getTerms().get(1).getType(), Term.Type.or);
Assert.assertEquals(terms.get(2).getColumn(), "test");
Assert.assertEquals(terms.get(2).getValue(), "test2");
Assert.assertEquals(terms.get(2).getTermType(), "like");
}
/**
* 测试日期字符串空格解析,'2019-07-26 12:00:00'
*/
@Test
public void testDateSpace() {
String expression = "(name=测试 or age=10) and (birth btw \"2019-07-26 12:00:00, 2019-08-04 12:00:00\" or startTime <= '2019-08-04 12:00:00') and finishTime >= '2019-08-01 00:00:00'";
List<Term> terms = TermExpressionParser.parse(expression);
System.out.println(JSON.toJSONString(terms, SerializerFeature.PrettyFormat));
Assert.assertEquals(terms.size(), 3);
Assert.assertEquals(terms.get(0).getTerms().size(), 2);
Assert.assertEquals(terms.get(1).getTerms().get(0).getColumn(), "birth");
Assert.assertEquals(terms.get(1).getTerms().get(0).getTermType(), "btw");
Assert.assertEquals(terms.get(1).getTerms().get(0).getValue(), "2019-07-26 12:00:00, 2019-08-04 12:00:00");
Assert.assertEquals(terms.get(1).getTerms().get(1).getColumn(), "startTime");
Assert.assertEquals(terms.get(1).getTerms().get(1).getTermType(), "lte");
Assert.assertEquals(terms.get(1).getTerms().get(1).getValue(), "2019-08-04 12:00:00");
Assert.assertEquals(terms.get(1).getTerms().get(1).getType(), Term.Type.or);
Assert.assertEquals(terms.get(2).getColumn(), "finishTime");
Assert.assertEquals(terms.get(2).getValue(), "2019-08-01 00:00:00");
Assert.assertEquals(terms.get(2).getTermType(), "gte");
}
}

View File

@@ -1,98 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ /*
~ * Copyright 2019 http://www.hswebframework.org
~ *
~ * Licensed under the Apache License, Version 2.0 (the "License");
~ * you may not use this file except in compliance with the License.
~ * You may obtain a copy of the License at
~ *
~ * http://www.apache.org/licenses/LICENSE-2.0
~ *
~ * Unless required by applicable law or agreed to in writing, software
~ * distributed under the License is distributed on an "AS IS" BASIS,
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ * See the License for the specific language governing permissions and
~ * limitations under the License.
~ */
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hsweb-commons</artifactId>
<groupId>org.hswebframework.web</groupId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hsweb-commons-utils</artifactId>
<description>通用模块-工具类</description>
<build>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
</build>
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hswebframework</groupId>
<artifactId>hsweb-expands-script</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>org.hswebframework</groupId>
<artifactId>hsweb-easy-orm-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2019 http://www.hswebframework.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.hswebframework.web;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
public final class AopUtils {
private AopUtils() {
}
public static <T extends Annotation> T findMethodAnnotation(Class targetClass, Method method, Class<T> annClass) {
Method m = method;
T a = AnnotationUtils.findAnnotation(m, annClass);
if (a != null) {
return a;
}
m = ClassUtils.getMostSpecificMethod(m, targetClass);
a = AnnotationUtils.findAnnotation(m, annClass);
if (a == null) {
List<Class> supers = new ArrayList<>(Arrays.asList(targetClass.getInterfaces()));
if (targetClass.getSuperclass() != Object.class) {
supers.add(targetClass.getSuperclass());
}
for (Class aClass : supers) {
if(aClass==null){
continue;
}
Method ims[] = new Method[1];
ReflectionUtils.doWithMethods(aClass, im -> {
if (im.getName().equals(method.getName()) && im.getParameterCount() == method.getParameterCount()) {
ims[0] = im;
}
});
if (ims[0] != null) {
a = findMethodAnnotation(aClass, ims[0], annClass);
if (a != null) {
return a;
}
}
}
}
return a;
}
public static <T extends Annotation> T findAnnotation(Class targetClass, Class<T> annClass) {
return AnnotationUtils.findAnnotation(targetClass, annClass);
}
public static <T extends Annotation> T findAnnotation(Class targetClass, Method method, Class<T> annClass) {
T a = findMethodAnnotation(targetClass, method, annClass);
if (a != null) {
return a;
}
return findAnnotation(targetClass, annClass);
}
public static <T extends Annotation> T findAnnotation(JoinPoint pjp, Class<T> annClass) {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method m = signature.getMethod();
Class<?> targetClass = pjp.getTarget().getClass();
return findAnnotation(targetClass, m, annClass);
}
public static String getMethodBody(JoinPoint pjp) {
StringBuilder methodName = new StringBuilder(pjp.getSignature().getName()).append("(");
MethodSignature signature = (MethodSignature) pjp.getSignature();
String[] names = signature.getParameterNames();
Class[] args = signature.getParameterTypes();
for (int i = 0, len = args.length; i < len; i++) {
if (i != 0) {
methodName.append(",");
}
methodName.append(args[i].getSimpleName()).append(" ").append(names[i]);
}
return methodName.append(")").toString();
}
public static Map<String, Object> getArgsMap(JoinPoint pjp) {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Map<String, Object> args = new LinkedHashMap<>();
String names[] = signature.getParameterNames();
for (int i = 0, len = names.length; i < len; i++) {
args.put(names[i], pjp.getArgs()[i]);
}
return args;
}
}

View File

@@ -1,28 +0,0 @@
package org.hswebframework.web;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author zhouhao
* @since 2.0
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext get() {
if (null == context) {
throw new UnsupportedOperationException("ApplicationContext not ready!");
}
return context;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (null == ApplicationContextHolder.context) {
ApplicationContextHolder.context = applicationContext;
}
}
}

View File

@@ -1,59 +0,0 @@
package org.hswebframework.web;
import java.util.*;
import java.util.function.Supplier;
/**
* List工具用于构建list等操作
* <pre>
* Lists.buildList("1","2")
* .add("3")
* .add("4","5","6")
* .get();
* </pre>
*
* @author zhouhao
*/
public class Lists {
public static <V> ListBuilder<V> buildList(Supplier<List<V>> supplier) {
return buildList(supplier.get());
}
public static <V> ListBuilder<V> buildList(V... array) {
return buildList(array.length == 0 ? new ArrayList<>() : new ArrayList<>(Arrays.asList(array)));
}
public static <V> ListBuilder<V> buildList(List<V> target) {
return new ListBuilder<>(target);
}
public static class ListBuilder<V> {
private final List<V> target;
private ListBuilder(List<V> target) {
Objects.requireNonNull(target);
this.target = target;
}
public ListBuilder<V> add(V value, V... more) {
this.target.add(value);
if (more.length > 0) {
addAll(Arrays.asList(more));
}
return this;
}
public ListBuilder<V> addAll(Collection<V> value) {
this.target.addAll(value);
return this;
}
public List<V> get() {
return target;
}
}
}

View File

@@ -1,54 +0,0 @@
package org.hswebframework.web;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
/**
* map工具类用于构造map等操作
* <p>
* <pre>
* Maps.<String, Object>buildMap()
* .put("name", "age")
* .put("age", 1)
* .get()
* </pre>
*
* @author zhouhao
*/
public final class Maps {
private Maps() {
}
public static <K, V> MapBuilder<K, V> buildMap(Map<K, V> target) {
return new MapBuilder<>(target);
}
public static <K, V> MapBuilder<K, V> buildMap() {
return new MapBuilder<>(new HashMap<K, V>());
}
public static <K, V> MapBuilder<K, V> buildMap(Supplier<Map<K, V>> mapSupplier) {
return new MapBuilder<>(mapSupplier.get());
}
public static class MapBuilder<K, V> {
final Map<K, V> target;
private MapBuilder(Map<K, V> target) {
Objects.requireNonNull(target);
this.target = target;
}
public MapBuilder<K, V> put(K key, V value) {
this.target.put(key, value);
return this;
}
public Map<K, V> get() {
return target;
}
}
}

View File

@@ -1,29 +0,0 @@
package org.hswebframework.web;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author zhouhao
*/
public class RegexUtils {
private static Set<Character> SPECIAL_WORDS = new HashSet<>(Arrays.asList('\\', '$', '(', ')', '*', '+', '.', '[', ']', '?', '^', '{', '}', '|'));
public static String escape(String regex) {
if (regex == null || regex.isEmpty()) {
return regex;
}
char[] chars = regex.toCharArray();
StringBuilder builder = new StringBuilder();
for (char aChar : chars) {
if (SPECIAL_WORDS.contains(aChar)) {
builder.append('\\');
}
builder.append(aChar);
}
return builder.toString();
}
}

View File

@@ -1,39 +0,0 @@
package org.hswebframework.web;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* @author zhouhao
*/
public class Sqls {
public static List<String> parse(String sqlText) {
String[] list = sqlText.split("[\n]");
List<String> sqlList = new ArrayList<>();
List<String> tmp = new ArrayList<>();
Stream.of(list)
.filter(s -> !s.startsWith("--") && s.trim().length() != 0)
.forEach(s1 -> {
if (s1.trim().endsWith(";")) {
s1 = s1.trim();
s1 = s1.substring(0, s1.length() - 1);
if (!StringUtils.isEmpty(s1))
tmp.add(s1);
sqlList.add(String.join("\n", tmp.toArray(new String[0])));
tmp.clear();
} else {
if (!StringUtils.isEmpty(s1))
tmp.add(s1);
}
});
if (!tmp.isEmpty()) {
sqlList.add(String.join("\n", tmp.toArray(new String[0])));
tmp.clear();
}
return sqlList;
}
}

View File

@@ -1,127 +0,0 @@
/*
*
* * Copyright 2019 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
/**
* ThreadLocal 工具类,通过在ThreadLocal存储map信息,来实现在ThreadLocal中维护多个信息
* <br>e.g.<code>
* ThreadLocalUtils.put("key",value);<br>
* ThreadLocalUtils.get("key");<br>
* ThreadLocalUtils.remove("key");<br>
* ThreadLocalUtils.getAndRemove("key");<br>
* ThreadLocalUtils.get("key",()-&gt;defaultValue);<br>
* ThreadLocalUtils.clear();<br>
* </code>
*
* @author zhouhao
* @since 2.0
*/
@SuppressWarnings("unchecked")
public final class ThreadLocalUtils {
private ThreadLocalUtils() {
}
private static final ThreadLocal<Map<String, Object>> local = ThreadLocal.withInitial(HashMap::new);
/**
* @return threadLocal中的全部值
*/
public static Map<String, Object> getAll() {
return new HashMap<>(local.get());
}
/**
* 设置一个值到ThreadLocal
*
* @param key 键
* @param value 值
* @param <T> 值的类型
* @return 被放入的值
* @see Map#put(Object, Object)
*/
public static <T> T put(String key, T value) {
local.get().put(key, value);
return value;
}
/**
* 删除参数对应的值
*
* @param key
* @see Map#remove(Object)
*/
public static void remove(String key) {
local.get().remove(key);
}
/**
* 清空ThreadLocal
*
* @see Map#clear()
*/
public static void clear() {
local.remove();
}
/**
* 从ThreadLocal中获取值
*
* @param key 键
* @param <T> 值泛型
* @return 值, 不存在则返回null, 如果类型与泛型不一致, 可能抛出{@link ClassCastException}
* @see Map#get(Object)
* @see ClassCastException
*/
public static <T> T get(String key) {
return ((T) local.get().get(key));
}
/**
* 从ThreadLocal中获取值,并指定一个当值不存在的提供者
*
* @see Supplier
* @since 3.0
*/
public static <T> T get(String key, Supplier<T> supplierOnNull) {
return ((T) local.get().computeIfAbsent(key, k -> supplierOnNull.get()));
}
/**
* 获取一个值后然后删除掉
*
* @param key 键
* @param <T> 值类型
* @return 值, 不存在则返回null
* @see this#get(String)
* @see this#remove(String)
*/
public static <T> T getAndRemove(String key) {
try {
return get(key);
} finally {
remove(key);
}
}
}

View File

@@ -1,15 +0,0 @@
package org.hswebframework.web.context;
import java.util.Map;
import java.util.Optional;
public interface Context {
<T> Optional<T> get(ContextKey<T> key);
<T> T getOrDefault(ContextKey<T> key,T defaultValue);
<T> void put(ContextKey<T> key,T value);
Map<String,Object> getAll();
}

View File

@@ -1,31 +0,0 @@
package org.hswebframework.web.context;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
public final class ContextKey<T> {
@Getter
private String key;
public static <T> ContextKey<T> of(String key) {
return new ContextKey<>(key);
}
public static <T> ContextKey<T> of(Class<T> key) {
return new ContextKey<>(key.getName());
}
public static ContextKey<String> string(String key) {
return of(key);
}
public static ContextKey<Integer> integer(String key) {
return of(key);
}
public static ContextKey<Boolean> bool(String key) {
return of(key);
}
}

View File

@@ -1,37 +0,0 @@
package org.hswebframework.web.context;
import reactor.core.publisher.Mono;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* @since 4.0.0
*/
public class ContextUtils {
public static Mono<Context> currentContext() {
return Mono.subscriberContext()
.<Context>handle((context, sink) -> {
if (context.hasKey(Context.class)) {
sink.next(context.get(Context.class));
}
})
.subscriberContext(acceptContext(ctx->{
}));
}
public static Function<reactor.util.context.Context, reactor.util.context.Context> acceptContext(Consumer<Context> contextConsumer) {
return context -> {
if (!context.hasKey(Context.class)) {
context = context.put(Context.class, new MapContext());
}
contextConsumer.accept(context.get(Context.class));
return context;
};
}
}

View File

@@ -1,33 +0,0 @@
package org.hswebframework.web.context;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
@SuppressWarnings("all")
class MapContext implements Context {
private Map<String, Object> map = new ConcurrentHashMap<>();
@Override
public <T> Optional<T> get(ContextKey<T> key) {
return Optional.ofNullable(map.get(key.getKey()))
.map(v -> ((T) v));
}
@Override
public <T> T getOrDefault(ContextKey<T> key, T defaultValue) {
return (T) map.computeIfAbsent(key.getKey(), __ -> defaultValue);
}
@Override
public <T> void put(ContextKey<T> key, T value) {
map.put(key.getKey(), value);
}
@Override
public Map<String, Object> getAll() {
return new HashMap<>(map);
}
}

View File

@@ -1,41 +0,0 @@
package org.hswebframework.web;
import lombok.SneakyThrows;
import org.junit.Assert;
import org.junit.Test;
import java.util.Collections;
import java.util.Map;
import static org.junit.Assert.*;
public class ExpressionUtilsTests {
@Test
public void testAnalytical() throws Exception {
String result = ExpressionUtils.analytical("test${1+2} ${''} ${1+4+5}", "spel");
Assert.assertEquals(result, "test3 10");
result = ExpressionUtils.analytical("test${#param}", Collections.singletonMap("param", "3"), "spel");
Assert.assertEquals(result, "test3");
}
@Test
@SneakyThrows
public void benchmark() {
String expression = "test${1+2} ${1+4+5}";
ExpressionUtils.analytical(expression, Collections.emptyMap(), "spel");
long time = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
ExpressionUtils.analytical(expression, Collections.emptyMap(), "spel");
}
System.out.println(System.currentTimeMillis() - time);
}
}

View File

@@ -1,90 +0,0 @@
package org.hswebframework.web;
import org.hswebframework.ezorm.core.dsl.Query;
import org.hswebframework.ezorm.core.param.QueryParam;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
public class HttpParameterConverterTests {
@Test
public void testConvertMap() {
Map<String, Object> target = new HashMap<>();
Map<String, Object> info = new HashMap<>();
info.put("nickName", "小宋");
info.put("address", "重庆");
Map<String, Object> loginInfo = new HashMap<>();
loginInfo.put("lastLoginIp", "127.0.0.1");
loginInfo.put("lastLoginTime", new Date());
loginInfo.put("lastLoginIp5Times", Arrays.asList("127.0.0.1", "localhost"));
info.put("loginInfo", loginInfo);
target.put("name", "admin");
target.put("age", 30);
target.put("money", new BigDecimal("1000000.00"));
target.put("createDate", new Date());
target.put("roles", Arrays.asList(1, 2, 3));
target.put("info", info);
HttpParameterConverter converter = new HttpParameterConverter(target);
Map<String, String> result = converter.convert();
System.out.println(result);
Assert.assertEquals(result.get("roles[0]"), "1");
Assert.assertEquals(result.get("roles[1]"), "2");
Assert.assertEquals(result.get("roles[2]"), "3");
Assert.assertEquals(result.get("name"), "admin");
Assert.assertEquals(result.get("info.nickName"), "小宋");
Assert.assertEquals(result.get("info.address"), "重庆");
Assert.assertEquals(result.get("info.loginInfo.lastLoginIp"), "127.0.0.1");
Assert.assertEquals(result.get("info.loginInfo.lastLoginIp5Times[0]"), "127.0.0.1");
Assert.assertEquals(result.get("info.loginInfo.lastLoginIp5Times[1]"), "localhost");
}
@Test
public void testConvertObject() {
QueryParam param = Query.of(new QueryParam())
.where("name", "张三")
.and().like("address", "%重庆%")
.nest()
.lt("age", 18)
.or()
.gt("age", 60)
.end()
.getParam();
HttpParameterConverter converter = new HttpParameterConverter(param);
Map<String, String> result = converter.convert();
System.out.println(result);
Assert.assertEquals(result.get("terms[0].column"), "name");
Assert.assertEquals(result.get("terms[0].value"), "张三");
Assert.assertEquals(result.get("terms[1].termType"), "like");
Assert.assertEquals(result.get("terms[1].value"), "%重庆%");
Assert.assertEquals(result.get("terms[2].terms[0].termType"), "lt");
Assert.assertEquals(result.get("terms[2].terms[0].value"), "18");
Assert.assertEquals(result.get("terms[1].value"), "%重庆%");
}
}

View File

@@ -1,29 +0,0 @@
package org.hswebframework.web;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import static org.junit.Assert.*;
public class ListsTests {
@Test
public void testCreate() {
assertEquals(Lists.buildList(2).add(1)
.get().get(0), (Integer) 2);
assertEquals(Lists.buildList(new ArrayList<>()).add(2,1)
.get().get(0), 2);
assertEquals(Lists.buildList(ArrayList::new)
.add(2,1)
.get()
.get(0), 2);
}
}

View File

@@ -1,27 +0,0 @@
package org.hswebframework.web;
import org.junit.Test;
import java.util.HashMap;
import static org.junit.Assert.*;
public class MapsTests {
@Test
public void testCreateMap() {
assertEquals(Maps.buildMap()
.put("1", 1)
.get().get("1"), 1);
assertEquals(Maps.buildMap(new HashMap<>())
.put("1", 1)
.get().get("1"), 1);
assertEquals(Maps.buildMap(HashMap::new)
.put("1", 1)
.get().get("1"), 1);
}
}

View File

@@ -1,26 +0,0 @@
package org.hswebframework.web;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
/**
* @author zhouhao
* @since 3.0.6
*/
public class ModuleUtilsTest {
@Test
public void test() {
ModuleUtils.ModuleInfo moduleInfo = ModuleUtils.getModuleByClass(ModuleUtilsTest.class);
Assert.assertNotNull(moduleInfo);
Assert.assertFalse(moduleInfo.isNone());
Assert.assertEquals(moduleInfo.getArtifactId(),"hsweb-commons-utils");
System.out.println(moduleInfo.getGitLocation());
System.out.println(moduleInfo.getGitClassLocation(Maps.class,10,12));
ModuleUtils.ModuleInfo noneInfo = ModuleUtils.getModuleByClass(Logger.class);
Assert.assertNotNull(noneInfo);
Assert.assertTrue(noneInfo.isNone());
}
}

View File

@@ -1,18 +0,0 @@
package org.hswebframework.web;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.*;
public class RegexUtilsTests {
@Test
public void test() {
Arrays.asList('\\', '$', '(', ')', '*', '+', '.', '[', ']', '?', '^', '{', '}', '|')
.forEach((s) -> assertEquals(RegexUtils.escape(String.valueOf(s)), "\\" + s));
}
}

View File

@@ -1,33 +0,0 @@
package org.hswebframework.web;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.*;
public class SqlsTests {
@Test
public void testParse() {
String sql = "select 1;\ndelete from user;";
List<String> strings = Sqls.parse(sql);
System.out.println(strings);
Assert.assertTrue(strings.size() == 2);
Assert.assertTrue("select 1".equals(strings.get(0)));
Assert.assertTrue("delete from user".equals(strings.get(1)));
sql = "select 1;\ndelete from user;\nselect * from user \nwhere name = 1 \n or name =2";
strings = Sqls.parse(sql);
System.out.println(strings);
Assert.assertTrue(strings.size() == 3);
Assert.assertEquals(strings.get(2),"select * from user \nwhere name = 1 \n or name =2");
}
}

View File

@@ -1,36 +0,0 @@
package org.hswebframework.web;
import org.junit.Assert;
import org.junit.Test;
import static org.junit.Assert.*;
public class ThreadLocalUtilsTests {
@Test
public void testAll() {
ThreadLocalUtils.put("test", "1");
Assert.assertEquals(ThreadLocalUtils.get("test"), "1");
ThreadLocalUtils.get("test2", () -> "2");
Assert.assertEquals(ThreadLocalUtils.get("test2"), "2");
Assert.assertEquals(ThreadLocalUtils.getAndRemove("test2"), "2");
Assert.assertTrue(ThreadLocalUtils.get("test2") == null);
ThreadLocalUtils.remove("test");
Assert.assertTrue(ThreadLocalUtils.get("test") == null);
ThreadLocalUtils.put("test", "1");
ThreadLocalUtils.put("test2", "2");
Assert.assertTrue(ThreadLocalUtils.getAll().size()==2);
ThreadLocalUtils.clear();
Assert.assertTrue(ThreadLocalUtils.getAll().size()==0);
}
}

View File

@@ -1,35 +0,0 @@
package org.hswebframework.web;
import org.junit.Assert;
import org.junit.Test;
import java.io.UnsupportedEncodingException;
import java.util.Map;
public class WebUtilTest {
@Test
public void queryStringToMap() throws UnsupportedEncodingException {
String parameter = "key1=value1&key2=value2&key3=&key4=key5=1";
Map<String, String> map = WebUtil.queryStringToMap(parameter, "utf-8");
System.out.println(map);
Assert.assertEquals(map.get("key1"), "value1");
Assert.assertEquals(map.get("key2"), "value2");
Assert.assertEquals(map.get("key3"), "");
Assert.assertEquals(map.get("key4"), "key5=1");
parameter = "key1=%e5%80%bc1&key2=%e5%80%bc2";
map = WebUtil.queryStringToMap(parameter, "utf-8");
System.out.println(map);
Assert.assertEquals(map.get("key1"), "值1");
Assert.assertEquals(map.get("key2"), "值2");
parameter = "key1=%D6%B51&key2=%D6%B52";
map = WebUtil.queryStringToMap(parameter, "gbk");
System.out.println(map);
Assert.assertEquals(map.get("key1"), "值1");
Assert.assertEquals(map.get("key2"), "值2");
}
}

View File

@@ -1,9 +0,0 @@
{
"groupId": "${project.groupId}",
"artifactId": "${project.artifactId}",
"path": "hsweb-commons/hsweb-commons-utils",
"gitCommitHash": "${git.commit.hash}",
"gitRepository": "http://github.com/hs-web/hsweb-framework",
"version": "${project.version}"
}

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.padual.com/java/logback.xsd"
debug="false" scan="true" scanPeriod="30 second">
<root level="warn">
</root>
</configuration>

View File

@@ -32,13 +32,6 @@
<artifactId>hsweb-commons</artifactId>
<packaging>pom</packaging>
<modules>
<module>hsweb-commons-entity</module>
<module>hsweb-commons-dao</module>
<module>hsweb-commons-service</module>
<module>hsweb-commons-controller</module>
<module>hsweb-commons-utils</module>
<module>hsweb-commons-model</module>
<module>hsweb-commons-bean</module>
<module>hsweb-commons-crud</module>
</modules>

View File

@@ -1,4 +1,4 @@
package org.hswebframework.web;
package org.hswebframework.web.utils;
import org.hswebframework.expands.script.engine.DynamicScriptEngine;
import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;

View File

@@ -1,4 +1,4 @@
package org.hswebframework.web;
package org.hswebframework.web.utils;
import org.apache.commons.beanutils.BeanMap;
import org.hswebframework.utils.time.DateFormatter;

View File

@@ -1,4 +1,4 @@
package org.hswebframework.web;
package org.hswebframework.web.utils;
import com.alibaba.fastjson.JSON;
import lombok.Getter;

Some files were not shown because too many files have changed in this diff Show More