架构师技能1:Java工程规范、浅析领域模型VO、DTO、DO、PO、优秀命名

2022-04-14 15:43:24 浏览数 (1)

把我们之前项目的相关规范做了个总结记录下来,仅供参考,望能有点帮助。

每个人的代码风格迥异,通过统一的编码风格使得代码可读性大大提高。

编程规约或者编码规范的的本质是提高了代码的可读性,最终目的是提高团队协作效率,降低工程维护成本。

一、项目的应用分层:

代码分层,让不同层次的代码做不同的动作。层次清晰的代码,提高可读性,从代码结构就大概能了解到代码是如何分层,每层大概功能是什么。例如常用的Controller、Service、Mapper/Dao三层代码结构,其各层的代码逻辑范围。

默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于Web 层,也可以直接依赖于 Service 层,依此类推:

1、开放接口api层/controller: 可直接封装 Service 接口暴露成 RPC 接口; 通过 Web 封装成 http 接口; 网关控 制层等。

2、Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。

3、Service 层:相对具体的业务逻辑服务层。

4、Manager 层:通用业务处理层,它有如下特征

  • 1)封装下沉通用能力: 对 Service 层通用能力的下沉,如缓存方案、 中间件通用处理;如mqMannager。
  • 2)封装第三方接口:对第三方平台封装的层,预处理返回结果及转化异常信息,如rpcMannager。
  • 3) 与 DAO 层交互,对 DAO 的业务通用能力的封装。

5、DAO 层:持久层,数据访问层,与底层 MySQL、 Oracle、 Hbase 进行数据交互,用来封装对数据库的访问(CRUD)

6、Model, 领域模型对象: (第二节专门讨论)

7、外部接口或第三方平台: 包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。

分层对于代码规范是比较重要,决定着以后的代码是否可复用,是否职责清晰,边界清晰。分层其实见仁见智,只要符合团队规范就可以。比如我们之前的团队工程规范: api层/controller:轻业务逻辑,参数校验,异常兜底。通常这种接口可以轻易更换接口类型,所以业务逻辑必须要轻,甚至不做具体逻辑。该层的领域模型不允许传入DAO层。 Service:业务层,复用性较低,推荐每一个controller方法都得对应一个service,不要把业务编排放在controller中去做。允许操作DO数据领域模型Model Mannager:高可复用逻辑层。主要是做的是业务逻辑处理和数据组装。允许操作DO数据领域模型Model。

二、领域模型

1、Model, 领域模型对象

领域模型POJO 一般是 DO/DTO/BO/VO 的统称,即POJO 专指只有 setter / getter / toString 的简单类,一般无业务逻辑代码,包括 DO/DTO/VO/PO 等。

  • DO (Domain Object):主要用于定义与数据库对象应的属性(ORM对象关系映射),实体bean的DO映射成一张表,通过 DAO 层向上传输数据源对象。看项目的编码规约,有的习惯使用命名为XxxEntity, 有的习惯命名为XxxDo, 比如UserEntity或者UserDO
  • DTO数据传输对象(Data Transfer Object):系统、服务之间交互传输用或者是传输类间数据, Service 或 Manager 向外传输的对象。 看项目的编码规约,比如我们习惯使用DTO来做rest 接口参数或者rpc参数。
  • VO View Object: 视图对象,用于展示层, 前台(APPWAPPC)展示用;即返回给前端的对象,VO可以仅向前端传输,页面需展示字段,如pageList等。
  • PO(Persistent Object): 持久化对象,就是对象是不是需要通过系列化持久化存储到数据库。 其作用就是项目的编码规约,比如我们把需要持久化保存的对象命名为PO.
  • Entity实体: 也可以用作DO,看项目的编码规约,比如我们使用Entity来做service直接的对象传递。

领域模型的后缀命名也是要结合团队工程规范:比如还有 BO(Business Object)后缀:业务对象。由Service层输出的封装业务逻辑的对象。 Query/Filter/Param后缀:数据查询对象,各层接收上层的查询请求。 Form后缀:表单提交对象

领域模型类里面到底要不要写逻辑,还是只能用最普通的get/set方法。这就是讨论失血模型、贫血模型、充血模型的问题。

2、失血模型

失血模型:domain object只有属性的get set方法的纯数据类,所有的业务逻辑完全由Service层来完成的,由于没有dao,Service直接操作数据库,进行数据持久化。

service: 肿胀的服务逻辑

model:只包含get set方法

显然失血模型service层负担太重,一般不会有这种设计。

3、贫血模型

领域模型是指领域对象domain ojbect里只有get和set方法(POJO),所有的业务逻辑都不包含在内而是放在Service层。

service :组合服务,也叫事务服务

model:除包含get set方法,还包含原子服务(如获得关联model的id)

dao:数据持久化

贫血模型比较常见,其问题在于原子服务往往不能直接拿到关联model,因此可以把这个原子服务变成直接用关联modelRepo拿到关联model,这就是充血模型。

贫血模型最早广泛应用源于EJB2,最强盛时期则是由Spring创造,将:

  • “行为”(逻辑、过程);
  • “状态”(数据,对应到语言就是对象成员变量)。

分离到不同的对象中:

  • 只有状态的对象就是所谓的“贫血对象”;
  • 只有行为的对象就是,我们常见的N层结构中的Logic/Service/Manager层(对应到EJB2中的Stateless Session Bean)。

——曾经Spring的作者Rod Johnson也承认,Spring不过是在沿袭EJB2时代的“事务脚本”,也就是面向过程编程。

贫血模型和面向对象设计背道而驰。但优点在于简单:

  • 对于只有少量业务逻辑的应用来说,使用起来非常自然;
  • 开发迅速,易于理解;
  • 注意:也不能完全排斥这种方式。

缺点无法良好的应对复杂逻辑。《重构-改善既有代码的设计》提到的坏代码味道之一就是纯数据类的问题,要求数据和行为是在一起,而贫血模型恰恰就是纯数据类的问题呢。

4、充血模型

面向对象设计的本质是:“一个对象是拥有状态和行为的”。

充血模型:绝大多业务逻辑都应该被放在domain object里面,包括持久化逻辑,而Service层是很薄的一层,仅仅封装事务和少量逻辑,不和DAO层打交道。

service :组合服务 也叫事务服务

model:除包含get set方法,还包含原子服务和数据持久化的逻辑

它的优点是面向对象,Business Logic符合单一职责,不像在贫血模型里面那样包含所有的业务逻辑service太过沉重。

充血模型的问题也很明显,当model中包含了数据持久化的逻辑,实例化的时候可能会有很大麻烦,拿到了太多不一定需要的关联model。

如果是数据库表的映射类DO,最好是越简单越好 如果是封装查询条件、查询结果(如dto之类)是可以加一些简单逻辑在里面的。

三、项目顶层代码结构和调用链

1、 项目顶层代码结构

com - xxxCompany - myprojectApplication.java (应用启动主类) | - myproject | -common/base //公共/基础相关相关 | - AppConst.java | - AppContext.java | -module1 | ---controller 控制层 | | - Userontroller.java | | | ---model //模型 | | - domain 实体对象 | | | -UserDO.java | | | | | - dto 传输对象 | | | -UserQueryDTO.java | | | | | - vo 前端对象 | | | -UserProfileVO.java | | | | | ---service//业务逻辑层,处理数据逻辑,验证数据 | | - impl | | | - UserServiceImpl.java | | - UserService.java | | | | | ---dao //数据访问层,与底层 MySQL、 Oracle、 Hbase 等进行数据交互。 | | - UserDao.java | - util//工具包 | - DateUtils.java //Utils后缀

2、调用链规约

我们项目编程规约,结合swaggger看:

1)controller层接收DTO请求参数 ,并进行简单参数校验。

2)controller层调用了Service层的接口方法。

3)Service层调用Dao层的方法,返回DO的entity对象。

4)Service层封装了前端需要VO对象,返回给controller层

5)controller层返回VO给前端。

四、常用命名原则和规约

规范命名约定目的是为了保持统一,减少沟通成本,提升团队研发效能。通过命名就能体现出代码的特征,含义或者是用途,让阅读者可以根据名称的含义快速厘清程序的脉络。做到见名知意,名副其实。

《Clean Code》这本书明确指出:

好的代码本身就是注释,我们要尽量规范和美化自己的代码来减少不必要的注释。 若编程语言足够有表达力,就不需要注释,尽量通过代码来阐述。

举个例子:

去掉下面复杂的注释,只需要创建一个与注释所言同一事物的函数即可

// check to see if the employee is eligible for full benefits if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

应替换为

if (employee.isEligibleForFullBenefits())

1、首先遵循一定的原则:

好的命名风格应该是看到变量或者函数名字就能“望文生义”,做到名副其实。毕竟我们不能把自己写的所有代码都做注释。好的命名是不需要注释来补充的,达到代码自解释。

1)、编码规范统一:

在编写一个子模块或派生类的时候,要遵循其基类或整体模块的命名风格,保持命名风格在整个模块中的同一性。如骆驼命名法,大括号位置等。

2)、命名一致性原则:

命名一致性:如booleam变量,前缀最好是is、can,need。

不要名不副实:比如一个 getXXX 的函数,结果里面还做了 add, update 的操作。对问题排查造成很大的困扰。因此命名一定要用实质内容相符。不论是类名,变量名,方法名都要有实际的意。 建议方法:先查查字典,找个通俗易懂而且比较贴近的名字。可以参考 jdk 的命名、通用词汇和行业词汇; 作用域小的采用短命名,作用域大的采用长命名。

3)、名字标识符组成:动词 名词

1、名字标识符采用英文单词,应当直观且可以拼读,可望文知意,用词应当准确。正确的英文拼写和语法可以让阅读者易于理解,避免歧义。

2、杜绝完全不规范的英文缩写,避免望文不知义。如Function“缩写”成 Fu,此类随意缩写严重降低了代码的可阅读性。

4、为了达到代码自解释的目标,任何自定义编程元素在命名时,使用完整的单词组合来表达。 正例:在 JDK 中,对某个对象引用的 volatile 字段进行原子更新的类名为:AtomicReferenceFieldUpdater。

4、只要命名合理,不要担心方法名称太长

首先需要保证命名有意义,只要命名合理,不要担心方法名称太长,但方法名称过长常常又意味着该方法干的事太多了,则需要思考是否可以拆分方法,这也反映了"职责单一"设计原则。

保证命名有意义的前提之下,尽量保证命名的简短,删除一些不影响表达的单词,或者采用缩写。举几个例子:

  1. ActivityRuleRepository.findActivityRuleById() 可以简写成ActivityRuleRepository.findById(),因为上下文已经说明白了这个一个查询活动规则的Repository接口。
  2. void updateRuleForRevision(String ruleString) 简写成void updateRule4Revision(String ruleStr)
  3. ActivityRule convert2ActivityRule(String ruleStr) 借鉴toString的简写方式,简写成ActivityRule toActivityRule(String ruleStr)

3)最小化长度 && 最大化信息量原则

在足够描述用途的前提下,尽量以最小词汇量去描述事物,如staff就company_person_collection要简捷易记。

也可采用有广泛共识的缩写后缀,如msg,idx,cnt,btn,id ,flg等,如果缩写共识度不高,请在取得同事们共识后用注释说明其含义。

变量/函数长度控制在4~18个字符内,有助于对代码的理解。

过长的变量:howLongDoesItTakeToOpenTheDoor, howBigIsTheMaterial…

简捷的变量:timeToOpenTheDoor, materialSize.

4)避免过于相似,也不要用双关语,避免歧义,比如add和append:

不要出现仅靠大小写区分的相似的标识符,例如“i”与“I”,“function”与“Function”等等。

正确命名具有互斥意义的标识符

用正确的反义词组命名具有互斥意义的标识符 ,如

add / remove begin / end create / destroy

insert / delete first / last get / set

increment / decrement put / get

add / delete lock / unlock open / close

min / max old / new start / stop

next / previous source / target show / hide

send / receive source / destination

cut / paste up / down

这些有助于理解成对的变量/函数的意义.

尽量避免名字中出现数字编号

尽量避免名字中出现数字编号,如value1,value2等,除非逻辑上的确需要编号。

5)少使用类型前缀

最好从名字上就能揣测出其类型。加后缀说明是可以的。

少单独使用含义广泛的词。如data,info,value等。

6)避免过度使用get作为方法前缀

应该用更精确的动词描述动作,如“请求”request,“获取”acquire,“查找”search/lookfor/find,“查询”inquire,“构建”build 或“创建”create

get和set和成对出现,是轻量的实现。

get这种方法命名一定是明确index,性能比较好,

query选择选择符合的。

2.常见类名命名规范:

1、类名通常以名词结尾。而且在类名中要体现它是以保存数据为主还是提供功能为主。例如 ObjectBuilder 这个类我们都可以猜到它的主要功能是创建Object对象,

2、以动词-er/or 结尾的类名,至少应该包含一个以该动词开头的方法。例如 ObjectBuilder 这个类,它至少应该包含一个以 build- 开头的方法。有了这种规约,阅读者就能更方便地理解这个类。

前缀名

意义

举例

Abstract 或者 Base 开头

抽象类

BaseUserService

后缀名

意义

举例

Controller

对外接口类

UserController

Service

服务类,里面包含了给其他类提同业务服务的方法

UserService

Impl

实现类,而不是接口

UserServiceImpl

Manager

通用业务处理层

UserManager

Ixxx

接口类

IUserDao

Dao

数据访问方法类

OrderDao

Listener

响应某种事件的类

PaymentSuccessListener

Event

这个类代表了某种事件

PaymentSuccessEvent

Factory

生成某种对象工厂的类

PaymentOrderFactory

Adapter

用来连接某种以前不被支持的对象的类

DatabaseLogAdapter

Job

某种按时间运行的任务

PaymentOrderCancelJob

Wrapper

这是一个包装类,为了给某个类提供没有的能力

SelectableOrderListWrapper

DO

定义与数据库对象应的属性(ORM对象关系映射

UserDO

DTO

DTO数据传输对象

UserDTO

VO

用于展示层, 前台(APPWAPPC)展示用

UserVO

Entity

实体

属性

约束

举例

设计模式相关类

Builder,Factory等

当使用到设计模式时,需要使用对应的设计模式作为后缀,如ThreadFactory

Util类

一般是无状态的,只包含静态方法。使用时无需创建类的实例。

Helper类

可以有状态(类的成员变量),一般需要创建实例才能使用。

POJO 是 DO / DTO / BO / VO 的统称,禁止命名成 xxxPOJO。

3.常见方法命名规范:

1、和类名不同的是,方法命名一般为动词或动词短语,与参数或参数名共同组成动宾短语,即动词 名词。一个好的函数名一般能通过名字直接获知该函数实现什么样的功能。

2、用具体的名字代替抽象的名字。比如:ServiceCanStart() 与 CanListenOnPort()

检测服务端口是否可以开始监听,明显后一个要更好一些。

3、首先需要保证命名有意义,只要命名合理,不要担心方法名称太长,但方法名称过长常常又意味着该方法干的事太多了,则需要思考是否可以拆分方法,这也反映了"职责单一"设计原则。

保证命名有意义的前提之下,尽量保证命名的简短,删除一些不影响表达的单词,或者采用缩写。举几个例子:

  1. UserDao.findUserById() 可以简写成UserDao.findById(),因为上下文已经说明白了这个一个查询用户的Dao接口。
  2. void updateUserForName(String name) 简写成void updateForName(String name)
  3. User convertToUserModel(String uid) 借鉴toString的简写方式,简写成ttoUserModel(String ruleStr)

4、如add、insert、append等词都有类似的意思,在使用时最好准确,而且统一。如字符串连接应该用append,而不是add。

1) 各层命名规约: Service / DAO 层方法命名规约:

  1. 获取单个对象的方法用 get 做前缀。
  2. 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects
  3. 获取统计值的方法用 count 做前缀。
  4. 插入的方法用 save / insert 做前缀。
  5. 删除的方法用 remove / delete 做前缀。
  6. 修改的方法用 update 做前缀。

1) 返回真伪值的方法

单词

位置

意义

例子

is

前缀

对象是否符合期待的状态

isValid

can

前缀

对象能否执行所期待的动作

canRemove

has

前缀

对象是否持有所期待的数据和属性

hasObservers

need

前缀

调用方是否需要执行某个命令或方法

needMigrate

should

前缀

是否应当执行操作

shouldCreate

2) 用来检查的方法

单词

位置

意义

例子

ensure

前缀

检查是否为期待的状态,不是则抛出异常或返回error code

ensureCapacity

validate

前缀

检查是否为正确的状态,不是则抛出异常或返回error code

validateInputs

3)、按需求才执行的方法

单词

位置

意义

例子

try

前缀

尝试执行,失败时抛出异常或是返回errorcode tryCreate

tryCreate

OrDefault

后缀

尝试执行,失败时返回默认值

getOrDefault

OrElse

后缀

尝试执行、失败时返回实际参数中指定的值

getOrElse

4) 异步相关方法命名

单词

位置

意义

例子

blocking

前缀

线程阻塞方法

blockingGetUser

Async/Sync

后缀

异步方法

sendAsync

异步相关方 schedule:Job和Task放入队列 schedule, scheduleJob execute:执行异步方法 execute, executeTask start/stop/cancel: 启动/停止/取消/异步任务 start, startJob

4.5 回调方法 on: 事件发生时执行,如onCompleted before: 事件发生前执行,如beforeUpdate after: 事件发生后执行 ,如afterUpdate

4.6 操作对象生命周期的方法 initialize:初始化。也可作为延迟初始化使用 initialize pause:暂停 onPause ,pause stop:停止 onStop,stop destroy:销毁 destroy

4.7 与集合操作相关的方法 contains:是否持有与指定对象相同的对象 contains add:添加 addJob append:添加 appendJob insert:插入到下标n insertJob put:添加与key对应的元素 putJob remove:移除元素 removeJob enqueue:添加到队列的最末位 enqueueJob dequeue:从队列中头部取出并移除 dequeueJob push:添加到栈头 pushJob pop:从栈头取出并移除 popJob peek:从栈头取出但不移除 peekJob find:寻找符合条件的某物 findById

4.8 与数据相关的方法 create:新创建 createAccount from:从既有的某物新建,或是从其他的数据新建 fromConfig to:转换 toString update:更新既有某物 updateAccount load: 读取 loadAccount fetch:远程读取 fetchAccount delete: 删除 deleteAccount remove:删除 removeAccount save:保存 saveAccount store:保存 storeAccount commit:保存 commitChange apply: 保存或应用 applyChange clear:清除数据或是恢复到初始状态 clearAll reset:清除数据或是恢复到初始状态 resetAll

4.9 成对出现的动词 get获取 set 设置 add 增加 remove 删除 create 创建 destory 移除 start 启动 stop 停止 open 打开 close 关闭 read 读取 write 写入 load 载入 save 保存 create 创建 destroy 销毁 begin 开始 end 结束 backup 备份 restore 恢复 import 导入 export 导出 split 分割 merge 合并 inject 注入 extract 提取 attach 附着 detach 脱离 bind 绑定 separate 分离 view 查看 browse 浏览 edit 编辑 modify 修改 select 选取 mark 标记 copy 复制 paste 粘贴 undo 撤销 redo 重做 insert 插入 delete 移除 add 加入 append 添加 clean 清理 clear 清除 index 索引 sort 排序 find 查找 search 搜索 increase 增加 decrease 减少 play 播放 pause 暂停 launch 启动 run 运行 compile 编译 execute 执行 debug 调试 trace 跟踪 observe 观察 listen 监听 build 构建 publish 发布 input 输入 output 输出 encode 编码 decode 解码 encrypt 加密 decrypt 解密 compress 压缩 decompress 解压缩 pack 打包 unpack 解包 parse 解析 emit 生成 connect 连接 disconnect 断开 send 发送 receive 接收 download 下载 upload 上传 refresh 刷新 synchronize 同步 update 更新 revert 复原 lock 锁定 unlock 解锁 check out 签出 check in 签入 submit 提交 commit 交付 push 推 pull 拉 expand 展开 collapse 折叠 begin 起始 end 结束 start 开始 finish 完成 enter 进入 exit 退出 abort 放弃 quit 离开 obsolete 废弃 depreciate 废旧 collect 收集 aggregate 聚集

4.10其他常用词汇

application 应用程式 应用、应用程序 application framework 应用程式框架、应用框架 应用程序框架 architecture 架构、系统架构 体系结构 argument 引数(传给函式的值)。叁见 parameter 叁数、实质叁数、实叁、自变量 array 阵列 数组 assign 指派、指定、设值、赋值 赋值 assignment 指派、指定 赋值、分配 attribute 属性 属性、特性 batch 批次(意思是整批作业) 批处理 binding 系结 绑定 build 建造、构筑、建置(MS 用语) build-in 内建 内置 business 商务,业务 业务 callback 回呼 回调 chain 串链(例 chain of function calls) 链 checked exception 可控式异常(Java) cleanup 清理、善后 清理、清除 client 客端、客户端、客户 客户 client-server 主从架构 客户/服务器 container 容器 容器 ###### (存放资料的某种结构如 list, vector…) context 背景关系、周遭环境、上下脉络 环境、上下文 control 控制元件、控件 控件

define 定义 预定义 definition 定义、定义区、定义式 定义 delegate 委派、委托、委任 委托

derived class 衍生类别 派生类 design by contract 契约式设计 design pattern 设计范式、设计样式 设计模式 ※ 最近我比较喜欢「设计范式」一词

driver 驱动程式 驱动(程序) dynamic binding 动态系结 动态绑定 escape code 转义码 转义码 evaluate 评估、求值、核定 评估 event 事件 事件 event driven 事件驱动的 事件驱动的 exception 异常情况 异常 exception declaration 异常宣告(ref. C Primer 3/e, 11.3) 异常声明 exception handling 异常处理、异常处理机制 异常处理、异常处理机制 exception specification 异常规格(ref. C Primer 3/e, 11.4) 异常规范 exit 退离(指离开函式时的那一个执行点) 退出 explicit 明白的、明显的、显式 显式 export 汇出 引出、导出 expression 运算式、算式 表达式 facility 设施、设备 设施、设备 feature 特性 field 栏位,资料栏(Java) 字段, 值域(Java) flush 清理、扫清 刷新 font 字型 字体

formal parameter 形式叁数 形式叁数 framework 框架 框架

generic algorithm 泛型演算法 通用算法 getter (相对於 setter) 取值函式 global 全域的(对应於 local) 全局的 global object 全域物件 全局对象 global scope resolution operator 全域生存空间(范围决议)运算子 :: 全局范围解析操作符 group 群组

handle 识别码、识别号、号码牌、权柄 句柄 handler 处理常式 处理函数 heap 堆积 堆

hook 挂钩 钩子

inheritance 继承、继承机制 继承、继承机制 inline 行内 内联 inline expansion 行内展开 内联展开 initialization 初始化(动作) 初始化 initialization list 初值列 初始值列表 initialize 初始化 初始化

invoke 唤起 调用

call: 比较明确的调用 (方法名,形参,返回值都是确定的) invoke: 调用的对象,方法名,参数,返回值都不确定,比较上层建筑的代码常用。 e.g jdk生成动态代理,设计动态代理类是上层建筑代码,负责生成 接口的对象,对象中的所有方法都是调用 invoke方法,来让目标对象做工作。 让哪个目标对象?, 执行那个方法(Method类),方法传参(args)都是未知不确定的,所以用invoke 祈求(本身祈求就是很虚的东西呀),更加符合当前代码的抽象场景

iterate 迭代(回圈一个轮回一个轮回地进行) 迭代

load 载入 装载 loader 载入器 装载器、载入器 local 区域的(对应於 global) 局部的 local object 区域物件 局部对象 lock 机锁 loop 回圈 循环 lvalue 左值 左值 macro 巨集 宏 magic number 魔术数字 魔法数

member 成员 成员

message 讯息 消息

multi-tasking 多工 多任务

postfix 后置式、后序式 后置式

prefix 前置式、前序式 前置式 preprocessor 前处理器 预处理器

schedule 排程 调度 scheduler 排程器 调度程序 scheme 结构纲目、组织纲目

4. 常见变量名命名规范:

1、在常量与变量命名时,表示类型的名词放在词尾,以提升辨识度。

正例: startTime / workQueue / nameList / TERMINATED_THREAD_COUNT 反例: startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD

在一个方法中将返回值命名为 result,能够让方法的脉络更清晰:userListResult

2、【强制】 POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。

说明: 本文 MySQL 规约中的建表约定第 1 条,表达是与否的变量采用 is_xxx 的命名方式,所以需要在<resultMap>设置从 is_xxx 到 xxx 的映射关系。 反例: 定 义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),框架在反向解析时,“误以为 ”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

3、常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

5、mybatisplus的Iservice规范命名例子:

代码语言:javascript复制
/**
 * 顶级 Service
 *
 * @author hubin
 * @since 2018-06-23
 */
public interface IService<T> {

    /**
     * 插入一条记录(选择字段,策略插入)
     *
     * @param entity 实体对象
     */
    boolean save(T entity);

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, 1000);
    }

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     */
    boolean saveBatch(Collection<T> entityList, int batchSize);

    /**
     * 批量修改插入
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, 1000);
    }

    /**
     * 批量修改插入
     *
     * @param entityList 实体对象集合
     * @param batchSize  每次的数量
     */
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    boolean removeById(Serializable id);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    boolean removeByMap(Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    boolean remove(Wrapper<T> queryWrapper);

    /**
     * 删除(根据ID 批量删除)
     *
     * @param idList 主键ID列表
     */
    boolean removeByIds(Collection<? extends Serializable> idList);

    /**
     * 根据 ID 选择修改
     *
     * @param entity 实体对象
     */
    boolean updateById(T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    boolean update(T entity, Wrapper<T> updateWrapper);

    /**
     * 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
     *
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    default boolean update(Wrapper<T> updateWrapper) {
        return update(null, updateWrapper);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean updateBatchById(Collection<T> entityList) {
        return updateBatchById(entityList, 1000);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     * @param batchSize  更新批次数量
     */
    boolean updateBatchById(Collection<T> entityList, int batchSize);

    /**
     * TableId 注解存在更新记录,否插入一条记录
     *
     * @param entity 实体对象
     */
    boolean saveOrUpdate(T entity);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T getById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表
     */
    Collection<T> listByIds(Collection<? extends Serializable> idList);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    Collection<T> listByMap(Map<String, Object> columnMap);

    /**
     * 根据 Wrapper,查询一条记录 <br/>
     * <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default T getOne(Wrapper<T> queryWrapper) {
        return getOne(queryWrapper, true);
    }

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param throwEx      有多个 result 是否抛出异常
     */
    T getOne(Wrapper<T> queryWrapper, boolean throwEx);

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    Map<String, Object> getMap(Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    int count(Wrapper<T> queryWrapper);

    /**
     * 查询总记录数
     *
     * @see Wrappers#emptyWrapper()
     */
    default int count() {
        return count(Wrappers.emptyWrapper());
    }

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    List<T> list(Wrapper<T> queryWrapper);

    /**
     * 查询所有
     *
     * @see Wrappers#emptyWrapper()
     */
    default List<T> list() {
        return list(Wrappers.emptyWrapper());
    }

    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);

    /**
     * 无条件翻页查询
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default IPage<T> page(IPage<T> page) {
        return page(page, Wrappers.emptyWrapper());
    }

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);

    /**
     * 查询所有列表
     *
     * @see Wrappers#emptyWrapper()
     */
    default List<Map<String, Object>> listMaps() {
        return listMaps(Wrappers.emptyWrapper());
    }

    /**
     * 查询全部记录
     */
    default List<Object> listObjs() {
        return listObjs(Function.identity());
    }

    /**
     * 查询全部记录
     *
     * @param mapper 转换函数
     */
    default <V> List<V> listObjs(Function<? super Object, V> mapper) {
        return listObjs(Wrappers.emptyWrapper(), mapper);
    }

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default List<Object> listObjs(Wrapper<T> queryWrapper) {
        return listObjs(queryWrapper, Function.identity());
    }

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

    /**
     * 无条件翻页查询
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default IPage<Map<String, Object>> pageMaps(IPage<T> page) {
        return pageMaps(page, Wrappers.emptyWrapper());
    }

    /**
     * 获取对应 entity 的 BaseMapper
     *
     * @return BaseMapper
     */
    BaseMapper<T> getBaseMapper();

    /**
     * 以下的方法使用介绍:
     *
     * 一. 名称介绍
     * 1. 方法名带有 query 的为对数据的查询操作, 方法名带有 update 的为对数据的修改操作
     * 2. 方法名带有 lambda 的为内部方法入参 column 支持函数式的
     *
     * 二. 支持介绍
     * 1. 方法名带有 query 的支持以 {@link ChainQuery} 内部的方法名结尾进行数据查询操作
     * 2. 方法名带有 update 的支持以 {@link ChainUpdate} 内部的方法名为结尾进行数据修改操作
     *
     * 三. 使用示例,只用不带 lambda 的方法各展示一个例子,其他类推
     * 1. 根据条件获取一条数据: `query().eq("column", value).one()`
     * 2. 根据条件删除一条数据: `update().eq("column", value).remove()`
     *
     */

    /**
     * 链式查询 普通
     *
     * @return QueryWrapper 的包装类
     */
    default QueryChainWrapper<T> query() {
        return new QueryChainWrapper<>(getBaseMapper());
    }

    /**
     * 链式查询 lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaQueryWrapper 的包装类
     */
    default LambdaQueryChainWrapper<T> lambdaQuery() {
        return new LambdaQueryChainWrapper<>(getBaseMapper());
    }

    /**
     * 链式更改 普通
     *
     * @return UpdateWrapper 的包装类
     */
    default UpdateChainWrapper<T> update() {
        return new UpdateChainWrapper<>(getBaseMapper());
    }

    /**
     * 链式更改 lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaUpdateWrapper 的包装类
     */
    default LambdaUpdateChainWrapper<T> lambdaUpdate() {
        return new LambdaUpdateChainWrapper<>(getBaseMapper());
    }
}

0 人点赞