From ce34b93b39c947a17d95f85fff6f0bea7df07cd8 Mon Sep 17 00:00:00 2001 From: Clive Yuan Date: Wed, 23 Dec 2020 23:25:59 +0800 Subject: [PATCH 1/4] init robin-base --- pom.xml | 8 + robin-base/pom.xml | 8 + .../cn/cliveyuan/robin/base/BaseMapper.java | 170 +++++++++++++- .../cn/cliveyuan/robin/base/BaseService.java | 179 +++++++++++++++ .../cliveyuan/robin/base/BaseServiceImpl.java | 207 +++++++++++++++++ .../robin/base/annotation/TableField.java | 33 +++ .../robin/base/annotation/TableId.java | 27 +++ .../robin/base/annotation/TableName.java | 27 +++ .../robin/base/condition/AbstractExample.java | 163 +++++++++++++ .../robin/base/condition/AbstractQuery.java | 35 +++ .../robin/base/condition/Compare.java | 126 +++++++++++ .../base/condition/ConditionExample.java | 15 ++ .../robin/base/condition/Conditional.java | 111 +++++++++ .../robin/base/condition/Criteria.java | 19 ++ .../robin/base/condition/Criterion.java | 148 ++++++++++++ .../robin/base/condition/Example.java | 17 ++ .../cliveyuan/robin/base/condition/Func.java | 90 ++++++++ .../base/condition/GeneratedCriteria.java | 192 ++++++++++++++++ .../robin/base/condition/LambdaCriteria.java | 26 +++ .../base/condition/PageQueryExample.java | 49 ++++ .../cliveyuan/robin/base/condition/Query.java | 29 +++ .../robin/base/condition/QueryExample.java | 15 ++ .../robin/base/condition/SqlKeyword.java | 41 ++++ .../robin/base/condition/SqlLike.java | 26 +++ .../robin/base/page/PageQueryRequest.java | 62 +++++ .../cliveyuan/robin/base/page/Pagination.java | 106 +++++++++ .../robin/base/provider/SqlProvider.java | 93 ++++++++ .../robin/base/support/ReflectEntity.java | 54 +++++ .../base/support/ReflectEntityHelper.java | 106 +++++++++ .../robin/base/support/ReflectField.java | 53 +++++ .../robin/base/support/SFunction.java | 15 ++ .../base/support/SqlProviderSupport.java | 123 ++++++++++ .../robin/base/util/AssertUtils.java | 27 +++ .../robin/base/util/LambdaUtils.java | 67 ++++++ .../robin/base/util/ReflectUtils.java | 214 ++++++++++++++++++ .../robin/base/util/RobinStrUtils.java | 36 +++ .../cliveyuan/robin/base/util/SqlUtils.java | 164 ++++++++++++++ robin-base/src/main/resources/sql-segment.xml | 2 +- 38 files changed, 2881 insertions(+), 2 deletions(-) create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/BaseService.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/BaseServiceImpl.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableField.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableId.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableName.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/AbstractExample.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/AbstractQuery.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Compare.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/ConditionExample.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Conditional.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Criteria.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Criterion.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Example.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Func.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/GeneratedCriteria.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/LambdaCriteria.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/PageQueryExample.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Query.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/QueryExample.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/SqlKeyword.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/condition/SqlLike.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/page/PageQueryRequest.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/page/Pagination.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/provider/SqlProvider.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectEntity.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectEntityHelper.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectField.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/support/SFunction.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/support/SqlProviderSupport.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/util/AssertUtils.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/util/LambdaUtils.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/util/ReflectUtils.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/util/RobinStrUtils.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/util/SqlUtils.java diff --git a/pom.xml b/pom.xml index 210b0e8..8366b01 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ 3.5.6 4.12 1.0.1 + 5.3.2 @@ -75,6 +76,13 @@ ${mybatis.version} provided + + + org.springframework + spring-beans + ${spring.version} + provided + diff --git a/robin-base/pom.xml b/robin-base/pom.xml index 6c327b1..ebeddc7 100644 --- a/robin-base/pom.xml +++ b/robin-base/pom.xml @@ -16,5 +16,13 @@ org.mybatis mybatis + + org.slf4j + slf4j-api + + + org.springframework + spring-beans + diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseMapper.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseMapper.java index 612a468..3abc32a 100644 --- a/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseMapper.java +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseMapper.java @@ -1,10 +1,178 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + package cn.cliveyuan.robin.base; +import cn.cliveyuan.robin.base.condition.Example; +import cn.cliveyuan.robin.base.provider.SqlProvider; +import org.apache.ibatis.annotations.DeleteProvider; +import org.apache.ibatis.annotations.InsertProvider; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.SelectProvider; +import org.apache.ibatis.annotations.UpdateProvider; + +import java.util.List; + /** + * 基础 Mapper + * * @author Clive Yuan - * @date 2020/12/23 + * @date 2020/10/28 */ public interface BaseMapper { + boolean USE_GENERATED_KEYS = true; + String KEY_PROPERTY = "id"; + + /** + * 选择性插入 + *

+ * 仅插入非null字段 + * + * @param entity 实体 + * @return 影响行数 + */ + @InsertProvider(SqlProvider.class) + @Options(useGeneratedKeys = USE_GENERATED_KEYS, keyProperty = KEY_PROPERTY) int insert(T entity); + + /** + * 全字段插入 + *

+ * 无论是否为null均进行插入 + * + * @param entity 实体 + * @return 影响行数 + */ + @InsertProvider(SqlProvider.class) + @Options(useGeneratedKeys = USE_GENERATED_KEYS, keyProperty = KEY_PROPERTY) + int insertAll(T entity); + + /** + * 批量选择性插入 + * + * @param list 实体列表 + * @return 影响行数 + */ + @InsertProvider(SqlProvider.class) + @Options(useGeneratedKeys = USE_GENERATED_KEYS, keyProperty = KEY_PROPERTY) + int batchInsert(@Param("list") List list); + + /** + * 根据ID删除 + * + * @param id 主键ID + * @return 影响行数 + */ + @DeleteProvider(SqlProvider.class) + int delete(@Param("id") Long id); + + /** + * 批量ID删除 + * + * @param ids ID列表 + * @return 影响行数 + */ + @DeleteProvider(SqlProvider.class) + int batchDelete(@Param("ids") List ids); + + /** + * 根据条件删除 + * + * @param example 条件构造器 + * @return 影响行数 + */ + @DeleteProvider(SqlProvider.class) + int deleteByExample(@Param("example") Example example); + + /** + * 选择性更新 + *

+ * 仅更新非null字段 + * + * @param entity 实体 + * @return 影响行数 + */ + @UpdateProvider(SqlProvider.class) + int update(@Param("entity") T entity); + + /** + * 全字段更新 + *

+ * 无论是否为null均进行更新 + * + * @param entity 实体 + * @return 影响行数 + */ + @UpdateProvider(SqlProvider.class) + int updateAll(@Param("entity") T entity); + + /** + * 根据条件选择性更新 + * + * @param entity 实体 + * @param example 条件构造器 + * @return 影响行数 + */ + @UpdateProvider(SqlProvider.class) + int updateByExample(@Param("entity") T entity, @Param("example") Example example); + + /** + * 根据条件更新全字段 + * + * @param entity 实体 + * @param example 条件构造器 + * @return 影响行数 + */ + @UpdateProvider(SqlProvider.class) + int updateByExampleAll(@Param("entity") T entity, @Param("example") Example example); + + /** + * 根据ID获取实体 + * + * @param id 主键ID + * @return 实体对象 + */ + @SelectProvider(SqlProvider.class) + T get(@Param("id") Long id); + + /** + * 批量获取实体 + * + * @param ids ID列表 + * @return 实体列表 + */ + @SelectProvider(SqlProvider.class) + List batchGet(@Param("ids") List ids); + + /** + * 根据条件获取实体 + *

+ * 多个时仅返回ID倒序排序首个 + * + * @param example 条件构造器 + * @return 实体对象 + */ + @SelectProvider(SqlProvider.class) + T getByExample(@Param("example") Example example); + + /** + * 根据条件列表查询 + * + * @param example 条件构造器 + * @return 实体列表 + */ + @SelectProvider(SqlProvider.class) + List list(@Param("example") Example example); + + /** + * 根据条件数量查询 + * + * @param example 条件构造器 + * @return 数量 + */ + @SelectProvider(SqlProvider.class) + int count(@Param("example") Example example); } diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseService.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseService.java new file mode 100644 index 0000000..4bcc731 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseService.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base; + +import cn.cliveyuan.robin.base.condition.PageQueryExample; +import cn.cliveyuan.robin.base.condition.Query; +import cn.cliveyuan.robin.base.page.Pagination; + +import java.util.List; + +/** + * 基础 Service + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public interface BaseService { + + /** + * 选择性插入 + *

+ * 仅插入非null字段 + * + * @param entity 实体 + * @return 影响行数 + */ + int insert(T entity); + + /** + * 全字段插入 + *

+ * 无论是否为null均进行插入 + * + * @param entity 实体 + * @return 影响行数 + */ + int insertAll(T entity); + + /** + * 选择性保存 + *

entity.id = null -> 插入

+ *

entity.id != null -> 更新

+ * + * @param entity 实体 + * @return 影响行数 + */ + int save(T entity); + + /** + * 全字段保存 + *

entity.id = null -> 插入

+ *

entity.id != null -> 更新

+ * + * @param entity 实体 + * @return 影响行数 + */ + int saveAll(T entity); + + /** + * 批量选择性插入 + * + * @param list 实体列表 + * @return 影响行数 + */ + int batchInsert(List list); + + /** + * 根据ID删除 + * + * @param id 主键ID + * @return 影响行数 + */ + int delete(Long id); + + /** + * 批量ID删除 + * + * @param ids ID列表 + * @return 影响行数 + */ + int batchDelete(List ids); + + /** + * 根据条件删除 + * + * @return 影响行数 + */ + int deleteByExample(Query query); + + /** + * 选择性更新 + *

+ * 仅更新非null字段 + * + * @param entity 实体 + * @return 影响行数 + */ + int update(T entity); + + /** + * 全字段更新 + *

+ * 无论是否为null均进行更新 + * + * @param entity 实体 + * @return 影响行数 + */ + int updateAll(T entity); + + /** + * 根据条件选择性更新 + * + * @param entity 实体 (set) + * @param query 查询条件 (where) + * @return 影响行数 + */ + int updateByExample(T entity, Query query); + + /** + * 根据条件更新全字段 + * + * @param entity 实体 (set) + * @param query 查询条件 (where) + * @return 影响行数 + */ + int updateByExampleAll(T entity, Query query); + + /** + * 根据ID获取实体 + * + * @param id 主键ID + * @return 实体对象 + */ + T get(Long id); + + /** + * 批量获取实体 + * + * @param ids ID列表 + * @return 实体列表 + */ + List batchGet(List ids); + + /** + * 根据条件获取实体 + *

+ * 多个时仅返回ID倒序排序首个 + * + * @param query 查询条件 + * @return 实体对象 + */ + T getByExample(Query query); + + /** + * 根据条件数量查询 + * + * @param query 查询条件 + * @return 条数 + */ + int count(Query query); + + /** + * 根据条件列表查询 + * + * @param query 查询条件 + * @return 实体列表 + */ + List list(Query query); + + /** + * 分页查询 + * + * @param query 查询条件 + * @return 实体分页 + */ + Pagination page(PageQueryExample query); +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseServiceImpl.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseServiceImpl.java new file mode 100644 index 0000000..52ea4f6 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseServiceImpl.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base; + +import cn.cliveyuan.robin.base.condition.Criteria; +import cn.cliveyuan.robin.base.condition.Example; +import cn.cliveyuan.robin.base.condition.GeneratedCriteria; +import cn.cliveyuan.robin.base.condition.LambdaCriteria; +import cn.cliveyuan.robin.base.condition.PageQueryExample; +import cn.cliveyuan.robin.base.condition.Query; +import cn.cliveyuan.robin.base.page.Pagination; +import cn.cliveyuan.robin.base.util.ReflectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 基础 Service 实现类 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +@SuppressWarnings({"serial", "unchecked"}) +public class BaseServiceImpl implements BaseService { + + @Autowired + private BaseMapper baseMapper; + + @Override + public int insert(T entity) { + this.checkEntity(entity); + return baseMapper.insert(entity); + } + + @Override + public int insertAll(T entity) { + this.checkEntity(entity); + return baseMapper.insertAll(entity); + } + + @Override + public int save(T entity) { + this.checkEntity(entity); + return ReflectUtils.isIdNull(entity) ? this.insert(entity) : this.update(entity); + } + + @Override + public int saveAll(T entity) { + this.checkEntity(entity); + return ReflectUtils.isIdNull(entity) ? this.insertAll(entity) : this.updateAll(entity); + } + + @Override + public int batchInsert(List list) { + this.checkEntityList(list); + return baseMapper.batchInsert(list); + } + + @Override + public int delete(Long id) { + this.checkId(id); + return baseMapper.delete(id); + } + + @Override + public int batchDelete(List ids) { + this.checkIdList(ids); + return baseMapper.batchDelete(ids); + } + + @Override + public int deleteByExample(Query query) { + this.checkQuery(query); + return baseMapper.deleteByExample(this.convertQueryToExample(query)); + } + + @Override + public int update(T entity) { + this.checkEntity(entity); + return baseMapper.update(entity); + } + + @Override + public int updateAll(T entity) { + this.checkEntity(entity); + return baseMapper.updateAll(entity); + } + + @Override + public int updateByExample(T entity, Query query) { + this.checkEntity(entity); + return baseMapper.updateByExample(entity, this.convertQueryToExample(query)); + } + + @Override + public int updateByExampleAll(T entity, Query query) { + this.checkEntity(entity); + return baseMapper.updateByExampleAll(entity, this.convertQueryToExample(query)); + } + + @Override + public T get(Long id) { + this.checkId(id); + return baseMapper.get(id); + } + + @Override + public List batchGet(List ids) { + this.checkIdList(ids); + return baseMapper.batchGet(ids); + } + + @Override + public T getByExample(Query query) { + this.checkQuery(query); + return baseMapper.getByExample(this.convertQueryToExample(query)); + } + + @Override + public int count(Query query) { + this.checkQuery(query); + return baseMapper.count(this.convertQueryToExample(query)); + } + + @Override + public List list(Query query) { + this.checkQuery(query); + return baseMapper.list(this.convertQueryToExample(query)); + } + + @Override + public Pagination page(PageQueryExample query) { + this.checkQuery(query); + Example example = this.convertQueryToExample(query); + int page = this.getPage(query.getPage()); + int rowsPerPage = this.getRowsPerPage(query.getRowsPerPage()); + example.setLimitStart(this.getOffset(page, rowsPerPage)); + example.setLimitEnd(rowsPerPage); + int totalCount = baseMapper.count(example); + if (totalCount == 0) { + return Pagination.buildEmpty(); + } + return new Pagination<>(totalCount, baseMapper.list(example), page, rowsPerPage); + } + + private Class getEntityClass() { + return ReflectUtils.getSuperClassGenericType(getClass(), 0); + } + + private Example convertQueryToExample(Query query) { + T entity = query.getEntity(); + if (Objects.nonNull(entity)) { + Map fieldAndValue = ReflectUtils.resolveEntityFieldAndValue(entity); + GeneratedCriteria criteria = query.getExistCriteria(); + if (criteria instanceof LambdaCriteria) { + LambdaCriteria lambdaCriteria = (LambdaCriteria) criteria; + fieldAndValue.forEach(lambdaCriteria::eq); + } else if (criteria instanceof Criteria) { + fieldAndValue.forEach(criteria::eq); + } + } + return (Example) query; + } + + private void checkId(Long id) { + Assert.notNull(id, "id can't be null"); + } + + private void checkEntity(T entity) { + Assert.notNull(entity, "entity can't be null"); + } + + private void checkQuery(Query query) { + Assert.notNull(query, "query can't be null"); + } + + private void checkIdList(List idList) { + Assert.isTrue(Objects.nonNull(idList) && idList.size() > 0, "ids can't be empty"); + } + + private void checkEntityList(List list) { + Assert.isTrue(Objects.nonNull(list) && list.size() > 0, "list can't be empty"); + } + + private int getPage(Integer page) { + if (Objects.isNull(page) || page <= 0) { + page = PageQueryExample.DEFAULT_PAGE_NO; + } + return page; + } + + private int getRowsPerPage(Integer rowsPerPage) { + if (Objects.isNull(rowsPerPage) || rowsPerPage <= 0) { + rowsPerPage = PageQueryExample.DEFAULT_PAGE_SIZE; + } + return rowsPerPage; + } + + private int getOffset(int page, int rowsPerPage) { + return (page - 1) * rowsPerPage; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableField.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableField.java new file mode 100644 index 0000000..691807e --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableField.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 表字段标识 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +public @interface TableField { + + /** + * 数据库字段值 + */ + String value() default ""; + + /** + * 是否保存时忽略 + */ + boolean ignoreSaving() default false; +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableId.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableId.java new file mode 100644 index 0000000..96048ab --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableId.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 表主键标识 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +public @interface TableId { + /** + * 字段名(该值可无) + */ + String value() default ""; +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableName.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableName.java new file mode 100644 index 0000000..cb35086 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/annotation/TableName.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 表名标识 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +public @interface TableName { + /** + * 实体对应的表名 + */ + String value() default ""; +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/AbstractExample.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/AbstractExample.java new file mode 100644 index 0000000..851467e --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/AbstractExample.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +import cn.cliveyuan.robin.base.util.RobinStrUtils; +import cn.cliveyuan.robin.base.util.SqlUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * 抽象查询示例 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +@SuppressWarnings({"serial", "unchecked"}) +public abstract class AbstractExample> implements Example { + + /** + * 排序语句 + */ + private String orderByClause; + + /** + * 是否去重 + */ + private boolean distinct; + + /** + * 条件 + */ + private List> oredCriteria; + + /** + * offset + */ + private int limitStart; + + /** + * limit + */ + private int limitEnd; + + public AbstractExample() { + oredCriteria = new ArrayList<>(); + } + + + public String getOrderByClause() { + return Objects.nonNull(orderByClause) ? orderByClause : "`id` DESC"; + } + + public boolean isDistinct() { + return distinct; + } + + public List> getOredCriteria() { + return oredCriteria; + } + + public int getLimitStart() { + return limitStart; + } + + public int getLimitEnd() { + return limitEnd; + } + + @Override + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + @Override + public void setLimitStart(int limitStart) { + this.limitStart = limitStart; + } + + @Override + public void setLimitEnd(int limitEnd) { + this.limitEnd = limitEnd; + } + + @Override + public Criteria createCriteria() { + Criteria criteria = this.createCriteriaInternal(); + if (oredCriteria.size() == 0) { + oredCriteria.add((GeneratedCriteria) criteria); + } + return criteria; + } + + @Override + public LambdaCriteria createLambdaCriteria() { + LambdaCriteria criteria = this.createLambdaCriteriaInternal(); + if (oredCriteria.size() == 0) { + oredCriteria.add((GeneratedCriteria) criteria); + } + return criteria; + } + + @Override + public void or(GeneratedCriteria criteria) { + oredCriteria.add(criteria); + } + + @Override + public Criteria or() { + Criteria criteria = this.createCriteriaInternal(); + oredCriteria.add((GeneratedCriteria) criteria); + return criteria; + } + + @Override + public LambdaCriteria orLambdaCriteria() { + LambdaCriteria criteria = this.createLambdaCriteriaInternal(); + oredCriteria.add((GeneratedCriteria) criteria); + return criteria; + } + + @Override + public void clear() { + oredCriteria.clear(); + orderByClause = null; + distinct = false; + } + + @Override + public GeneratedCriteria getExistCriteria() { + if (oredCriteria.size() > 0) { + return oredCriteria.get(0); + } + return this.createCriteria(); + } + + private Criteria createCriteriaInternal() { + return new Criteria<>(); + } + + private LambdaCriteria createLambdaCriteriaInternal() { + return new LambdaCriteria<>(); + } + + /** + * 添加排序 + * + * @param columnName 字段名 + * @param isAsc 是否正序 + */ + public void addOrderBy(String columnName, boolean isAsc) { + SqlUtils.checkColumnName(columnName); + if (Objects.isNull(this.orderByClause)) { + this.orderByClause = RobinStrUtils.EMPTY; + } + String direction = (isAsc ? SqlKeyword.ASC : SqlKeyword.DESC).getKeyword(); + String orderBy = String.format(", `%s` %s", columnName, direction); + this.orderByClause += orderBy; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/AbstractQuery.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/AbstractQuery.java new file mode 100644 index 0000000..77d562b --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/AbstractQuery.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +import java.util.Objects; + +/** + * 抽象查询 + * + * @author Clive Yuan + * @date 2020/10/30 + */ +public abstract class AbstractQuery extends ConditionExample implements Query { + + private T entity; + + @Override + public void setEntity(T entity) { + Objects.requireNonNull(entity); + this.entity = entity; + } + + @Override + public void clear() { + super.clear(); + entity = null; + } + + @Override + public T getEntity() { + return entity; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Compare.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Compare.java new file mode 100644 index 0000000..461949d --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Compare.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +import java.io.Serializable; + +/** + * 比较 + * + * @author Clive Yuan + * @date 2020/10/28 + */ +public interface Compare extends Serializable { + + /** + * 等于 = + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children eq(R column, Object val); + + /** + * 不等于 <> + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children ne(R column, Object val); + + /** + * 大于 > + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children gt(R column, Object val); + + /** + * 大于等于 >= + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children ge(R column, Object val); + + /** + * 小于 < + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children lt(R column, Object val); + + /** + * 小于等于 <= + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children le(R column, Object val); + + /** + * BETWEEN 值1 AND 值2 + * + * @param column 字段 + * @param val1 值1 + * @param val2 值2 + * @return children + */ + Children between(R column, Object val1, Object val2); + + /** + * NOT BETWEEN 值1 AND 值2 + * + * @param column 字段 + * @param val1 值1 + * @param val2 值2 + * @return children + */ + Children notBetween(R column, Object val1, Object val2); + + /** + * LIKE '%值%' + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children like(R column, Object val); + + /** + * NOT LIKE '%值%' + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children notLike(R column, Object val); + + /** + * LIKE '%值' + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children likeLeft(R column, Object val); + + /** + * LIKE '值%' + * + * @param column 字段 + * @param val 值 + * @return children + */ + Children likeRight(R column, Object val); +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/ConditionExample.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/ConditionExample.java new file mode 100644 index 0000000..3b752d9 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/ConditionExample.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +/** + * 条件示例 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class ConditionExample extends AbstractExample> { + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Conditional.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Conditional.java new file mode 100644 index 0000000..2c2654f --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Conditional.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +/** + * 条件构造器 + * + * @author Clive Yuan + * @date 2020/10/30 + */ +public interface Conditional { + + /** + * 创建条件 + * + *

    + *
  • 如果条件列表为空则默认放入
  • + *
  • 否则不放入
  • + *
+ * + * @return + */ + Criteria createCriteria(); + + /** + * 创建Lambda条件 + * + *
    + *
  • 如果条件列表为空则默认放入
  • + *
  • 否则不放入
  • + *
+ * + * @return + */ + LambdaCriteria createLambdaCriteria(); + + /** + * 设置或者条件 + * + * @param criteria 条件 + */ + void or(GeneratedCriteria criteria); + + /** + * 创建或者条件 + *

+ * 默认放入条件列表 + * + * @return + */ + Criteria or(); + + /** + * 创建Lambda或者条件 + * + *
    + *
  • 默认放入条件列表
  • + *
+ * + * @return + */ + LambdaCriteria orLambdaCriteria(); + + /** + * 获取首个条件 + * + *
    + *
  • 如果条件存在则获取
  • + *
  • 否则创建Criteria并放入
  • + *
+ * + * @return 需判断类型是LambdaCriteria还是Criteria + */ + GeneratedCriteria getExistCriteria(); + + /** + * 清除所有 + */ + void clear(); + + /** + * 设置是否去重 + * + * @param distinct 是否去重 + */ + void setDistinct(boolean distinct); + + /** + * offset + * + * @param limitStart offset + */ + void setLimitStart(int limitStart); + + /** + * limit + * + * @param limitEnd limit + */ + void setLimitEnd(int limitEnd); + + /** + * 添加排序 + * + * @param columnName 字段名 (如果字段名不合法将抛出异常) + * @param isAsc 是否正序 + */ + void addOrderBy(String columnName, boolean isAsc); +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Criteria.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Criteria.java new file mode 100644 index 0000000..4de0e52 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Criteria.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +/** + * 条件集合 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class Criteria extends GeneratedCriteria> { + + @Override + protected String columnToString(String column) { + return column; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Criterion.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Criterion.java new file mode 100644 index 0000000..8f597dc --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Criterion.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +import java.util.List; +import java.util.Objects; + +/** + * 条件明细 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class Criterion { + + private String condition; + + private Object value; + + private Object secondValue; + + private boolean noValue; + + private boolean singleValue; + + private boolean likeValue; + + private boolean betweenValue; + + private boolean listValue; + + private String typeHandler; + + private SqlLike sqlLike; + + public String getCondition() { + return condition; + } + + public Object getValue() { + return value; + } + + public Object getSecondValue() { + return secondValue; + } + + public boolean isNoValue() { + return noValue; + } + + public boolean isSingleValue() { + return singleValue; + } + + public boolean isBetweenValue() { + return betweenValue; + } + + public boolean isListValue() { + return listValue; + } + + public String getTypeHandler() { + return typeHandler; + } + + public boolean isLikeValue() { + return likeValue; + } + + public SqlLike getSqlLike() { + return sqlLike; + } + + protected Criterion(String condition) { + super(); + this.condition = condition; + this.typeHandler = null; + this.noValue = true; + } + + protected Criterion(String condition, Object value, String typeHandler) { + this(condition, value, typeHandler, (SqlLike) null); + } + + protected Criterion(String condition, Object value, String typeHandler, SqlLike sqlLike) { + super(); + this.condition = condition; + this.value = value; + this.typeHandler = typeHandler; + if (Objects.nonNull(sqlLike)) { + this.likeValue = true; + this.sqlLike = sqlLike; + } else { + if (value instanceof List) { + this.listValue = true; + } else { + this.singleValue = true; + } + } + } + + protected Criterion(String condition, Object value) { + this(condition, value, (String) null); + } + + protected Criterion(String condition, Object value, SqlLike sqlLike) { + this(condition, value, null, sqlLike); + } + + protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.secondValue = secondValue; + this.typeHandler = typeHandler; + this.betweenValue = true; + } + + protected Criterion(String condition, Object value, Object secondValue) { + this(condition, value, secondValue, null); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Criterion criterion = (Criterion) o; + return noValue == criterion.noValue && + singleValue == criterion.singleValue && + likeValue == criterion.likeValue && + betweenValue == criterion.betweenValue && + listValue == criterion.listValue && + Objects.equals(condition, criterion.condition) && + Objects.equals(value, criterion.value) && + Objects.equals(secondValue, criterion.secondValue) && + Objects.equals(typeHandler, criterion.typeHandler) && + sqlLike == criterion.sqlLike; + } + + @Override + public int hashCode() { + return Objects.hash(condition, value, secondValue, noValue, singleValue, likeValue, betweenValue, listValue, typeHandler, sqlLike); + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Example.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Example.java new file mode 100644 index 0000000..ca11beb --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Example.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +/** + * 查询示例 + * + *

用于Mapper层面

+ * + * @author Clive Yuan + * @date 2020/10/29 + */ +public interface Example extends Conditional { + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Func.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Func.java new file mode 100644 index 0000000..e877897 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Func.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; + +import static java.util.stream.Collectors.toList; + +/** + * 函数 + * + * @author Clive Yuan + * @date 2020/10/28 + */ +public interface Func extends Serializable { + + + /** + * 字段 IS NULL + *

例: isNull("name")

+ * + * @param column 字段 + * @return children + */ + Children isNull(R column); + + /** + * 字段 IS NOT NULL + *

例: isNotNull("name")

+ * + * @param column 字段 + * @return children + */ + Children isNotNull(R column); + + /** + * 字段 IN (value.get(0), value.get(1), ...) + *

例: in("id", Arrays.asList(1, 2, 3, 4, 5))

+ * + *
  • 如果集合为 empty 则不会进行 sql 拼接
  • + * + * @param column 字段 + * @param coll 数据集合 + * @return children + */ + Children in(R column, Collection coll); + + /** + * 字段 IN (v0, v1, ...) + *

    例: in("id", 1, 2, 3, 4, 5)

    + * + *
  • 如果动态数组为 empty 则不会进行 sql 拼接
  • + * + * @param column 字段 + * @param values 数据数组 + * @return children + */ + default Children in(R column, Object... values) { + return in(column, Arrays.stream(Optional.ofNullable(values).orElseGet(() -> new Object[]{})) + .collect(toList())); + } + + /** + * 字段 NOT IN (value.get(0), value.get(1), ...) + *

    例: notIn("id", Arrays.asList(1, 2, 3, 4, 5))

    + * + * @param column 字段 + * @param coll 数据集合 + * @return children + */ + Children notIn(R column, Collection coll); + + /** + * 字段 NOT IN (v0, v1, ...) + *

    例: notIn("id", 1, 2, 3, 4, 5)

    + * + * @param column 字段 + * @param values 数据数组 + * @return children + */ + default Children notIn(R column, Object... values) { + return notIn(column, Arrays.stream(Optional.ofNullable(values).orElseGet(() -> new Object[]{})) + .collect(toList())); + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/GeneratedCriteria.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/GeneratedCriteria.java new file mode 100644 index 0000000..7124259 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/GeneratedCriteria.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +import cn.cliveyuan.robin.base.util.SqlUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * 生成的条件集合 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +@SuppressWarnings({"serial", "unchecked"}) +public abstract class GeneratedCriteria implements Compare, Func { + + /** + * 占位符 + */ + protected final Children typedThis = (Children) this; + + private List criteria; + + public GeneratedCriteria() { + this.criteria = new ArrayList<>(); + } + + public boolean isValid() { + return criteria.size() > 0; + } + + public List getCriteria() { + return criteria; + } + + public void setCriteria(List criteria) { + this.criteria = criteria; + } + + protected void addCriterion(String condition) { + if (condition == null) { + throw new RuntimeException("Value for condition cannot be null"); + } + criteria.add(new Criterion(condition)); + } + + protected void addCriterion(R column, SqlKeyword sqlKeyword) { + this.addCriterion(this.contactCondition(this.columnToString(column), sqlKeyword)); + } + + protected void addCriterion(R column, SqlKeyword sqlKeyword, Object value) { + String columnName = this.columnToString(column); + this.addCriterion(this.contactCondition(columnName, sqlKeyword), value, columnName); + } + + protected void addCriterion(R column, SqlKeyword sqlKeyword, Object value1, Object value2) { + String columnName = this.columnToString(column); + this.addCriterion(this.contactCondition(columnName, sqlKeyword), value1, value2, columnName); + } + + protected void addCriterion(String condition, Object value, String property) { + if (value == null) { + throw new RuntimeException("Value for " + property + " cannot be null"); + } + criteria.removeIf(x -> Objects.equals(x.getCondition(), condition)); + criteria.add(new Criterion(condition, value)); + } + + protected void addLikeCriterion(SqlKeyword sqlKeyword, SqlLike sqlLike, Object value, R column) { + String columnName = this.columnToString(column); + if (value == null) { + throw new RuntimeException("Value for " + columnName + " cannot be null"); + } + criteria.add(new Criterion(this.contactCondition(columnName, sqlKeyword), value, sqlLike)); + } + + protected void addCriterion(String condition, Object value1, Object value2, String property) { + if (value1 == null || value2 == null) { + throw new RuntimeException("Between values for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value1, value2)); + } + + public String contactCondition(String columnName, SqlKeyword sqlKeyword) { + SqlUtils.checkColumnName(columnName); + return String.format("`%s` %s", columnName, sqlKeyword.getKeyword()); + } + + protected abstract String columnToString(R column); + + @Override + public Children eq(R column, Object val) { + addCriterion(column, SqlKeyword.EQ, val); + return typedThis; + } + + @Override + public Children ne(R column, Object val) { + addCriterion(column, SqlKeyword.NE, val); + return typedThis; + } + + @Override + public Children gt(R column, Object val) { + addCriterion(column, SqlKeyword.GT, val); + return typedThis; + } + + @Override + public Children ge(R column, Object val) { + addCriterion(column, SqlKeyword.GE, val); + return typedThis; + } + + @Override + public Children lt(R column, Object val) { + addCriterion(column, SqlKeyword.LT, val); + return typedThis; + } + + @Override + public Children le(R column, Object val) { + addCriterion(column, SqlKeyword.LE, val); + return typedThis; + } + + @Override + public Children between(R column, Object val1, Object val2) { + addCriterion(column, SqlKeyword.BETWEEN, val1, val2); + return typedThis; + } + + @Override + public Children notBetween(R column, Object val1, Object val2) { + addCriterion(column, SqlKeyword.NOT_BETWEEN, val1, val2); + return typedThis; + } + + @Override + public Children like(R column, Object val) { + addLikeCriterion(SqlKeyword.LIKE, SqlLike.DEFAULT, val, column); + return typedThis; + } + + @Override + public Children notLike(R column, Object val) { + addLikeCriterion(SqlKeyword.NOT_LIKE, SqlLike.DEFAULT, val, column); + return typedThis; + } + + @Override + public Children likeLeft(R column, Object val) { + addLikeCriterion(SqlKeyword.LIKE, SqlLike.LEFT, val, column); + return typedThis; + } + + @Override + public Children likeRight(R column, Object val) { + addLikeCriterion(SqlKeyword.LIKE, SqlLike.RIGHT, val, column); + return typedThis; + } + + @Override + public Children isNull(R column) { + addCriterion(column, SqlKeyword.IS_NULL); + return typedThis; + } + + @Override + public Children isNotNull(R column) { + addCriterion(column, SqlKeyword.IS_NOT_NULL); + return typedThis; + } + + @Override + public Children in(R column, Collection coll) { + addCriterion(column, SqlKeyword.IN, coll); + return typedThis; + } + + @Override + public Children notIn(R column, Collection coll) { + addCriterion(column, SqlKeyword.NOT_IN, coll); + return typedThis; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/LambdaCriteria.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/LambdaCriteria.java new file mode 100644 index 0000000..62df215 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/LambdaCriteria.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +import cn.cliveyuan.robin.base.support.SFunction; +import cn.cliveyuan.robin.base.util.LambdaUtils; + +/** + * Lambda条件 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class LambdaCriteria extends GeneratedCriteria, LambdaCriteria> { + @Override + protected String columnToString(SFunction column) { + return LambdaUtils.getFieldName(column); + } + + public LambdaCriteria eq(String column, String val) { + this.addCriterion(this.contactCondition(column, SqlKeyword.EQ), val, column); + return this; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/PageQueryExample.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/PageQueryExample.java new file mode 100644 index 0000000..aa7f45d --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/PageQueryExample.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +/** + * 分页查询示例 + * + * @author Clive Yuan + * @date 2020/10/30 + */ +public class PageQueryExample extends QueryExample { + /** + * 默认页码 + */ + public static final int DEFAULT_PAGE_NO = 1; + + /** + * 默认分页大小 + */ + public static final int DEFAULT_PAGE_SIZE = 10; + + /** + * 页号 (默认1) + */ + private Integer page = DEFAULT_PAGE_NO; + + /** + * 每页条数 (默认10) + */ + private Integer rowsPerPage = DEFAULT_PAGE_SIZE; + + public Integer getPage() { + return page; + } + + public void setPage(Integer page) { + this.page = page; + } + + public Integer getRowsPerPage() { + return rowsPerPage; + } + + public void setRowsPerPage(Integer rowsPerPage) { + this.rowsPerPage = rowsPerPage; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Query.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Query.java new file mode 100644 index 0000000..2541730 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/Query.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +/** + * 查询 + * + *

    用于Service层面

    + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public interface Query extends Conditional { + + /** + * 设置实体 + * + * @param entity 实体 + */ + void setEntity(T entity); + + /** + * 获取实体 + */ + T getEntity(); + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/QueryExample.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/QueryExample.java new file mode 100644 index 0000000..c6192fa --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/QueryExample.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +/** + * 查询示例 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class QueryExample extends AbstractQuery { + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/SqlKeyword.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/SqlKeyword.java new file mode 100644 index 0000000..8876e69 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/SqlKeyword.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + + +/** + * SQL 保留关键字枚举 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public enum SqlKeyword { + IN("IN"), + NOT_IN("NOT IN"), + LIKE("LIKE"), + NOT_LIKE("NOT LIKE"), + EQ("="), + NE("<>"), + GT(">"), + GE(">="), + LT("<"), + LE("<="), + IS_NULL("IS NULL"), + IS_NOT_NULL("IS NOT NULL"), + BETWEEN("BETWEEN"), + NOT_BETWEEN("NOT BETWEEN"), + ASC("ASC"), + DESC("DESC"); + + private final String keyword; + + SqlKeyword(String keyword) { + this.keyword = keyword; + } + + public String getKeyword() { + return keyword; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/SqlLike.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/SqlLike.java new file mode 100644 index 0000000..75aa669 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/SqlLike.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.condition; + +/** + * SQL like 枚举 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public enum SqlLike { + /** + * %值 + */ + LEFT, + /** + * 值% + */ + RIGHT, + /** + * %值% + */ + DEFAULT +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/page/PageQueryRequest.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/page/PageQueryRequest.java new file mode 100644 index 0000000..fc357f8 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/page/PageQueryRequest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.page; + +/** + * 分页查询 + * + * @author Clive Yuan + * @date 2020/12/07 + */ +public class PageQueryRequest { + + /** + * 页码 (最小为1) + */ + private Integer pageNo; + + /** + * 每页条数 (最小为1) + */ + private Integer pageSize; + + /** + * 实体 + */ + private T entity; + + public Integer getPageNo() { + return pageNo; + } + + public void setPageNo(Integer pageNo) { + this.pageNo = pageNo; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public T getEntity() { + return entity; + } + + public void setEntity(T entity) { + this.entity = entity; + } + + @Override + public String toString() { + return "PageQueryRequest{" + + "pageNo=" + pageNo + + ", pageSize=" + pageSize + + ", entity=" + entity + + '}'; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/page/Pagination.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/page/Pagination.java new file mode 100644 index 0000000..12f19fe --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/page/Pagination.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.page; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * 分页返回对象 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class Pagination implements Serializable { + + public static final int DEFAULT_PAGE_NUM = 1; + public static final int DEFAULT_PAGE_SIZE = 20; + public static final int MAX_PAGE_SIZE = 200; + private static final long serialVersionUID = 1L; + /** + * 总条数 + */ + private int totalCount; + + /** + * 数据列表 + */ + private List dataList; + + /** + * 是否有下一页 + */ + private boolean hasNext; + + public Pagination() { + + } + + /** + * 构建空分页 + * + * @return 返回空分页 + */ + public static Pagination buildEmpty() { + return new Pagination(0, new ArrayList<>(), DEFAULT_PAGE_NUM, DEFAULT_PAGE_SIZE); + } + + public Pagination(int totalCount, List dataList, int pageNo, int pageSize) { + this.totalCount = totalCount; + this.dataList = dataList; + int pageNum = (totalCount / pageSize) + ((totalCount % pageSize == 0) ? 0 : 1); + this.hasNext = pageNo < pageNum; + } + + public int getTotalCount() { + return totalCount; + } + + public void setTotalCount(int totalCount) { + this.totalCount = totalCount; + } + + public List getDataList() { + return dataList; + } + + public void setDataList(List dataList) { + this.dataList = dataList; + } + + public boolean isHasNext() { + return hasNext; + } + + public void setHasNext(boolean hasNext) { + this.hasNext = hasNext; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Pagination that = (Pagination) o; + return totalCount == that.totalCount && + hasNext == that.hasNext && + Objects.equals(dataList, that.dataList); + } + + @Override + public int hashCode() { + return Objects.hash(totalCount, dataList, hasNext); + } + + @Override + public String toString() { + return "Pagination{" + + "totalCount=" + totalCount + + ", dataList=" + dataList + + ", hasNext=" + hasNext + + '}'; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/provider/SqlProvider.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/provider/SqlProvider.java new file mode 100644 index 0000000..58f06e3 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/provider/SqlProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.provider; + +import cn.cliveyuan.robin.base.support.SqlProviderSupport; +import org.apache.ibatis.builder.annotation.ProviderContext; +import org.apache.ibatis.builder.annotation.ProviderMethodResolver; + +import java.util.Map; + +/** + * SQL提供者 + * + * @author Clive Yuan + * @date 2020/10/28 + */ +public class SqlProvider implements ProviderMethodResolver { + + public String insert(ProviderContext providerContext, T entity) { + SqlProviderSupport.checkEntity(entity); + return SqlProviderSupport.getSqlScript("insert", providerContext); + } + + public String insertAll(ProviderContext providerContext, T entity) { + SqlProviderSupport.checkEntity(entity); + return SqlProviderSupport.getSqlScript("insertAll", providerContext); + } + + public String batchInsert(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkEntityList(paramMap); + return SqlProviderSupport.getSqlScript("batchInsert", providerContext); + } + + public String delete(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkId(paramMap); + return SqlProviderSupport.getSqlScript("delete", providerContext); + } + + public String batchDelete(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkIds(paramMap); + return SqlProviderSupport.getSqlScript("batchDelete", providerContext); + } + + public String deleteByExample(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkCondition(paramMap); + return SqlProviderSupport.getSqlScript("deleteByExample", providerContext); + } + + public String update(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkEntity(paramMap); + return SqlProviderSupport.getSqlScript("update", providerContext); + } + + public String updateAll(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkEntity(paramMap); + return SqlProviderSupport.getSqlScript("updateAll", providerContext); + } + + public String updateByExample(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkCondition(paramMap); + return SqlProviderSupport.getSqlScript("updateByExample", providerContext); + } + + public String updateByExampleAll(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkCondition(paramMap); + return SqlProviderSupport.getSqlScript("updateByExampleAll", providerContext); + } + + public String get(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkId(paramMap); + return SqlProviderSupport.getSqlScript("get", providerContext); + } + + public String batchGet(ProviderContext providerContext, Map paramMap) { + SqlProviderSupport.checkIds(paramMap); + return SqlProviderSupport.getSqlScript("batchGet", providerContext); + } + + public String getByExample(ProviderContext providerContext) { + return SqlProviderSupport.getSqlScript("getByExample", providerContext); + } + + public String list(ProviderContext providerContext) { + return SqlProviderSupport.getSqlScript("list", providerContext); + } + + public String count(ProviderContext providerContext) { + return SqlProviderSupport.getSqlScript("count", providerContext); + } + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectEntity.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectEntity.java new file mode 100644 index 0000000..c434448 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectEntity.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.support; + +import java.io.Serializable; +import java.util.List; + +/** + * 反射实体对象 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class ReflectEntity implements Serializable { + + private String simpleName; + private String tableName; + private List fields; + private ReflectEntityHelper reflectEntityHelper; + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public List getFields() { + return fields; + } + + public void setFields(List fields) { + this.fields = fields; + } + + public String getSimpleName() { + return simpleName; + } + + public void setSimpleName(String simpleName) { + this.simpleName = simpleName; + } + + public ReflectEntityHelper getReflectEntityHelper() { + return reflectEntityHelper; + } + + public void setReflectEntityHelper(ReflectEntityHelper reflectEntityHelper) { + this.reflectEntityHelper = reflectEntityHelper; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectEntityHelper.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectEntityHelper.java new file mode 100644 index 0000000..a384d14 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectEntityHelper.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.support; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 反射实体帮助类 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class ReflectEntityHelper { + + private final Map variableMap = new ConcurrentHashMap<>(); + private final ReflectEntity reflectEntity; + + private ReflectEntityHelper(ReflectEntity reflectEntity) { + this.reflectEntity = reflectEntity; + } + + public static ReflectEntityHelper build(ReflectEntity reflectEntity) { + Objects.requireNonNull(reflectEntity); + ReflectEntityHelper reflectEntityHelper = new ReflectEntityHelper(reflectEntity); + reflectEntityHelper.initVariableMap(); + return reflectEntityHelper; + } + + private void initVariableMap() { + variableMap.put("tableName", reflectEntity.getTableName()); + variableMap.put("allFieldsString", allFieldsString()); + variableMap.put("fieldsString", fieldsString()); + variableMap.put("fieldsParamString", fieldsParamString()); + variableMap.put("itemFieldsParamString", itemFieldsParamString()); + variableMap.put("conditionalFieldsString", conditionalFieldsString()); + variableMap.put("conditionalFieldsParamString", conditionalFieldsParamString()); + variableMap.put("conditionalSetFieldsString", conditionalSetFieldsString()); + variableMap.put("setFieldsString", setFieldsString()); + } + + public Map getVariableMap() { + return variableMap; + } + + /** + * 获取非保存忽略字段 + * + * @return + */ + public List getNotIgnoreSavingFields() { + return reflectEntity.getFields().stream().filter(x -> !x.getIgnoreSaving()).collect(Collectors.toList()); + } + + /** + * 获取全部字段, 以逗号分隔 + * + * @return + */ + private String allFieldsString() { + return reflectEntity.getFields().stream().map(x -> String.format("`%s`", x.getFieldName())) + .collect(Collectors.joining(",")); + } + + private String fieldsString() { + return this.getNotIgnoreSavingFields().stream().map(x -> String.format("`%s`", x.getFieldName())) + .collect(Collectors.joining(",")); + } + + private String fieldsParamString() { + return this.getNotIgnoreSavingFields().stream().map(x -> String.format("#{%s}", x.getName())) + .collect(Collectors.joining(",")); + } + + private String itemFieldsParamString() { + return this.getNotIgnoreSavingFields().stream().map(x -> String.format("#{item.%s}", x.getName())) + .collect(Collectors.joining(",")); + } + + private String conditionalFieldsString() { + return this.getNotIgnoreSavingFields().stream().map(x -> String.format("`%s`,", x.getName(), x.getFieldName())) + .collect(Collectors.joining("")); + } + + private String conditionalFieldsParamString() { + return this.getNotIgnoreSavingFields().stream().map(x -> String.format("#{%s},", x.getName(), x.getName())) + .collect(Collectors.joining("")); + } + + private String conditionalSetFieldsString() { + return this.getNotIgnoreSavingFields().stream().map(x -> String.format("`%s` = #{entity.%s},", x.getName(), x.getFieldName(), x.getName())) + .collect(Collectors.joining("")); + } + + private String setFieldsString() { + return this.getNotIgnoreSavingFields().stream().map(x -> String.format("`%s` = #{entity.%s}", x.getFieldName(), x.getName())) + .collect(Collectors.joining(",")); + } + + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectField.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectField.java new file mode 100644 index 0000000..7a8398c --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/ReflectField.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.support; + +import java.io.Serializable; + +/** + * 反射字段 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class ReflectField implements Serializable { + + private String name; + private String fieldName; + private Boolean isPrimaryKey; + private Boolean ignoreSaving; + + public String getFieldName() { + return fieldName; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + public Boolean getPrimaryKey() { + return isPrimaryKey; + } + + public void setPrimaryKey(Boolean primaryKey) { + isPrimaryKey = primaryKey; + } + + public Boolean getIgnoreSaving() { + return ignoreSaving; + } + + public void setIgnoreSaving(Boolean ignoreSaving) { + this.ignoreSaving = ignoreSaving; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/support/SFunction.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/SFunction.java new file mode 100644 index 0000000..470a3a1 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/SFunction.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.support; + +import java.io.Serializable; +import java.util.function.Function; + +/** + * 支持序列化的 Function + */ +@FunctionalInterface +public interface SFunction extends Function, Serializable { +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/support/SqlProviderSupport.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/SqlProviderSupport.java new file mode 100644 index 0000000..4705079 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/support/SqlProviderSupport.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.support; + +import cn.cliveyuan.robin.base.condition.ConditionExample; +import cn.cliveyuan.robin.base.util.AssertUtils; +import cn.cliveyuan.robin.base.util.ReflectUtils; +import cn.cliveyuan.robin.base.util.SqlUtils; +import org.apache.ibatis.builder.annotation.ProviderContext; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +/** + * SQL提供者支持类 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +@SuppressWarnings("unchecked") +public class SqlProviderSupport { + private static final Map SQL_CACHE = new ConcurrentHashMap<>(); + + public static String getSqlScript(String method, ProviderContext providerContext) { + return getSqlScript(method, getEntityClassFromProviderContext(providerContext)); + } + + public static String getSqlScript(String method, Class entityClass) { + return SqlProviderSupport.getSqlScript(method, entityClass, () -> { + ReflectEntity reflectEntity = ReflectUtils.resolveEntity(entityClass); + ReflectEntityHelper reflectEntityHelper = reflectEntity.getReflectEntityHelper(); + Map paramMap = reflectEntityHelper.getVariableMap(); + String sqlSegment = SqlUtils.getSqlSegmentMap().get(method); + Objects.requireNonNull(sqlSegment, String.format("sqlSegment named '%s' is not exist", method)); + return SqlUtils.resolveMapperScript(sqlSegment, paramMap); + }); + } + + public static String getSqlScript(String method, Class clazz, Supplier supplier) { + ReflectEntity reflectEntity = ReflectUtils.resolveEntity(clazz); + String tableName = reflectEntity.getTableName(); + String key = tableName.concat(".").concat(method); + return Optional.ofNullable(SQL_CACHE.get(key)).orElseGet(() -> { + String sqlScript = supplier.get(); + SQL_CACHE.put(key, sqlScript); + return sqlScript; + }); + } + + public static Class getEntityClassFromEntity(Map paramMap) { + T entity = (T) paramMap.get("entity"); + AssertUtils.notNull(entity, "entity cant be null"); + return entity.getClass(); + } + + public static Class getEntityClassFromProviderContext(ProviderContext providerContext) { + return ReflectUtils.getClassGenericType(true, providerContext.getMapperType(), 0); + } + + public static Class getEntityClassFromMap(Map paramMap) { + Class entityClass = (Class) paramMap.get("entityClass"); + AssertUtils.notNull(entityClass, "entityClass cant be null"); + return entityClass; + } + + public static Class getEntityClassFromList(Map paramMap) { + List list = (List) paramMap.get("list"); + AssertUtils.isTrue(list.size() > 0, "list is empty"); + T entity = list.get(0); + AssertUtils.notNull(entity, "entity in list cant be null"); + return entity.getClass(); + } + + /** + * 检查条件, 避免误操作 + * + * @param paramMap 参数map + */ + public static void checkCondition(Map paramMap) { + ConditionExample example = (ConditionExample) paramMap.get("example"); + AssertUtils.notNull(example, "example cant be null"); + AssertUtils.isTrue(example.getOredCriteria().size() > 0, "Condition can't be empty"); + } + + /** + * 检查id集合 + * + * @param paramMap 参数map + */ + public static void checkIds(Map paramMap) { + List ids = (List) paramMap.get("ids"); + AssertUtils.isTrue(Objects.nonNull(ids) && ids.size() > 0, "ids can't be empty"); + } + + /** + * 检查实体集合 + * + * @param paramMap 参数map + */ + public static void checkEntityList(Map paramMap) { + List list = (List) paramMap.get("list"); + AssertUtils.isTrue(Objects.nonNull(list) && list.size() > 0, "list can't be empty"); + } + + public static void checkEntity(Map paramMap) { + checkEntity((T) paramMap.get("entity")); + } + + public static void checkEntity(T entity) { + AssertUtils.notNull(entity, "entity can't be empty"); + } + + public static void checkId(Map paramMap) { + Long id = (Long) paramMap.get("id"); + AssertUtils.notNull(id, "id can't be empty"); + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/AssertUtils.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/AssertUtils.java new file mode 100644 index 0000000..dce2357 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/AssertUtils.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.util; + +import com.sun.istack.internal.Nullable; + +/** + * @author Clive Yuan + * @date 2020/12/23 + */ +public class AssertUtils { + + public static void isTrue(boolean expression, String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + } + + public static void notNull(@Nullable Object object, String message) { + if (object == null) { + throw new IllegalArgumentException(message); + } + } + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/LambdaUtils.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/LambdaUtils.java new file mode 100644 index 0000000..d0fe970 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/LambdaUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.util; + +import cn.cliveyuan.robin.base.support.SFunction; + +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Lambda工具 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class LambdaUtils { + + /** + * SerializedLambda 反序列化缓存 + */ + private static final Map FUNC_CACHE = new ConcurrentHashMap<>(); + + public static String getFieldName(SFunction func) { + SerializedLambda serializedLambda = resolveSerializedLambdaCache(func); + String getter = serializedLambda.getImplMethodName(); + return resolveFieldName(getter); + } + + private static SerializedLambda resolveSerializedLambdaCache(SFunction func) { + Class clazz = func.getClass(); + String name = clazz.getName(); + return Optional.ofNullable(FUNC_CACHE.get(name)) + .orElseGet(() -> { + SerializedLambda lambda = resolveSerializedLambda(func); + FUNC_CACHE.put(name, lambda); + return lambda; + }); + } + + private static SerializedLambda resolveSerializedLambda(SFunction func) { + try { + // 通过获取对象方法,判断是否存在该方法 + Method method = func.getClass().getDeclaredMethod("writeReplace"); + method.setAccessible(Boolean.TRUE); + // 利用jdk的SerializedLambda 解析方法引用 + return (SerializedLambda) method.invoke(func); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + private static String resolveFieldName(String getMethodName) { + if (getMethodName.startsWith("get")) { + getMethodName = getMethodName.substring(3); + } else if (getMethodName.startsWith("is")) { + getMethodName = getMethodName.substring(2); + } + // 小写第一个字母 + return SqlUtils.firstToLowerCase(getMethodName); + } + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/ReflectUtils.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/ReflectUtils.java new file mode 100644 index 0000000..d9ff4aa --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/ReflectUtils.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.util; + +import cn.cliveyuan.robin.base.annotation.TableField; +import cn.cliveyuan.robin.base.annotation.TableId; +import cn.cliveyuan.robin.base.annotation.TableName; +import cn.cliveyuan.robin.base.support.ReflectEntity; +import cn.cliveyuan.robin.base.support.ReflectEntityHelper; +import cn.cliveyuan.robin.base.support.ReflectField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 反射工具 + * + * @author Clive Yuan + * @date 2020/10/29 + */ +public class ReflectUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReflectUtils.class); + private static final Map REFLECT_ENTITY_CACHE = new ConcurrentHashMap<>(); + private static final String GET_METHOD_PREFIX = "get"; + private static final String PRIMARY_KEY_NAME = "id"; + + public static Class getSuperClassGenericType(final Class clazz, final int index) { + return getClassGenericType(false, clazz, index); + } + + public static Class getClassGenericType(final boolean isInterface, final Class clazz, final int index) { + Type genType = clazz.getGenericSuperclass(); + if (isInterface) { + Type[] genericInterfaces = clazz.getGenericInterfaces(); + if (genericInterfaces.length > 0) { + genType = genericInterfaces[0]; + } + } + if (!(genType instanceof ParameterizedType)) { + LOGGER.warn(String.format("Warn: %s's superclass not ParameterizedType", clazz.getSimpleName())); + return Object.class; + } + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + if (index >= params.length || index < 0) { + LOGGER.warn(String.format("Warn: Index: %s, Size of %s's Parameterized Type: %s .", index, + clazz.getSimpleName(), params.length)); + return Object.class; + } + if (!(params[index] instanceof Class)) { + LOGGER.warn(String.format("Warn: %s not set the actual class on superclass generic parameter", + clazz.getSimpleName())); + return Object.class; + } + return (Class) params[index]; + } + + /** + * 解析反射实体 + * + * @param clazz 实体类对象 + * @return + */ + public static ReflectEntity resolveEntity(Class clazz) { + String name = clazz.getName(); + return Optional.ofNullable(REFLECT_ENTITY_CACHE.get(name)) + .orElseGet(() -> { + ReflectEntity reflectEntity = doResolveEntity(clazz); + REFLECT_ENTITY_CACHE.put(name, reflectEntity); + return reflectEntity; + }); + } + + private static ReflectEntity doResolveEntity(Class clazz) { + Objects.requireNonNull(clazz); + ReflectEntity reflectEntity = new ReflectEntity(); + String simpleName = clazz.getSimpleName(); + String tableName = simpleName; + // 读取类 + TableName tableNameAnnotation = clazz.getAnnotation(TableName.class); + if (Objects.nonNull(tableNameAnnotation)) { + tableName = tableNameAnnotation.value(); + } + reflectEntity.setTableName(tableName); + reflectEntity.setSimpleName(simpleName); + + // 读取字段 + List fields = new ArrayList<>(); + reflectEntity.setFields(fields); + List declaredFields = getDeclaredFields(clazz); + for (Field declaredField : declaredFields) { + String name = declaredField.getName(); + boolean primaryKey = false; + boolean ignoreSaving = false; + String fieldName = name; + TableId tableIdAnnotation = declaredField.getAnnotation(TableId.class); + if (Objects.nonNull(tableIdAnnotation)) { + if (RobinStrUtils.isNotBlank(tableIdAnnotation.value())) { + fieldName = tableIdAnnotation.value(); + } + primaryKey = true; + ignoreSaving = true; + } + + TableField tableFieldAnnotation = declaredField.getAnnotation(TableField.class); + if (Objects.nonNull(tableFieldAnnotation)) { + if (RobinStrUtils.isNotBlank(tableFieldAnnotation.value())) { + fieldName = tableFieldAnnotation.value(); + } + ignoreSaving = tableFieldAnnotation.ignoreSaving(); + } + + ReflectField field = new ReflectField(); + field.setName(name); + field.setFieldName(fieldName); + field.setIgnoreSaving(ignoreSaving); + field.setPrimaryKey(primaryKey); + fields.add(field); + } + reflectEntity.setReflectEntityHelper(ReflectEntityHelper.build(reflectEntity)); + return reflectEntity; + } + + + public static boolean isIdNull(T entity) { + return isFieldNull(entity, PRIMARY_KEY_NAME); + } + + public static boolean isFieldNull(T entity, String fieldName) { + Object value = methodInvoke(getMethod(entity.getClass(), fieldName), entity); + return Objects.isNull(value); + } + + /** + * 获取实体字段和值 + * + * @param entity 实体对象 + * @param 实体对象类 + * @return + */ + public static Map resolveEntityFieldAndValue(T entity) { + Map fieldValueMap = new HashMap<>(); + Class entityClass = entity.getClass(); + List declaredFields = getDeclaredFields(entityClass); + List fieldNames = declaredFields.stream() + .map(Field::getName) + .collect(Collectors.toList()); + + Method[] declaredMethods = entityClass.getDeclaredMethods(); + Map getMethodMap = Arrays.stream(declaredMethods).filter(x -> x.getName().startsWith(GET_METHOD_PREFIX)) + .collect(Collectors.toMap(Method::getName, Function.identity())); + + fieldNames.forEach(fieldName -> { + String methodName = GET_METHOD_PREFIX.concat(SqlUtils.firstToUpperCase(fieldName)); + Method method = getMethodMap.get(methodName); + Object value = methodInvoke(method, entity); + if (Objects.nonNull(value)) { + fieldValueMap.put(fieldName, value.toString()); + } + }); + return fieldValueMap; + } + + public static Object methodInvoke(Method method, Object obj, Object... args) { + try { + method.setAccessible(true); + return method.invoke(obj, args); + } catch (IllegalAccessException | InvocationTargetException e) { + LOGGER.error("methodInvoke error", e); + throw new RuntimeException(e); + } + } + + public static Method getMethod(Class entityClass, String fieldName) { + try { + return entityClass.getDeclaredMethod(GET_METHOD_PREFIX.concat(SqlUtils.firstToUpperCase(fieldName))); + } catch (NoSuchMethodException e) { + LOGGER.error("getMethod error", e); + throw new RuntimeException(e); + } + } + + /** + * 获取所有声明字段,且非static修饰 + * + * @param clazz 类对象 + * @return + */ + private static List getDeclaredFields(Class clazz) { + Field[] declaredFields = clazz.getDeclaredFields(); + return Arrays.stream(declaredFields).filter(x -> !Modifier.isStatic(x.getModifiers())) + .collect(Collectors.toList()); + + } + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/RobinStrUtils.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/RobinStrUtils.java new file mode 100644 index 0000000..fc1e1c1 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/RobinStrUtils.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.util; + +/** + * @author Clive Yuan + * @date 2020/12/23 + */ +public class RobinStrUtils { + + public static final String EMPTY = ""; + + public static boolean isBlank(final CharSequence cs) { + final int strLen = length(cs); + if (strLen == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + return true; + } + + public static int length(final CharSequence cs) { + return cs == null ? 0 : cs.length(); + } + + public static boolean isNotBlank(final CharSequence cs) { + return !isBlank(cs); + } + +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/SqlUtils.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/SqlUtils.java new file mode 100644 index 0000000..49ffbfa --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/SqlUtils.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.base.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Clive Yuan + * @date 2020/10/29 + */ +public class SqlUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(SqlUtils.class); + private static final Pattern SQL_PARAM_PATTERN = Pattern.compile("(@\\{)([\\w]+)(\\})"); + private static final Pattern TPL_PARAM_PATTERN = Pattern.compile("(@sql\\{)([\\w]+)(\\})"); + private static final String SQL_SEGMENT_PATH = "/code-generator/sql-segment.xml"; + private static final Map SQL_SEGMENT = new ConcurrentHashMap<>(); + private static final String INJECTION_REGEX = "[A-Za-z0-9\\_\\-\\+\\.]+"; + + + /** + * 检查字段名是否合法 + * + * @param columnName 字段名 + */ + public static void checkColumnName(String columnName) { + AssertUtils.isTrue(!SqlUtils.isSQLInjection(columnName), "columnName is illegal: " + columnName); + } + + public static boolean isSQLInjection(String str) { + return !Pattern.matches(INJECTION_REGEX, str); + } + + /** + * get sql segment + * + * @return + */ + public static Map getSqlSegmentMap() { + synchronized (SQL_SEGMENT) { + if (SQL_SEGMENT.isEmpty()) { + SQL_SEGMENT.putAll(doResolveSqlSegment()); + } + } + return SQL_SEGMENT; + } + + private static Map doResolveSqlSegment() { + Map map = new HashMap<>(); + try { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); //NOSONAR + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + InputStream inputStream = SqlUtils.class.getResourceAsStream(SQL_SEGMENT_PATH); + Document document = builder.parse(inputStream); + Element rootElement = document.getDocumentElement(); + NodeList childNodes = rootElement.getElementsByTagName("sql"); + for (int i = 0; i < childNodes.getLength(); i++) { + Element element = (Element) childNodes.item(i); + String id = element.getAttribute("id"); + String textContent = element.getTextContent(); + if (textContent.contains("@sql")) { + textContent = parseTplVariable(textContent, map); + } + map.put(id, textContent); + } + } catch (Exception e) { + LOGGER.error("resolveSqlSegment", e); + } + return map; + } + + public static String parseSqlVariable(String template, Map paramMap) { + return parseVariable(SQL_PARAM_PATTERN, template, paramMap); + } + + public static String parseTplVariable(String template, Map paramMap) { + List matches = getMatches(TPL_PARAM_PATTERN, template); + if (matches.isEmpty()) { + return template; + } + Map newParamMap = new HashMap<>(); + matches.forEach(x -> newParamMap.put(x, replaceDollar(paramMap.get(x)))); + return restoreDollar(parseVariable(TPL_PARAM_PATTERN, template, newParamMap)); + } + + private static String replaceDollar(String content) { + return content.replace("$", "$"); + } + + private static String restoreDollar(String content) { + return content.replace("$", "$"); + } + + /** + * 解析变量 + * + * @param pattern 模式 + * @param template 模板 变量格式为 @{name} + * @param paramMap 参数 + * @return + */ + private static String parseVariable(Pattern pattern, String template, Map paramMap) { + Objects.requireNonNull(template); + Objects.requireNonNull(paramMap); + Matcher m = pattern.matcher(template); + StringBuffer sb = new StringBuffer(); + while (m.find()) { + String group = m.group(2); + String value = paramMap.get(group); + m.appendReplacement(sb, value); + } + m.appendTail(sb); + return sb.toString(); + } + + private static List getMatches(Pattern pattern, String template) { + List list = new ArrayList<>(); + Matcher m = pattern.matcher(template); + while (m.find()) { + list.add(m.group(2)); + } + return list; + } + + public static String wrapperScript(String sqlScript) { + return String.format("", sqlScript); + } + + public static String resolveMapperScript(String template, Map paramMap) { + return wrapperScript(parseSqlVariable(template, paramMap)); + } + + public static String firstToLowerCase(String param) { + if (RobinStrUtils.isBlank(param)) { + return RobinStrUtils.EMPTY; + } + return param.substring(0, 1).toLowerCase() + param.substring(1); + } + + public static String firstToUpperCase(String param) { + if (RobinStrUtils.isBlank(param)) { + return RobinStrUtils.EMPTY; + } + return param.substring(0, 1).toUpperCase() + param.substring(1); + } +} diff --git a/robin-base/src/main/resources/sql-segment.xml b/robin-base/src/main/resources/sql-segment.xml index 30f6cda..422d18f 100644 --- a/robin-base/src/main/resources/sql-segment.xml +++ b/robin-base/src/main/resources/sql-segment.xml @@ -1,5 +1,5 @@ - + -- Gitee From 4613bf11762358cda84ed9f3e3cca6e96816181f Mon Sep 17 00:00:00 2001 From: CliveYuan Date: Thu, 24 Dec 2020 16:59:10 +0800 Subject: [PATCH 2/4] add robin-generator --- .gitignore | 3 + pom.xml | 91 ++++- robin-base/pom.xml | 5 + .../cn/cliveyuan/robin/base/BaseService.java | 2 +- .../cliveyuan/robin/base/BaseServiceImpl.java | 6 +- .../robin/base/common/ApiErrorCode.java | 32 ++ .../robin/base/common/ApiResponse.java | 110 ++++++ .../{page => common}/PageQueryRequest.java | 2 +- .../base/{page => common}/Pagination.java | 2 +- .../base/condition/PageQueryExample.java | 20 +- .../robin/base/util/BeanCopyUtils.java | 78 ++++ .../robin/base/test/BeanCopyUtilsTest.java | 79 ++++ .../cn/cliveyuan/robin/base/test/User.java | 57 +++ .../cn/cliveyuan/robin/base/test/UserDTO.java | 56 +++ robin-generator/pom.xml | 71 ++++ .../core/CodeGeneratorXmlConfig.java | 70 ++++ .../robin/generator/core/Entity.java | 48 +++ .../cliveyuan/robin/generator/core/Field.java | 61 +++ .../robin/generator/core/FileType.java | 23 ++ .../robin/generator/core/GenerateParam.java | 34 ++ .../robin/generator/core/Generator.java | 18 + .../robin/generator/core/GeneratorChain.java | 152 ++++++++ .../robin/generator/core/GeneratorConst.java | 15 + .../generator/core/GeneratorContext.java | 23 ++ .../core/GeneratorContextResolver.java | 358 ++++++++++++++++++ .../robin/generator/core/GeneratorEnum.java | 57 +++ .../robin/generator/db/ColumnInfo.java | 33 ++ .../robin/generator/db/ConnectionFactory.java | 45 +++ .../robin/generator/db/JdbcType.java | 44 +++ .../robin/generator/db/JdbcTypeResolver.java | 39 ++ .../generator/db/MysqlTableIntrospect.java | 98 +++++ .../robin/generator/db/TableInfo.java | 28 ++ .../robin/generator/db/TableIntrospect.java | 32 ++ .../generator/ControllerGenerator.java | 24 ++ .../generator/generator/DTOGenerator.java | 32 ++ .../generator/generator/EntityGenerator.java | 28 ++ .../generator/MapperJavaGenerator.java | 24 ++ .../generator/MapperJavaImplGenerator.java | 33 ++ .../generator/MapperXmlGenerator.java | 24 ++ .../generator/generator/ServiceGenerator.java | 24 ++ .../generator/ServiceImplGenerator.java | 32 ++ .../generator/impl/MybatisGenerator.java | 28 +- .../robin/generator/util/CollectionUtils.java | 33 ++ .../robin/generator/util/FreemarkerUtils.java | 58 +++ .../robin/generator/util/GeneratorUtils.java | 46 +++ .../robin/generator/util/ReflectUtils.java | 25 ++ .../resources/templates/controller.java.ftl | 78 ++++ .../src/main/resources/templates/dto.java.ftl | 80 ++++ .../main/resources/templates/entity.java.ftl | 64 ++++ .../main/resources/templates/mapper.java.ftl | 11 + .../main/resources/templates/mapper.xml.ftl | 19 + .../main/resources/templates/service.java.ftl | 11 + .../resources/templates/serviceimpl.java.ftl | 14 + .../generator/test/RobinGeneratorTest.java | 2 +- .../src/test/resources/code-generator.xml | 51 +++ 55 files changed, 2513 insertions(+), 20 deletions(-) create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/common/ApiErrorCode.java create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/common/ApiResponse.java rename robin-base/src/main/java/cn/cliveyuan/robin/base/{page => common}/PageQueryRequest.java (96%) rename robin-base/src/main/java/cn/cliveyuan/robin/base/{page => common}/Pagination.java (98%) create mode 100644 robin-base/src/main/java/cn/cliveyuan/robin/base/util/BeanCopyUtils.java create mode 100644 robin-base/src/test/java/cn/cliveyuan/robin/base/test/BeanCopyUtilsTest.java create mode 100644 robin-base/src/test/java/cn/cliveyuan/robin/base/test/User.java create mode 100644 robin-base/src/test/java/cn/cliveyuan/robin/base/test/UserDTO.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/CodeGeneratorXmlConfig.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Entity.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Field.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/FileType.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GenerateParam.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Generator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorChain.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorConst.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContext.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContextResolver.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorEnum.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/ColumnInfo.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/ConnectionFactory.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/JdbcType.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/JdbcTypeResolver.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/MysqlTableIntrospect.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/TableInfo.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/TableIntrospect.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ControllerGenerator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/DTOGenerator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/EntityGenerator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaGenerator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaImplGenerator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperXmlGenerator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceGenerator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceImplGenerator.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/CollectionUtils.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/FreemarkerUtils.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/GeneratorUtils.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/ReflectUtils.java create mode 100644 robin-generator/src/main/resources/templates/controller.java.ftl create mode 100644 robin-generator/src/main/resources/templates/dto.java.ftl create mode 100644 robin-generator/src/main/resources/templates/entity.java.ftl create mode 100644 robin-generator/src/main/resources/templates/mapper.java.ftl create mode 100644 robin-generator/src/main/resources/templates/mapper.xml.ftl create mode 100644 robin-generator/src/main/resources/templates/service.java.ftl create mode 100644 robin-generator/src/main/resources/templates/serviceimpl.java.ftl create mode 100755 robin-generator/src/test/resources/code-generator.xml diff --git a/.gitignore b/.gitignore index 8add38f..7a5c221 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ nbproject *.sublime-workspace .flattened-pom.xml + +robin-generator/src/test/java/cn/cliveyuan/robin/generator/test/generated/** +robin-generator/src/test/resources/generated/** diff --git a/pom.xml b/pom.xml index 8366b01..4853dcc 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,8 @@ - 1.0.0 + 1.0.1 + 1.0.1 ${java.home}/../bin/javadoc 1.8 UTF-8 @@ -51,6 +52,14 @@ 4.12 1.0.1 5.3.2 + 4.3 + 1.6.1 + 2.3.23 + 1.18.12 + 27.1-jre + 3.10 + 2.6 + 8.0.19 @@ -61,6 +70,7 @@ org.slf4j slf4j-api ${slf4j.version} + provided @@ -83,6 +93,85 @@ ${spring.version} provided + + + mysql + mysql-connector-java + ${mysql.version} + runtime + + + + commons-io + commons-io + ${commons-io.version} + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + com.google.guava + guava + ${guava.version} + + + + dom4j + dom4j + ${dom4j.version} + + + + org.freemarker + freemarker + ${freemarker.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + + ch.qos.logback + logback-classic + 1.2.3 + test + + + javax.annotation + javax.annotation-api + 1.3.2 + test + + + io.swagger + swagger-annotations + 1.6.0 + test + + + javax.validation + validation-api + 2.0.1.Final + test + + + org.hibernate.validator + hibernate-validator + 6.1.5.Final + test + + + org.springframework + spring-webmvc + 5.2.9.RELEASE + test + diff --git a/robin-base/pom.xml b/robin-base/pom.xml index ebeddc7..14acccc 100644 --- a/robin-base/pom.xml +++ b/robin-base/pom.xml @@ -24,5 +24,10 @@ org.springframework spring-beans + + junit + junit + test + diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseService.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseService.java index 4bcc731..f8a886a 100644 --- a/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseService.java +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseService.java @@ -6,7 +6,7 @@ package cn.cliveyuan.robin.base; import cn.cliveyuan.robin.base.condition.PageQueryExample; import cn.cliveyuan.robin.base.condition.Query; -import cn.cliveyuan.robin.base.page.Pagination; +import cn.cliveyuan.robin.base.common.Pagination; import java.util.List; diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseServiceImpl.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseServiceImpl.java index 52ea4f6..7b9afb6 100644 --- a/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseServiceImpl.java +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/BaseServiceImpl.java @@ -10,7 +10,7 @@ import cn.cliveyuan.robin.base.condition.GeneratedCriteria; import cn.cliveyuan.robin.base.condition.LambdaCriteria; import cn.cliveyuan.robin.base.condition.PageQueryExample; import cn.cliveyuan.robin.base.condition.Query; -import cn.cliveyuan.robin.base.page.Pagination; +import cn.cliveyuan.robin.base.common.Pagination; import cn.cliveyuan.robin.base.util.ReflectUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.Assert; @@ -137,8 +137,8 @@ public class BaseServiceImpl implements BaseService { public Pagination page(PageQueryExample query) { this.checkQuery(query); Example example = this.convertQueryToExample(query); - int page = this.getPage(query.getPage()); - int rowsPerPage = this.getRowsPerPage(query.getRowsPerPage()); + int page = this.getPage(query.getPageNo()); + int rowsPerPage = this.getRowsPerPage(query.getPageSize()); example.setLimitStart(this.getOffset(page, rowsPerPage)); example.setLimitEnd(rowsPerPage); int totalCount = baseMapper.count(example); diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/common/ApiErrorCode.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/common/ApiErrorCode.java new file mode 100644 index 0000000..82f8da1 --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/common/ApiErrorCode.java @@ -0,0 +1,32 @@ +package cn.cliveyuan.robin.base.common; + +/** + * @author Clive Yuan + * @date 2020/12/24 + */ +public enum ApiErrorCode { + /** + * 成功 + */ + SUCCESS(0, "success"), + /** + * 失败 + */ + FAIL(-1, "fail"); + + private final int code; + private final String msg; + + ApiErrorCode(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/common/ApiResponse.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/common/ApiResponse.java new file mode 100644 index 0000000..7d143ca --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/common/ApiResponse.java @@ -0,0 +1,110 @@ +package cn.cliveyuan.robin.base.common; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 统一接口响应对象 + * + * @author Clive Yuan + * @date 2020/12/24 + */ +public class ApiResponse implements Serializable { + private static final long serialVersionUID = 1L; + /** + * 编码 + */ + private int code = ApiErrorCode.FAIL.getCode(); + /** + * 消息 + */ + private String msg; + /** + * 数据 + */ + private T data; + + + public ApiResponse() { + } + + + public static ApiResponse success(T data) { + ApiResponse apiResponse = new ApiResponse<>(); + apiResponse.setCode(0); + apiResponse.setMsg("success"); + apiResponse.setData(data); + return apiResponse; + } + + public static ApiResponse fail(ApiErrorCode apiErrorCode) { + ApiResponse apiResponse = new ApiResponse<>(); + apiResponse.setCode(apiErrorCode.getCode()); + apiResponse.setMsg(apiErrorCode.getMsg()); + return apiResponse; + } + + public static ApiResponse fail(ApiErrorCode apiErrorCode, T data) { + ApiResponse apiResponse = new ApiResponse<>(); + apiResponse.setCode(apiErrorCode.getCode()); + apiResponse.setMsg(apiErrorCode.getMsg()); + apiResponse.setData(data); + return apiResponse; + } + + public static ApiResponse fail(int code, String msg) { + ApiResponse apiResponse = new ApiResponse<>(); + apiResponse.setCode(code); + apiResponse.setMsg(msg); + return apiResponse; + } + + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + @Override + public String toString() { + return "ApiResponse{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", data=" + data + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ApiResponse that = (ApiResponse) o; + return code == that.code && + Objects.equals(msg, that.msg) && + Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(code, msg, data); + } +} diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/page/PageQueryRequest.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/common/PageQueryRequest.java similarity index 96% rename from robin-base/src/main/java/cn/cliveyuan/robin/base/page/PageQueryRequest.java rename to robin-base/src/main/java/cn/cliveyuan/robin/base/common/PageQueryRequest.java index fc357f8..90de824 100644 --- a/robin-base/src/main/java/cn/cliveyuan/robin/base/page/PageQueryRequest.java +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/common/PageQueryRequest.java @@ -2,7 +2,7 @@ * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). */ -package cn.cliveyuan.robin.base.page; +package cn.cliveyuan.robin.base.common; /** * 分页查询 diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/page/Pagination.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/common/Pagination.java similarity index 98% rename from robin-base/src/main/java/cn/cliveyuan/robin/base/page/Pagination.java rename to robin-base/src/main/java/cn/cliveyuan/robin/base/common/Pagination.java index 12f19fe..aee6a5c 100644 --- a/robin-base/src/main/java/cn/cliveyuan/robin/base/page/Pagination.java +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/common/Pagination.java @@ -2,7 +2,7 @@ * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). */ -package cn.cliveyuan.robin.base.page; +package cn.cliveyuan.robin.base.common; import java.io.Serializable; import java.util.ArrayList; diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/PageQueryExample.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/PageQueryExample.java index aa7f45d..ce60037 100644 --- a/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/PageQueryExample.java +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/condition/PageQueryExample.java @@ -24,26 +24,26 @@ public class PageQueryExample extends QueryExample { /** * 页号 (默认1) */ - private Integer page = DEFAULT_PAGE_NO; + private Integer pageNo = DEFAULT_PAGE_NO; /** * 每页条数 (默认10) */ - private Integer rowsPerPage = DEFAULT_PAGE_SIZE; + private Integer pageSize = DEFAULT_PAGE_SIZE; - public Integer getPage() { - return page; + public Integer getPageNo() { + return pageNo; } - public void setPage(Integer page) { - this.page = page; + public void setPageNo(Integer pageNo) { + this.pageNo = pageNo; } - public Integer getRowsPerPage() { - return rowsPerPage; + public Integer getPageSize() { + return pageSize; } - public void setRowsPerPage(Integer rowsPerPage) { - this.rowsPerPage = rowsPerPage; + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; } } diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/BeanCopyUtils.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/BeanCopyUtils.java new file mode 100644 index 0000000..77d988f --- /dev/null +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/BeanCopyUtils.java @@ -0,0 +1,78 @@ +package cn.cliveyuan.robin.base.util; + +import cn.cliveyuan.robin.base.common.Pagination; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author Clive Yuan + * @date 2020/12/24 + */ +public class BeanCopyUtils { + + private static final Logger logger = LoggerFactory.getLogger(BeanCopyUtils.class); + + /** + * 对象浅拷贝 + * + * @param source 源对象 + * @param targetClass 目标对象类 + * @param 类 + * @return + */ + public static T copy(Object source, Class targetClass) { + AssertUtils.notNull(source, "source is required"); + AssertUtils.notNull(targetClass, "targetClass is required"); + try { + T targetObject = targetClass.getDeclaredConstructor().newInstance(); + BeanUtils.copyProperties(source, targetObject); + return targetObject; + } catch (Exception e) { + logger.error("copy error", e); + throw new RuntimeException(e); + } + } + + /** + * @param sourceList 源对象列表 + * @param targetClass 目标对象类 + * @param 源类 + * @param 目标类 + * @return + */ + public static List copyList(List sourceList, Class targetClass) { + AssertUtils.notNull(sourceList, "sourceList is required"); + AssertUtils.notNull(targetClass, "targetClass is required"); + if (sourceList.size() == 0) { + return new ArrayList<>(); + } + return sourceList.stream().map(x -> copy(x, targetClass)).collect(Collectors.toList()); + } + + /** + * @param sourcePagination 源对象分页 + * @param targetClass 目标对象类 + * @param 源类 + * @param 目标类 + * @return + */ + public static Pagination copyPagination(Pagination sourcePagination, Class targetClass) { + AssertUtils.notNull(sourcePagination, "sourcePagination is required"); + AssertUtils.notNull(targetClass, "targetClass is required"); + if (Objects.isNull(sourcePagination.getDataList())) { + return Pagination.buildEmpty(); + } + List list = copyList(sourcePagination.getDataList(), targetClass); + Pagination pagination = new Pagination<>(); + pagination.setDataList(list); + pagination.setTotalCount(sourcePagination.getTotalCount()); + pagination.setHasNext(sourcePagination.isHasNext()); + return pagination; + } +} diff --git a/robin-base/src/test/java/cn/cliveyuan/robin/base/test/BeanCopyUtilsTest.java b/robin-base/src/test/java/cn/cliveyuan/robin/base/test/BeanCopyUtilsTest.java new file mode 100644 index 0000000..8f61201 --- /dev/null +++ b/robin-base/src/test/java/cn/cliveyuan/robin/base/test/BeanCopyUtilsTest.java @@ -0,0 +1,79 @@ +package cn.cliveyuan.robin.base.test; + +import cn.cliveyuan.robin.base.common.Pagination; +import cn.cliveyuan.robin.base.util.BeanCopyUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author Clive Yuan + * @date 2020/12/24 + */ +public class BeanCopyUtilsTest { + + @Test + public void copy() { + User user = new User(); + user.setId(1); + user.setUsername("Clive"); + user.setAge(18); + user.setBirthday(new Date()); + UserDTO userDTO = BeanCopyUtils.copy(user, UserDTO.class); + verify(user, userDTO); + } + + @Test + public void copyList() { + List list = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + User user = new User(); + user.setId(1); + user.setUsername("Clive"); + user.setAge(18); + user.setBirthday(new Date()); + list.add(user); + } + + List targetList = BeanCopyUtils.copyList(list, UserDTO.class); + for (int i = 0; i < targetList.size(); i++) { + User user = list.get(i); + UserDTO userDTO = targetList.get(i); + verify(user, userDTO); + } + } + + @Test + public void copyPagination() { + List list = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + User user = new User(); + user.setId(1); + user.setUsername("Clive"); + user.setAge(18); + user.setBirthday(new Date()); + list.add(user); + } + Pagination pagination = new Pagination<>(list.size(), list, 1, list.size()); + Pagination targetPagination = BeanCopyUtils.copyPagination(pagination, UserDTO.class); + Assert.assertNotNull(targetPagination); + Assert.assertEquals(pagination.getTotalCount(), targetPagination.getTotalCount()); + Assert.assertEquals(pagination.isHasNext(), targetPagination.isHasNext()); + for (int i = 0; i < targetPagination.getDataList().size(); i++) { + User user = pagination.getDataList().get(i); + UserDTO userDTO = targetPagination.getDataList().get(i); + verify(user, userDTO); + } + } + + private void verify(User user, UserDTO userDTO) { + Assert.assertNotNull(userDTO); + Assert.assertEquals(user.getId(), userDTO.getId()); + Assert.assertEquals(user.getUsername(), userDTO.getUsername()); + Assert.assertEquals(user.getAge(), userDTO.getAge()); + Assert.assertEquals(user.getBirthday(), userDTO.getBirthday()); + } +} diff --git a/robin-base/src/test/java/cn/cliveyuan/robin/base/test/User.java b/robin-base/src/test/java/cn/cliveyuan/robin/base/test/User.java new file mode 100644 index 0000000..c13ac56 --- /dev/null +++ b/robin-base/src/test/java/cn/cliveyuan/robin/base/test/User.java @@ -0,0 +1,57 @@ +package cn.cliveyuan.robin.base.test; + +import java.util.Date; + +/** + * @author Clive Yuan + * @date 2020/12/24 + */ +public class User { + private Integer id; + private String username; + private Integer age; + private Date birthday; + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Date getBirthday() { + return birthday; + } + + public void setBirthday(Date birthday) { + this.birthday = birthday; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", username='" + username + '\'' + + ", age=" + age + + ", birthday=" + birthday + + '}'; + } +} diff --git a/robin-base/src/test/java/cn/cliveyuan/robin/base/test/UserDTO.java b/robin-base/src/test/java/cn/cliveyuan/robin/base/test/UserDTO.java new file mode 100644 index 0000000..f91a03d --- /dev/null +++ b/robin-base/src/test/java/cn/cliveyuan/robin/base/test/UserDTO.java @@ -0,0 +1,56 @@ +package cn.cliveyuan.robin.base.test; + +import java.util.Date; + +/** + * @author Clive Yuan + * @date 2020/12/24 + */ +public class UserDTO { + private Integer id; + private String username; + private Integer age; + private Date birthday; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Date getBirthday() { + return birthday; + } + + public void setBirthday(Date birthday) { + this.birthday = birthday; + } + + @Override + public String toString() { + return "UserDTO{" + + "id=" + id + + ", username='" + username + '\'' + + ", age=" + age + + ", birthday=" + birthday + + '}'; + } +} diff --git a/robin-generator/pom.xml b/robin-generator/pom.xml index f5fc82f..9626331 100644 --- a/robin-generator/pom.xml +++ b/robin-generator/pom.xml @@ -2,6 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 robin-generator + ${robin-generator.revision} jar @@ -12,10 +13,80 @@ + + mysql + mysql-connector-java + runtime + + + dom4j + dom4j + + + org.freemarker + freemarker + + + org.projectlombok + lombok + + + org.apache.commons + commons-lang3 + + + commons-io + commons-io + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + test + junit junit test + + org.springframework + spring-webmvc + 5.2.9.RELEASE + test + + + javax.annotation + javax.annotation-api + test + + + io.swagger + swagger-annotations + test + + + javax.validation + validation-api + test + + + org.hibernate.validator + hibernate-validator + test + + + com.gitee.opensource4clive + robin-base + ${revision} + test + diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/CodeGeneratorXmlConfig.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/CodeGeneratorXmlConfig.java new file mode 100644 index 0000000..3d750da --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/CodeGeneratorXmlConfig.java @@ -0,0 +1,70 @@ +package cn.cliveyuan.robin.generator.core; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * 代码生成配置 + * + * @author Clive Yuan + * @date 2020/09/07 + */ +@Data +public class CodeGeneratorXmlConfig implements Serializable { + + private BaseConfig baseConfig; + private JdbcConnection jdbcConnection; + private MapperGeneratorConfig javaModelGenerator; + private MapperGeneratorConfig sqlMapGenerator; + private MapperGeneratorConfig javaClientGenerator; + private MapperGeneratorConfig serviceGenerator; + private MapperGeneratorConfig controllerGenerator; + private MapperGeneratorConfig dtoGenerator; + private List tables; + private List fixedColumns; + private List fixedField; + + @Data + @Builder + public static class BaseConfig { + private boolean enableLombok; + private boolean enableSwagger; + private boolean enableValidation; + private boolean enableReadWriteSeparation; + } + + @Data + @Builder + public static class JdbcConnection { + private String driverClass; + private String connectionURL; + private String username; + private String password; + } + + @Data + @Builder + public static class MapperGeneratorConfig { + private boolean disabled; + private String templatePath; + private String codePath; + private String targetPackage; + private String moduleName; + } + + @Data + @Builder + public static class Table { + private String tableName; + private String entityObjectName; + private Set ignoreColumns; + private String entityObjectSuffix; + } + +} + + diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Entity.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Entity.java new file mode 100644 index 0000000..76fd50e --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Entity.java @@ -0,0 +1,48 @@ +package cn.cliveyuan.robin.generator.core; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 实体 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +@Data +public class Entity implements Serializable { + /** + * 是否有BigDecimal类型的字段 + */ + private boolean hasBigDecimalField; + /** + * 小写驼峰名 + */ + private String lowerCamelName; + /** + * 实体名 (用户自定义, 若无则为首字母大写的驼峰) + */ + private String entityName; + /** + * 表名 + */ + private String tableName; + /** + * 大写驼峰名 + */ + private String upperCamelName; + /** + * 备注 + */ + private String comment; + /** + * 字段 + */ + private List fields; + /** + * 文件名(生成文件用) + */ + private String fileName; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Field.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Field.java new file mode 100644 index 0000000..092bedb --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Field.java @@ -0,0 +1,61 @@ +package cn.cliveyuan.robin.generator.core; + +import cn.cliveyuan.robin.generator.db.JdbcType; +import lombok.Data; + +import java.io.Serializable; + +/** + * 字段 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +@Data +public class Field implements Serializable { + /** + * JDBC类型 + */ + private JdbcType jdbcType; + /** + * 小写驼峰名 + */ + private String lowerCamelName; + /** + * 大写驼峰名 + */ + private String upperCamelName; + /** + * 列名 + */ + private String columnName; + /** + * 长度 + */ + private Integer length; + /** + * 是否可空 + */ + private Boolean nullable; + /** + * 备注 + */ + private String comment; + /** + * 是否标注字段名 + *

    当lowerCamelName与columnName不一致时需要标注

    + */ + private boolean markColumnName; + /** + * 是否为主键 + */ + private boolean primaryKey; + /** + * 保存忽略 + */ + private boolean ignoreSaving; + /** + * swagger 字段隐藏 + */ + private boolean swaggerHidden; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/FileType.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/FileType.java new file mode 100644 index 0000000..760bd24 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/FileType.java @@ -0,0 +1,23 @@ +package cn.cliveyuan.robin.generator.core; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 文件类型 + * + * @author Clive Yuan + * @date 2020/11/06 + */ +@Getter +@AllArgsConstructor +public enum FileType { + + JAVA(".java"), + XML(".xml"); + + /** + * 扩展名 + */ + private final String extension; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GenerateParam.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GenerateParam.java new file mode 100644 index 0000000..27ae2de --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GenerateParam.java @@ -0,0 +1,34 @@ +package cn.cliveyuan.robin.generator.core; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; +import java.util.function.Function; + +/** + * 生成参数 + * + * @author Clive Yuan + * @date 2020/11/06 + */ +@Data +@Builder +public class GenerateParam implements Serializable { + /** + * 上下文 + */ + private GeneratorContext context; + /** + * 文件名方法 + */ + private Function fileNameFunction; + /** + * 生成器 + */ + private Generator generator; + /** + * 生成配置 + */ + private CodeGeneratorXmlConfig.MapperGeneratorConfig generatorConfig; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Generator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Generator.java new file mode 100644 index 0000000..a8799cd --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Generator.java @@ -0,0 +1,18 @@ +package cn.cliveyuan.robin.generator.core; + +/** + * 代码生成器接口 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public interface Generator { + + /** + * 生成 + * + * @param context 上下文 + * @param generatorChain 生成器链 + */ + void generate(GeneratorContext context, GeneratorChain generatorChain); +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorChain.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorChain.java new file mode 100644 index 0000000..8018bfe --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorChain.java @@ -0,0 +1,152 @@ +package cn.cliveyuan.robin.generator.core; + +import cn.cliveyuan.robin.generator.util.FreemarkerUtils; +import cn.cliveyuan.robin.generator.util.ReflectUtils; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * 生成器链 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +@Slf4j +public class GeneratorChain implements Generator { + + /** + * 生成器集合 + */ + private final List generators = new ArrayList<>(); + + /** + * 执行下标 + */ + private int index = 0; + + public GeneratorChain() { + this.init(); + } + + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + if (generators.size() == index) { + return; + } + Generator generator = generators.get(index); + log.info("GeneratorChain generate: generator={}, index={}", generator.getClass().getSimpleName(), index); + index++; + generator.generate(context, generatorChain); + } + + /** + * 初始化: 从生成器注册枚举中读取生成器并将其初始化然后加入列表 + */ + private void init() { + log.info("GeneratorChain init START"); + for (GeneratorEnum value : GeneratorEnum.values()) { + generators.add(ReflectUtils.newInstance(value.getClazz())); + } + log.info("GeneratorChain init END, generators.size={}", generators.size()); + } + + /** + * 生成文件 + * + * @param generateParam 生成参数 + */ + public void doGenerate(GenerateParam generateParam) { + try { + CodeGeneratorXmlConfig.MapperGeneratorConfig generatorConfig = generateParam.getGeneratorConfig(); + if (generatorConfig.isDisabled()) { + log.info("doGenerate skip the module: {}", generatorConfig.getModuleName()); + return; + } + GeneratorEnum generatorEnum = GeneratorEnum.valueOf(generateParam.getGenerator()); + GeneratorContext context = generateParam.getContext(); + // 设置文件名 + this.setFileName(context.getEntityList(), generateParam.getFileNameFunction()); + // 文件保存路径 + String codeFilePath = this.getCodeFilePath(generatorConfig); + // 模板变量 + Map paramMap = this.getParamMap(context); + String customizedTemplatePath = generatorConfig.getTemplatePath(); + // 代码模板: 如果自定义为空 则使用默认 + String templatePath = StringUtils.isNotBlank(customizedTemplatePath) ? + customizedTemplatePath : generatorEnum.getTemplatePath(); + // 遍历实体列表,生成文件 + context.getEntityList().forEach(entity -> this.generateFile(paramMap, templatePath, codeFilePath, entity, generatorEnum)); + + } finally { + // 继续责任链 + this.generate(generateParam.getContext(), this); + } + } + + private void setFileName(List entities, Function function) { + entities.forEach(x -> x.setFileName(function.apply(x))); + } + + private String getCodeFilePath(CodeGeneratorXmlConfig.MapperGeneratorConfig generatorConfig) { + String targetPackage = generatorConfig.getTargetPackage(); + String projectPath = System.getProperty("user.dir"); + String codePath = generatorConfig.getCodePath(); + String packagePath = targetPackage.replace(".", "/"); + return String.format("%s/%s/%s", projectPath, codePath, packagePath); + } + + private Map getParamMap(GeneratorContext context) { + CodeGeneratorXmlConfig xmlConfig = context.getXmlConfig(); + CodeGeneratorXmlConfig.MapperGeneratorConfig javaModelGenerator = xmlConfig.getJavaModelGenerator(); + CodeGeneratorXmlConfig.MapperGeneratorConfig javaClientGenerator = xmlConfig.getJavaClientGenerator(); + CodeGeneratorXmlConfig.MapperGeneratorConfig serviceGenerator = xmlConfig.getServiceGenerator(); + CodeGeneratorXmlConfig.MapperGeneratorConfig controllerGenerator = xmlConfig.getControllerGenerator(); + CodeGeneratorXmlConfig.MapperGeneratorConfig dtoGenerator = xmlConfig.getDtoGenerator(); + // 设置变量 + Map paramMap = Maps.newHashMap(); + paramMap.put("entityPackage", javaModelGenerator.getTargetPackage()); + paramMap.put("mapperPackage", javaClientGenerator.getTargetPackage()); + paramMap.put("mapperImplPackage", javaClientGenerator.getTargetPackage() + GeneratorConst.IMPL_PKG_SUFFIX); + paramMap.put("servicePackage", serviceGenerator.getTargetPackage()); + paramMap.put("serviceImplPackage", serviceGenerator.getTargetPackage() + GeneratorConst.IMPL_PKG_SUFFIX); + paramMap.put("controllerPackage", controllerGenerator.getTargetPackage()); + paramMap.put("dtoPackage", dtoGenerator.getTargetPackage()); + paramMap.put("baseConfig", xmlConfig.getBaseConfig()); + return paramMap; + } + + private void generateFile(Map paramMap, String ftlPath, String codeFilePath, + Entity entity, GeneratorEnum generatorEnum) { + String fileName = entity.getFileName() + generatorEnum.getFileType().getExtension(); + paramMap.put("entity", entity); + // 通过模板生成代码内容 + String codeContent = FreemarkerUtils.parseTemplate(ftlPath, paramMap); + String filePath = String.format("%s/%s", codeFilePath, fileName); + // 写入指定目录 + this.writeStringToFile(filePath, codeContent, generatorEnum.isOverwrite()); + } + + private void writeStringToFile(String filePath, String data, boolean overwrite) { + try { + File file = new File(filePath); + if (file.exists() && !overwrite) { + log.debug("The file is exist, skip creating it: {}", file.getName()); + return; + } + FileUtils.writeStringToFile(file, data, StandardCharsets.UTF_8); + log.debug("The file was created successfully: {}", file.getName()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorConst.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorConst.java new file mode 100644 index 0000000..f63a7e6 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorConst.java @@ -0,0 +1,15 @@ +package cn.cliveyuan.robin.generator.core; + +/** + * 常量 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public class GeneratorConst { + public final static String PRIMARY_KEY_COL = "id"; + public final static String CREATED_TIME_COL = "created_at"; + public final static String UPDATED_TIME_COL = "updated_at"; + public final static String KEY_WORD_PREFIX = "$"; + public final static String IMPL_PKG_SUFFIX = ".impl"; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContext.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContext.java new file mode 100644 index 0000000..72447ac --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContext.java @@ -0,0 +1,23 @@ +package cn.cliveyuan.robin.generator.core; + +import lombok.Data; + +import java.util.List; + +/** + * 上下文 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +@Data +public class GeneratorContext { + /** + * 实体列表 + */ + private List entityList; + /** + * 配置 + */ + private CodeGeneratorXmlConfig xmlConfig; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContextResolver.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContextResolver.java new file mode 100644 index 0000000..d329b61 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContextResolver.java @@ -0,0 +1,358 @@ +package cn.cliveyuan.robin.generator.core; + + +import cn.cliveyuan.robin.generator.db.ColumnInfo; +import cn.cliveyuan.robin.generator.db.ConnectionFactory; +import cn.cliveyuan.robin.generator.db.JdbcType; +import cn.cliveyuan.robin.generator.db.JdbcTypeResolver; +import cn.cliveyuan.robin.generator.db.MysqlTableIntrospect; +import cn.cliveyuan.robin.generator.db.TableInfo; +import cn.cliveyuan.robin.generator.db.TableIntrospect; +import cn.cliveyuan.robin.generator.util.CollectionUtils; +import cn.cliveyuan.robin.generator.util.GeneratorUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.dom4j.Attribute; +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.springframework.util.Assert; + +import javax.lang.model.SourceVersion; +import java.io.File; +import java.io.InputStream; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 配置上下文解析器 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +@Slf4j +public class GeneratorContextResolver { + + private static final String CONFIG_XML_NAME = "code-generator.xml"; + private static final String DEFAULT_CONFIG_FILE_PATH = "/" + CONFIG_XML_NAME; + + private CodeGeneratorXmlConfig xmlConfig; + private TableIntrospect tableIntrospect; + + /** + * 解析上下文 + * + * @param configFilePath 配置文件 + * @return + */ + public GeneratorContext resolve(String configFilePath) { + log.info("GeneratorContextResolver resolving START, configFilePath={}", configFilePath); + CodeGeneratorXmlConfig xmlConfig = this.parseXmlConfig(configFilePath); + List entityList = this.parseEntityList(xmlConfig); + GeneratorContext generatorContext = new GeneratorContext(); + generatorContext.setEntityList(entityList); + generatorContext.setXmlConfig(xmlConfig); + this.closeTableIntrospect(); + log.info("GeneratorContextResolver resolving END, entityList.size={}", entityList.size()); + return generatorContext; + } + + private void initTableIntrospect() { + CodeGeneratorXmlConfig.JdbcConnection jdbcConnection = xmlConfig.getJdbcConnection(); + Connection connection = ConnectionFactory.createConnection(jdbcConnection.getDriverClass(), + jdbcConnection.getConnectionURL(), jdbcConnection.getUsername(), jdbcConnection.getPassword()); + this.tableIntrospect = new MysqlTableIntrospect(connection); + } + + private void closeTableIntrospect() { + this.tableIntrospect.closeConnection(); + } + + private List parseEntityList(CodeGeneratorXmlConfig xmlConfig) { + List tables = xmlConfig.getTables(); + return tables.stream().map(this::table2Entity).collect(Collectors.toList()); + } + + private Entity table2Entity(CodeGeneratorXmlConfig.Table table) { + TableInfo tableInfo = tableIntrospect.introspect(table.getTableName()); + Entity entity = new Entity(); + String lowerCamelName = GeneratorUtils.getLowerCamelName(tableInfo.getName()); + String upperCamelName = StringUtils.capitalize(lowerCamelName); + String entityName = table.getEntityObjectName(); + String suffix = StringUtils.isNotBlank(table.getEntityObjectSuffix()) ? table.getEntityObjectSuffix() : ""; + if (StringUtils.isNotBlank(entityName)) { + lowerCamelName = GeneratorUtils.getLowerCamelName(entityName); + upperCamelName = StringUtils.capitalize(entityName); + } + entityName = upperCamelName + suffix; + entity.setLowerCamelName(lowerCamelName); + entity.setEntityName(entityName); + entity.setTableName(tableInfo.getName()); + entity.setComment(tableInfo.getComment()); + entity.setUpperCamelName(upperCamelName); + + List fields = new ArrayList<>(); + fields.addAll(xmlConfig.getFixedField()); + fields.addAll(this.column2Field(table, tableInfo.getColumns())); + entity.setFields(fields); + entity.setHasBigDecimalField(this.hasBigDecimalField(entity.getFields())); + return entity; + } + + private List column2Field(CodeGeneratorXmlConfig.Table table, List columns) { + return columns.stream() + .filter(x -> !table.getIgnoreColumns().contains(x.getName())) // 排除忽略字段 + .map(x -> { + Field field = new Field(); + field.setJdbcType(JdbcTypeResolver.resolve(x.getType(), x.getLength())); + String lowerCamelName = GeneratorUtils.getLowerCamelName(x.getName()); + if (SourceVersion.isKeyword(lowerCamelName)) { + lowerCamelName += GeneratorConst.KEY_WORD_PREFIX; + } + field.setLowerCamelName(lowerCamelName); + field.setUpperCamelName(StringUtils.capitalize(lowerCamelName)); + field.setColumnName(x.getName()); + field.setLength(x.getLength()); + field.setNullable(x.getNullable()); + field.setComment(x.getComment()); + field.setMarkColumnName(!Objects.equals(field.getLowerCamelName(), field.getColumnName())); + return field; + }).collect(Collectors.toList()); + } + + private boolean hasBigDecimalField(List fields) { + return fields.stream().anyMatch(x -> JdbcType.DECIMAL.equals(x.getJdbcType())); + } + + private CodeGeneratorXmlConfig parseXmlConfig(String configFilePath) { + if (StringUtils.isBlank(configFilePath)) { + configFilePath = DEFAULT_CONFIG_FILE_PATH; + } + log.info("GeneratorContextResolver: configFilePath={}", configFilePath); + try { + CodeGeneratorXmlConfig config = new CodeGeneratorXmlConfig(); + this.xmlConfig = config; + InputStream xmlInputStream = this.getClass().getResourceAsStream(configFilePath); + if (Objects.isNull(xmlInputStream)) { + log.info("can't find file in resource path, try to find in file system"); + File file = new File(configFilePath); + xmlInputStream = FileUtils.openInputStream(file); + } + Assert.notNull(xmlInputStream, "code generator config file is not exist: " + configFilePath); + SAXReader reader = new SAXReader(); + Document document = reader.read(xmlInputStream); + Element rootElement = document.getRootElement(); + + config.setBaseConfig(this.getBaseConfig(rootElement)); + config.setJdbcConnection(this.getJdbcConnection(rootElement)); + this.initTableIntrospect(); + config.setJavaModelGenerator(this.getMapperGeneratorConfig(rootElement, "javaModelGenerator")); + config.setSqlMapGenerator(this.getMapperGeneratorConfig(rootElement, "sqlMapGenerator")); + config.setJavaClientGenerator(this.getMapperGeneratorConfig(rootElement, "javaClientGenerator")); + config.setServiceGenerator(this.getMapperGeneratorConfig(rootElement, "serviceGenerator")); + config.setControllerGenerator(this.getMapperGeneratorConfig(rootElement, "controllerGenerator")); + config.setDtoGenerator(this.getMapperGeneratorConfig(rootElement, "dtoGenerator")); + this.verifyDtoGenerator(config); + config.setTables(this.getTables(rootElement)); + return config; + } catch (Exception e) { + log.error("Fail to parse xml file: " + configFilePath, e); + throw new RuntimeException(e); + } + } + + private void verifyDtoGenerator(CodeGeneratorXmlConfig config) { + CodeGeneratorXmlConfig.MapperGeneratorConfig controllerGenerator = config.getControllerGenerator(); + CodeGeneratorXmlConfig.MapperGeneratorConfig dtoGenerator = config.getDtoGenerator(); + if (!controllerGenerator.isDisabled()) { + Assert.isTrue(!dtoGenerator.isDisabled(), "DTO生成配置未生效, 请检查'dtoGenerator'标签"); + } + } + + private CodeGeneratorXmlConfig.BaseConfig getBaseConfig(Element rootElement) { + CodeGeneratorXmlConfig.BaseConfig baseConfig = CodeGeneratorXmlConfig.BaseConfig.builder().build(); + Element baseConfigEl = this.getElement(rootElement, "baseConfig"); + Iterator iterator = baseConfigEl.elementIterator("property"); + while (iterator.hasNext()) { + Element property = (Element) iterator.next(); + String name = this.getAttributeValue(property, "name"); + if (Objects.equals("enableLombok", name)) { + baseConfig.setEnableLombok("true".equalsIgnoreCase(property.getStringValue())); + } else if (Objects.equals("enableSwagger", name)) { + baseConfig.setEnableSwagger("true".equalsIgnoreCase(property.getStringValue())); + } else if (Objects.equals("enableValidation", name)) { + baseConfig.setEnableValidation("true".equalsIgnoreCase(property.getStringValue())); + } else if (Objects.equals("enableReadWriteSeparation", name)) { + baseConfig.setEnableReadWriteSeparation("true".equalsIgnoreCase(property.getStringValue())); + } + } + return baseConfig; + } + + private CodeGeneratorXmlConfig.JdbcConnection getJdbcConnection(Element rootElement) { + Element jdbcConnection = this.getElement(rootElement, "jdbcConnection"); + String host = this.getAttributeValueNonBlank(jdbcConnection, "host"); + String database = this.getAttributeValueNonBlank(jdbcConnection, "database"); + String tinyInt1isBit = this.getAttributeValue(jdbcConnection, "tinyInt1isBit", true); + String queryString = StringUtils.EMPTY; + if ("false".equalsIgnoreCase(tinyInt1isBit)) { + queryString = "?tinyInt1isBit=false"; + } + String connectionURL = String.format("jdbc:mysql://%s/%s%s", host, database, queryString); + return CodeGeneratorXmlConfig.JdbcConnection.builder() + .driverClass(this.getAttributeValueNonBlank(jdbcConnection, "driverClass")) + .connectionURL(connectionURL) + .username(this.getAttributeValue(jdbcConnection, "username")) + .password(this.getAttributeValue(jdbcConnection, "password")) + .build(); + } + + private List getTables(Element rootElement) { + List tables = new ArrayList<>(); + Element tableEls = this.getElement(rootElement, "tables"); + String all = this.getAttributeValue(tableEls, "all", true); + boolean allTables = "true".equalsIgnoreCase(all); + String createTimeColumn = this.getAttributeValue(tableEls, "createTimeColumn", true); + String updateTimeColumn = this.getAttributeValue(tableEls, "updateTimeColumn", true); + String entityObjectSuffix = this.getAttributeValue(tableEls, "entityObjectSuffix", true); + Set globalIgnoreColumnSet = this.getIgnoreColumnSet(tableEls); + + String primaryKeyColumn = GeneratorConst.PRIMARY_KEY_COL; + List fixedColumns = new ArrayList<>(); + if (StringUtils.isBlank(createTimeColumn)) { + createTimeColumn = GeneratorConst.CREATED_TIME_COL; + } + if (StringUtils.isBlank(updateTimeColumn)) { + updateTimeColumn = GeneratorConst.UPDATED_TIME_COL; + } + + fixedColumns.add(primaryKeyColumn); + fixedColumns.add(createTimeColumn); + fixedColumns.add(updateTimeColumn); + + xmlConfig.setFixedColumns(fixedColumns); + List fixedFields = new ArrayList<>(); + fixedFields.add(this.getPrimaryKeyField(primaryKeyColumn)); + fixedFields.add(this.getDateField(createTimeColumn, "创建时间")); + fixedFields.add(this.getDateField(updateTimeColumn, "修改时间")); + xmlConfig.setFixedField(fixedFields); + globalIgnoreColumnSet.addAll(fixedColumns); + + if (allTables) { + List tableNames = tableIntrospect.getAllTables(); + tableNames.forEach(tableName -> { + tables.add(CodeGeneratorXmlConfig.Table.builder() + .tableName(tableName) + .ignoreColumns(globalIgnoreColumnSet) + .build()); + }); + } else { + Iterator iterator = tableEls.elementIterator("table"); + while (iterator.hasNext()) { + Element tableEl = (Element) iterator.next(); + Set tableIgnoreColumnSet = new HashSet<>(globalIgnoreColumnSet); + tableIgnoreColumnSet.addAll(this.getIgnoreColumnSet(tableEl)); + tables.add(CodeGeneratorXmlConfig.Table.builder() + .tableName(this.getAttributeValue(tableEl, "tableName")) + .entityObjectName(this.getAttributeValue(tableEl, "entityObjectName", true)) + .entityObjectSuffix(entityObjectSuffix) + .ignoreColumns(tableIgnoreColumnSet) + .build()); + } + } + + return tables; + } + + private Field getPrimaryKeyField(String primaryKeyColumn) { + Field field = new Field(); + field.setJdbcType(JdbcType.LONG); + String lowerCamelName = GeneratorUtils.getLowerCamelName(primaryKeyColumn); + if (SourceVersion.isKeyword(lowerCamelName)) { + lowerCamelName += GeneratorConst.KEY_WORD_PREFIX; + } + field.setLowerCamelName(lowerCamelName); + field.setUpperCamelName(StringUtils.capitalize(lowerCamelName)); + field.setColumnName(primaryKeyColumn); + field.setLength(20); + field.setNullable(true); + field.setComment("ID 主键"); + field.setMarkColumnName(false); + field.setPrimaryKey(true); + return field; + } + + private Field getDateField(String column, String comment) { + Field field = new Field(); + field.setJdbcType(JdbcType.DATE); + String lowerCamelName = GeneratorUtils.getLowerCamelName(column); + if (SourceVersion.isKeyword(lowerCamelName)) { + lowerCamelName += GeneratorConst.KEY_WORD_PREFIX; + } + field.setLowerCamelName(lowerCamelName); + field.setUpperCamelName(StringUtils.capitalize(lowerCamelName)); + field.setColumnName(column); + field.setLength(19); + field.setNullable(true); + field.setComment(comment); + field.setMarkColumnName(!Objects.equals(field.getLowerCamelName(), field.getColumnName())); + field.setSwaggerHidden(true); + field.setIgnoreSaving(true); + return field; + } + + private Set getIgnoreColumnSet(Element element) { + String ignoreColumns = this.getAttributeValue(element, "ignoreColumns", true); + Set ignoreColumnSet = new HashSet<>(); + if (StringUtils.isNotBlank(ignoreColumns)) { + ignoreColumnSet.addAll(CollectionUtils.listOf(ignoreColumns.split(","))); + } + return ignoreColumnSet; + } + + private CodeGeneratorXmlConfig.MapperGeneratorConfig getMapperGeneratorConfig(Element rootElement, String nodeName) { + Element element = rootElement.element(nodeName); + if (Objects.isNull(element)) { + return CodeGeneratorXmlConfig.MapperGeneratorConfig.builder().disabled(true).moduleName(nodeName).build(); + } + String disabledValue = this.getAttributeValue(element, "disabled", true); + return CodeGeneratorXmlConfig.MapperGeneratorConfig.builder() + .disabled("true".equalsIgnoreCase(disabledValue)) + .targetPackage(this.getAttributeValue(element, "targetPackage")) + .codePath(this.getAttributeValue(element, "codePath")) + .templatePath(this.getAttributeValue(element, "templatePath", true)) + .moduleName(nodeName) + .build(); + } + + private Element getElement(Element element, String name) { + Element childElement = element.element(name); + Assert.notNull(childElement, "Element '" + name + "' is not exist"); + return childElement; + } + + private String getAttributeValueNonBlank(Element element, String attributeName) { + String attributeValue = this.getAttributeValue(element, attributeName, false); + Assert.isTrue(StringUtils.isNotBlank(attributeValue), "Attribute '" + attributeName + "' is empty string"); + return attributeValue; + } + + private String getAttributeValue(Element element, String attributeName) { + return this.getAttributeValue(element, attributeName, false); + } + + private String getAttributeValue(Element element, String attributeName, boolean nullable) { + Attribute attribute = element.attribute(attributeName); + if (!nullable) { + Assert.notNull(attribute, "Attribute '" + attributeName + "' is not exist"); + } + return Objects.nonNull(attribute) ? attribute.getValue() : null; + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorEnum.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorEnum.java new file mode 100644 index 0000000..05de072 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorEnum.java @@ -0,0 +1,57 @@ +package cn.cliveyuan.robin.generator.core; + +import cn.cliveyuan.robin.generator.generator.ControllerGenerator; +import cn.cliveyuan.robin.generator.generator.DTOGenerator; +import cn.cliveyuan.robin.generator.generator.EntityGenerator; +import cn.cliveyuan.robin.generator.generator.MapperJavaGenerator; +import cn.cliveyuan.robin.generator.generator.MapperJavaImplGenerator; +import cn.cliveyuan.robin.generator.generator.MapperXmlGenerator; +import cn.cliveyuan.robin.generator.generator.ServiceGenerator; +import cn.cliveyuan.robin.generator.generator.ServiceImplGenerator; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 生成器注册枚举类 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +@AllArgsConstructor +@Getter +public enum GeneratorEnum { + + ENTITY(EntityGenerator.class, FileType.JAVA, "templates/entity.java.ftl", true), + MAPPER_XML(MapperXmlGenerator.class, FileType.XML, "templates/mapper.xml.ftl", false), + MAPPER_JAVA(MapperJavaGenerator.class, FileType.JAVA, "templates/mapper.java.ftl", false), + MAPPER_JAVA_IMPL(MapperJavaImplGenerator.class, FileType.JAVA, "templates/mapperimpl.java.ftl", false), + SERVICE(ServiceGenerator.class, FileType.JAVA, "templates/service.java.ftl", false), + SERVICE_IMPL(ServiceImplGenerator.class, FileType.JAVA, "templates/serviceimpl.java.ftl", false), + DTO(DTOGenerator.class, FileType.JAVA, "templates/dto.java.ftl", false), + CONTROLLER(ControllerGenerator.class, FileType.JAVA, "templates/controller.java.ftl", false); + + /** + * 生成器类 + */ + private final Class clazz; + /** + * 文件类型 + */ + private final FileType fileType; + /** + * 默认模板路径 + */ + private final String templatePath; + /** + * 是否覆盖写入 + */ + private final boolean overwrite; + + public static GeneratorEnum valueOf(Generator generator) { + return Arrays.stream(GeneratorEnum.values()).filter(x -> Objects.equals(generator.getClass(), x.getClazz())) + .findAny().orElseThrow(() -> new IllegalArgumentException("GeneratorEnum not fund: " + generator.getClass())); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/ColumnInfo.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/ColumnInfo.java new file mode 100644 index 0000000..95b6fd5 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/ColumnInfo.java @@ -0,0 +1,33 @@ +package cn.cliveyuan.robin.generator.db; + +import lombok.Data; + +/** + * 列信息 + * + * @author Clive Yuan + * @date 2020/10/28 + */ +@Data +public class ColumnInfo { + /** + * 列名 + */ + private String name; + /** + * 类型 + */ + private String type; + /** + * 长度 + */ + private Integer length; + /** + * 能否为空 + */ + private Boolean nullable; + /** + * 备注 + */ + private String comment; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/ConnectionFactory.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/ConnectionFactory.java new file mode 100644 index 0000000..6af85ec --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/ConnectionFactory.java @@ -0,0 +1,45 @@ +package cn.cliveyuan.robin.generator.db; + +import lombok.extern.slf4j.Slf4j; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +/** + * 连接工厂 + * + * @author Clive Yuan + * @date 2020/10/28 + */ +@Slf4j +public class ConnectionFactory { + private static final String QUESTION_MARK = "?"; + private static final String AND_MARK = "&"; + private static final String URL_PARAMS = "useInformationSchema=true"; + + /** + * 创建数据库连接 + * + * @param driver 驱动类 + * @param url 连接 + * @param username 用户名 + * @param password 密码 + * @return + */ + public static Connection createConnection(String driver, String url, String username, String password) { + try { + Class.forName(driver); + if (url.contains(QUESTION_MARK)) { + url += AND_MARK.concat(URL_PARAMS); + } else { + url += QUESTION_MARK.concat(URL_PARAMS); + } + return DriverManager.getConnection(url, username, password); + } catch (ClassNotFoundException | SQLException e) { + log.error("createConnection", e); + throw new RuntimeException(String.format("Fail to create connection with driver=%s,url=%s,username=%s,password=%s", driver, url, username, password)); //NOSONAR + } + } + +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/JdbcType.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/JdbcType.java new file mode 100644 index 0000000..75858ce --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/JdbcType.java @@ -0,0 +1,44 @@ +package cn.cliveyuan.robin.generator.db; + +import lombok.Getter; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 类型映射 + * + * doc + * + * @author Clive Yuan + * @date 2020/11/04 + */ +@Getter +public enum JdbcType { + + BOOLEAN(Boolean.class, "BOOL", "BOOLEAN"), + BYTE_ARRAY(byte[].class, "BIT", "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB"), + INTEGER(Integer.class, "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "INTEGER"), + LONG(Long.class, "BIGINT"), + DOUBLE(Double.class, "DOUBLE"), + FLOAT(Float.class, "FLOAT"), + DECIMAL(BigDecimal.class, "DECIMAL"), + DATE(Date.class, "DATE", "DATETIME", "TIMESTAMP", "TIME", "YEAR"), + STRING(String.class, "ENUM", "SET", "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"), + OBJECT(Object.class, ""); + + + JdbcType(Class javaType, String... sqlTypes) { + this.javaType = javaType; + this.sqlTypes = sqlTypes; + } + + /** + * Java类型 + */ + private final Class javaType; + /** + * 数据库字段类型 + */ + private final String[] sqlTypes; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/JdbcTypeResolver.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/JdbcTypeResolver.java new file mode 100644 index 0000000..46c2966 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/JdbcTypeResolver.java @@ -0,0 +1,39 @@ +package cn.cliveyuan.robin.generator.db; + +import cn.cliveyuan.robin.generator.util.CollectionUtils; + +import java.util.Arrays; + +/** + * 类型解析器 + * + * @author Clive Yuan + * @date 2020/11/04 + */ +public class JdbcTypeResolver { + + private static final String UNSIGNED = " UNSIGNED"; + private static final String BIT = "BIT"; + + /** + * 解析类型 + * + * @param type jdbc类型 + * @param length 长度 + * @return + */ + public static JdbcType resolve(String type, Integer length) { + if (type.contains(UNSIGNED)) { + type = type.replace(UNSIGNED, ""); + } + String finalType = type; + JdbcType jdbcType = Arrays.stream(JdbcType.values()) + .filter(x -> CollectionUtils.listOf(x.getSqlTypes()).contains(finalType)) + .findAny() + .orElse(JdbcType.OBJECT); + if (BIT.equals(finalType) && length == 1) { + jdbcType = JdbcType.BOOLEAN; + } + return jdbcType; + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/MysqlTableIntrospect.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/MysqlTableIntrospect.java new file mode 100644 index 0000000..b75a3f6 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/MysqlTableIntrospect.java @@ -0,0 +1,98 @@ +package cn.cliveyuan.robin.generator.db; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * MySQL 表信息探测器 + * + * @author Clive Yuan + * @date 2020/10/28 + */ +@Slf4j +public class MysqlTableIntrospect implements TableIntrospect { + + private final Connection connection; + + public MysqlTableIntrospect(Connection connection) { + this.connection = connection; + } + + @Override + public TableInfo introspect(String tableName) { + try { + TableInfo tableInfo = new TableInfo(); + tableInfo.setName(tableName); + DatabaseMetaData metaData = connection.getMetaData(); + // introspect table info + try (ResultSet tableRs = metaData.getTables(connection.getCatalog(), null, tableName, null)) { + if (tableRs.next()) { + String remarks = tableRs.getString("REMARKS"); + if (StringUtils.isBlank(remarks)) { + remarks = tableName; + } + tableInfo.setComment(remarks); + } + } + List columns = new ArrayList(); + tableInfo.setColumns(columns); + + // introspect column info + try (ResultSet columnRs = metaData.getColumns(connection.getCatalog(), null, tableName, null)) { + while (columnRs.next()) { + String columnName = columnRs.getString("COLUMN_NAME"); + String typeName = columnRs.getString("TYPE_NAME"); + int columnSize = columnRs.getInt("COLUMN_SIZE"); + int nullable = columnRs.getInt("NULLABLE"); + String comment = columnRs.getString("REMARKS"); + ColumnInfo column = new ColumnInfo(); + column.setName(columnName); + column.setType(typeName); + column.setLength(columnSize); + column.setNullable(nullable == 1); + column.setComment(StringUtils.isNotBlank(comment) ? comment : columnName); + columns.add(column); + } + } + return tableInfo; + } catch (SQLException e) { + log.error("introspect", e); + throw new RuntimeException("Fail to introspect table: " + tableName); + } + } + + @Override + public List getAllTables() { + List list = new ArrayList<>(); + try { + DatabaseMetaData metaData = connection.getMetaData(); + ResultSet tableRs = metaData.getTables(connection.getCatalog(), null, null, null); + while (tableRs.next()) { + list.add(tableRs.getString("TABLE_NAME")); + } + } catch (SQLException e) { + log.error("introspect", e); + throw new RuntimeException(e); + } + return list; + } + + @Override + public void closeConnection() { + if (Objects.nonNull(connection)) { + try { + connection.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/TableInfo.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/TableInfo.java new file mode 100644 index 0000000..1c57267 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/TableInfo.java @@ -0,0 +1,28 @@ +package cn.cliveyuan.robin.generator.db; + +import lombok.Data; + +import java.util.List; + +/** + * 表信息 + * + * @author Clive Yuan + * @date 2020/10/28 + */ +@Data +public class TableInfo { + /** + * 名称 + */ + private String name; + /** + * 备注 + */ + private String comment; + /** + * 字段列表 + */ + private List columns; + +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/TableIntrospect.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/TableIntrospect.java new file mode 100644 index 0000000..964fd16 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/db/TableIntrospect.java @@ -0,0 +1,32 @@ +package cn.cliveyuan.robin.generator.db; + +import java.util.List; + +/** + * 表信息探测器 + * + * @author Clive Yuan + * @date 2020/10/28 + */ +public interface TableIntrospect { + + /** + * 探测表信息 + * + * @param tableName 表名 + * @return + */ + TableInfo introspect(String tableName); + + /** + * 获取所有表名 + * + * @return + */ + List getAllTables(); + + /** + * 关闭数据库连接 + */ + void closeConnection(); +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ControllerGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ControllerGenerator.java new file mode 100644 index 0000000..bca0423 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ControllerGenerator.java @@ -0,0 +1,24 @@ +package cn.cliveyuan.robin.generator.generator; + +import cn.cliveyuan.robin.generator.core.GenerateParam; +import cn.cliveyuan.robin.generator.core.Generator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorContext; + +/** + * 控制器 生成器 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public class ControllerGenerator implements Generator { + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + generatorChain.doGenerate(GenerateParam.builder() + .generator(this) + .context(context) + .generatorConfig(context.getXmlConfig().getControllerGenerator()) + .fileNameFunction(x -> x.getUpperCamelName() + "Controller") + .build()); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/DTOGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/DTOGenerator.java new file mode 100644 index 0000000..bd8a378 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/DTOGenerator.java @@ -0,0 +1,32 @@ +package cn.cliveyuan.robin.generator.generator; + +import cn.cliveyuan.robin.generator.core.CodeGeneratorXmlConfig; +import cn.cliveyuan.robin.generator.core.GenerateParam; +import cn.cliveyuan.robin.generator.core.Generator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorContext; + +/** + * DTO 生成器 + * + * @author Clive Yuan + * @date 2020/12/07 + */ +public class DTOGenerator implements Generator { + + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + CodeGeneratorXmlConfig.MapperGeneratorConfig controllerGenerator = context.getXmlConfig().getControllerGenerator(); + CodeGeneratorXmlConfig.MapperGeneratorConfig dtoGenerator = context.getXmlConfig().getDtoGenerator(); + // dto 依附于controller, 若不生成controller, 则不生成dto + dtoGenerator.setDisabled(controllerGenerator.isDisabled()); + generatorChain.doGenerate(GenerateParam.builder() + .generator(this) + .context(context) + .fileNameFunction(x -> x.getUpperCamelName() + "DTO") + .generatorConfig(dtoGenerator) + .build()); + } + + +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/EntityGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/EntityGenerator.java new file mode 100644 index 0000000..4ae588c --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/EntityGenerator.java @@ -0,0 +1,28 @@ +package cn.cliveyuan.robin.generator.generator; + +import cn.cliveyuan.robin.generator.core.Entity; +import cn.cliveyuan.robin.generator.core.GenerateParam; +import cn.cliveyuan.robin.generator.core.Generator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorContext; + +/** + * 实体 生成器 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public class EntityGenerator implements Generator { + + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + generatorChain.doGenerate(GenerateParam.builder() + .generator(this) + .context(context) + .fileNameFunction(Entity::getEntityName) + .generatorConfig(context.getXmlConfig().getJavaModelGenerator()) + .build()); + } + + +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaGenerator.java new file mode 100644 index 0000000..7f9a158 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaGenerator.java @@ -0,0 +1,24 @@ +package cn.cliveyuan.robin.generator.generator; + +import cn.cliveyuan.robin.generator.core.GenerateParam; +import cn.cliveyuan.robin.generator.core.Generator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorContext; + +/** + * Java Mapper 生成器 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public class MapperJavaGenerator implements Generator { + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + generatorChain.doGenerate(GenerateParam.builder() + .generator(this) + .context(context) + .generatorConfig(context.getXmlConfig().getJavaClientGenerator()) + .fileNameFunction(x -> x.getUpperCamelName() + "Mapper") + .build()); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaImplGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaImplGenerator.java new file mode 100644 index 0000000..f4dbb65 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaImplGenerator.java @@ -0,0 +1,33 @@ +package cn.cliveyuan.robin.generator.generator; + +import cn.cliveyuan.robin.generator.core.CodeGeneratorXmlConfig; +import cn.cliveyuan.robin.generator.core.GenerateParam; +import cn.cliveyuan.robin.generator.core.Generator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorConst; +import cn.cliveyuan.robin.generator.core.GeneratorContext; +import org.springframework.beans.BeanUtils; + +/** + * Java MapperImpl 生成器 + * + * @author Clive Yuan + * @date 2020/12/07 + */ +public class MapperJavaImplGenerator implements Generator { + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + CodeGeneratorXmlConfig.MapperGeneratorConfig mapperGenerator = context.getXmlConfig().getJavaClientGenerator(); + CodeGeneratorXmlConfig.MapperGeneratorConfig mapperImplGenerator = CodeGeneratorXmlConfig.MapperGeneratorConfig.builder().build(); + BeanUtils.copyProperties(mapperGenerator, mapperImplGenerator); + mapperImplGenerator.setTargetPackage(mapperGenerator.getTargetPackage() + GeneratorConst.IMPL_PKG_SUFFIX); + mapperImplGenerator.setModuleName("mapperImplGenerator"); + mapperImplGenerator.setDisabled(!context.getXmlConfig().getBaseConfig().isEnableReadWriteSeparation()); + generatorChain.doGenerate(GenerateParam.builder() + .generator(this) + .context(context) + .generatorConfig(mapperImplGenerator) + .fileNameFunction(x -> x.getUpperCamelName() + "MapperImpl") + .build()); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperXmlGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperXmlGenerator.java new file mode 100644 index 0000000..551f3c6 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperXmlGenerator.java @@ -0,0 +1,24 @@ +package cn.cliveyuan.robin.generator.generator; + +import cn.cliveyuan.robin.generator.core.GenerateParam; +import cn.cliveyuan.robin.generator.core.Generator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorContext; + +/** + * Xml Mapper 生成器 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public class MapperXmlGenerator implements Generator { + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + generatorChain.doGenerate(GenerateParam.builder() + .generator(this) + .context(context) + .generatorConfig(context.getXmlConfig().getSqlMapGenerator()) + .fileNameFunction(x -> x.getUpperCamelName() + "Mapper") + .build()); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceGenerator.java new file mode 100644 index 0000000..4730231 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceGenerator.java @@ -0,0 +1,24 @@ +package cn.cliveyuan.robin.generator.generator; + +import cn.cliveyuan.robin.generator.core.GenerateParam; +import cn.cliveyuan.robin.generator.core.Generator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorContext; + +/** + * Service 生成器 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public class ServiceGenerator implements Generator { + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + generatorChain.doGenerate(GenerateParam.builder() + .generator(this) + .context(context) + .generatorConfig(context.getXmlConfig().getServiceGenerator()) + .fileNameFunction(x -> x.getUpperCamelName() + "Service") + .build()); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceImplGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceImplGenerator.java new file mode 100644 index 0000000..6aa9765 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceImplGenerator.java @@ -0,0 +1,32 @@ +package cn.cliveyuan.robin.generator.generator; + +import cn.cliveyuan.robin.generator.core.CodeGeneratorXmlConfig; +import cn.cliveyuan.robin.generator.core.GenerateParam; +import cn.cliveyuan.robin.generator.core.Generator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorConst; +import cn.cliveyuan.robin.generator.core.GeneratorContext; +import org.springframework.beans.BeanUtils; + +/** + * Service 实现 生成器 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public class ServiceImplGenerator implements Generator { + @Override + public void generate(GeneratorContext context, GeneratorChain generatorChain) { + CodeGeneratorXmlConfig.MapperGeneratorConfig serviceGenerator = context.getXmlConfig().getServiceGenerator(); + CodeGeneratorXmlConfig.MapperGeneratorConfig serviceImplGenerator = CodeGeneratorXmlConfig.MapperGeneratorConfig.builder().build(); + BeanUtils.copyProperties(serviceGenerator, serviceImplGenerator); + serviceImplGenerator.setTargetPackage(serviceGenerator.getTargetPackage() + GeneratorConst.IMPL_PKG_SUFFIX); + serviceImplGenerator.setModuleName("serviceImplGenerator"); + generatorChain.doGenerate(GenerateParam.builder() + .generator(this) + .context(context) + .generatorConfig(serviceImplGenerator) + .fileNameFunction(x -> x.getUpperCamelName() + "ServiceImpl") + .build()); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/impl/MybatisGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/impl/MybatisGenerator.java index 244619b..0f2911d 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/impl/MybatisGenerator.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/impl/MybatisGenerator.java @@ -1,16 +1,40 @@ package cn.cliveyuan.robin.generator.impl; import cn.cliveyuan.robin.generator.RobinGenerator; +import cn.cliveyuan.robin.generator.core.GeneratorChain; +import cn.cliveyuan.robin.generator.core.GeneratorContext; +import cn.cliveyuan.robin.generator.core.GeneratorContextResolver; +import lombok.Builder; +import lombok.extern.slf4j.Slf4j; /** * @author Clive Yuan * @date 2020/12/23 */ +@Slf4j public class MybatisGenerator implements RobinGenerator { + private final String configFilePath; + + @Builder + public MybatisGenerator(String configFilePath) { + this.configFilePath = configFilePath; + } + @Override public void generate() { - // TODO: implement me - System.out.println("Hello RobinGenerator"); + log.info("MybatisGenerator.generate [START] customized configFilePath is {}", configFilePath); + + long start = System.currentTimeMillis(); + // 初始化上下文解析器 + GeneratorContextResolver generatorContextResolver = new GeneratorContextResolver(); + // 解析配置上下文 + GeneratorContext generatorContext = generatorContextResolver.resolve(configFilePath); + // 创建生成链路 + GeneratorChain generatorChain = new GeneratorChain(); + // 执行生成链路 + generatorChain.generate(generatorContext, generatorChain); + + log.info("MybatisGenerator.generate [SUCCESS] cost {}ms", System.currentTimeMillis() - start); } } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/CollectionUtils.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/CollectionUtils.java new file mode 100644 index 0000000..9dd473e --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/CollectionUtils.java @@ -0,0 +1,33 @@ +package cn.cliveyuan.robin.generator.util; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * @author Clive Yuan + * @date 2020/12/24 + */ +public class CollectionUtils { + + + public static List listOf(E... elements) { + checkNotNull(elements); + return Arrays.asList(elements); + } + + private static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + public static boolean isEmpty(final Collection coll) { + return coll == null || coll.isEmpty(); + } + + public static boolean isNotEmpty(final Collection coll) { + return !isEmpty(coll); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/FreemarkerUtils.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/FreemarkerUtils.java new file mode 100644 index 0000000..5fa7fc6 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/FreemarkerUtils.java @@ -0,0 +1,58 @@ +package cn.cliveyuan.robin.generator.util; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Locale; +import java.util.Map; + +/** + * ftl解析工具 + * + * @author Clive Yuan + * @date 2020-11-07 + */ +public class FreemarkerUtils { + + private static final Logger logger = LoggerFactory.getLogger(FreemarkerUtils.class); + private static final String ENCODING = "UTF-8"; + + /** + * 解析模板 + * + * @param filePath 模板文件路径 (resources下的路径) + * @param dataModelMap 模板中的变量和值 + * @return 返回解析后的内容 + */ + public static String parseTemplate(String filePath, Map dataModelMap) { + try { + int index = filePath.lastIndexOf('/'); + String basePackagePath = filePath.substring(0, index + 1); + String fileName = filePath.substring(index + 1); + Template template = getTemplateByName(basePackagePath, fileName); + StringWriter strWriter = new StringWriter(); + template.setOutputEncoding(ENCODING); + template.process(dataModelMap, strWriter); + return strWriter.toString(); + } catch (Exception e) { + logger.error("parseTemplate Exc", e); + throw new IllegalArgumentException("freemarker 模板解析失败"); + } + } + + @SuppressWarnings("deprecation") + private static Template getTemplateByName(String basePackagePath, String fileName) throws IOException { + Configuration configuration = new Configuration(); + configuration.setTagSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX); + configuration.setClassLoaderForTemplateLoading(FreemarkerUtils.class.getClassLoader(), basePackagePath); + configuration.setEncoding(Locale.ENGLISH, ENCODING); + configuration.setDefaultEncoding(ENCODING); + configuration.setOutputEncoding(ENCODING); + return configuration.getTemplate(fileName); + } + +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/GeneratorUtils.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/GeneratorUtils.java new file mode 100644 index 0000000..dd289ce --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/GeneratorUtils.java @@ -0,0 +1,46 @@ +package cn.cliveyuan.robin.generator.util; + +import com.google.common.base.CaseFormat; +import org.apache.commons.lang3.StringUtils; + +import java.util.Objects; + +/** + * 生成工具类 + * + * @author Clive Yuan + * @date 2020/09/09 + */ +public class GeneratorUtils { + + /** + * 获取小写驼峰名 + * + *

    user -> user

    + *

    User -> user

    + *

    USER -> user

    + *

    UsER -> usER

    + *

    TemplateEnterprise -> templateEnterprise

    + *

    t_user_info -> tUserInfo

    + *

    user_list_detail -> userListDetail

    + *

    USER_LIST_DETAIL -> userListDetail

    + * + * @param str 字符串 + * @return + */ + public static String getLowerCamelName(String str) { + // 全大写, 包含下划线 + Objects.requireNonNull(str, "str can't be null"); + if (str.contains("_")) { + return StringUtils.uncapitalize(lineToHump(str)); + } + if (StringUtils.isAllUpperCase(str)) { + return str.toLowerCase(); + } + return StringUtils.uncapitalize(str); + } + + private static String lineToHump(String str) { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, str.toUpperCase()); + } +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/ReflectUtils.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/ReflectUtils.java new file mode 100644 index 0000000..3698524 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/ReflectUtils.java @@ -0,0 +1,25 @@ +package cn.cliveyuan.robin.generator.util; + +/** + * 反射工具类 + * + * @author Clive Yuan + * @date 2020/11/05 + */ +public class ReflectUtils { + + /** + * 初始化 + * + * @param clazz 类 + * @param 泛型 + * @return + */ + public static T newInstance(Class clazz) { + try { + return (T)clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/robin-generator/src/main/resources/templates/controller.java.ftl b/robin-generator/src/main/resources/templates/controller.java.ftl new file mode 100644 index 0000000..e9e9f5d --- /dev/null +++ b/robin-generator/src/main/resources/templates/controller.java.ftl @@ -0,0 +1,78 @@ +package ${controllerPackage}; + +import ${entityPackage}.${entity.entityName}; +import ${dtoPackage}.${entity.upperCamelName}DTO; +import ${servicePackage}.${entity.upperCamelName}Service; +import cn.cliveyuan.robin.base.condition.PageQueryExample; +import cn.cliveyuan.robin.base.common.PageQueryRequest; +import cn.cliveyuan.robin.base.util.BeanCopyUtils; +import cn.cliveyuan.robin.base.common.ApiResponse; +import cn.cliveyuan.robin.base.common.Pagination; + +[#if baseConfig.enableSwagger] +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +[/#if] +[#if baseConfig.enableValidation] +import org.springframework.validation.annotation.Validated; +[/#if] +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * ${entity.comment} 控制器 + */ +@RestController +@RequestMapping("/${entity.lowerCamelName}") +[#if baseConfig.enableSwagger] +@Api(tags = "${entity.comment}接口") +[/#if] +public class ${entity.upperCamelName}Controller { + + @Resource + private ${entity.upperCamelName}Service ${entity.lowerCamelName}Service; + + @PostMapping("save") +[#if baseConfig.enableSwagger] + @ApiOperation("保存") +[/#if] + public ApiResponse save([#if baseConfig.enableValidation]@Validated[/#if] ${entity.upperCamelName}DTO request) { + ${entity.entityName} entity = BeanCopyUtils.copy(request, ${entity.entityName}.class); + ${entity.lowerCamelName}Service.save(entity); + return ApiResponse.success(entity.getId()); + } + + @PostMapping("delete") +[#if baseConfig.enableSwagger] + @ApiOperation("删除") +[/#if] + public ApiResponse delete(Long id) { + return ApiResponse.success(${entity.lowerCamelName}Service.delete(id) > 0); + } + + @PostMapping("get") +[#if baseConfig.enableSwagger] + @ApiOperation("获取") +[/#if] + public ApiResponse<${entity.upperCamelName}DTO> get(Long id) { + return ApiResponse.success(BeanCopyUtils.copy(${entity.lowerCamelName}Service.get(id), ${entity.upperCamelName}DTO.class)); + } + + @PostMapping("page") +[#if baseConfig.enableSwagger] + @ApiOperation("分页查询") +[/#if] + public ApiResponse> page(@RequestBody PageQueryRequest<${entity.upperCamelName}DTO> request) { + PageQueryExample<${entity.entityName}> query = new PageQueryExample<>(); + query.setPageNo(request.getPageNo()); + query.setPageSize(request.getPageSize()); + if (request.getEntity() != null) { + query.setEntity(BeanCopyUtils.copy(request.getEntity(), ${entity.entityName}.class)); + } + return ApiResponse.success(BeanCopyUtils.copyPagination(${entity.lowerCamelName}Service.page(query), ${entity.upperCamelName}DTO.class)); + } +} diff --git a/robin-generator/src/main/resources/templates/dto.java.ftl b/robin-generator/src/main/resources/templates/dto.java.ftl new file mode 100644 index 0000000..b905302 --- /dev/null +++ b/robin-generator/src/main/resources/templates/dto.java.ftl @@ -0,0 +1,80 @@ +package ${dtoPackage}; + +[#if baseConfig.enableSwagger] +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +[/#if] +[#if baseConfig.enableValidation] +import org.hibernate.validator.constraints.Length; +[/#if] +[#if baseConfig.enableLombok] + +import lombok.Data; +[/#if] +[#if baseConfig.enableValidation] + +import javax.validation.constraints.NotNull; +[/#if] +import java.io.Serializable; +import java.util.Date; +[#if entity.hasBigDecimalField] +import java.math.BigDecimal; +[/#if] + + +/** + * ${entity.comment} + */ +[#if baseConfig.enableLombok] +@Data +[/#if] +[#if baseConfig.enableSwagger] +@ApiModel(value = "${entity.comment}") +[/#if] +public class ${entity.upperCamelName}DTO implements Serializable { + +[#--fields--] + private static final long serialVersionUID = 1L; + +[#list entity.fields as field] + /** + * ${field.comment} + */ + [#if baseConfig.enableSwagger] + @ApiModelProperty(value = "${field.comment}"[#if field.swaggerHidden], hidden = true[/#if]) + [/#if] + [#if baseConfig.enableValidation] + [#if !field.nullable] + @NotNull(message = "${field.comment}不能为空") + [/#if] + [#if field.jdbcType.javaType.simpleName == 'String'] + [#assign fieldLength = field.length?string('#')] + @Length(max = ${fieldLength}, message = "${field.comment}长度不能超过${fieldLength}个字符") + [/#if] + [/#if] + private ${field.jdbcType.javaType.simpleName} ${field.lowerCamelName}; + +[/#list] +[#-- getters & setters --] +[#if !baseConfig.enableLombok] +[#list entity.fields as field] + public ${field.jdbcType.javaType.simpleName} get${field.upperCamelName}() { + return ${field.lowerCamelName}; + } + + public void set${field.upperCamelName}(${field.jdbcType.javaType.simpleName} ${field.lowerCamelName}) { + this.${field.lowerCamelName} = ${field.lowerCamelName}; + } + +[/#list] +[#--toString--] + @Override + public String toString() { + return "${entity.entityName}{" + + [#list entity.fields as field] + "${field.lowerCamelName}=" + ${field.lowerCamelName} + + [/#list] + '}'; + } +[/#if] +} diff --git a/robin-generator/src/main/resources/templates/entity.java.ftl b/robin-generator/src/main/resources/templates/entity.java.ftl new file mode 100644 index 0000000..b736df5 --- /dev/null +++ b/robin-generator/src/main/resources/templates/entity.java.ftl @@ -0,0 +1,64 @@ +package ${entityPackage}; + +import cn.cliveyuan.robin.base.annotation.TableField; +import cn.cliveyuan.robin.base.annotation.TableId; +import cn.cliveyuan.robin.base.annotation.TableName; + +import java.io.Serializable; +[#if baseConfig.enableLombok] + +import lombok.Data; +[/#if] +[#if entity.hasBigDecimalField] +import java.math.BigDecimal; +[/#if] +import java.util.Date; + +/** + * ${entity.comment} + */ +[#if baseConfig.enableLombok] +@Data +[/#if] +@TableName("${entity.tableName}") +public class ${entity.entityName} implements Serializable { + +[#--fields--] + private static final long serialVersionUID = 1L; + +[#list entity.fields as field] + /** + * ${field.comment} + */ + [#if field.markColumnName || field.ignoreSaving] + @TableField([#if field.markColumnName]value = "${field.columnName}"[/#if][#if field.markColumnName && field.ignoreSaving], [/#if][#if field.ignoreSaving]ignoreSaving = true[/#if]) + [/#if] + [#if field.primaryKey] + @TableId + [/#if] + private ${field.jdbcType.javaType.simpleName} ${field.lowerCamelName}; + +[/#list] +[#-- getters & setters --] +[#if !baseConfig.enableLombok] +[#list entity.fields as field] + public ${field.jdbcType.javaType.simpleName} get${field.upperCamelName}() { + return ${field.lowerCamelName}; + } + + public void set${field.upperCamelName}(${field.jdbcType.javaType.simpleName} ${field.lowerCamelName}) { + this.${field.lowerCamelName} = ${field.lowerCamelName}; + } + +[/#list] +[#--toString--] + @Override + public String toString() { + return "${entity.entityName}{" + + [#list entity.fields as field] + "${field.lowerCamelName}=" + ${field.lowerCamelName} + + [/#list] + '}'; + } +[/#if] +} diff --git a/robin-generator/src/main/resources/templates/mapper.java.ftl b/robin-generator/src/main/resources/templates/mapper.java.ftl new file mode 100644 index 0000000..5721ff3 --- /dev/null +++ b/robin-generator/src/main/resources/templates/mapper.java.ftl @@ -0,0 +1,11 @@ +package ${mapperPackage}; + +import ${entityPackage}.${entity.entityName}; +import cn.cliveyuan.robin.base.BaseMapper; + +/** + * ${entity.comment} Mapper + */ +public interface ${entity.upperCamelName}Mapper extends BaseMapper<${entity.entityName}> { + +} diff --git a/robin-generator/src/main/resources/templates/mapper.xml.ftl b/robin-generator/src/main/resources/templates/mapper.xml.ftl new file mode 100644 index 0000000..14f6a36 --- /dev/null +++ b/robin-generator/src/main/resources/templates/mapper.xml.ftl @@ -0,0 +1,19 @@ + + + + + + [#list entity.fields as field]`${field.columnName}`[#if field_has_next],[/#if][/#list] + + + +[#list entity.fields as field] + [#if field.columnName == 'id'] + + [#else ] + + [/#if] +[/#list] + + + diff --git a/robin-generator/src/main/resources/templates/service.java.ftl b/robin-generator/src/main/resources/templates/service.java.ftl new file mode 100644 index 0000000..ddca336 --- /dev/null +++ b/robin-generator/src/main/resources/templates/service.java.ftl @@ -0,0 +1,11 @@ +package ${servicePackage}; + +import cn.cliveyuan.robin.base.BaseService; +import ${entityPackage}.${entity.entityName}; + +/** + * ${entity.comment} 服务接口 + */ +public interface ${entity.upperCamelName}Service extends BaseService<${entity.entityName}> { + +} diff --git a/robin-generator/src/main/resources/templates/serviceimpl.java.ftl b/robin-generator/src/main/resources/templates/serviceimpl.java.ftl new file mode 100644 index 0000000..fe0d594 --- /dev/null +++ b/robin-generator/src/main/resources/templates/serviceimpl.java.ftl @@ -0,0 +1,14 @@ +package ${serviceImplPackage}; + +import cn.cliveyuan.robin.base.BaseServiceImpl; +import ${entityPackage}.${entity.entityName}; +import ${servicePackage}.${entity.upperCamelName}Service; +import org.springframework.stereotype.Service; + +/** + * ${entity.comment} 服务实现 + */ +@Service +public class ${entity.upperCamelName}ServiceImpl extends BaseServiceImpl<${entity.entityName}> implements ${entity.upperCamelName}Service { + +} diff --git a/robin-generator/src/test/java/cn/cliveyuan/robin/generator/test/RobinGeneratorTest.java b/robin-generator/src/test/java/cn/cliveyuan/robin/generator/test/RobinGeneratorTest.java index 153a9b9..b7caa79 100644 --- a/robin-generator/src/test/java/cn/cliveyuan/robin/generator/test/RobinGeneratorTest.java +++ b/robin-generator/src/test/java/cn/cliveyuan/robin/generator/test/RobinGeneratorTest.java @@ -12,7 +12,7 @@ public class RobinGeneratorTest { @Test public void testGenerate() { - RobinGenerator robinGenerator = new MybatisGenerator(); + RobinGenerator robinGenerator = MybatisGenerator.builder().build(); robinGenerator.generate(); } } diff --git a/robin-generator/src/test/resources/code-generator.xml b/robin-generator/src/test/resources/code-generator.xml new file mode 100755 index 0000000..aba0e14 --- /dev/null +++ b/robin-generator/src/test/resources/code-generator.xml @@ -0,0 +1,51 @@ + + + + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + + -- Gitee From a9423cd1ad9929c55542857b2d4350cc1366d6eb Mon Sep 17 00:00:00 2001 From: CliveYuan Date: Fri, 25 Dec 2020 10:19:15 +0800 Subject: [PATCH 3/4] update to 1.0.2 --- pom.xml | 4 +--- robin-base/pom.xml | 5 ++--- .../java/cn/cliveyuan/robin/base/util/AssertUtils.java | 4 +--- robin-generator/pom.xml | 7 +++---- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 4853dcc..764a373 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.gitee.opensource4clive robin pom - ${revision} + 1.0.0.RELEASE robin Robin is a lightweight framework based on MyBatis that provides adding, deleting, modifying and querying capabilities. https://gitee.com/opensource4clive/robin @@ -41,8 +41,6 @@ - 1.0.1 - 1.0.1 ${java.home}/../bin/javadoc 1.8 UTF-8 diff --git a/robin-base/pom.xml b/robin-base/pom.xml index 14acccc..4f011ff 100644 --- a/robin-base/pom.xml +++ b/robin-base/pom.xml @@ -3,12 +3,11 @@ 4.0.0 robin-base jar - + 1.0.2 com.gitee.opensource4clive robin - ${revision} - ../ + 1.0.0.RELEASE diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/AssertUtils.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/AssertUtils.java index dce2357..bc640d8 100644 --- a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/AssertUtils.java +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/AssertUtils.java @@ -4,8 +4,6 @@ package cn.cliveyuan.robin.base.util; -import com.sun.istack.internal.Nullable; - /** * @author Clive Yuan * @date 2020/12/23 @@ -18,7 +16,7 @@ public class AssertUtils { } } - public static void notNull(@Nullable Object object, String message) { + public static void notNull(Object object, String message) { if (object == null) { throw new IllegalArgumentException(message); } diff --git a/robin-generator/pom.xml b/robin-generator/pom.xml index 9626331..5346f41 100644 --- a/robin-generator/pom.xml +++ b/robin-generator/pom.xml @@ -2,14 +2,13 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 robin-generator - ${robin-generator.revision} + 1.0.2 jar com.gitee.opensource4clive robin - ${revision} - ../ + 1.0.0.RELEASE @@ -85,7 +84,7 @@ com.gitee.opensource4clive robin-base - ${revision} + 1.0.2 test -- Gitee From 6d7c812e7bb477525d3cb7543cc5665468935568 Mon Sep 17 00:00:00 2001 From: CliveYuan Date: Tue, 2 Feb 2021 17:55:19 +0800 Subject: [PATCH 4/4] deploy: v1.1.0 --- pom.xml | 29 +++++- robin-base/pom.xml | 4 +- .../cliveyuan/robin/base/util/SqlUtils.java | 2 +- robin-generator/dtd/robin-generator.dtd | 88 ++++++++++++++++++ robin-generator/pom.xml | 9 +- .../core/CodeGeneratorXmlConfig.java | 22 +++++ .../robin/generator/core/Entity.java | 2 + .../robin/generator/core/ExtensionParam.java | 31 +++++++ .../robin/generator/core/GenerateParam.java | 6 ++ .../robin/generator/core/GeneratorChain.java | 43 ++++++++- .../core/GeneratorContextResolver.java | 92 ++++++++++++------- .../robin/generator/core/GeneratorEnum.java | 20 ++-- .../generator/ControllerGenerator.java | 1 - .../generator/generator/DTOGenerator.java | 1 - .../generator/MapperJavaGenerator.java | 1 - .../generator/MapperJavaImplGenerator.java | 3 +- .../generator/MapperXmlGenerator.java | 42 ++++++++- .../generator/generator/ServiceGenerator.java | 1 - .../generator/ServiceImplGenerator.java | 3 +- .../util/FileContentModifyUtils.java | 61 ++++++++++++ .../resources/templates/controller.java.ftl | 27 +++--- .../src/main/resources/templates/dto.java.ftl | 19 ++-- .../main/resources/templates/entity.java.ftl | 15 ++- .../main/resources/templates/mapper.java.ftl | 7 +- .../main/resources/templates/mapper.xml.ftl | 20 ++-- .../segment/base_column_list.inc.ftl | 3 + .../templates/segment/base_result_map.inc.ftl | 9 ++ .../main/resources/templates/service.java.ftl | 7 +- .../resources/templates/serviceimpl.java.ftl | 9 +- .../src/test/resources/code-generator.xml | 3 +- update_versions.sh | 4 + 31 files changed, 483 insertions(+), 101 deletions(-) create mode 100644 robin-generator/dtd/robin-generator.dtd create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/ExtensionParam.java create mode 100644 robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/FileContentModifyUtils.java create mode 100644 robin-generator/src/main/resources/templates/segment/base_column_list.inc.ftl create mode 100644 robin-generator/src/main/resources/templates/segment/base_result_map.inc.ftl create mode 100755 update_versions.sh diff --git a/pom.xml b/pom.xml index 764a373..2c78548 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.gitee.opensource4clive robin pom - 1.0.0.RELEASE + 1.1.0 robin Robin is a lightweight framework based on MyBatis that provides adding, deleting, modifying and querying capabilities. https://gitee.com/opensource4clive/robin @@ -58,10 +58,22 @@ 3.10 2.6 8.0.19 + 1.2.0 + 2.7 + + com.gitee.opensource4clive + robin-base + ${project.version} + + + com.gitee.opensource4clive + robin-generator + ${project.version} + @@ -120,6 +132,12 @@ dom4j ${dom4j.version} + + jaxen + jaxen + ${jaxen.version} + + org.freemarker @@ -174,7 +192,6 @@ - @@ -239,6 +256,14 @@ + + org.codehaus.mojo + versions-maven-plugin + ${versions-maven-plugin.version} + + false + + diff --git a/robin-base/pom.xml b/robin-base/pom.xml index 4f011ff..79bc93b 100644 --- a/robin-base/pom.xml +++ b/robin-base/pom.xml @@ -3,11 +3,11 @@ 4.0.0 robin-base jar - 1.0.2 + 1.1.0 com.gitee.opensource4clive robin - 1.0.0.RELEASE + 1.1.0 diff --git a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/SqlUtils.java b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/SqlUtils.java index 49ffbfa..5feaba0 100644 --- a/robin-base/src/main/java/cn/cliveyuan/robin/base/util/SqlUtils.java +++ b/robin-base/src/main/java/cn/cliveyuan/robin/base/util/SqlUtils.java @@ -31,7 +31,7 @@ public class SqlUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SqlUtils.class); private static final Pattern SQL_PARAM_PATTERN = Pattern.compile("(@\\{)([\\w]+)(\\})"); private static final Pattern TPL_PARAM_PATTERN = Pattern.compile("(@sql\\{)([\\w]+)(\\})"); - private static final String SQL_SEGMENT_PATH = "/code-generator/sql-segment.xml"; + private static final String SQL_SEGMENT_PATH = "/sql-segment.xml"; private static final Map SQL_SEGMENT = new ConcurrentHashMap<>(); private static final String INJECTION_REGEX = "[A-Za-z0-9\\_\\-\\+\\.]+"; diff --git a/robin-generator/dtd/robin-generator.dtd b/robin-generator/dtd/robin-generator.dtd new file mode 100644 index 0000000..3ca713b --- /dev/null +++ b/robin-generator/dtd/robin-generator.dtd @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/robin-generator/pom.xml b/robin-generator/pom.xml index 5346f41..ab1c007 100644 --- a/robin-generator/pom.xml +++ b/robin-generator/pom.xml @@ -2,13 +2,13 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 robin-generator - 1.0.2 + 1.1.0 jar com.gitee.opensource4clive robin - 1.0.0.RELEASE + 1.1.0 @@ -21,6 +21,10 @@ dom4j dom4j + + jaxen + jaxen + org.freemarker freemarker @@ -84,7 +88,6 @@ com.gitee.opensource4clive robin-base - 1.0.2 test diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/CodeGeneratorXmlConfig.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/CodeGeneratorXmlConfig.java index 3d750da..2fc2701 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/CodeGeneratorXmlConfig.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/CodeGeneratorXmlConfig.java @@ -35,6 +35,7 @@ public class CodeGeneratorXmlConfig implements Serializable { private boolean enableSwagger; private boolean enableValidation; private boolean enableReadWriteSeparation; + private boolean disableUpdatingMapperXml; } @Data @@ -49,10 +50,31 @@ public class CodeGeneratorXmlConfig implements Serializable { @Data @Builder public static class MapperGeneratorConfig { + /** + * 是否禁用 + */ private boolean disabled; + /** + * 模板路径(相对resource路径) + */ private String templatePath; + /** + * 代码路径(相对root路径) + */ private String codePath; + /** + * 目标包名 + */ private String targetPackage; + /** + * 后缀 + */ + private String suffix; + + // 以下为内部字段,不从xml中读取 + /** + * 模块名 + */ private String moduleName; } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Entity.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Entity.java index 76fd50e..ebd02b7 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Entity.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/Entity.java @@ -41,6 +41,8 @@ public class Entity implements Serializable { * 字段 */ private List fields; + + // 内部使用字段 /** * 文件名(生成文件用) */ diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/ExtensionParam.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/ExtensionParam.java new file mode 100644 index 0000000..4c7e02a --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/ExtensionParam.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.generator.core; + +import lombok.Builder; +import lombok.Data; + +import java.util.Map; + +/** + * @author Clive Yuan + * @date 2021/02/02 + */ +@Data +@Builder +public class ExtensionParam { + /** + * 生成上下文 + */ + private GeneratorContext context; + /** + * 模板参数 + */ + private Map paramMap; + /** + * 代码文件保存路径 + */ + private String codeFilePath; +} diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GenerateParam.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GenerateParam.java index 27ae2de..cde93cd 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GenerateParam.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GenerateParam.java @@ -4,6 +4,7 @@ import lombok.Builder; import lombok.Data; import java.io.Serializable; +import java.util.function.Consumer; import java.util.function.Function; /** @@ -31,4 +32,9 @@ public class GenerateParam implements Serializable { * 生成配置 */ private CodeGeneratorXmlConfig.MapperGeneratorConfig generatorConfig; + /** + * 生成前 扩展处理 + */ + private Consumer beforeExtensionHandle; + } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorChain.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorChain.java index 8018bfe..e819f9b 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorChain.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorChain.java @@ -13,6 +13,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Function; /** @@ -75,7 +76,7 @@ public class GeneratorChain implements Generator { GeneratorEnum generatorEnum = GeneratorEnum.valueOf(generateParam.getGenerator()); GeneratorContext context = generateParam.getContext(); // 设置文件名 - this.setFileName(context.getEntityList(), generateParam.getFileNameFunction()); + this.setFileName(generateParam); // 文件保存路径 String codeFilePath = this.getCodeFilePath(generatorConfig); // 模板变量 @@ -84,6 +85,14 @@ public class GeneratorChain implements Generator { // 代码模板: 如果自定义为空 则使用默认 String templatePath = StringUtils.isNotBlank(customizedTemplatePath) ? customizedTemplatePath : generatorEnum.getTemplatePath(); + + if (Objects.nonNull(generateParam.getBeforeExtensionHandle())) { + generateParam.getBeforeExtensionHandle().accept(ExtensionParam.builder() + .context(generateParam.getContext()) + .paramMap(paramMap) + .codeFilePath(codeFilePath) + .build()); + } // 遍历实体列表,生成文件 context.getEntityList().forEach(entity -> this.generateFile(paramMap, templatePath, codeFilePath, entity, generatorEnum)); @@ -93,8 +102,30 @@ public class GeneratorChain implements Generator { } } - private void setFileName(List entities, Function function) { - entities.forEach(x -> x.setFileName(function.apply(x))); + public String getSuffix(CodeGeneratorXmlConfig.MapperGeneratorConfig generatorConfig, Generator generator) { + return getSuffix(generatorConfig, GeneratorEnum.valueOf(generator)); + } + + public String getSuffix(CodeGeneratorXmlConfig.MapperGeneratorConfig generatorConfig, GeneratorEnum generatorEnum) { + String suffix = generatorEnum.getDefaultSuffix(); + if (StringUtils.isNotBlank(generatorConfig.getSuffix())) { + suffix = generatorConfig.getSuffix(); + } + return suffix; + } + + private void setFileName(GenerateParam generateParam) { + Function function = generateParam.getFileNameFunction(); + List entities = generateParam.getContext().getEntityList(); + + if (Objects.nonNull(function)) { + // 处理自定义文件名 + entities.forEach(x -> x.setFileName(function.apply(x))); + } else { + // 默认为 大小驼峰+后缀 + String suffix = this.getSuffix(generateParam.getGeneratorConfig(), generateParam.getGenerator()); + entities.forEach(x -> x.setFileName(x.getUpperCamelName() + suffix)); + } } private String getCodeFilePath(CodeGeneratorXmlConfig.MapperGeneratorConfig generatorConfig) { @@ -112,6 +143,9 @@ public class GeneratorChain implements Generator { CodeGeneratorXmlConfig.MapperGeneratorConfig serviceGenerator = xmlConfig.getServiceGenerator(); CodeGeneratorXmlConfig.MapperGeneratorConfig controllerGenerator = xmlConfig.getControllerGenerator(); CodeGeneratorXmlConfig.MapperGeneratorConfig dtoGenerator = xmlConfig.getDtoGenerator(); + String dtoSuffix = this.getSuffix(dtoGenerator, GeneratorEnum.DTO); + String mapperSuffix = this.getSuffix(javaClientGenerator, GeneratorEnum.MAPPER_JAVA); + String serviceSuffix = this.getSuffix(serviceGenerator, GeneratorEnum.SERVICE); // 设置变量 Map paramMap = Maps.newHashMap(); paramMap.put("entityPackage", javaModelGenerator.getTargetPackage()); @@ -122,6 +156,9 @@ public class GeneratorChain implements Generator { paramMap.put("controllerPackage", controllerGenerator.getTargetPackage()); paramMap.put("dtoPackage", dtoGenerator.getTargetPackage()); paramMap.put("baseConfig", xmlConfig.getBaseConfig()); + paramMap.put("dtoSuffix", dtoSuffix); + paramMap.put("mapperSuffix", mapperSuffix); + paramMap.put("serviceSuffix", serviceSuffix); return paramMap; } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContextResolver.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContextResolver.java index d329b61..ff614bd 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContextResolver.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorContextResolver.java @@ -1,6 +1,5 @@ package cn.cliveyuan.robin.generator.core; - import cn.cliveyuan.robin.generator.db.ColumnInfo; import cn.cliveyuan.robin.generator.db.ConnectionFactory; import cn.cliveyuan.robin.generator.db.JdbcType; @@ -10,6 +9,7 @@ import cn.cliveyuan.robin.generator.db.TableInfo; import cn.cliveyuan.robin.generator.db.TableIntrospect; import cn.cliveyuan.robin.generator.util.CollectionUtils; import cn.cliveyuan.robin.generator.util.GeneratorUtils; +import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; @@ -101,30 +101,47 @@ public class GeneratorContextResolver { List fields = new ArrayList<>(); fields.addAll(xmlConfig.getFixedField()); fields.addAll(this.column2Field(table, tableInfo.getColumns())); + this.matchIdType(fields, tableInfo.getColumns()); entity.setFields(fields); entity.setHasBigDecimalField(this.hasBigDecimalField(entity.getFields())); return entity; } + // 匹配id类型 + private void matchIdType(List fields, List columns) { + columns.stream().filter(x -> Objects.equals(GeneratorConst.PRIMARY_KEY_COL, x.getName())) + .findAny().ifPresent(idColumn -> { + fields.removeIf(x -> Objects.equals(GeneratorConst.PRIMARY_KEY_COL, x.getColumnName())); + Field idField = this.convertColumnInfo2Field(idColumn); + idField.setNullable(true); + idField.setComment("ID 主键"); + idField.setMarkColumnName(false); + idField.setPrimaryKey(true); + fields.add(0, idField); + }); + } + private List column2Field(CodeGeneratorXmlConfig.Table table, List columns) { return columns.stream() .filter(x -> !table.getIgnoreColumns().contains(x.getName())) // 排除忽略字段 - .map(x -> { - Field field = new Field(); - field.setJdbcType(JdbcTypeResolver.resolve(x.getType(), x.getLength())); - String lowerCamelName = GeneratorUtils.getLowerCamelName(x.getName()); - if (SourceVersion.isKeyword(lowerCamelName)) { - lowerCamelName += GeneratorConst.KEY_WORD_PREFIX; - } - field.setLowerCamelName(lowerCamelName); - field.setUpperCamelName(StringUtils.capitalize(lowerCamelName)); - field.setColumnName(x.getName()); - field.setLength(x.getLength()); - field.setNullable(x.getNullable()); - field.setComment(x.getComment()); - field.setMarkColumnName(!Objects.equals(field.getLowerCamelName(), field.getColumnName())); - return field; - }).collect(Collectors.toList()); + .map(this::convertColumnInfo2Field).collect(Collectors.toList()); + } + + private Field convertColumnInfo2Field(ColumnInfo columnInfo) { + Field field = new Field(); + field.setJdbcType(JdbcTypeResolver.resolve(columnInfo.getType(), columnInfo.getLength())); + String lowerCamelName = GeneratorUtils.getLowerCamelName(columnInfo.getName()); + if (SourceVersion.isKeyword(lowerCamelName)) { + lowerCamelName += GeneratorConst.KEY_WORD_PREFIX; + } + field.setLowerCamelName(lowerCamelName); + field.setUpperCamelName(StringUtils.capitalize(lowerCamelName)); + field.setColumnName(columnInfo.getName()); + field.setLength(columnInfo.getLength()); + field.setNullable(columnInfo.getNullable()); + field.setComment(columnInfo.getComment()); + field.setMarkColumnName(!Objects.equals(field.getLowerCamelName(), field.getColumnName())); + return field; } private boolean hasBigDecimalField(List fields) { @@ -147,6 +164,7 @@ public class GeneratorContextResolver { } Assert.notNull(xmlInputStream, "code generator config file is not exist: " + configFilePath); SAXReader reader = new SAXReader(); + reader.setValidation(true); Document document = reader.read(xmlInputStream); Element rootElement = document.getRootElement(); @@ -172,25 +190,28 @@ public class GeneratorContextResolver { CodeGeneratorXmlConfig.MapperGeneratorConfig controllerGenerator = config.getControllerGenerator(); CodeGeneratorXmlConfig.MapperGeneratorConfig dtoGenerator = config.getDtoGenerator(); if (!controllerGenerator.isDisabled()) { - Assert.isTrue(!dtoGenerator.isDisabled(), "DTO生成配置未生效, 请检查'dtoGenerator'标签"); + Assert.isTrue(!dtoGenerator.isDisabled(), "DTO生成配置未生效, 请检查'dtoGenerator'标签(生成controller时DTO也需要开启)"); } } private CodeGeneratorXmlConfig.BaseConfig getBaseConfig(Element rootElement) { CodeGeneratorXmlConfig.BaseConfig baseConfig = CodeGeneratorXmlConfig.BaseConfig.builder().build(); - Element baseConfigEl = this.getElement(rootElement, "baseConfig"); - Iterator iterator = baseConfigEl.elementIterator("property"); - while (iterator.hasNext()) { - Element property = (Element) iterator.next(); - String name = this.getAttributeValue(property, "name"); - if (Objects.equals("enableLombok", name)) { - baseConfig.setEnableLombok("true".equalsIgnoreCase(property.getStringValue())); - } else if (Objects.equals("enableSwagger", name)) { - baseConfig.setEnableSwagger("true".equalsIgnoreCase(property.getStringValue())); - } else if (Objects.equals("enableValidation", name)) { - baseConfig.setEnableValidation("true".equalsIgnoreCase(property.getStringValue())); - } else if (Objects.equals("enableReadWriteSeparation", name)) { - baseConfig.setEnableReadWriteSeparation("true".equalsIgnoreCase(property.getStringValue())); + Element baseConfigEl = this.getNullableElement(rootElement, "baseConfig"); + if (Objects.nonNull(baseConfigEl)) { + Iterator iterator = baseConfigEl.elementIterator("property"); + while (iterator.hasNext()) { + Element property = (Element) iterator.next(); + String name = this.getAttributeValue(property, "name"); + if (StringUtils.isBlank(name)) { + continue; + } + switch (name) { + case "enableLombok": baseConfig.setEnableLombok("true".equalsIgnoreCase(property.getStringValue())); break; + case "enableSwagger": baseConfig.setEnableSwagger("true".equalsIgnoreCase(property.getStringValue())); break; + case "enableValidation": baseConfig.setEnableValidation("true".equalsIgnoreCase(property.getStringValue())); break; + case "enableReadWriteSeparation": baseConfig.setEnableReadWriteSeparation("true".equalsIgnoreCase(property.getStringValue())); break; + case "disableUpdatingMapperXml": baseConfig.setDisableUpdatingMapperXml("true".equalsIgnoreCase(property.getStringValue())); break; + } } } return baseConfig; @@ -215,7 +236,7 @@ public class GeneratorContextResolver { } private List getTables(Element rootElement) { - List tables = new ArrayList<>(); + List tables = Lists.newArrayList(); Element tableEls = this.getElement(rootElement, "tables"); String all = this.getAttributeValue(tableEls, "all", true); boolean allTables = "true".equalsIgnoreCase(all); @@ -328,16 +349,21 @@ public class GeneratorContextResolver { .targetPackage(this.getAttributeValue(element, "targetPackage")) .codePath(this.getAttributeValue(element, "codePath")) .templatePath(this.getAttributeValue(element, "templatePath", true)) + .suffix(this.getAttributeValue(element, "suffix", true)) .moduleName(nodeName) .build(); } private Element getElement(Element element, String name) { - Element childElement = element.element(name); + Element childElement = this.getNullableElement(element, name); Assert.notNull(childElement, "Element '" + name + "' is not exist"); return childElement; } + private Element getNullableElement(Element element, String name) { + return element.element(name); + } + private String getAttributeValueNonBlank(Element element, String attributeName) { String attributeValue = this.getAttributeValue(element, attributeName, false); Assert.isTrue(StringUtils.isNotBlank(attributeValue), "Attribute '" + attributeName + "' is empty string"); diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorEnum.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorEnum.java index 05de072..954775f 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorEnum.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/core/GeneratorEnum.java @@ -24,14 +24,14 @@ import java.util.Objects; @Getter public enum GeneratorEnum { - ENTITY(EntityGenerator.class, FileType.JAVA, "templates/entity.java.ftl", true), - MAPPER_XML(MapperXmlGenerator.class, FileType.XML, "templates/mapper.xml.ftl", false), - MAPPER_JAVA(MapperJavaGenerator.class, FileType.JAVA, "templates/mapper.java.ftl", false), - MAPPER_JAVA_IMPL(MapperJavaImplGenerator.class, FileType.JAVA, "templates/mapperimpl.java.ftl", false), - SERVICE(ServiceGenerator.class, FileType.JAVA, "templates/service.java.ftl", false), - SERVICE_IMPL(ServiceImplGenerator.class, FileType.JAVA, "templates/serviceimpl.java.ftl", false), - DTO(DTOGenerator.class, FileType.JAVA, "templates/dto.java.ftl", false), - CONTROLLER(ControllerGenerator.class, FileType.JAVA, "templates/controller.java.ftl", false); + ENTITY(EntityGenerator.class, FileType.JAVA, "templates/entity.java.ftl", true, ""), + MAPPER_XML(MapperXmlGenerator.class, FileType.XML, "templates/mapper.xml.ftl", false, "Mapper"), + MAPPER_JAVA(MapperJavaGenerator.class, FileType.JAVA, "templates/mapper.java.ftl", false, "Mapper"), + MAPPER_JAVA_IMPL(MapperJavaImplGenerator.class, FileType.JAVA, "templates/mapperimpl.java.ftl", false, "Mapper"), + SERVICE(ServiceGenerator.class, FileType.JAVA, "templates/service.java.ftl", false, "Service"), + SERVICE_IMPL(ServiceImplGenerator.class, FileType.JAVA, "templates/serviceimpl.java.ftl", false, "Service"), + DTO(DTOGenerator.class, FileType.JAVA, "templates/dto.java.ftl", false, "DTO"), + CONTROLLER(ControllerGenerator.class, FileType.JAVA, "templates/controller.java.ftl", false, "Controller"); /** * 生成器类 @@ -49,6 +49,10 @@ public enum GeneratorEnum { * 是否覆盖写入 */ private final boolean overwrite; + /** + * 默认后缀 + */ + private final String defaultSuffix; public static GeneratorEnum valueOf(Generator generator) { return Arrays.stream(GeneratorEnum.values()).filter(x -> Objects.equals(generator.getClass(), x.getClazz())) diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ControllerGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ControllerGenerator.java index bca0423..903bf13 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ControllerGenerator.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ControllerGenerator.java @@ -18,7 +18,6 @@ public class ControllerGenerator implements Generator { .generator(this) .context(context) .generatorConfig(context.getXmlConfig().getControllerGenerator()) - .fileNameFunction(x -> x.getUpperCamelName() + "Controller") .build()); } } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/DTOGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/DTOGenerator.java index bd8a378..fbc1b1a 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/DTOGenerator.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/DTOGenerator.java @@ -23,7 +23,6 @@ public class DTOGenerator implements Generator { generatorChain.doGenerate(GenerateParam.builder() .generator(this) .context(context) - .fileNameFunction(x -> x.getUpperCamelName() + "DTO") .generatorConfig(dtoGenerator) .build()); } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaGenerator.java index 7f9a158..dfc7659 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaGenerator.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaGenerator.java @@ -18,7 +18,6 @@ public class MapperJavaGenerator implements Generator { .generator(this) .context(context) .generatorConfig(context.getXmlConfig().getJavaClientGenerator()) - .fileNameFunction(x -> x.getUpperCamelName() + "Mapper") .build()); } } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaImplGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaImplGenerator.java index f4dbb65..74b9c34 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaImplGenerator.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperJavaImplGenerator.java @@ -23,11 +23,12 @@ public class MapperJavaImplGenerator implements Generator { mapperImplGenerator.setTargetPackage(mapperGenerator.getTargetPackage() + GeneratorConst.IMPL_PKG_SUFFIX); mapperImplGenerator.setModuleName("mapperImplGenerator"); mapperImplGenerator.setDisabled(!context.getXmlConfig().getBaseConfig().isEnableReadWriteSeparation()); + String suffix = generatorChain.getSuffix(mapperGenerator, this); generatorChain.doGenerate(GenerateParam.builder() .generator(this) .context(context) .generatorConfig(mapperImplGenerator) - .fileNameFunction(x -> x.getUpperCamelName() + "MapperImpl") + .fileNameFunction(x -> x.getUpperCamelName() + suffix + "Impl") .build()); } } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperXmlGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperXmlGenerator.java index 551f3c6..2583810 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperXmlGenerator.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/MapperXmlGenerator.java @@ -1,9 +1,16 @@ package cn.cliveyuan.robin.generator.generator; +import cn.cliveyuan.robin.generator.core.ExtensionParam; import cn.cliveyuan.robin.generator.core.GenerateParam; import cn.cliveyuan.robin.generator.core.Generator; import cn.cliveyuan.robin.generator.core.GeneratorChain; import cn.cliveyuan.robin.generator.core.GeneratorContext; +import cn.cliveyuan.robin.generator.core.GeneratorEnum; +import cn.cliveyuan.robin.generator.util.FileContentModifyUtils; +import cn.cliveyuan.robin.generator.util.FreemarkerUtils; + +import java.io.File; +import java.util.Map; /** * Xml Mapper 生成器 @@ -18,7 +25,40 @@ public class MapperXmlGenerator implements Generator { .generator(this) .context(context) .generatorConfig(context.getXmlConfig().getSqlMapGenerator()) - .fileNameFunction(x -> x.getUpperCamelName() + "Mapper") + .beforeExtensionHandle(this::modifyExistMapperXml) .build()); } + + private void modifyExistMapperXml(ExtensionParam extensionParam) { + extensionParam.getContext().getEntityList().forEach(entity -> { + String fileName = entity.getFileName() + GeneratorEnum.MAPPER_XML.getFileType().getExtension(); + String filePath = String.format("%s/%s", extensionParam.getCodeFilePath(), fileName); + // 判断文件是否存在 + File file = new File(filePath); + if (!file.exists()) { + return; + } + Map paramMap = extensionParam.getParamMap(); + paramMap.put("entity", entity); + + // 修改BaseColumnList + this.modifyBaseColumnList(paramMap, filePath); + // 修改BaseResultMap + this.modifyBaseResultMap(paramMap, filePath); + }); + } + + private void modifyBaseColumnList(Map paramMap, String filePath) { + this.modifyContent(paramMap, filePath, "templates/segment/base_column_list.inc.ftl", "", ""); + } + + private void modifyBaseResultMap(Map paramMap, String filePath) { + this.modifyContent(paramMap, filePath, "templates/segment/base_result_map.inc.ftl", ""); + } + + private void modifyContent(Map paramMap, String filePath, String ftlPath, String start, String end) { + String codeContent = FreemarkerUtils.parseTemplate(ftlPath, paramMap); + FileContentModifyUtils.modify(filePath, start, end, String.format(" %s", codeContent.trim())); + } + } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceGenerator.java index 4730231..de8fe10 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceGenerator.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceGenerator.java @@ -18,7 +18,6 @@ public class ServiceGenerator implements Generator { .generator(this) .context(context) .generatorConfig(context.getXmlConfig().getServiceGenerator()) - .fileNameFunction(x -> x.getUpperCamelName() + "Service") .build()); } } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceImplGenerator.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceImplGenerator.java index 6aa9765..cfb123f 100644 --- a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceImplGenerator.java +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/generator/ServiceImplGenerator.java @@ -22,11 +22,12 @@ public class ServiceImplGenerator implements Generator { BeanUtils.copyProperties(serviceGenerator, serviceImplGenerator); serviceImplGenerator.setTargetPackage(serviceGenerator.getTargetPackage() + GeneratorConst.IMPL_PKG_SUFFIX); serviceImplGenerator.setModuleName("serviceImplGenerator"); + String suffix = generatorChain.getSuffix(serviceGenerator, this); generatorChain.doGenerate(GenerateParam.builder() .generator(this) .context(context) .generatorConfig(serviceImplGenerator) - .fileNameFunction(x -> x.getUpperCamelName() + "ServiceImpl") + .fileNameFunction(x -> x.getUpperCamelName() + suffix + "Impl") .build()); } } diff --git a/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/FileContentModifyUtils.java b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/FileContentModifyUtils.java new file mode 100644 index 0000000..b4ea5a8 --- /dev/null +++ b/robin-generator/src/main/java/cn/cliveyuan/robin/generator/util/FileContentModifyUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020 Clive Yuan (cliveyuan@foxmail.com). + */ + +package cn.cliveyuan.robin.generator.util; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +/** + * 文件内容修改工具 + * + * @author Clive Yuan + * @date 2021/01/06 + */ +@Slf4j +public class FileContentModifyUtils { + + public static void modify(String fileName, String startMark, String endMark, String content) { + try { + List newLines = new ArrayList<>(); + List lines = Files.readAllLines(Paths.get(fileName), StandardCharsets.UTF_8); + int start = -1; + int end = -1; + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i).trim(); + if (line.startsWith(startMark)) { + start = i; + } + if (line.contains(endMark)) { + end = i; + if (start >= 0) { + break; + } + } + } + log.info("FileContentModifyUtils.modify start={}, end={}", start, end); + if (start >= 0 && end >= 0) { + for (int i = 0; i < lines.size(); i++) { + if (i < start || i > end) { + newLines.add(lines.get(i)); + } + } + + newLines.add(start, content); + Files.write(Paths.get(fileName), newLines, StandardCharsets.UTF_8); + } else { + log.info("FileContentModifyUtils.modify can't find the segment to modify"); + } + } catch (IOException e) { + log.error("FileContentModifyUtils.modify error", e); + } + } + +} diff --git a/robin-generator/src/main/resources/templates/controller.java.ftl b/robin-generator/src/main/resources/templates/controller.java.ftl index e9e9f5d..c7d9874 100644 --- a/robin-generator/src/main/resources/templates/controller.java.ftl +++ b/robin-generator/src/main/resources/templates/controller.java.ftl @@ -1,8 +1,13 @@ +/* + * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. + * All rights reserved. + */ + package ${controllerPackage}; import ${entityPackage}.${entity.entityName}; -import ${dtoPackage}.${entity.upperCamelName}DTO; -import ${servicePackage}.${entity.upperCamelName}Service; +import ${dtoPackage}.${entity.upperCamelName}${dtoSuffix}; +import ${servicePackage}.${entity.upperCamelName}${serviceSuffix}; import cn.cliveyuan.robin.base.condition.PageQueryExample; import cn.cliveyuan.robin.base.common.PageQueryRequest; import cn.cliveyuan.robin.base.util.BeanCopyUtils; @@ -31,18 +36,18 @@ import javax.annotation.Resource; [#if baseConfig.enableSwagger] @Api(tags = "${entity.comment}接口") [/#if] -public class ${entity.upperCamelName}Controller { +public class ${entity.fileName} { @Resource - private ${entity.upperCamelName}Service ${entity.lowerCamelName}Service; + private ${entity.upperCamelName}${serviceSuffix} ${entity.lowerCamelName}${serviceSuffix}; @PostMapping("save") [#if baseConfig.enableSwagger] @ApiOperation("保存") [/#if] - public ApiResponse save([#if baseConfig.enableValidation]@Validated[/#if] ${entity.upperCamelName}DTO request) { + public ApiResponse save([#if baseConfig.enableValidation]@Validated[/#if] ${entity.upperCamelName}${dtoSuffix} request) { ${entity.entityName} entity = BeanCopyUtils.copy(request, ${entity.entityName}.class); - ${entity.lowerCamelName}Service.save(entity); + ${entity.lowerCamelName}${serviceSuffix}.save(entity); return ApiResponse.success(entity.getId()); } @@ -51,28 +56,28 @@ public class ${entity.upperCamelName}Controller { @ApiOperation("删除") [/#if] public ApiResponse delete(Long id) { - return ApiResponse.success(${entity.lowerCamelName}Service.delete(id) > 0); + return ApiResponse.success(${entity.lowerCamelName}${serviceSuffix}.delete(id) > 0); } @PostMapping("get") [#if baseConfig.enableSwagger] @ApiOperation("获取") [/#if] - public ApiResponse<${entity.upperCamelName}DTO> get(Long id) { - return ApiResponse.success(BeanCopyUtils.copy(${entity.lowerCamelName}Service.get(id), ${entity.upperCamelName}DTO.class)); + public ApiResponse<${entity.upperCamelName}${dtoSuffix}> get(Long id) { + return ApiResponse.success(BeanCopyUtils.copy(${entity.lowerCamelName}${serviceSuffix}.get(id), ${entity.upperCamelName}${dtoSuffix}.class)); } @PostMapping("page") [#if baseConfig.enableSwagger] @ApiOperation("分页查询") [/#if] - public ApiResponse> page(@RequestBody PageQueryRequest<${entity.upperCamelName}DTO> request) { + public ApiResponse> page(@RequestBody PageQueryRequest<${entity.upperCamelName}${dtoSuffix}> request) { PageQueryExample<${entity.entityName}> query = new PageQueryExample<>(); query.setPageNo(request.getPageNo()); query.setPageSize(request.getPageSize()); if (request.getEntity() != null) { query.setEntity(BeanCopyUtils.copy(request.getEntity(), ${entity.entityName}.class)); } - return ApiResponse.success(BeanCopyUtils.copyPagination(${entity.lowerCamelName}Service.page(query), ${entity.upperCamelName}DTO.class)); + return ApiResponse.success(BeanCopyUtils.copyPagination(${entity.lowerCamelName}${serviceSuffix}.page(query), ${entity.upperCamelName}${dtoSuffix}.class)); } } diff --git a/robin-generator/src/main/resources/templates/dto.java.ftl b/robin-generator/src/main/resources/templates/dto.java.ftl index b905302..c1b174f 100644 --- a/robin-generator/src/main/resources/templates/dto.java.ftl +++ b/robin-generator/src/main/resources/templates/dto.java.ftl @@ -1,3 +1,8 @@ +/* + * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. + * All rights reserved. + */ + package ${dtoPackage}; [#if baseConfig.enableSwagger] @@ -6,17 +11,15 @@ import io.swagger.annotations.ApiModelProperty; [/#if] [#if baseConfig.enableValidation] import org.hibernate.validator.constraints.Length; -[/#if] -[#if baseConfig.enableLombok] - -import lombok.Data; -[/#if] -[#if baseConfig.enableValidation] import javax.validation.constraints.NotNull; [/#if] import java.io.Serializable; import java.util.Date; +[#if baseConfig.enableLombok] +import lombok.Data; +[/#if] + [#if entity.hasBigDecimalField] import java.math.BigDecimal; [/#if] @@ -31,7 +34,7 @@ import java.math.BigDecimal; [#if baseConfig.enableSwagger] @ApiModel(value = "${entity.comment}") [/#if] -public class ${entity.upperCamelName}DTO implements Serializable { +public class ${entity.fileName} implements Serializable { [#--fields--] private static final long serialVersionUID = 1L; @@ -72,7 +75,7 @@ public class ${entity.upperCamelName}DTO implements Serializable { public String toString() { return "${entity.entityName}{" + [#list entity.fields as field] - "${field.lowerCamelName}=" + ${field.lowerCamelName} + + "[#if field_index !=0], [/#if]${field.lowerCamelName}=" + ${field.lowerCamelName} + [/#list] '}'; } diff --git a/robin-generator/src/main/resources/templates/entity.java.ftl b/robin-generator/src/main/resources/templates/entity.java.ftl index b736df5..5b2e612 100644 --- a/robin-generator/src/main/resources/templates/entity.java.ftl +++ b/robin-generator/src/main/resources/templates/entity.java.ftl @@ -1,3 +1,8 @@ +/* + * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. + * All rights reserved. + */ + package ${entityPackage}; import cn.cliveyuan.robin.base.annotation.TableField; @@ -5,14 +10,14 @@ import cn.cliveyuan.robin.base.annotation.TableId; import cn.cliveyuan.robin.base.annotation.TableName; import java.io.Serializable; -[#if baseConfig.enableLombok] - -import lombok.Data; -[/#if] [#if entity.hasBigDecimalField] import java.math.BigDecimal; [/#if] import java.util.Date; +[#if baseConfig.enableLombok] + +import lombok.Data; +[/#if] /** * ${entity.comment} @@ -56,7 +61,7 @@ public class ${entity.entityName} implements Serializable { public String toString() { return "${entity.entityName}{" + [#list entity.fields as field] - "${field.lowerCamelName}=" + ${field.lowerCamelName} + + "[#if field_index !=0], [/#if]${field.lowerCamelName}=" + ${field.lowerCamelName} + [/#list] '}'; } diff --git a/robin-generator/src/main/resources/templates/mapper.java.ftl b/robin-generator/src/main/resources/templates/mapper.java.ftl index 5721ff3..7b939c6 100644 --- a/robin-generator/src/main/resources/templates/mapper.java.ftl +++ b/robin-generator/src/main/resources/templates/mapper.java.ftl @@ -1,3 +1,8 @@ +/* + * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. + * All rights reserved. + */ + package ${mapperPackage}; import ${entityPackage}.${entity.entityName}; @@ -6,6 +11,6 @@ import cn.cliveyuan.robin.base.BaseMapper; /** * ${entity.comment} Mapper */ -public interface ${entity.upperCamelName}Mapper extends BaseMapper<${entity.entityName}> { +public interface ${entity.fileName} extends BaseMapper<${entity.entityName}> { } diff --git a/robin-generator/src/main/resources/templates/mapper.xml.ftl b/robin-generator/src/main/resources/templates/mapper.xml.ftl index 14f6a36..323a6aa 100644 --- a/robin-generator/src/main/resources/templates/mapper.xml.ftl +++ b/robin-generator/src/main/resources/templates/mapper.xml.ftl @@ -1,19 +1,13 @@ + - + - - [#list entity.fields as field]`${field.columnName}`[#if field_has_next],[/#if][/#list] - + [#include 'segment/base_column_list.inc.ftl'] - -[#list entity.fields as field] - [#if field.columnName == 'id'] - - [#else ] - - [/#if] -[/#list] - + [#include 'segment/base_result_map.inc.ftl'] diff --git a/robin-generator/src/main/resources/templates/segment/base_column_list.inc.ftl b/robin-generator/src/main/resources/templates/segment/base_column_list.inc.ftl new file mode 100644 index 0000000..00f67cc --- /dev/null +++ b/robin-generator/src/main/resources/templates/segment/base_column_list.inc.ftl @@ -0,0 +1,3 @@ + + [#list entity.fields as field]`${field.columnName}`[#if field_has_next],[/#if][/#list] + diff --git a/robin-generator/src/main/resources/templates/segment/base_result_map.inc.ftl b/robin-generator/src/main/resources/templates/segment/base_result_map.inc.ftl new file mode 100644 index 0000000..272dc5d --- /dev/null +++ b/robin-generator/src/main/resources/templates/segment/base_result_map.inc.ftl @@ -0,0 +1,9 @@ + +[#list entity.fields as field] + [#if field.columnName == 'id'] + + [#else ] + + [/#if] +[/#list] + diff --git a/robin-generator/src/main/resources/templates/service.java.ftl b/robin-generator/src/main/resources/templates/service.java.ftl index ddca336..dcf8af0 100644 --- a/robin-generator/src/main/resources/templates/service.java.ftl +++ b/robin-generator/src/main/resources/templates/service.java.ftl @@ -1,3 +1,8 @@ +/* + * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. + * All rights reserved. + */ + package ${servicePackage}; import cn.cliveyuan.robin.base.BaseService; @@ -6,6 +11,6 @@ import ${entityPackage}.${entity.entityName}; /** * ${entity.comment} 服务接口 */ -public interface ${entity.upperCamelName}Service extends BaseService<${entity.entityName}> { +public interface ${entity.fileName} extends BaseService<${entity.entityName}> { } diff --git a/robin-generator/src/main/resources/templates/serviceimpl.java.ftl b/robin-generator/src/main/resources/templates/serviceimpl.java.ftl index fe0d594..d5b086a 100644 --- a/robin-generator/src/main/resources/templates/serviceimpl.java.ftl +++ b/robin-generator/src/main/resources/templates/serviceimpl.java.ftl @@ -1,14 +1,19 @@ +/* + * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. + * All rights reserved. + */ + package ${serviceImplPackage}; import cn.cliveyuan.robin.base.BaseServiceImpl; import ${entityPackage}.${entity.entityName}; -import ${servicePackage}.${entity.upperCamelName}Service; +import ${servicePackage}.${entity.upperCamelName}${serviceSuffix}; import org.springframework.stereotype.Service; /** * ${entity.comment} 服务实现 */ @Service -public class ${entity.upperCamelName}ServiceImpl extends BaseServiceImpl<${entity.entityName}> implements ${entity.upperCamelName}Service { +public class ${entity.fileName} extends BaseServiceImpl<${entity.entityName}> implements ${entity.upperCamelName}${serviceSuffix} { } diff --git a/robin-generator/src/test/resources/code-generator.xml b/robin-generator/src/test/resources/code-generator.xml index aba0e14..dfb4148 100755 --- a/robin-generator/src/test/resources/code-generator.xml +++ b/robin-generator/src/test/resources/code-generator.xml @@ -1,5 +1,6 @@ - + diff --git a/update_versions.sh b/update_versions.sh new file mode 100755 index 0000000..61de130 --- /dev/null +++ b/update_versions.sh @@ -0,0 +1,4 @@ +#!/bin/sh + + +mvn versions:set -DnewVersion=1.1.0 -- Gitee