diff --git a/README.md b/README.md index 6969512214f2224d6d7bb924c52b2512027f1d4e..9fdf02c0e40637d147109d81825ae10825c9076a 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,75 @@ -# robin +# Robin -#### 介绍 -robin是一个基于MyBatis提供增删改查功能的轻量级框架。
+## 介绍 +`Robin`是一个基于`MyBatis`框架, 提供基础的增删改查增强功能的轻量级框架。
Robin is a lightweight framework based on MyBatis that provides adding, deleting, modifying and querying capabilities -#### 软件架构 -软件架构说明 +## 项目背景 +此项目参考了[mybatis-generator](http://mybatis.org/generator/)和[mybatis-plus](https://mybatis.plus/)。mybatis-generator会生成很多冗余代码,而mybatis-plus则不用生成冗余代码,功能也很强大,但是其对底层侵入太深,对mybatis的SqlSessionFactory进行了重写,不能直接集成到现有项目中使用;参考两者优秀之处`Robin诞生了,既不生成冗余代码,也不会对底层进行侵入,使用方便集成快速,适合快速开发,让coder将更多的时间用在业务逻辑上。 +### 此项目提供2个核心功能 +- 基础CRUD +- 代码生成 -#### 安装教程 +### 基础CRUD `robin-base` +通用Mapper和Service提供CRUD基本操作的支持 (`BaseMapper`, `BaseService` ) +### 代码生成 `robin-generator` +提供代码生成功能,在表结构变动时可高效快速生成对应的实体对象 -1. xxxx -2. xxxx -3. xxxx -#### 使用说明 +## 特性 +- **无侵入**:轻量级,只做增强不做改变,引入它不会对现有工程产生影响 +- **无损耗**:启动即会自动注入基本增删改查功能,性能无损耗,直接面向对象操作 +- **CRUD 操作**:内置BaseMapper、BaseService,无需任何配置直接继承 CRUD 操作 +- **支持 Lambda 形式调用**:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 +- **代码生成器**:可快速生成 Entity、Mapper、Service、Controller 层代码,支持自定义模板 +- **分页**:基于 MyBatis 物理分页,直接返回grus框架封装的分页对象, 同时也支持pagehelper分页插件 -1. xxxx -2. xxxx -3. xxxx -#### 参与贡献 +## 软件架构 +``` +project 项目根目录 +├─robin-base 基础 +├─robin-generator 代码生成 +├─doc 文档 +└─ +``` -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +## 安装教程 +最新版本`last.version`= + maven + +### 如仅需要增删改查的增强功能 +>仅依赖robin-base项目即可 +```xml + + com.gitee.opensource4clive + robin-base + ${last.version} + +``` +### 如需要增删改查的增强功能和代码生成功能 +>依赖robin-base和robin-generator 其中 robin-generator的scrop建议设置为**test** +```xml + + com.gitee.opensource4clive + robin-generator + ${last.version} + test + +``` +## 项目文档 -#### 特技 +### [快速开始](doc/fast-start.md) + +### [CRUD 接口](doc/crud-interface.md) + +### [条件构造器](doc/conditional.md) + +### [代码生成器](doc/generator.md) + +### [常见问题](doc/faq.md) + +### [示例项目](https://gitee.com/opensource4clive/robin-example) -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/doc/conditional.md b/doc/conditional.md new file mode 100644 index 0000000000000000000000000000000000000000..a9d10f4d3934eb4ab59e0e139d81437c3fc973d3 --- /dev/null +++ b/doc/conditional.md @@ -0,0 +1,214 @@ +# 条件构造器 +> - 不建议将带条件构造器的接口直接暴露出去,应写一个 DTO 进行传输 +> - 以下方法入参中的`R column`均表示数据库字段 + +## Conditional 条件构造器 + +### 创建并且条件 + +```java +// 创建条件: 如果条件列表为空则默认放入, 否则不放入 +Criteria createCriteria(); +// 创建Lambda条件: 如果条件列表为空则默认放入, 否则不放入 +LambdaCriteria createLambdaCriteria(); +``` + +### 创建或者条件 + +```java +// 设置或者条件 +void or(GeneratedCriteria criteria); +// 创建或者条件, 并放入条件列表 +Criteria or(); +``` + +### 其他接口 + +```java +// 清除条件和设置值 +void clear(); +// 设置是否去重 +void setDistinct(boolean distinct); +// 设置offset: limit ${offset}, ${limit} +void setLimitStart(int limitStart); +// 设置limit: limit ${offset}, ${limit} +void setLimitEnd(int limitEnd); +// 添加排序,如未添加排序,则默认排序是id DESC +void addOrderBy(String columnName, boolean isAsc); +``` + +## Query 条件构造器, 继承于Conditional + +### 设置查询实体 + +```java +// 设置实体: 将所有非空字段加入eq条件列表 +void setEntity(T entity); +// 此方法可与Criteria条件叠加使用 +``` + +### + +---- + + + +## Criteria 条件 + +### eq + +``` java +eq(R column, Object val) +``` +- 等于 = +- 例: `eq("name", "老王")`--->`name = '老王'` + +### ne +``` java +ne(R column, Object val) +``` +- 不等于 <> +- 例: `ne("name", "老王")`--->`name <> '老王'` + +### gt +``` java +gt(R column, Object val) +``` +- 大于 > +- 例: `gt("age", 18)`--->`age > 18` + +### ge +``` java +ge(R column, Object val) +``` +- 大于等于 >= +- 例: `ge("age", 18)`--->`age >= 18` + +### lt +``` java +lt(R column, Object val) +``` +- 小于 < +- 例: `lt("age", 18)`--->`age < 18` + +### le +``` java +le(R column, Object val) +``` +- 小于等于 <= +- 例: `le("age", 18)`--->`age <= 18` + +### between +``` java +between(R column, Object val1, Object val2) +``` +- BETWEEN 值1 AND 值2 +- 例: `between("age", 18, 30)`--->`age between 18 and 30` + +### notBetween +``` java +notBetween(R column, Object val1, Object val2) +``` +- NOT BETWEEN 值1 AND 值2 +- 例: `notBetween("age", 18, 30)`--->`age not between 18 and 30` + +### like +``` java +like(R column, Object val) +``` +- LIKE '%值%' +- 例: `like("name", "王")`--->`name like '%王%'` + +### notLike +``` java +notLike(R column, Object val) +``` +- NOT LIKE '%值%' +- 例: `notLike("name", "王")`--->`name not like '%王%'` + +### likeLeft +``` java +likeLeft(R column, Object val) +``` +- LIKE '%值' +- 例: `likeLeft("name", "王")`--->`name like '%王'` + +### likeRight +``` java +likeRight(R column, Object val) +``` +- LIKE '值%' +- 例: `likeRight("name", "王")`--->`name like '王%'` + +### isNull +``` java +isNull(R column) +``` +- 字段 IS NULL +- 例: `isNull("name")`--->`name is null` + +### isNotNull +``` java +isNotNull(R column) +``` +- 字段 IS NOT NULL +- 例: `isNotNull("name")`--->`name is not null` + +### in +``` java +in(R column, Collection value) +``` +- 字段 IN (value.get(0), value.get(1), ...) +- 例: `in("age",{1,2,3})`--->`age in (1,2,3)` + +``` java +in(R column, Object... values) +``` +- 字段 IN (v0, v1, ...) +- 例: `in("age", 1, 2, 3)`--->`age in (1,2,3)` + +### notIn +``` java +notIn(R column, Collection value) +``` +- 字段 NOT IN (value.get(0), value.get(1), ...) +- 例: `notIn("age",{1,2,3})`--->`age not in (1,2,3)` + +``` java +notIn(R column, Object... values) +``` +- 字段 NOT IN (v0, v1, ...) +- 例: `notIn("age", 1, 2, 3)`--->`age not in (1,2,3)` + +---- + +## 示例 + +```java +// 创建条件构造器 +Example example = new ConditionExample<>(); +// 创建第一个条件:id in (1,2,3) and type is not null +example.createCriteria().in("id", 1,2,3).isNotNull("name"); +// 创建第二个条件: 因为第二个条件起不会默认放入条件列表, title like %啄木鸟% +LambdaCriteria criteria = example.createLambdaCriteria().like(User::getName, "啄木鸟"); +// 将第二个条件设置为或者条件 +example.or(criteria); +// 创建第三个或者条件:timeCreated between '2020-09-11' and '2020-11-11' +example.orLambdaCriteria().between(User::getCreatedAt, "2020-09-11", "2020-11-11"); +// 设置去重 +example.setDistinct(true); +// 添加排序 +example.addOrderBy("username", true); +example.addOrderBy("id", false); +List list = userMapper.list(example); +// 以上条件构造的SQL语句为: +/* + SELECT DISTINCT `id`,`created_at`,`updated_at`,`username`,`password`,`name` + FROM `User` + WHERE ( `id` IN ( 1 , 2 , 3 ) and `name` IS NOT NULL ) + OR ( `name` LIKE CONCAT('%','啄木鸟','%') ) + OR ( `createdAt` BETWEEN '2020-09-11' and '2020-11-11' ) + ORDER BY `username` ASC, `id` DESC; + */ +``` + diff --git a/doc/crud-interface.md b/doc/crud-interface.md new file mode 100644 index 0000000000000000000000000000000000000000..3dccc0da600c1ae92efef4469c328ea1b07153dc --- /dev/null +++ b/doc/crud-interface.md @@ -0,0 +1,179 @@ +# CRUD 接口 + +## Mapper CRUD 接口 + +通用Mapper接口:`cn.cliveyuan.robin.base.BaseMapper.java` + +⚠️特别提醒:Mapper和xml中不能重载BaseMapper中的方法 + +### 插入 Insert + +``` java +// 选择性插入(仅插入非null字段) +int insert(T entity); +// 全字段插入(无论是否为null均进行插入) +int insertAll(T entity); +// 批量全字段插入(注:如表新加字段不为空时插入对象需要手动设置默认值) +int batchInsert(List list); +``` + +### 删除 Delete + +``` java +// 根据ID删除 +int delete(Long id); +// 批量ID删除 +int batchDelete(List ids); +// 根据条件删除 +int deleteByExample(Example example); +``` + +### 更新 Update + +``` java +// 根据ID选择性更新 +int update(T entity); +// 根据ID全字段更新 (无论是否为null均进行更新) +int updateAll(T entity); +// 根据条件选择性更新 +int updateByExample(T entity, Example example); +// 根据条件全字段更新 +int updateByExampleAll(T entity, Example example); +``` + +### 获取 Get + +``` java +// 根据ID获取实体 +T get(Long id); +// 批量获取实体 +List batchGet(List ids); +// 根据条件获取实体 +T getByExample(Example example); +``` + +### 查询数量 Count + +``` java +// 根据条件数量查询 +int count(Example example); +``` + +### 查询列表 List + +``` java +// 根据条件列表查询 +List list(Example example); +``` + + + +注:`Example`和`Query`都继承于`Conditional`, 在不同层面使用不同的接口: + +- mapper层: `Example` -> `ConditionExample` +- service层: Query -> `QueryExample` , `PageQueryExample` + +示例 + +```java +// mapper: Example +Example example = new ConditionExample<>(); +userMapper.getByExample(example); + +// service: Query +Query query = new QueryExample<>(); +userService.getByExample(query); + +Query query1 = new PageQueryExample<>(); +userService.page(query1); +``` + + + +--- + + + +## Service CRUD 接口 + +通用Service接口:`cn.cliveyuan.robin.base.BaseService.java` + +⚠️特别提醒:继承BaseService前提需要对应的Mapper继承BaseMapper + +### 插入 Insert + +``` java +// 选择性插入(仅插入非null字段) +int insert(T entity); +// 全字段插入(无论是否为null均进行插入) +int insertAll(T entity); +// 批量全字段插入(注:如表新加字段不为空时插入对象需要手动设置默认值) +int batchInsert(List list); +``` +### 删除 Delete + +``` java +// 根据ID删除 +int delete(Long id); +// 批量ID删除 +int batchDelete(List ids); +// 根据条件删除 +int deleteByExample(Query query); +``` +### 更新 Update + +``` java +// 根据ID选择性更新 +int update(T entity); +// 根据ID全字段更新 (无论是否为null均进行更新) +int updateAll(T entity); +// 根据条件选择性更新 +int updateByExample(T entity, Query query); +// 根据条件全字段更新 +int updateByExampleAll(T entity, Query query); +``` +### 保存 Save + +> `entity.id == null` -> 插入 +> +> `entity.id != null` -> 更新 + +``` java +// 选择性保存 +int save(T entity); +// 全字段保存 +int saveAll(T entity); +``` +### 获取 Get + +``` java +// 根据ID获取实体 +T get(Long id); +// 批量获取实体 +List batchGet(List ids); +// 根据条件获取实体 +T getByExample(Query query); +``` + +### 查询数量 Count + +``` java +// 根据条件数量查询 +int count(Query query); +``` + +### 查询列表 List + +``` java +// 根据条件列表查询 +List list(Query query); +``` +### 查询分页 Page + +``` java +// 根据条件查询分页 +Pagination page(PageQueryExample query); +``` + + + diff --git a/doc/faq.md b/doc/faq.md new file mode 100644 index 0000000000000000000000000000000000000000..9fb2b4f0e05a7be3a765b268f91deb375d957a2b --- /dev/null +++ b/doc/faq.md @@ -0,0 +1,78 @@ +# 常见问题 + + + +## TINYINT(1)类型 + +1个长度的tinyint为什么转为Boolean类型而不是Integer? + +> Mysql官方参考文档关于布尔类型的说明:BOO, BOOLEAN +> These types are synonyms(同义词) for TINYINT(1). A value of zero is considered(认为是) false. Nonzero(不为0) values are considered true. + + 解决方法: + + 1. 如果需要将tinyint(1)转为Integer类型需要将配置文件中`jdbcConnection`的`tinyInt1isBit`属性显式设置为`false` + 2. 将数据表tinyint(1)改为tinyint(2) + +注:tinyint(1) 如果加了`unsigned `则jdbc读取到的长度是3,对应转换为Integer类型 + +---- + + + +## 常见异常 + +### 1. Mapped Statements collection already contains value for + ```java +java.lang.IllegalArgumentException: Mapped Statements collection already contains value for xxx.XxMapper.xxMethod + ``` + +原因: + +a. Mapper继承了BaseMapper之后,如果在Mapper中重载了BaseMapper中的方法,在应用启动时会出现如上异常; + +b. Xml Mapper中定义了与BaseMapper同名sql的id + +解决: 重命名该方法 + +### 2. No qualifying bean of type + +```java +NoSuchBeanDefinitionException: No qualifying bean of type 'cn.cliveyuan.robin.base.BaseMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true) +``` + +原因:Mapper所在的包未被扫描到 + +解决:将其配置到`Application.java`的`@MapperScan`中 + +### 3. Invalid bound statement (not found) + +```java +org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): xx.XxxService.xxMethod +``` + +原因: +- 1. Mapper Xml文件未被扫描 +- 2. Mapper Xml中无对应方法 +- 3. Mapper Xml与Mapper的ResultType不一致 +- 4. MapperScan未直接配置Mapper的包,可能只配置了Mapper的父包 + +解决: +- 1. `application.properties`中配置xml路径 如:`mybatis.mapper-locations=classpath*:mapper/*.xml` +- 2. 检查并加入对应方法 +- 3. 检查并使其一致 +- 4. 配置`@MapperScan`到具体的Mapper所在的包,如果存在多个mapper包可考虑使用通配符 + +### 4. BaseMapper could not be found + +```java +Field baseMapper in cn.cliveyuan.robin.base.BaseServiceImpl required a bean of type 'cn.cliveyuan.robin.base.BaseMapper' that could not be found. +``` + +原因: 只继承了BaseService没有继承BaseMapper + +解决:对应的Mapper需继承BaseMapper + + +---- + diff --git a/doc/fast-start.md b/doc/fast-start.md new file mode 100644 index 0000000000000000000000000000000000000000..c76a1012401b08066f026046a1e47a41145cc985 --- /dev/null +++ b/doc/fast-start.md @@ -0,0 +1,219 @@ +## 快速开始 + +这里以一个空的工程来示例 + +#### 创建数据库和表 + +> 以本地数据库为例,库名: robin_example + +```sql +/*创建数据库*/ +CREATE DATABASE `robin_example` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; +/*创建用户表*/ +CREATE TABLE `t_user` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `username` varchar(50) NOT NULL COMMENT '用户名', + `password` varchar(100) NOT NULL DEFAULT '' COMMENT '密码', + `name` varchar(50) NOT NULL COMMENT '姓名', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户'; +``` + +#### 创建Maven项目并添加依赖 + +引入 Spring Boot Starter 父工程 + +```xml + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + +``` + +引入相关依赖 最新版 + maven + + +```xml + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.2 + + + + mysql + mysql-connector-java + runtime + + + + com.gitee.opensource4clive + robin-base + 1.2.0 + + + com.gitee.opensource4clive + robin-generator + 1.2.0 + test + + + + org.projectlombok + lombok + 1.18.12 + + + + io.springfox + springfox-boot-starter + 3.0.0 + + + + org.hibernate.validator + hibernate-validator + 6.1.5.Final + + + +``` + +#### 配置项目 + +在`${项目根目录}/src/main/resources/application.yml`中配置基本信息 + +> 对应数据库账号信息可改为自己的 + +```yaml +server: + port: 8080 + servlet: + context-path: / + tomcat: + uri-encoding: UTF-8 + +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/robin_example?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true + username: root + password: 123456 + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + +mybatis: + mapper-locations: classpath:mapper/*Mapper.xml + configuration: + map-underscore-to-camel-case: true + +logging: + level: + cn: + cliveyuan: + robinexample: + mapper: debug + +``` + +添加启动类`Application.java` + +```java +@SpringBootApplication +@MapperScan(basePackages = "cn.cliveyuan.robinexample.mapper") +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} +``` + +#### 编码 + +编写实体类`User.java` + +```java +@Data +@TableName("t_user") +public class User { + /** + * ID 主键 + */ + @TableId + private Long id; + /** + * 创建时间 + */ + @TableField(value = "created_at", ignoreSaving = true) + private Date createdAt; + /** + * 修改时间 + */ + @TableField(value = "updated_at", ignoreSaving = true) + private Date updatedAt; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 姓名 + */ + private String name; +} +``` + +编写Mapper类`UserMapper.java` + +```java +public interface UserMapper extends BaseMapper { + // 如有其他方法直接书写其中即可 + // User selectOne(Long id); +} +``` + +同时也支持`@Mapper`注解 + +#### 开始使用 + +添加测试类 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class SampleTest { + @Resource + private UserMapper userMapper; + @Test + public void testList() { + Example example = new ConditionExample<>(); + List list = userMapper.list(example); + Assert.assertTrue(list.size() > 0); + list.forEach(System.out::println); + } +} +``` + +#### [示例项目源码](https://gitee.com/opensource4clive/robin-example) + diff --git a/doc/generator.md b/doc/generator.md new file mode 100644 index 0000000000000000000000000000000000000000..9fa00879e3eb91d444b07c541e67663996c41f24 --- /dev/null +++ b/doc/generator.md @@ -0,0 +1,104 @@ +# 代码生成器 + +## 简介 +快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率 + +关于覆盖: + - Entity文件: Entity是与数据库表结构保持一致的, 尽量不要去修改, 如需扩展请添加Bean去继承它, 所以每次生成都会覆盖Entity + - 其他文件: 其他文件一次生成之后可以添加属性和方法, 所以每次生成不会覆盖 + +## 快速开始 +在前面那个项目上添加代码生成器 + +### 添加依赖 + +引入代码生成器,`scope`设置为`test` +```xml + + com.gitee.opensource4clive + robin-generator + ${last.version} + test + +``` + +### 配置 +添加生成配置文件`code-generator.xml`, 建议放到测试资源目录`resources/` +```xml + + + + + + + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +``` + +[查看配置文档](xml-config.md) + +### 开始使用 + +创建生成测试单元测试文件`CodeGeneratorTest.java` + +```java +public class CodeGeneratorTest { + @Test + public void generate() { + MybatisGenerator.builder().configFilePath().build().generate(); + // 若配置文件名非code-generator.xml, 或配置文件路径不在resources下,需要指定配置文件路径 + // 如在resources/robin-cg.xml + // MybatisGenerator.builder().configFilePath("/robin-cg.xml").build().generate(); + // 如在/Users/clive/cg-cofnig.xml + // MybatisGenerator.builder().configFilePath("/Users/clive/cg-cofnig.xml").build().generate(); + } +} +``` + +注:配置文件名为`code-generator.xml`时无需指定配置文件路径,否则需要指定`configFilePath` + diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000000000000000000000000000000000000..0e51c22bc267b8b75478eda9cebce38abafeb0b5 --- /dev/null +++ b/doc/index.md @@ -0,0 +1,152 @@ +# 简介 + +`code-generator`是一个简单的代码生成工具,在不侵入框架底层的前提下,达到简化开发、提高开发效率的目的。 + +## 项目背景 + +此项目分为两个部分:第一个部分为通用Mapper和Service提供CRUD基本操作的支持;第二个部分是代码生成功能,为表结构变动时提供高效快速生成对应的实体对象。 +此项目参考了[mybatis-generator](http://mybatis.org/generator/)和[mybatis-plus](https://mybatis.plus/), 取其精华并与grus框架融合。mybatis-generator会生成很多冗余代码,而mybatis-plus则不用生成冗余代码,功能也很强大,但是其对底层侵入太深,对mybatis的SqlSessionFactory进行了重写,不能直接集成到我司项目中使用;参考两者优秀之处code-generator诞生了,既不需要生成冗余代码,也不需要对底层侵入,使用方便集成快速,适合快速开发,将更多的时间用在业务逻辑上。 + +## 特性 +- **无侵入**:只做增强不做改变,引入它不会对现有工程产生影响 +- **无损耗**:启动即会自动注入基本增删改查功能,性能无损耗,直接面向对象操作 +- **CRUD 操作**:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 +- **支持 Lambda 形式调用**:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 +- **代码生成器**:可快速生成 Entity、Mapper、Service、Controller 层代码,支持自定义模板 +- **分页**:基于 MyBatis 物理分页,直接返回grus框架封装的分页对象, 同时支持pagehelper分页插件 + +## 快速开始 + +这里以一个空的工程来示例 + +#### 添加依赖 + +引入 Grus Boot Starter 父工程: + +```xml + + com.ciicgat.grus.boot + grus-boot-starter-parent + 2020.12.1.RELEASE + +``` + +引入服务端应用常用的依赖 + +```xml + + grus-boot-starter-server-general + com.ciicgat.grus.boot + +``` + +引入数据依赖 + +```xml + + com.ciicgat.grus.boot + grus-boot-starter-data + +``` + +引入测试依赖 + +```xml + + org.springframework.boot + spring-boot-starter-test + test + +``` + +#### 配置 + +在`application.properties`中配置基本信息 + +```properties +spring.application.name=ecappetc +server.port=8088 +server.tomcat.uri-encoding=UTF-8 +logging.level.com.ciicgat.grusgenerator.codegenerator.example.mapper=debug +spring.jackson.date-format=yyyy-MM-dd HH:mm:ss +spring.jackson.time-zone=GMT+8 +grus.feign.log-req=true +grus.feign.log-resp=true +mybatis.mapper-locations=classpath*:mapper/*.xml + +``` + +添加启动类`Application.java` + +```java +@SpringBootApplication +@MapperScan("com.ciicgat.grusgenerator.codegenerator.example.mapper") +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} +``` + +#### 编码 + +编写实体类`ShopTip.java` + +```java +@Data +@TableName("ShopTip") +public class ShopTip { + @TableId + private Long id; + @TableField(ignoreSaving = true) + private Date timeCreated; + @TableField(ignoreSaving = true) + private Date timeModified; + private Long ecappId; + private Integer type; + private String title; + private String content; + private Integer enable; +} +``` + +编写Mapper类`ShopTipMapper.java` + +```java +public interface ShopTipMapper extends BaseMapper { + // 如有其他方法直接书写其中即可 + // ShopTip selectOne(Long id); +} +``` + +同时也支持`@Mapper`注解 + +#### 开始使用 + +添加测试类 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class SampleTest { + @Resource + private ShopTipMapper shopTipMapper; + @Test + public void testList() { + Example example = new ConditionExample<>(); + List list = shopTipMapper.list(example); + Assert.assertTrue(list.size() > 0); + list.forEach(System.out::println); + } +} +``` + +## [CRUD 接口](doc/code-generator/crud-interface.md) + +## [条件构造器](doc/code-generator/conditional.md) + +## [代码生成器](doc/code-generator/generator.md) + +## [读写分离](doc/code-generator/read-write-separation.md) + +## [常见问题](doc/code-generator/faq.md) diff --git a/doc/xml-config.md b/doc/xml-config.md new file mode 100644 index 0000000000000000000000000000000000000000..81106506c302236e7e3fa23f06f72568a3973969 --- /dev/null +++ b/doc/xml-config.md @@ -0,0 +1,103 @@ +# 生成器配置文件 code-generator.xml + +## code-generator 根元素 + +配置从此元素开始 + +### baseConfig 基础配置 + +#### property 属性 + +- **enableSwagger** 启用Swagger,在生成的DTO和Controller中使用Swagger注解,需要添加对应的maven依赖 +- **enableValidation** 启用验证,在生成的DTO和Controller中使用验证注解,根据数据库表字段添加@NotNull,@Length注解,需要添加对应的maven依赖 +- **disableUpdatingMapperXml** 禁用更新`MapperXml`文件,每次生成会同步更新mapper中`id=BaseColumnList`和`id=BaseResultMap`内容,如果不需要更新此项设置为true禁用更新 +- **enableMapperAnnotation** 在生成的Mapper.java中添加@Mapper注解,默认不启用 + +### jdbcConnection JDBC连接 + +| 属性 | 名称 | 是否必填 | 描述 | +| ------------- | ----------- | -------- | ------------------------------------------------------------ | +| driverClass | 驱动类 | 是 | jdbc驱动类全路径 | +| host | 主机地址 | 是 | 数据库主机地址,如非默认端口`3306`需带上端口号 | +| database | 数据库 | 是 | 数据库名称 | +| username | 用户名 | 是 | 数据库用户名 | +| password | 密码 | 是 | 用户名密码 | +| tinyInt1isBit | tinyint转换 | 否 | 默认为true, tinyint(1)类型转Boolean; 为false时,tinyint(1)类型转为Integer | + +### javaModelGenerator 实体对象生成器 + +| 属性 | 名称 | 是否必填 | 描述 | +| ------------- | -------------- | -------- | ------------------------------------------------------------ | +| targetPackage | 包路径 | 是 | 文件所在的包全路径(小数点分隔的包名) | +| codePath | 代码地址 | 是 | 文件所在相对项目根目录的路径 | +| disabled | 是否禁用 | 否 | 禁用时,将跳过此模块生成 | +| templatePath | 自定义模板路径 | 否 | 模板路径是相对路径,需放于resources下,同名即覆盖,不同名需指定路径 | +| suffix | 自定义后缀 | 否 | 为生成的模块指定固定后缀,此属性对Entity不生效,Entity自定义后缀参考table标签 | + +注:如果需要自定义代码模板,需要从默认模板复制一份到本地项目进行修改,复制后选择**无格式粘贴**,否则格式会混乱 + +默认模板文件路径 + +- 实体类 **entity** templates/entity.java.ftl +- xml映射 **mapperXml** templates/mapper.xml.ftl +- Mapper **mapperJava** templates/mapper.java.ftl +- 服务 **service** templates/service.java.ftl +- 服务实现 **serviceImpl** templates/serviceimpl.java.ftl +- 控制器 **controller** templates/controller.java.ftl +- 转换类 **dto** templates/dto.java.ftl + +### sqlMapGenerator XMLMapper生成器 + +同上 + +默认后缀`Mapper` + +### javaClientGenerator JavaMapper生成器 + +同上 + +默认后缀`Mapper` + +### serviceGenerator Service生成器 + +同上 + +默认后缀`Service` + +### controllerGenerator Controller生成器 + +同上 + +默认后缀`Controller` + +### dtoGenerator Controoer生成器 + +同上;用于对象转换DTO,依附于controllerGenerator,若生成controllerc才可以配置此项,否则不生效 + +默认后缀`DTO` + +### tables 要生成的表集合 + +| 属性 | 名称 | 是否必填 | 描述 | +| ---------------- | ------------ | -------- | ------------------------------------------------------- | +| all | 是否所有表 | 否 | 为true时,将读取数据库所有表, 将忽略其所有`table`子元素 | +| ignoreColumns | 忽略的列 | 否 | 所有表需要忽略的列,多个以逗号分隔 | +| createTimeColumn | 创建时间字段 | 否 | 默认为`created_at` | +| updateTimeColumn | 更新时间字段 | 否 | 默认为`updated_at` | +| entityObjectSuffix | 实体后缀 | 否 | 给生成的实体加上统一后缀, 如 `DO` -> UserDO | + +#### table 要生成的表 + +| 属性 | 名称 | 是否必填 | 描述 | +| ---------------- | ---------- | -------- | ---------------------------------------- | +| tableName | 表名 | 是 | 表名 | +| entityObjectName | 实体对象名 | 否 | 如果为空,则以表名大写驼峰作为实体对象名 | +| ignoreColumns | 忽略的列 | 否 | 此表需要忽略的列,多个以逗号分隔 | + +注: + +- 忽略的列将不在生成的Entity中展示,一般为不需要操作的可空列,`table`的`ignoreColumns`属性会继承`tables`的 +- `entityObjectName`和`entityObjectSuffix`都存在时,生成的实体名为`${entityObjectName}${entityObjectSuffix}` + + + diff --git a/pom.xml b/pom.xml index 2c7854866974a4313944ea9943c1ca3bf1986778..2c1c5091a7333c1b688b319f954db17d871706d5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.gitee.opensource4clive robin pom - 1.1.0 + 1.2.0 robin Robin is a lightweight framework based on MyBatis that provides adding, deleting, modifying and querying capabilities. https://gitee.com/opensource4clive/robin diff --git a/robin-base/pom.xml b/robin-base/pom.xml index 79bc93be5d7095a58dfb2584a047ba10ef5073ec..fac9fa3580df799db549107215962998503f2da2 100644 --- a/robin-base/pom.xml +++ b/robin-base/pom.xml @@ -3,11 +3,11 @@ 4.0.0 robin-base jar - 1.1.0 + 1.2.0 com.gitee.opensource4clive robin - 1.1.0 + 1.2.0 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 ce60037fc2842530aea01773e552111c6b45d5f3..76f0eae0a1f550927266e6c779194a14c976e066 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 @@ -4,6 +4,11 @@ package cn.cliveyuan.robin.base.condition; +import cn.cliveyuan.robin.base.common.PageQueryRequest; +import cn.cliveyuan.robin.base.util.BeanCopyUtils; + +import java.util.Objects; + /** * 分页查询示例 * @@ -31,6 +36,19 @@ public class PageQueryExample extends QueryExample { */ private Integer pageSize = DEFAULT_PAGE_SIZE; + public PageQueryExample() { + } + + public PageQueryExample(PageQueryRequest pageQueryRequest, Class tClass) { + if (Objects.nonNull(pageQueryRequest)) { + this.setPageNo(pageQueryRequest.getPageNo()); + this.setPageSize(pageQueryRequest.getPageSize()); + if (pageQueryRequest.getEntity() != null) { + this.setEntity(BeanCopyUtils.copy(pageQueryRequest.getEntity(), tClass)); + } + } + } + public Integer getPageNo() { return pageNo; } 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 index 77d988f0c01446da4303fb7836ba52097ff802e3..50d836dcd610e3a2a1693852a1ca8665a06209c0 100644 --- 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 @@ -27,7 +27,10 @@ public class BeanCopyUtils { * @return */ public static T copy(Object source, Class targetClass) { - AssertUtils.notNull(source, "source is required"); + // AssertUtils.notNull(source, "source is required"); + if (Objects.isNull(source)) { + return null; + } AssertUtils.notNull(targetClass, "targetClass is required"); try { T targetObject = targetClass.getDeclaredConstructor().newInstance(); diff --git a/robin-generator/pom.xml b/robin-generator/pom.xml index ab1c007b6ebdf8c5ac7d8535546a5ea75583f7fb..64a1ebc1c1cc487752c28b75fcbd0c1d2305044d 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.1.0 + 1.2.0 jar com.gitee.opensource4clive robin - 1.1.0 + 1.2.0 @@ -88,7 +88,6 @@ com.gitee.opensource4clive robin-base - 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 2fc27013864620e492d577ca0293104a9e2c4cfd..2e49b8abbc08b5f317ea32aed57669a668073f2a 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 @@ -31,11 +31,18 @@ public class CodeGeneratorXmlConfig implements Serializable { @Data @Builder public static class BaseConfig { + // 启用Lombok private boolean enableLombok; + // 启用swagger private boolean enableSwagger; + // 启用验证 private boolean enableValidation; + // 启用读写分离 private boolean enableReadWriteSeparation; + // 禁用更新MapperXml字段 private boolean disableUpdatingMapperXml; + // 启用@Mapper注解 + private boolean enableMapperAnnotation; } @Data 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 ff614bd951c3c27f6dc6bee7e076480ab674e829..ba51c211e2d66caf4c1614353315e10575e9ada5 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 @@ -211,6 +211,7 @@ public class GeneratorContextResolver { 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; + case "enableMapperAnnotation": baseConfig.setEnableMapperAnnotation("true".equalsIgnoreCase(property.getStringValue())); break; } } } diff --git a/robin-generator/src/main/resources/templates/controller.java.ftl b/robin-generator/src/main/resources/templates/controller.java.ftl index c7d98740126b7cf6c7129558ffad0ea8de1f3f3f..4f61efe2915da7d8de83a7460f2370a5863cae2b 100644 --- a/robin-generator/src/main/resources/templates/controller.java.ftl +++ b/robin-generator/src/main/resources/templates/controller.java.ftl @@ -1,8 +1,3 @@ -/* - * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. - * All rights reserved. - */ - package ${controllerPackage}; import ${entityPackage}.${entity.entityName}; @@ -45,10 +40,8 @@ public class ${entity.fileName} { [#if baseConfig.enableSwagger] @ApiOperation("保存") [/#if] - public ApiResponse save([#if baseConfig.enableValidation]@Validated[/#if] ${entity.upperCamelName}${dtoSuffix} request) { - ${entity.entityName} entity = BeanCopyUtils.copy(request, ${entity.entityName}.class); - ${entity.lowerCamelName}${serviceSuffix}.save(entity); - return ApiResponse.success(entity.getId()); + public ApiResponse save([#if baseConfig.enableValidation]@Validated[/#if] ${entity.upperCamelName}${dtoSuffix} request) { + return ApiResponse.success(${entity.lowerCamelName}${serviceSuffix}.save(BeanCopyUtils.copy(request, ${entity.entityName}.class)) > 0); } @PostMapping("delete") @@ -72,12 +65,6 @@ public class ${entity.fileName} { @ApiOperation("分页查询") [/#if] 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}${serviceSuffix}.page(query), ${entity.upperCamelName}${dtoSuffix}.class)); + return ApiResponse.success(BeanCopyUtils.copyPagination(${entity.lowerCamelName}${serviceSuffix}.page(new PageQueryExample<>(request, ${entity.entityName}.class)), ${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 c1b174ff0271b51a0e2cf4ab5d2578aff1b2f6ae..724151fbd718a53dac36ac244b3ce176b53dc430 100644 --- a/robin-generator/src/main/resources/templates/dto.java.ftl +++ b/robin-generator/src/main/resources/templates/dto.java.ftl @@ -1,8 +1,3 @@ -/* - * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. - * All rights reserved. - */ - package ${dtoPackage}; [#if baseConfig.enableSwagger] diff --git a/robin-generator/src/main/resources/templates/entity.java.ftl b/robin-generator/src/main/resources/templates/entity.java.ftl index 5b2e612c6cfc0b189a362527d71cf782745ca360..8f942c4b00de21b66c1225a952f07443a9534df6 100644 --- a/robin-generator/src/main/resources/templates/entity.java.ftl +++ b/robin-generator/src/main/resources/templates/entity.java.ftl @@ -1,8 +1,3 @@ -/* - * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. - * All rights reserved. - */ - package ${entityPackage}; import cn.cliveyuan.robin.base.annotation.TableField; diff --git a/robin-generator/src/main/resources/templates/mapper.java.ftl b/robin-generator/src/main/resources/templates/mapper.java.ftl index 7b939c6083c6efd9a04df01d71236a88c70ff176..2596b604819b0bcffc55bd6e596ac92412135c93 100644 --- a/robin-generator/src/main/resources/templates/mapper.java.ftl +++ b/robin-generator/src/main/resources/templates/mapper.java.ftl @@ -1,16 +1,13 @@ -/* - * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. - * All rights reserved. - */ - package ${mapperPackage}; import ${entityPackage}.${entity.entityName}; -import cn.cliveyuan.robin.base.BaseMapper; +import cn.cliveyuan.robin.base.BaseMapper;[#if baseConfig.enableMapperAnnotation] + import org.apache.ibatis.annotations.Mapper;[/#if] /** * ${entity.comment} Mapper - */ + */[#if baseConfig.enableMapperAnnotation] + @Mapper[/#if] 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 323a6aabd49daf3c44a7ec423104641582a627fd..15c24b321776f347bdbac5ad3d878c35a947d3ac 100644 --- a/robin-generator/src/main/resources/templates/mapper.xml.ftl +++ b/robin-generator/src/main/resources/templates/mapper.xml.ftl @@ -1,8 +1,4 @@ - diff --git a/robin-generator/src/main/resources/templates/service.java.ftl b/robin-generator/src/main/resources/templates/service.java.ftl index dcf8af008b804eb69ec7bbfbbe7882d1d4a23a95..68e1bce425c458cd04a350945e375a4cc23db1ab 100644 --- a/robin-generator/src/main/resources/templates/service.java.ftl +++ b/robin-generator/src/main/resources/templates/service.java.ftl @@ -1,8 +1,3 @@ -/* - * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. - * All rights reserved. - */ - package ${servicePackage}; import cn.cliveyuan.robin.base.BaseService; diff --git a/robin-generator/src/main/resources/templates/serviceimpl.java.ftl b/robin-generator/src/main/resources/templates/serviceimpl.java.ftl index d5b086a04b9592e94f8eacffa29ec68c10220a68..b9eadf0268caa4c3999708015eb4ad8497db1f53 100644 --- a/robin-generator/src/main/resources/templates/serviceimpl.java.ftl +++ b/robin-generator/src/main/resources/templates/serviceimpl.java.ftl @@ -1,8 +1,3 @@ -/* - * Copyright 2007-${.now?string('yyyy')}, CIIC Guanaitong, Co., Ltd. - * All rights reserved. - */ - package ${serviceImplPackage}; import cn.cliveyuan.robin.base.BaseServiceImpl; diff --git a/robin-generator/src/test/resources/code-generator.xml b/robin-generator/src/test/resources/code-generator.xml index dfb414895e384c938b4b61aee35b7f6fff92b374..2b3bcfc2e10458b6d6972232361f8364888201e4 100755 --- a/robin-generator/src/test/resources/code-generator.xml +++ b/robin-generator/src/test/resources/code-generator.xml @@ -1,12 +1,13 @@ - + true true + false diff --git a/update_versions.sh b/update_versions.sh index 61de130efd218465e2dc7f682915523293d7172c..9af421c73504fd5379580c38983ae306f675df3a 100755 --- a/update_versions.sh +++ b/update_versions.sh @@ -1,4 +1,3 @@ #!/bin/sh - -mvn versions:set -DnewVersion=1.1.0 +mvn versions:set -DnewVersion=1.2.0