后台代码约定
### zywork-app-pro项目约定
#### 代码规范
1、zywork-app-pro套件中的Java代码遵循《阿里巴巴Java开发手册》。
#### 数据库约定
1、数据库字符集:`utf8mb4`,排序规则:`utf8mb4_general_ci`。
2、所有数据表使用`t_`前缀,表字段如果由多个单词组成,单词间使用`_`连接。
3、每个数据表都具有的字段及数据类型:
```sql
(
id bigint(20) primary key auto_increment comment '**编号',
version int(11) default 1 comment '**版本号',
create_time datetime default CURRENT_TIMESTAMP comment '创建时间#**创建时间',
update_time datetime comment '更新时间#**更新时间',
is_active tinyint(4) default 1 comment '是否可用#**是否可用'
)
```
4、表示状态、是否、有限集的数据通常是`tinyint(4)`类型,并结合数据字典使用。
5、表示是否的,如果需要默认值,1为是,0为否。
6、所有字段都必须增加注释,注释要求:
1)简洁明了
2)准确描述
3)增加必要的详细注释,如:
```sql
is_active tinyint(4) default 1 comment '是否可用#**是否可用'
```
`#`前为字段说明,字段说明默认为Java属性的中文描述,最终会展现在后台前端的各类表单及表格中;
`#`后为字段详细注释,用于更加充分地说明字段的含义,最终会展现在后台前端的表格中。
7、系统中已经存在的数据库表,设计理念是*尽量减少数据表的关联操作*,所以**增加了关联表的冗余字段**。
如`t_user_detail`表:
```sql
(
`id` bigint(20) NOT NULL COMMENT '用户编号$t_user:id',
`user_phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号$t_user:phone_',
`user_nickname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '昵称$t_user:nickname',
`user_headicon` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '头像地址$t_user:headicon',
`user_gender` tinyint(4) NULL DEFAULT 0 COMMENT '性别$t_user:gender',
`user_is_active` tinyint(4) NOT NULL COMMENT '用户是否可用$t_user:is_active',
`birthday` date NULL DEFAULT NULL COMMENT '生日',
`location` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所在地',
)
```
其中,`user_phone, user_nickname, user_headicon, user_gender, user_is_active`都是冗余字段,这些字段原本存在于`t_user`表中,在查询`t_user_detail`用户详情信息时,为了减少与`t_user`表的关联操作,则进行了重要数据的冗余。
在zywork-app-pro的整个系统的表设计中,都始终贯彻了这种设计理念。在查询数据时确实减少了表的关联操作,但是同时却带来了另外一个问题,在更新A表数据时,需要同步去更新B表、C表,甚至更多数据表中冗余的A表字段,这样才能保证表数据的一致性。
*目前系统的解决方法是在更新表数据时,同步去更新其他表中的冗余字段,以保证数据的一致性,所有数据的更新在同一个事务中。*
我们在zywork-app-pro项目的`resoures`目录中增加了一个`relation_update.json`的配置文件,该配置文件指定了在更新某个表的某些字段的时候,需要同步去更新另外的表的某些字段。
如下配置:
```json
{
"UserDTO": [
{
"fromProps": [
"id",
"phone",
"nickname",
"gender",
"headicon",
"isActive"
],
"toTable": "t_user_detail",
"toColumns": [
"id",
"user_phone",
"user_nickname",
"user_gender",
"user_headicon",
"user_is_active"
]
}
}
```
以上配置表示:当更新`t_user`表的`phone, nickname, gender, headicon, isActive`字段的时候,同步去更新`t_user_detail`表的`user_phone, user_nickname, user_gender, user_headicon, user_is_active`字段。
该方案待完善的方向(即将完善并测试):
1)加入消息队列
2)使用MySQL binlog
8、对于冗余字段的注释,我们增加了一个`$`符号,在`$`后说明冗余字段来源于哪张表的哪个字段,在某些情况下,字段的注释可能同时出现`#`和`$`。
如`t_user_detail`表:
```sql
(
`id` bigint(20) NOT NULL COMMENT '用户编号$t_user:id',
`user_phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号$t_user:phone_',
)
```
`user_phone`字段来源于`t_user`表的`phone`字段。
为什么要使用`$`标记出来呢?
在我们使用zywork-generator-pro自动生成代码的时候,我们可以在前端界面中明确地知道冗余字段的信息,并根据此信息去生成后台前端的一些特殊界面及操作逻辑(后续会有详细说明)。
#### 代码约定
##### 后台
1、DO, DTO, VO和Query对象
1)DO,数据对象,与数据库表对应的JavaBean
2)DTO,数据传输对象,service层使用
3)VO,值对象,与视图层对应,把数据返回给视图层
4)Query,查询对象,封装视图层查询数据的对象
*注意一:在返回分页数据时,涉及到`cn.zywork.vo.PagerVO`对象。*
*注意二:其中可能会涉及到一些数据的转换,可使用自定义封装的`cn.zywork.common.BeanUtils`工具类。*
2、BaseDAO, BaseService和AbstractBaseService
**cn.zywork.dao.BaseDAO**
```java
public interface BaseDAO {
/**
* 添加数据到数据库中
* @param dataObj DO实体类
* @return 返回插入的行数
*/
int save(Object dataObj);
/**
* 批量添加数据到数据库中
* @param dataObjList DO实体类集合
* @return 返回插入的行数
*/
int saveBatch(List<Object> dataObjList);
/**
* 根据主键从数据库中删除数据
* @param id 主键ID
* @return 返回删除的行数
*/
int removeById(Serializable id);
/**
* 根据多个主键批量删除数据
* @param ids 多个主键组成的数组
* @return 返回删除的行数
*/
int removeByIds(Serializable[] ids);
/**
* 根据对象更新数据库中的数据
* @param dataObj DO实体类
* @return 返回更新的行数
*/
int update(Object dataObj);
/**
* 根据对象集合批量更新数据库中的数据
* @param dataObjList DO实体类集合
* @return 返回更新的行数
*/
int updateBatch(List<Object> dataObjList);
/**
* 根据主键id查找数据,针对单表查询
* @param id 主键字段值
* @return 主键字段对应记录的DO对象
*/
Object getById(Serializable id);
/**
* 根据主键获取记录版本号
* @param id 主键字段值
* @return 记录版本号
*/
Integer getVersionById(Serializable id);
/**
* 查找所有记录数据
* @return 所有记录数据DO对象组成的List列表
*/
List<Object> listAll();
/**
* 根据条件查询对象查找符合条件的所有数据
* @param queryObj 条件查询对象
* @return 按照条件的所有数据DO对象组成的List列表
*/
List<Object> listAllByCondition(Object queryObj);
/**
* 根据分页查询对象和条件查询对象查找数据
* @param queryObj 条件查询对象,包括分页数据
* @return 按照条件的分页数据DO对象所组成的List列表
*/
List<Object> listPageByCondition(Object queryObj);
/**
* 根据条件查询对象计数
* @param queryObj 条件查询对象
* @return 按照条件查询对象的记录数
*/
Long countByCondition(Object queryObj);
}
```
**cn.zywork.service.BaseService**
```java
public interface BaseService {
/**
* 添加数据到数据库中
* @param dataTransferObj DTO数据传输对象
* @return 返回插入的行数
*/
int save(Object dataTransferObj);
/**
* 批量添加数据到数据库中
* @param dataTransferObjList DTO数据传输对象集合
* @return 返回插入的行数
*/
int saveBatch(List<Object> dataTransferObjList);
/**
* 根据主键从数据库中删除数据
* @param id 主键ID
* @return 返回删除的行数
*/
int removeById(Serializable id);
/**
* 根据多个主键批量删除数据
* @param ids 多个主键组成的数组
* @return 返回删除的行数
*/
int removeByIds(Serializable[] ids);
/**
* 根据对象更新数据库中的数据<br/>
* 为了保证在高并发状态下的数据正确性,使用了version版本号乐观锁机制来控制数据的更新<br/>
* @param dataTransferObj DTO数据传输对象
* @return 返回更新的行数。如果数据与原先的记录数据一致,则不更新,返回0;如果版本号小于等于数据库中的版本号,也不更新,返回0
*/
int update(Object dataTransferObj);
/**
* 根据对象集合批量更新数据库中的数据
* @param dataTransferObjList DTO数据传输对象集合
* @return 返回更新的行数
*/
int updateBatch(List<Object> dataTransferObjList);
/**
* 根据主键id查找数据
* @param id 主键字段值
* @return DTO数据传输对象
*/
Object getById(Serializable id);
/**
* 根据主键获取记录版本号
* @param id 主键字段值
* @return 记录版本号
*/
Integer getVersionById(Serializable id);
/**
* 查找所有记录数据
* @return DTO数据传输对象组成的PagerDTO
*/
PagerDTO listAll();
/**
* 根据条件查询对象查找符合条件的所有数据
* @param queryObj 条件查询对象
* @return DTO数据传输对象组成的PagerDTO
*/
PagerDTO listAllByCondition(Object queryObj);
/**
* 根据分页查询对象和条件查询对象查找数据
* @param queryObj 条件查询对象,包括分页数据
* @return 按照条件的分页数据DTO对象所组成的PagerDTO
*/
PagerDTO listPageByCondition(Object queryObj);
/**
* 根据条件查询对象计数
* @param queryObj 条件查询对象
* @return 按照条件查询对象的记录数
*/
Long countByCondition(Object queryObj);
}
```
`cn.zywork.service.AbstractBaseService`是`cn.zywork.service.BaseService`接口的抽象实现类,完成了服务层接口的基础逻辑,业务层的CRUD代码可不需要开发人员重复编写,每个Service实现类都会继承自`cn.zywork.service.AbstractBaseService`。
3、Mapper映射文件
Mapper映射文件中的SQL语句,需要注意的如下:
1)version字段实现了乐观锁,`cn.zywork.service.AbstractBaseService`抽象实现类中已经对version字段进行了处理,在update逻辑中version会设置为原始version + 1的值。如果是自己编写update相关代码,需要注意version字段的赋值。
2)select语句条件中增加了`id in (id1, id2, id3...)`的逻辑,方便查询指定的多个id的记录。
3)select语句条件中的`order by`部分,默认会按`update_time desc, create_time desc`排序。
如未指定排序字段,则`update_time desc, create_time desc`,如指定了排序字段则`sortColumn sortOrder, update_time desc, create_time desc`,注意仅支持指定单个排序字段及其排序方式。
4)与DAO接口对应的Mapper映射文件都会自动生成两份,一份为`*DAOMapper.xml`,一份为`*DAOExMapper.xml`。以`DAOExMapper`为后缀的文件表示扩展的Mapper映射,如果我们需要在DAO接口中扩展一些自定义的接口,则可把对应的SQL写到`DAOExMapper`映射文件中,从而不影响由代码生成器生成的`DAOMapper.xml`映射文件。
4、Controller控制器
所有控制器都返回`ResponseStatusVO`实例的`JSON`对象,包含三个属性:
```json
{
"code": 1001,
"message": "添加成功",
"data": null
}
```
**code**表示返回状态码,全部为自定义状态码:
```java
public enum ResponseStatusEnum {
OK(1001, "成功"),
ERROR(1002, "系统错误"),
DATA_ERROR(1003, "参数错误"),
AUTHENTICATION_FAILURE(1004, "用户认证失败"),
AUTHENTICATION_ERROR(1005, "未认证的用户"),
AUTHENTICATION_TOKEN_ERROR(1006, "用户Token错误"),
AUTHORIZATION_ERROR(1007, "未授权的用户"),
REQUEST_INVALID(1008, "请求不合法");
}
```
**message**表示返回消息,中文,可由开发团队成员自定义。
**data**表示返回数据,对于添加、更新、删除操作,`data`为`null`,对于查询操作,
`data`为实际返回的数据,如:
```json
{
"code": 1001,
"message": "查询成功",
"data": {
"id": 1,
"phone": "18888888888",
"email": null,
"gender": 1
}
}
```
或
```json
{
"code": 1001,
"message": "分页查询成功",
"data": {
total: 20,
rows: [
{
"id": 1,
"phone": "18888888888",
"email": null,
"gender": 1
},
......
]
}
}
```