背景
在2B场景下,相当数量中小项目的领域模型是多字段的单表(实体)增删改查。对后端研发同学,实现这样的业务逻辑,粗略地看,无非下面几个基本操作。
- 创建数据库表,并添加业务字段。
- 撰写Java实体类DO,添加对应的字段。
- 撰写Java数据操作类,DAO,撰写相关方法。
- 撰写Service类,增加对相关实体的CRUD操作。
- 如果是Web应用,撰写Controller类,暴露相关的REST API。
也就是说,在定义了数据库表结构以后,上述都是一些机械化和模板化的重复操作。因此我们开发了基于Intellij的CRUD代码自动生成插件。
主要功能
主要功能非常简单,就是在IDE中连接数据库后,选择数据库表后,根据表结构生成:
- Java实体类
- Mybatis或者Mybatis plus风格的Mapper类
- 我们推荐使用Mybatis plus,因为它极大地降低了代码的复杂度。当然联表查询情况下,用户依然可以使用Mybatis撰写SQL语句。
- 对应的mapper xml文件,如果有的话
- 对应的Service与Controller类
- 各个模块的单元测试文件
- 根据选择的表在resources/schema.sql中目录生成建表语句
对应文件生成的风格根据当前目录结构自动判别,支持两种风格:
- dayu-boot六边形脚手架(dayu-boot脚手架是阿里云GTS开发的面向交付场景设计的后端Java脚手架)
- 分层结构(controller/service/dao)脚手架
快速开始
添加插件库地址
该地址暂时不对外透出,请联系李晨(ruli.lc@alibaba-inc.com)获取
安装“code-generation”插件
我们下面用一个例子来说明在大禹脚手架的场景下,如何使用插件来生成代码。
假设现在我们有一张名为employee(员工)的表,字段如下:
字段名 |
意义 |
类型 |
id |
bigint |
主键 |
name |
varchar |
姓名 |
age |
int |
年龄 |
同时我们通过大禹交付平台,使用大禹脚手架初始化完成一份基础代码。
生成代码
生成代码的方式非常简单。在项目目录右击或在代码空白处右击,选中code-generation -> 从MySQL生成后,插件会根据当前项目的代码结构识别脚手架类型。点击“Next”,选择当前保存的数据库连接,或者新建一个连接,测试其连通性。如果本地可以连接数据库,那么选择连接以后,会展现当前数据库实例中的所有库。
选择想展示的库以后,点击“Next”,展示所有表信息。这里我们既可以选择一个表,生成该表对应的Java实体及其相关的代码,也可以选择多个表,生成多套代码。这里我们选择刚刚创建的employee表。
注意在六边形脚手架中,我们按照规范,只会生产MybatisPlus风格的mapper类。这样我们的employee相关的代码就生成完毕了。我们来看看插件都帮我们生产了什么。下面列举主要的几个核心文件。
模型实体类
一个和表结构相关的Java实体类。如果建表的时候提供了comment信息,相应的注释上会填充相关信息。
@Data
public class Employee {
/**
*
*/
private Long id;
/**
*
*/
private String name;
/**
*
*/
private Integer age;
}
数据库实体类
也就是我们通常说的“DO”。
@Data
@TableName("employee")
public class EmployeeEntity {
/**
*
*/
@TableId
@TableField("id")
private Long id;
/**
*
*/
@TableField("name")
private String name;
/**
*
*/
@TableField("age")
private Integer age;
}
DAO相关的类
在dayu-boot脚手架的场景下,一个Repository实现类和一个MybatisPlus风格的Mapper接口类。
@AllArgsConstructor
public class EmployeeMyBatisRepository implements EmployeeRepository {
private EmployeeEntityMapper employeeEntityMapper;
@Override
public Employee findById(Long id) {
EmployeeEntity employeeEntity = employeeEntityMapper.selectById(id);
if (employeeEntity != null) {
Employee employee = new Employee();
BeanUtils.copyProperties(employeeEntity, employee);
return employee;
}
return null;
}
//省略其他方法
}
Service层的操作类
@AllArgsConstructor
public class EmployeeDomainService {
private EmployeeRepository employeeRepository;
public Employee createEmployee(Employee employee) {
Employee newEmployee = employeeRepository.save(employee);
return newEmployee;
}
public Employee findEmployeeById(Long id) {
return employeeRepository.findById(id);
}
public Employee updateEmployee(Employee employee) {
return employeeRepository.update(employee);
}
public Boolean deleteEmployeeById(Long id) {
return employeeRepository.delete(id);
}
public PageResult searchByPage(EmployeeSearchByPage employeeSearchByPage) {
return employeeRepository.findByPage(employeeSearchByPage);
}
}
Controller类
生成的Controller中相关的REST API。
@RestController
@RequestMapping("/employee")
public class EmployeeController implements EmployeeAPIHttp {
@Autowired
private EmployeeAppService employeeAppService;
@Override
public ResultResponse<EmployeeDTO> createEmployee(EmployeeDTO employeeDTO) {
return employeeAppService.createEmployee(employeeDTO);
}
@Override
public ResultResponse<EmployeeDTO> findEmployeeById(Long id) {
return employeeAppService.findEmployeeById(id);
}
@Override
public ResultResponse<EmployeeDTO> updateEmployee(EmployeeDTO employeeDTO) {
return employeeAppService.updateEmployee(employeeDTO);
}
@Override
public ResultResponse<Boolean> deleteEmployeeById(Long id) {
return employeeAppService.deleteEmployeeById(id);
}
@Override
public ResultResponse<ApiPageResult> searchByPage(EmployeeSearch employeeSearch) {
return employeeAppService.searchByPage(employeeSearch);
}
}
最后插件也在每一个module中生成了相关的单测方法,我们现在举一个在domain层的单测类为例。
单元测试
@Test
public void testEmployee() {
Employee employee = new Employee ();
Employee newEmployee = employeeDomainService.createEmployee(employee);
assertNotNull(newEmployee .getId());
Employee searchEmployee = employeeDomainService.findEmployeeById(newEmployee.getId());
assertEquals(newEmployee.getId(),searchEmployee .getId());
Employee updateEmployee = employeeDomainService.updateEmployee (searchEmployee );
assertEquals(searchEmployee .getId(),updateEmployee .getId());
EmployeeSearchByPage employeeSearchByPage = new EmployeeSearchByPage();
employeeSearchByPage.setPageSize(5);
employeeSearchByPage.setPageNum(1);
PageResult<Employee > pageResult = employeeDomainService.searchByPage(employeeSearchByPage);
assertEquals(pageResult.getSize(),Long.valueOf(employeeSearchByPage.getPageSize()));
assertNotNull(pageResult.getList());
Boolean aBoolean = employeeDomainService.deleteEmployeeById(searchEmployee.getId());
assertTrue(aBoolean);
Employee searchNewEmployee = employeeDomainService.findEmployeeById(searchEmployee.getId());
assertNull(searchNewEmployee );
}
基本设置是:
- 创建实体
- 通过id查询实体,验证创建成功
- 更新实体,验证更新成功
- 按条件查询实体
非大禹脚手架的分层结构也非常类似,不再赘述。
未来规划
我们希望推出一系列面向交付的研发插件套件,能涵盖从表结构设计,业务代码生成,单元测试,前端代码生成,前后端联调等场景,从而提高交付场景下的研发效能。它们是我们交付场景下的“Dayu Toolkit”。