Mybatis增强组件
组件编码
hzero-starter-mybatis-mapper
一、简介
1.1 概述
增强ORM框架Mybatis的数据库DML处理能力,支持分页、数据多语言、基于对象的SQL编写,数据防篡改等功能。
1.2 组件坐标
<dependency>
<groupId>org.hzero.starter</groupId>
<artifactId>hzero-boot-starter-mybatis-mapper</artifactId>
<version>${hzero.starter.version}</version>
</dependency>
1.3 特性
- 基于猪齿鱼choerodon-starter-mybatis-mapper组件拓展
- 支持复杂条件查询
- 扩展多语言支持
- 添加数据防篡改功能
- 添加数据加密存储功能
- 添加唯一校验功能
二、组件功能
2.1 CRUD支持
2.1.1 新增支持
int insert(T record)
: 插入一条记录int insertSelective(T record)
: 插入一条记录,Bean中null的字段不会被插入int insertOptional(T record)
: 插入一条记录,指定插入的列,插入前调用io.choerodon.mybatis.helper.OptionalHelper#optional(java.util.List<java.lang.String>)
方法int insertList(List<T> recordList)
: 批量插入,如果主键名称不叫id
,需要再mapper中重新覆写该方法,在注解中声明主键的名称。
@Options(useGeneratedKeys = true, keyProperty = "主键名称")
@InsertProvider(type = SpecialProvider.class, method = "dynamicSql")
int insertList(List<T> recordList);
2.1.2 更新支持
int updateByPrimaryKey(T record)
: 根据主键更新实体全部字段,null值会被更新int updateByPrimaryKeySelective(T record)
: 根据主键更新属性不为null的值int updateOptional(T record)
: 更新一条记录,指定更新的列,更新前调用io.choerodon.mybatis.helper.OptionalHelper#optional(java.util.List<java.lang.String>)
方法
2.1.3 删除支持
int delete(T record)
: 根据实体属性作为条件进行删除,查询条件使用等号int deleteByPrimaryKey(Object key)
: 根据主键字段进行删除,方法参数必须包含完整的主键属性
2.1.4 查询支持
List<T> select(T record)
: 根据实体中的属性值进行查询,查询条件使用等号List<T> selectAll()
: 查询全表结果,慎用T selectByPrimaryKey(Object key)
: 根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号List<T> selectByIds(String ids)
: 根据主键字符串进行查询,类中只有存在一个带有@Id
注解的字段,多个主键使用,
分割int selectCount(T record)
: 根据实体中的属性查询总数,查询条件使用等号T selectOne(T record)
: 根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号List<T> selectByCondition(Object condition)
: 根据Condition条件进行查询int selectCountByCondition(Object condition)
: 根据Condition条件进行查询总数
mapper.selectByCondition(
org.hzero.mybatis.domian.Condition.builder(Entity.class)
.andWhere(
org.hzero.mybatis.util.Sqls.custom()
.andEnqualTo(FIELD1, VALUE1)
.andLike(FIELD2, VALUE2)
).build()
);
List<T> selectOptional(T condition, Criteria criteria)
:复杂查询,condition
参数是查询条件,criteria
可以指定查询的列以及一些其他的查询参数,以及排序等。该方法支持多表关联查询,使用关联查询时需要在与其他表的关联字段上添加@org.hzero.mybatis.common.query.JoinTable
注解,该注解指定关联的表以及关联方式和关联字段,然后需要在Entity
中新建关联表中的字段,并且添加@org.hzero.mybatis.common.query.JoinColumn
注解,如果字段作为查询条件,必须添加@Where
注解。
// Entity
@Table(name = "hmsg_user_receive_config")
class UserReceiveConfig extends AuditDomain {
// other field ...
@Where
private Long userId;
@ApiModelProperty(value = "hmsg_receive_config .receiver_code", required = true)
@JoinTable(name = "receiveConfigJoin", target = ReceiveConfig.class, on = @JoinOn(joinField = ReceiveConfig.FIELD_RECEIVE_CODE))
private String receiveCode;
@Transient
@JoinColumn(joinName = "receiveConfigJoin", field = ReceiveConfig.FIELD_DEFAULT_RECEIVE_TYPE)
private String defaultReceiveType;
// getter and setter ...
}
// Entity
@Table(name = "hmsg_receive_config")
class ReceiveConfig extends AuditDomain {
// other field ...
private String receiveCode;
private String defaultReceiveType;
// getter and setter ...
}
// Repository
userReceiveConfigRepository
.selectOptional(new UserReceiveConfig().setUserId(1L),
new Criteria().select("userReceiveId", "receiveCode", "receiveType", "defaultReceiveType", "userId"));
-- Result SQL
SELECT
A.object_version_number,
A.user_receive_id,
A.receive_code,
A.receive_type,
B.default_receive_type AS default_receive_type,
A.user_id
FROM
hmsg_user_receive_config A
INNER JOIN hmsg_receive_config B
ON A.receive_code = B.receive_code
WHERE
(A.user_id = ?)
2.2 多语言支持
2.2.1 功能说明
- 在业务处理中,经常会有有一些数据需要做多语言支持,根据用户选择的语言来动态切换显示内容。使用多语言组件时,提供的查询方法会自动join多语言表,不必开发人员再去手写SQL,自己创建的mapper接口需要自行join多语言表。
2.2.2 使用说明
- 创建表时创建对应的多语言表,多语言表的表明需要在原表明的基础上增加
_tl
,所以在设计多语言表的时候主表表名长度不要超过23,以免多语言表字段长度超出限制,多语言表中需要包含对应表的主键,以及需要多语言的列,再加上lang varchar(30)
字段。 - 在数据库对应的多语言java实体类上继承
io.choerodon.mybatis.domain.AuditDomain
类,添加@io.choerodon.mybatis.annotation.MultiLanguage
注解,在对应的多语言列上添加@io.choerodon.mybatis.annotation.MultiLanguageField
注解 - 新增/更新数据时,实体类json中需要添加多语言map,结构示例:
{
// other field ...
_tls:{
roleName : {
zh_CN : '管理员',
en_GB : 'Admin'
},
description : {
zh_CN : '管理员',
en_GB : 'administrator'
}
}
}
- 如果因为一些特殊需求,需要在新增或者删除时关闭多语言支持,可以调用
org.hzero.mybatis.helper.MultiLanguageHelper#close
方法临时关闭多语言支持,在一次mybatis操作之后,恢复启用状态,只在当前线程内生效。 - 自行创建的mapper方法,在关联多语言时,可以在xml中添加
<bind name="lang" value="@io.choerodon.mybatis.helper.LanguageHelper@language()"/>
标签来获取当前用户的语言。
<select id="selectEntity" resultType="c.x.Entity">
<bind name="lang" value="@io.choerodon.mybatis.helper.LanguageHelper@language()"/>
SELECT
t.id,
ttl.multi_lang
-- other column ..
FROM table1 t
JOIN table1_tl ttl ON t.id = ttl.id AND ttl.lang = #{lang}
WHERE
-- condition
</select>
2.3 数据防篡改
2.3.1 功能说明
- 数据从后端传输到前端之后,在进行更新操作时,经常需要对主键字段做校验,防止恶意篡改主键导致后端数据被破坏,数据防篡改功能将数据主键进行加密,进行数据更新时,对加密信息做校验用来验证主键信息有没有被篡改。多语言支持也是基于数据防篡改实现的。
2.3.2 使用说明
- 如果是和数据库对应的实体类,只需要继承
io.choerodon.mybatis.domain.AuditDomain
即可,如果是自定义的VO/DTO
,也可以继承AuditDomain
类,或者实现org.hzero.mybatis.domian.SecurityToken
接口,接口中的set_token
方法用于往VO/DTO
中保存加密信息,get_token
方法用于获取VO/DTO
中保存的加密信息,associateEntityClass
方法用于将VO/DTO
与数据库对应的实体类关联在一起,需要注意在VO/DTO
中必须和实体类主键属性 - 在更新数据前调用
org.hzero.mybatis.helper.SecurityTokenHelper#validToken(..)
方法校验主键有没有被篡改。
class Entity extends io.choerodon.mybatis.domain.AuditDomain {
@Id
private long id;
// other field ...
// getter and setter ...
}
class EntityDTO implements org.hzero.mybatis.domian.SecurityToken {
private long id;
// other field ...
// getter and setter ...
private String _token;
@Override
public void set_token(String _token) {
this._token = _token;
}
@Override
public String get_token() {
return this._token;
}
@Override
public Class<? extends SecurityToken> associateEntityClass() {
return Entity.class;
}
}
// controller
// GET
public ResponseEntity<List<EntityDTO>> selectEntity(...){
List<EntityDTO> list = // select ...
return Results.success(list)
}
// PUT
public ResponseEntity<Entity> updateEntity(Entity entity){
org.hzero.mybatis.helper.SecurityTokenHelper.validToken(entity);
// update ...
}
2.4 数据加密存储
2.4.1 功能说明
- 有一些保密新比较强的信息需要加密之后保存到数据库,例如配置的用户的邮箱密码,某些其他服务的密钥等,在数据库做加密存储主要是防止数据库信息被盗取导致用户信息泄露。
2.4.2 使用说明
- 在数据库对应的实体类只需要加密的字段上添加
@org.hzero.mybatis.annotation.DataSecurity
注解。 - 在新增/更新数据之前,调用
org.hzero.mybatis.helper.DataSecurityHelper#open
方法开启数据加密 - 在查询数据之前,调用
org.hzero.mybatis.helper.DataSecurityHelper#open
方法开
- 加密使用AES加密
2.5 租户条件过滤
2.5.1 功能说明
- 为了防止Saas模式下的租户功能越权(查询到不属于自己租户的数据),在没有租户参数进行数据过滤控制的情况下,增加了后端通用过滤规则。
2.5.2 使用说明
1.注解模式,此模式针对使用平台封装好的查询方法生效。
1.1 在Controller类方法上添加注解@org.hzero.mybatis.annotation.TenantLimitedRequest
注解,默认SQL拼装为IN
模式,如:
WHERE 1 = 1
AND tenant_id IN (可访问租户ID列表)
1.2 设置注解属性TenantLimitedRequest(equal=true)
,SQL拼装为=
模式,如:
WHERE 1 = 1
AND tenant_id = (当前租户ID)
- 针对自定义Mapper的SQL语句,注解方式不支持自动改写,需要在
Mapper
中使用bind的方式进行引用
2.1 获取可访问租户列表函数,示例:
<bind name="__tenantIds" value="@org.hzero.mybatis.helper.TenantLimitedHelper@tenantIds()" />
引入后,在使用到的地方进行使用,如:
<if test="__tenantIds != null and !__tenantIds.isEmpty()">
and tenant_id in
<foreach colletion="__tenantIds" item="__tenantId" separator="," open="(" close=")">
#{__tenantId}
</foreach>
</if>
2.2 获取当前租户函数,示例:
<bind name="__tenantId" value="@org.hzero.mybatis.helper.TenantLimitedHelper@tenantId()" />
引入后,在使用到的地方进行使用,如:
<if test="__tenantId != null">
and tenant_id = #{__tenantId}
</if>
2.6 数据唯一校验
2.6.1 功能说明
- 在开发过程中,经常需要到传到后端的数据在数据库中做唯一校验,该功能是对该需求的封装,旨在简化开发过程中重复的工作,降低开发量。
2.6.2 使用说明
- 声明唯一校验字段:在
Entity
需要校验唯一的字段上添加注解@org.hzero.mybatis.annotation.Unique
- 调用校验方法:
org.hzero.mybatis.helper.UniqueHelper#valid(T)
方法,该方法返回一个布尔值,如果返回true
表示校验通过,返回false
表示数据已存在。
Assert.isTrue(UniqueHelper.valid(bank), BaseConstants.ErrorCode.DATA_EXISTS);
三、版本更新日志
0.8.0.RELEASE [2019-03-29]
- 添加复杂查询
selectOptional