Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
L
ldp-docs
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
doc
ldp-docs
Commits
df704292
Commit
df704292
authored
Jul 15, 2021
by
祁新
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' of
http://gitlab.dev.shxrtech.com/doc/ldp-docs
into develop
parents
8ab51227
7f2bcea5
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
555 additions
and
394 deletions
+555
-394
LDP框架ChangeLog&升级方式.md
LDP框架ChangeLog&升级方式.md
+20
-4
LDP集中式安装部署说明.md
LDP集中式安装部署说明.md
+25
-0
LDP Mybatis-Plus脚手架使用.md
开发文档/LDP Mybatis-Plus脚手架使用.md
+1
-1
LDP框架自定义注解.md
开发文档/LDP框架自定义注解.md
+509
-389
No files found.
LDP框架ChangeLog&升级方式.md
View file @
df704292
...
...
@@ -24,7 +24,7 @@
## 2021-07-16
版本号:
**
1.2.4
3
**
版本号:
**
1.2.4
5
**
**
base-service
**
...
...
@@ -64,8 +64,6 @@
**
gen-service
**
`
feat
`
:
...
...
@@ -81,7 +79,7 @@
**
bpm-service
**
版本:
**
1.0.2
0
**
版本:
**
1.0.2
2
**
`
Fix
`
:
...
...
@@ -89,6 +87,24 @@
### 本次更新方式
1. 替换ldp-manage/fatjar 目录下对应的jar包
2. 替换nginx中mcs、bpm的前端代码
3. 脚手架更新到
`
1.2.45
`
- 下载gen-service配置文件:[gen-service配置文件]
(
http://devdown.shxrtech.com/nacos_config_genservice_2021-07-15.zip
)
,并导入Nacos。
修改
`
gen-service-dev.yml
`
、
`
gen-service-prod.yml
`
数据库连接配置及redis配置
- 新版代码生成使用说明文档:
- 新版hibernate自动填充文档:
## 2021-06-15
版本号:
**
1.2.42
**
...
...
LDP集中式安装部署说明.md
View file @
df704292
...
...
@@ -219,6 +219,9 @@ DEPLOY_ENV=prod
#### 新建数据库
-
在mysql内创建ldp数据库: sinra_ldp_db ,字符集:utf8mb4
-
在mysql内创建代码生成数据库: sinra_ldp_generator,字符集:utf8mb4(可选,不创建则代码生成服务无法使用)
-
在mysql内创建流程系统数据库: sinra_bpm_db,字符集:utf8mb4(可选,不安装流程系统则可以不创建)
-
在mysql内创建代码生成数据库: sinra_ldp_report,字符集:utf8mb4(可选,不使用报表服务则可以不创建)
#### nacos 启动 & 访问
...
...
@@ -268,6 +271,16 @@ cd nacos/bin
*
修改后保存即可
#### ***代码生成服务配置(1.2.45及以上版本)
-
修改 gen-service-dev.yml或gen-service-prod.yml配置文件,包含
**数据库配置、redis配置**
修改方式,和上述方式一致
------
### 启动 ldp 服务
```
shell
...
...
@@ -286,6 +299,13 @@ cd tool/
./ldp-boot.sh status
```
PS:如果需要使用
`报表服务`
,则需要单独启动
```
shell
# 启动报表服务
./report-boot.sh start
```
也可以访问地址:http://nacos-server0:8018/nacos
点击服务列表,即可看到以启动并注册的服务信息。如下图:
...
...
@@ -399,3 +419,8 @@ http://服务域名或ip:9080/mcs
初始用户名:admin
初始密码:1234561
PS:如果需用部署流程系统,则参照
[
BPM安装部署文档.md
](
流程系统集成/BPM安装部署文档.md
)
如果需要部署报表系统,则参照
[
报表系统安装部署文档.md
](
报表系统集成/报表系统安装部署文档.md
)
开发文档/LDP Mybatis-Plus脚手架使用.md
View file @
df704292
...
...
@@ -278,7 +278,7 @@ page(page);
public
List
<
LdpArea
>
ldpAreas
;
```
关联查询,在mapper.xml中先声明一个resultMap,再写查询语句,返回指定为对应的resultMap,由于这里是一
堆
多,所以使用
`collection`
作为关联填充,如果是一对一,则使用
`association`
关联查询,在mapper.xml中先声明一个resultMap,再写查询语句,返回指定为对应的resultMap,由于这里是一
对
多,所以使用
`collection`
作为关联填充,如果是一对一,则使用
`association`
```
xml
<resultMap
id=
"BaseResultMapToOne"
type=
"com.sinra.ldp.example.entity.LdpCity"
>
...
...
开发文档/LDP框架自定义注解.md
View file @
df704292
...
...
@@ -4,476 +4,271 @@
## 一、自动填充注解
### 1.1. @Auto
Comput
ed
### 1.1. @Auto
Fill
ed
依赖:base-api
。
依赖:base-api
#### 1.1.1 注解介绍
**@AutoComputed**
是一个字段注解,标识
**实体类**
字段为自动填充的注解,需要配合服务层的
**@AutoService**
使用,拥有四个属性,command、ref、refs、format。注解可以通过command标识字段自动装配的方式,根据字段名查找对应计算方法,或根据command类型查找对应系统计算方法。command默认值为
**ComputedCommand.AUTO**
,以下是内置command每个值的解释:
**AUTO**
:根据字段名进行填充。
**MD5**
:将字段值进行一次md5加密。
**DATETIME**
:当前时间。
**USERID**
:当前登录用户。
**UUID**
:生成32位随机字符串。
**@AutoComputed**
是一个字段注解,标识
**实体类**
字段为自动填充的注解,拥有三个属性,value、strategy、scope。
**
PINYIN**
:将ref字段的拼音填充到此字段中
**
strategy**
:
`FillStrategy.BEAN_NAME`
、
`FillStrategy.CLASS_NAME`
strategy标识value填充实现是bean还是带包名的类名。默认值是
`FillStrategy.BEAN_NAME`
。
**
REFERENCE**
:将refs字段的值通过format规则填充到此字段
**
scope**
:
`FillScope.INSERT`
、
`FillScope.UPDATE`
、
`FillScope.INSERT_UPDATE`
标识什么时候执行填充,INSERT插入时填充、UPDATE更新时填充,INSERT_UPDATE插入及更新时都会填充。默认值是
`FillScope.INSERT`
。
**value**
:自动填充实现值,当strategy是
`FillStrategy.BEAN_NAME`
时,则获取bean实现,如果是
`FillStrategy.CLASS_NAME`
则通过反射获取实现。有几个默认实现如下:
| 默认填充 | 填充值 | 替换方式 |
| --------------------------------- | ------------------ | ------------------------------------------------------------ |
| DefaultFillType.FILL_DATE | 填充当前时间 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_DATE |
| DefaultFillType.FILL_USER_ACCOUNT | 填充用户账号 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_USER_ACCOUNT |
| DefaultFillType.FILL_USER_ID | 填充用户ID | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_USER_ID |
| DefaultFillType.FILL_MD5 | 填充原字段md5值 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_MD5 |
| DefaultFillType.FILL_PINYIN | 填充原字段pinyin值 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_PINYIN |
| DefaultFillType.FILL_VERSION | 填充version值 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_VERSION |
#### 1.1.2 使用案例
```
java
// 值为AUTO,通过属性名查找计算方式,这里的计算方式就是createId
@AutoComputed
private
String
createId
;
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed
(
command
=
ComputedCommand
.
MD5
)
private
String
userPassword
;
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed
(
command
=
ComputedCommand
.
DATETIME
)
private
Date
createTime
;
// 值为USERID,将当前登录用户ID填充到字段中
@AutoComputed
(
command
=
ComputedCommand
.
USERID
)
private
String
updateId
;
/**
* 创建人,scope为INSERT,新增时填充,填充值为用户account
*/
@ApiModelProperty
(
value
=
"创建人"
)
@Column
(
name
=
"create_id"
)
@AutoFilled
(
scope
=
FillScope
.
INSERT
,
value
=
DefaultFillType
.
FILL_USER_ACCOUNT
)
private
String
createId
;
// 值为UUID,将随机生成的UUID填充到字段中
@AutoComputed
(
command
=
ComputedCommand
.
UUID
)
private
String
uuId
;
/**
* 创建时间,scope为INSERT,新增时填充,填充值为当前时间
*/
@ApiModelProperty
(
value
=
"创建时间"
)
@Column
(
name
=
"create_time"
)
@AutoFilled
(
scope
=
FillScope
.
INSERT
,
value
=
DefaultFillType
.
FILL_DATE
)
private
Date
createTime
;
// 值为PINYIN,ref为userName,将userName字段的中文拼音自动填充到字段中
@AutoComputed
(
command
=
ComputedCommand
.
PINYIN
,
ref
=
"userName"
)
private
String
sortName
;
/**
* 更新人,scope为INSERT_UPDATE,新增和更新都会填充,填充值为用户account
*/
@ApiModelProperty
(
value
=
"更新人"
)
@Column
(
name
=
"update_id"
)
@AutoFilled
(
scope
=
FillScope
.
INSERT_UPDATE
,
value
=
DefaultFillType
.
FILL_USER_ACCOUNT
)
private
String
updateId
;
// 值为REFERENCE,refs是一个数组, 将数组中的字段值按照format的规则填充到字段中
@AutoComputed
(
command
=
ComputedCommand
.
REFERENCE
,
refs
=
{
"name"
,
"age"
,
"gender"
},
format
=
"%s-%s-%s"
)
private
String
referenceValue
;
/**
* 更新时间,scope为INSERT_UPDATE,新增和更新都会填充,填充值为当前时间
*/
@ApiModelProperty
(
value
=
"更新时间"
)
@Column
(
name
=
"update_time"
)
@AutoFilled
(
scope
=
FillScope
.
INSERT_UPDATE
,
value
=
DefaultFillType
.
FILL_DATE
)
private
Date
updateTime
;
```
#### 1.1.3 自定义填充算法
当command值为
**ComputedCommand.AUTO**
时,会通过字段名查找计算方式,这里介绍一下如何自定义填充算法。自动填充算法分为三种:
#### 1.1.3 扩展填充算法
-
第一种是有参算法,例如MD5算法需要使用到原字段中的值作为参数:
扩展填充算法的方式,新建一个类,并实现AutoFillHandler,根据业务判断需要 填充的值,这里举例,例如需要填充一个随机数,新建一个RandomFillHandler类,实现AutoFillHandler方法
```
java
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed
(
command
=
ComputedCommand
.
MD5
)
private
String
userPassword
;
```
需要实现
**ComputerArgs**
接口,将自动填充算法写到computed中。例如Md5填充算法代码如下:
/**
* 随机数填充实现
*/
public
class
RandomFillHandler
implements
AutoFillHandler
<
Integer
>
{
```
java
public
class
ComputerMd5
implements
ComputerArgs
{
/**
* 自动填充方法
* 部分自动填充需要使用实体的其它字段值或者当前字段值,例如MD5填充,需要先获取到当前字段值,再进行md5编码。
*
* entity 为当前实体
* currentValue 为注解字段值
*/
@Override
public
String
computed
(
Object
plain
)
{
//
这里进行处理,并返回自动填充内容
return
DigestUtils
.
md5Hex
(
plain
.
toString
()
);
public
Integer
getFillValue
(
Object
entity
,
Object
currentValue
)
{
//
返回随机数
return
new
Random
().
nextInt
(
);
}
}
```
-
第二种也是有参算法,主要是为了按照format进行自动填充,例如:
这样一个自动填充算法就是实现了,下面写如何使用。
指定策略为CLASS_NAME,value值为带全包名的实现类。
```
java
// 值为REFERENCE,refs是一个数组, 将数组中的字段值按照format的规则填充到字段中
@AutoComputed
(
command
=
ComputedCommand
.
REFERENCE
,
refs
=
{
"name"
,
"age"
,
"gender"
},
format
=
"%s-%s-%s
"
)
private
String
referenceValue
;
// 执行插入时自动填充
@AutoFilled
(
strategy
=
FillStrategy
.
CLASS_NAME
,
scope
=
FillScope
.
INSERT
,
value
=
"com.sinra.ldp.demo.RandomFillHandler
"
)
private
int
randomNum
;
```
需要实现
**ComputerFormatArgs**
接口,将自动填充算法写到computed中。例如REFERENCE填充算法如下:
**另一种写法**
则是先注册为bean,再使用
```
java
public
class
ComputerReference
implements
ComputerFormatArgs
{
@Override
public
String
computed
(
Object
target
,
String
format
)
{
String
result
=
""
;
//核心代码如下,如果format为空则默认以下划线拼接,否则按fromat格式化
if
(
StringUtils
.
isEmpty
(
format
))
{
result
=
StringUtils
.
joinWith
(
"_"
,
objects
);
}
else
{
result
=
String
.
format
(
format
,
objects
);
}
return
result
;
// 注册bean
@Bean
(
"fill_random"
)
public
AutoFillHandler
randomFillHandler
()
{
return
new
RandomFillHandler
();
}
}
```
-
第三种是无参算法,例如DATETIME算法就是一个无参算法,只需要获取当前时间
:
使用时,在实体类字段标记
:
```
java
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed
(
command
=
ComputedCommand
.
DATETIME
)
private
Date
createTime
;
// 执行插入时自动填充
@AutoFilled
(
scope
=
FillScope
.
INSERT
,
value
=
"fill_random"
)
private
int
randomNum
;
```
无参算法与有参类似,只是没有参数,需要实现
**ComputerNoArgs**
接口
## 二 、缓存注解
### 2.1. @ApplicationCacheable
依赖:common-cache
#### 2.1.1 注解介绍
**@ApplicationCacheable**
注解是方法注解,将注解添加到方法上,会自动将方法结果缓存下来,之后再次调用此接口,会直接从缓存中获取结果。缓存key是自动生成的,生成算法为application:+包名+类名+方法名+:参数长度。其它属性字段与Spring cache 注解
**@Cacheable**
属性字段一致。
#### 2.1.2 使用案例
一般此注解在REST层使用,需要注意的是Application缓存不会过期,会一直存在,如果需要刷新,需要另外写刷新方法,建议在数据变化较小,数据量大的接口使用。
```
java
public
class
ComputerDate
implements
ComputerNoArgs
<
Date
>
{
@Override
public
Date
computed
()
{
return
new
Date
();
}
/**
* rest获取用户分页数据
*
* @return
*/
@GetMapping
(
"/rest/page"
)
@ApplicationCacheable
public
RestResult
getListByRest
()
{
return
new
RestResult
(
ResultMsg
.
SUCCESS
,
exampleService
.
getUserListByRest
());
}
```
-
完成算法编写后,还需要将计算类与字段名对应起来。
### 2.2. @SessionCacheable
在service包中新建computed包(推荐),新建一个类,加上
**@Configuration**
注解,在
**@Bean**
注解中的name属性填上字段名。例如字段createId的填充算法是一个无参算法,方法返回类型为
**ComputerNoArgs**
,如果为有参算法,则应该返回
**ComputerArgs**
或者
**ComputerFormatArgs**
。
依赖:common-cache
```
java
@Configuration
public
class
ComputedResolver
{
#### 2.2.1 注解介绍
@Bean
(
name
=
"createId"
)
public
ComputerNoArgs
getCreateId
()
{
return
new
ComputerUser
();
}
**@SessionCacheable**
注解是方法注解,用法与
**@ApplicationCacheable**
注解基本一致,不一致的地方有两点:1、SessionCache生命周期是跟随登录的,也就是退出登录后失效; 2、缓存key生成规则不一致,生成算法:token:+包名+类名+方法名+:参数长度。
#### 2.2.2 使用案例
退出登录后,Session缓存失效
```
java
/**
* jdbc 获取用户list
*
* @param paramMap
* @return
* @throws Exception
*/
@GetMapping
(
"/jdbc/list"
)
@SessionCacheable
public
RestResult
getListByJdbc
(
@RequestParamMap
(
typeCovertMapping
=
ExampleUserInfo
.
class
)
Map
<
String
,
Object
>
paramMap
)
throws
Exception
{
List
<
ExampleUserInfo
>
userInfoList
=
exampleService
.
getUserListByJDBC
(
StringUtils
.
EMPTY
,
paramMap
);
return
new
RestResult
(
ResultMsg
.
SUCCESS
,
userInfoList
);
}
```
当AOP在处理自动填充时,这个字段就会根据上面编辑的算法进行自动填充。
## 三、日志注解
###
1.2. @AutoService
###
3.1. @AvoidRepeatableSubmit
依赖:mcs-common
#### 3.1.1 注解介绍
#### 1.2.1 注解介绍
此注解配合定时任务进行使用,在被需要定时任务调用的服务接口上,添加此注解防止接口由于网络波动或其他原因重复调用。
**@AutoService**
是一个方法注解,被
**@AutoService**
注解的方法,都会被AOP拦截,并根据实体类字段中的
**@AutoComputed**
规则对字段自动填充。拥有两个属性 mode、init,mode为填充方式,默认为
**ComputedMode.COMPUTED**
,init为是否初始化,初始化则全部填充,否则只填充updateId、updateTime、sortName。
#### 3.2.2 使用案例
```
java
@PostMapping
(
value
=
"/testExampleJob"
)
@AvoidRepeatableSubmit
public
RestResult
testJob
(
@RequestParam
String
taskJobId
,
@RequestBody
ExampleUserInfo
userInfo
){
exampleService
.
add
(
userInfo
);
return
new
RestResult
(
ResultMsg
.
SUCCESS
,
""
);
}
```
**使用时仅需要在被定时任务调用的接口上添加@AvoidRepeatableSubmit即可**
默认取方法的第一个参数进行填充,支持单个实体,也支持List集合。
### 3.2. @SysAuditLog
#### 1.2.2 使用案例
#### 3.2.1 注解介绍
@SysAuditLog 是一个方法注解,需要放在controller方法上。 会拦截包裹的方法,会根据登陆信息自动获取当前用户。此注解进行被请求方法拦截,获取请求地址、请求参数、请求内容、请求方法(GET OR POST)、客户端浏览器信息。
根据请求状态,判断是异常请求还是正常请求。
一般@AutoService用于新增方法,以及更新方法
#### 3.2.2 使用案例
```
java
// 新增方法
@AutoService
public
void
add
(
LdpMcsElementInfo
elementInfo
)
{
genericDaoService
.
insert
(
elementInfo
);
}
// 更新方法
@AutoService
(
init
=
false
)
public
void
update
(
LdpMcsElementInfo
elementInfo
)
{
genericDaoService
.
dynamicUpdate
(
elementInfo
);
}
/**
* 用户列表(非分页)
*
* @param paramMap 字段条件
* @return
*/
@GetMapping
(
"/get/list"
)
@SysAuditLog
(
moduleKey
=
"example"
,
subKey
=
"test"
)
public
RestResult
findList
(
@RequestParamMap
(
typeCovertMapping
=
ExampleUserInfo
.
class
)
Map
<
String
,
Object
>
paramMap
)
throws
Exception
{
List
<
ExampleUserInfo
>
userInfoList
=
exampleService
.
findList
(
paramMap
);
return
new
RestResult
(
ResultMsg
.
SUCCESS
,
userInfoList
);
}
```
### 1.3. @AutoRelativeComputed
**使用时需传递 moduleKey(模块名称,如果是新模块需提前建立表,以ldp_audit_log_ 开头+ modulekey 录入的值结尾的表 如ldp_audit_log_example , 表结构可参考 ldp_audit_log_sys), subKey 子模块名称最终会保存在日志中作为二级子模块**
;
依赖:mcs-common
### 3.3. @SysJobLog
####
1
.3.1 注解介绍
####
3
.3.1 注解介绍
**@AutoRelativeComputed**
是一个方法注解,对使用JPA配置了关联关系实体类进行自动填充,主要作用于Service层,对新增关联和移除关联方法参数进行自动填充。
由于系统架构为微服务,服务与服务之间解耦合。因此定时任务设计之初,考虑为定时接口任务调用。
定时任务执行接口调用, 在被调用接口引用的Service具体业务方法添加此@SysJobLog 注解。
注解包含两大功能,1 异步执行,防止接口执行时间过长,否则定时任务调用机器一直连接被占用; 2 更新定时任务执行情况。
定时任务执行流程:
1 定时任务调度服务启动后,满足定时条件自动执行任务调度。 根据后台配置好的服务名称、接口信息调用其他服务接口。
2 被调用接口,必须添加@RequestParam String taskJobId 作为接收参数,此参数后续作为更新任务执行状态使用。由于在业务方法上添加@SysJobLog, 业务方法与Controller不在同一个线程中,无法
获取上一个线程中的信息。因此具体业务Service也必须添加名称为String taskJobId 的参数,后续会自动获取参数的值进行更新任务执行情况。
3 被调用接口也必须添加 @AvoidRepeatableSubmit 防止重复执行
@AutoRelativeComputed字段详解:
**mode**
:关联类型,目前支持RelativeMode.MTM(多对多)、RelativeMode.OTM(一对多)、RelativeMode.MTO(多对一)。
**PS:一对多、多对一暂时不建议使用**
**inverse**
:是否为逆向,false为单向绑定,true为双向绑定,默认为false。
**source**
:参数源类型。
#### 3.3.2 使用案例
**target**
:参数目标类型。
**joinField**
:关联字段。
Controller
**inverseField**
:逆向关联字段。
```
java
**exComputed**
:扩展的填充字段。
@PostMapping
(
value
=
"/testExampleJob"
)
@AvoidRepeatableSubmit
public
RestResult
testJob
(
@RequestBody
ExampleUserInfo
userInfo
,
@RequestParam
String
taskJobId
){
exampleService
.
doJob
(
userInfo
,
taskJobId
);
return
new
RestResult
(
ResultMsg
.
SUCCESS
,
""
);
}
#### 1.3.2 使用案例
```
##### 1). 多对多关系
具体Service
这里举例为用户和角色的多对多关系。
```
java
用户表(user):
@Override
@AutoService
@SysJobLog
public
void
doJob
(
ExampleUserInfo
exampleUserInfo
,
String
taskJobId
)
{
genericDaoService
.
insert
(
exampleUserInfo
);
}
| 字段名称 | 类型 | 备注 |
| --------- | ------ | ---------------- |
| id | String | 用户ID,唯一标识 |
| user_name | String | 用户名 |
```
角色表(role):
**具体业务方法上添加@SysJobLog 进行异步任务及String taskJobId 参数,名称需要保持一致,系统会自动进行更新执行状态**
``
| 字段名称 | 类型 | 备注 |
| --------- | ------ | ---------------- |
| id | String | 角色ID,唯一标识 |
| role_name | String | 角色名称 |
## 四、数据库查询注解
用户角色关联表(user_role):
| 字段名称 | 类型 | 备注 |
| -------- | ------ | ------------ |
| id | String | ID,唯一标识 |
| user_id | String | 用户ID |
| role_id | String | 角色ID |
1.
使用JPA配置实体类关联关系
**User.java**
```
java
/**
* 与角色的多对多关联
*/
@JsonIgnoreProperties
(
"userInfos"
)
//避免互相关联死循环
@ManyToMany
(
fetch
=
FetchType
.
EAGER
)
@JoinTable
(
name
=
"user_role"
,
//中间表的名称
joinColumns
=
{
@JoinColumn
(
name
=
"user_id"
,
referencedColumnName
=
"id"
)},
inverseJoinColumns
=
{
@JoinColumn
(
name
=
"role_id"
,
referencedColumnName
=
"id"
)})
private
Set
<
Role
>
roleInfos
=
new
HashSet
<>();
```
**
Role
.
java
**
```
java
@JsonIgnoreProperties
(
"roleInfos"
)
@ManyToMany
(
mappedBy
=
"roleInfos"
,
fetch
=
FetchType
.
EAGER
,
targetEntity
=
User
.
class
)
private
Set
<
User
>
userInfos
=
new
HashSet
<>(
0
);
```
2
.
在
**
UserServiceImpl
**
中添加方法和注解
```
java
/**
* 添加角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed
(
mode
=
RelativeMode
.
MTM
,
source
=
Role
.
class
,
target
=
UserRole
.
class
,
joinField
=
"userId"
,
inverseField
=
"roleId"
)
public
void
addRoleToUser
(
String
id
,
List
list
,
Map
<
String
,
Object
>
param
)
{
LinkedList
<
TranscationEntity
>
entityList
=
new
LinkedList
<>();
// 删除重复数据
entityList
.
add
(
new
TranscationEntity
(
TransItemExeType
.
DELETE_ENTITY
,
UserRole
.
class
,
param
));
// 新增
entityList
.
add
(
new
TranscationEntity
(
TransItemExeType
.
INSERT_ENTITY
,
list
));
genericDaoService
.
executeTrans
(
entityList
);
}
/**
* 移除角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed
(
mode
=
RelativeMode
.
MTM
,
source
=
Role
.
class
,
target
=
UserRole
.
class
,
joinField
=
"userId"
,
inverseField
=
"roleId"
)
public
void
removeRoleFromUser
(
String
id
,
List
list
,
Map
<
String
,
Object
>
param
)
{
genericDaoService
.
deleteList
(
UserRole
.
class
,
param
);
}
```
####
1.3
.
3
扩展填充算法
**
@AutoRelativeComputed
**
注解主要填充中间关联数据,有部分数据和关联无关,但是又必须填充,这种时候就需要
**
exComputed
**
来扩展填充算法,可以通过算法来填充其它字段。例如,岗位和组织多对多关联自动填充,
exComputed
字段值为
**
orgPosition
**
:
```
java
@AutoRelativeComputed(mode = RelativeMode.MTM, source = LdpMcsOrganization.class, target = LdpMcsPositionOrganization.class,joinField = "postId", inverseField = "orgId", exComputed = "orgPosition")
```
编写扩展算法需要实现 **CustomComputer** 接口,并在computed方法中编写自动填充逻辑。computed方法有一个参数,这个参数是注解中target类的对象,可以在方法中拿到对象,并对对象中的其它字段进行填充。这里对manager字段进行填充:
```
java
public class ComputerOrgPosition implements CustomComputer {
@Override
public void computed(Object target) {
LdpMcsPositionOrganization positionOrganization = (LdpMcsPositionOrganization) target;
positionOrganization.setManager(false);
}
}
```
在有 **@Configuration** 注解类中完成Bean的装配:
```
java
@Configuration
public class ExtraComputer {
/
**
*
关联表其它业务数据填充
*
*
@return
*
/
@Bean(name = "orgPosition")
public CustomComputer getUserPositionComputer() {
return new ComputerOrgPosition();
}
}
```
## 二 、缓存注解
### 2.1. @ApplicationCacheable
依赖:common-cache
#### 2.1.1 注解介绍
**@ApplicationCacheable** 注解是方法注解,将注解添加到方法上,会自动将方法结果缓存下来,之后再次调用此接口,会直接从缓存中获取结果。缓存key是自动生成的,生成算法为application:+包名+类名+方法名+:参数长度。其它属性字段与Spring cache 注解 **@Cacheable** 属性字段一致。
#### 2.1.2 使用案例
一般此注解在REST层使用,需要注意的是Application缓存不会过期,会一直存在,如果需要刷新,需要另外写刷新方法,建议在数据变化较小,数据量大的接口使用。
```
java
/
**
*
rest获取用户分页数据
*
*
@return
*
/
@GetMapping("/rest/page")
@ApplicationCacheable
public RestResult getListByRest() {
return new RestResult(ResultMsg.SUCCESS, exampleService.getUserListByRest());
}
```
### 2.2. @SessionCacheable
依赖:common-cache
#### 2.2.1 注解介绍
**@SessionCacheable** 注解是方法注解,用法与 **@ApplicationCacheable** 注解基本一致,不一致的地方有两点:1、SessionCache生命周期是跟随登录的,也就是退出登录后失效; 2、缓存key生成规则不一致,生成算法:token:+包名+类名+方法名+:参数长度。
#### 2.2.2 使用案例
退出登录后,Session缓存失效
```
java
/
**
*
jdbc 获取用户list
*
*
@param paramMap
*
@return
*
@throws Exception
*
/
@GetMapping("/jdbc/list")
@SessionCacheable
public RestResult getListByJdbc(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map
<String
,
Object
>
paramMap) throws Exception {
List
<ExampleUserInfo>
userInfoList = exampleService.getUserListByJDBC(StringUtils.EMPTY, paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
}
```
## 三、日志注解
### 3.1. @AvoidRepeatableSubmit
#### 3.1.1 注解介绍
此注解配合定时任务进行使用,在被需要定时任务调用的服务接口上,添加此注解防止接口由于网络波动或其他原因重复调用。
#### 3.2.2 使用案例
```
java
@PostMapping(value = "/testExampleJob")
@AvoidRepeatableSubmit
public RestResult testJob(@RequestParam String taskJobId, @RequestBody ExampleUserInfo userInfo){
exampleService.add(userInfo);
return new RestResult(ResultMsg.SUCCESS,"");
}
```
**使用时仅需要在被定时任务调用的接口上添加@AvoidRepeatableSubmit即可**
### 3.2. @SysAuditLog
#### 3.2.1 注解介绍
@SysAuditLog 是一个方法注解,需要放在controller方法上。 会拦截包裹的方法,会根据登陆信息自动获取当前用户。此注解进行被请求方法拦截,获取请求地址、请求参数、请求内容、请求方法(GET OR POST)、客户端浏览器信息。
根据请求状态,判断是异常请求还是正常请求。
#### 3.2.2 使用案例
```
java
/
**
*
用户列表(非分页)
*
*
@param paramMap 字段条件
*
@return
*
/
@GetMapping("/get/list")
@SysAuditLog(moduleKey = "example",subKey = "test")
public RestResult findList(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map
<String
,
Object
>
paramMap) throws Exception {
List
<ExampleUserInfo>
userInfoList = exampleService.findList(paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
}
```
**使用时需传递 moduleKey(模块名称,如果是新模块需提前建立表,以ldp_audit_log_ 开头+ modulekey 录入的值结尾的表 如ldp_audit_log_example , 表结构可参考 ldp_audit_log_sys), subKey 子模块名称最终会保存在日志中作为二级子模块**;
### 3.3. @SysJobLog
#### 3.3.1 注解介绍
由于系统架构为微服务,服务与服务之间解耦合。因此定时任务设计之初,考虑为定时接口任务调用。
定时任务执行接口调用, 在被调用接口引用的Service具体业务方法添加此@SysJobLog 注解。
注解包含两大功能,1 异步执行,防止接口执行时间过长,否则定时任务调用机器一直连接被占用; 2 更新定时任务执行情况。
定时任务执行流程:
1 定时任务调度服务启动后,满足定时条件自动执行任务调度。 根据后台配置好的服务名称、接口信息调用其他服务接口。
2 被调用接口,必须添加@RequestParam String taskJobId 作为接收参数,此参数后续作为更新任务执行状态使用。由于在业务方法上添加@SysJobLog, 业务方法与Controller不在同一个线程中,无法
获取上一个线程中的信息。因此具体业务Service也必须添加名称为String taskJobId 的参数,后续会自动获取参数的值进行更新任务执行情况。
3 被调用接口也必须添加 @AvoidRepeatableSubmit 防止重复执行
#### 3.3.2 使用案例
Controller
```
java
@PostMapping(value = "/testExampleJob")
@AvoidRepeatableSubmit
public RestResult testJob(@RequestBody ExampleUserInfo userInfo, @RequestParam String taskJobId){
exampleService.doJob (userInfo, taskJobId);
return new RestResult(ResultMsg.SUCCESS,"");
}
```
具体Service
```
java
@Override
@AutoService
@SysJobLog
public void doJob(ExampleUserInfo exampleUserInfo, String taskJobId) {
genericDaoService.insert(exampleUserInfo);
}
```
**具体业务方法上添加@SysJobLog 进行异步任务及String taskJobId 参数,名称需要保持一致,系统会自动进行更新执行状态**``
## 四、数据库查询注解
### 4.1. @QueryBind
### 4.1. @QueryBind
依赖:common-query
...
...
@@ -775,3 +570,328 @@ public class RootCodeDTO {
第二步与8.1.2中的填充方式一致。
## 老版本注解
### 一、自动填充
### 1.1. ~~@AutoComputed(1.2.45版已弃用)~~
依赖:base-api。
#### 1.1.1 注解介绍
**@AutoComputed**是一个字段注解,标识**实体类**字段为自动填充的注解,需要配合服务层的 **@AutoService** 使用,拥有四个属性,command、ref、refs、format。注解可以通过command标识字段自动装配的方式,根据字段名查找对应计算方法,或根据command类型查找对应系统计算方法。command默认值为 **ComputedCommand.AUTO** ,以下是内置command每个值的解释:
**AUTO**:根据字段名进行填充。
**MD5**:将字段值进行一次md5加密。
**DATETIME**:当前时间。
**USERID**:当前登录用户。
**UUID**:生成32位随机字符串。
**PINYIN**:将ref字段的拼音填充到此字段中
**REFERENCE**:将refs字段的值通过format规则填充到此字段
#### 1.1.2 使用案例
```java
// 值为AUTO,通过属性名查找计算方式,这里的计算方式就是createId
@AutoComputed
private String createId;
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed(command = ComputedCommand.MD5)
private String userPassword;
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
// 值为USERID,将当前登录用户ID填充到字段中
@AutoComputed(command = ComputedCommand.USERID)
private String updateId;
// 值为UUID,将随机生成的UUID填充到字段中
@AutoComputed(command = ComputedCommand.UUID)
private String uuId;
// 值为PINYIN,ref为userName,将userName字段的中文拼音自动填充到字段中
@AutoComputed(command = ComputedCommand.PINYIN, ref = "userName")
private String sortName;
// 值为REFERENCE,refs是一个数组, 将数组中的字段值按照format的规则填充到字段中
@AutoComputed(command = ComputedCommand.REFERENCE, refs = {"name", "age", "gender"}, format = "%s-%s-%s")
private String referenceValue;
```
#### 1.1.3 自定义填充算法
当command值为 **ComputedCommand.AUTO** 时,会通过字段名查找计算方式,这里介绍一下如何自定义填充算法。自动填充算法分为三种:
- 第一种是有参算法,例如MD5算法需要使用到原字段中的值作为参数:
```java
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed(command = ComputedCommand.MD5)
private String userPassword;
```
需要实现 **ComputerArgs** 接口,将自动填充算法写到computed中。例如Md5填充算法代码如下:
```java
public class ComputerMd5 implements ComputerArgs {
@Override
public String computed(Object plain) {
//这里进行处理,并返回自动填充内容
return DigestUtils.md5Hex(plain.toString());
}
}
```
- 第二种也是有参算法,主要是为了按照format进行自动填充,例如:
```java
// 值为REFERENCE,refs是一个数组, 将数组中的字段值按照format的规则填充到字段中
@AutoComputed(command = ComputedCommand.REFERENCE, refs = {"name", "age", "gender"}, format = "%s-%s-%s")
private String referenceValue;
```
需要实现 **ComputerFormatArgs** 接口,将自动填充算法写到computed中。例如REFERENCE填充算法如下:
```java
public class ComputerReference implements ComputerFormatArgs {
@Override
public String computed(Object target, String format) {
String result = "";
//核心代码如下,如果format为空则默认以下划线拼接,否则按fromat格式化
if (StringUtils.isEmpty(format)) {
result = StringUtils.joinWith("_", objects);
} else {
result = String.format(format, objects);
}
return result;
}
}
```
- 第三种是无参算法,例如DATETIME算法就是一个无参算法,只需要获取当前时间:
```java
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
```
无参算法与有参类似,只是没有参数,需要实现 **ComputerNoArgs** 接口
```java
public class ComputerDate implements ComputerNoArgs<Date> {
@Override
public Date computed() {
return new Date();
}
}
```
- 完成算法编写后,还需要将计算类与字段名对应起来。
在service包中新建computed包(推荐),新建一个类,加上 **@Configuration** 注解,在 **@Bean** 注解中的name属性填上字段名。例如字段createId的填充算法是一个无参算法,方法返回类型为 **ComputerNoArgs** ,如果为有参算法,则应该返回 **ComputerArgs** 或者 **ComputerFormatArgs**。
```java
@Configuration
public class ComputedResolver {
@Bean(name = "createId")
public ComputerNoArgs getCreateId() {
return new ComputerUser();
}
}
```
当AOP在处理自动填充时,这个字段就会根据上面编辑的算法进行自动填充。
### 1.2. ~~@AutoService(1.2.45版已弃用)~~
依赖:mcs-common
#### 1.2.1 注解介绍
**@AutoService**是一个方法注解,被 **@AutoService** 注解的方法,都会被AOP拦截,并根据实体类字段中的 **@AutoComputed** 规则对字段自动填充。拥有两个属性 mode、init,mode为填充方式,默认为 **ComputedMode.COMPUTED** ,init为是否初始化,初始化则全部填充,否则只填充updateId、updateTime、sortName。
默认取方法的第一个参数进行填充,支持单个实体,也支持List集合。
#### 1.2.2 使用案例
一般@AutoService用于新增方法,以及更新方法
```java
// 新增方法
@AutoService
public void add(LdpMcsElementInfo elementInfo) {
genericDaoService.insert(elementInfo);
}
// 更新方法
@AutoService(init = false)
public void update(LdpMcsElementInfo elementInfo) {
genericDaoService.dynamicUpdate(elementInfo);
}
```
### 1.3. ~~@AutoRelativeComputed(1.2.45版已弃用)~~
依赖:mcs-common
#### 1.3.1 注解介绍
**@AutoRelativeComputed** 是一个方法注解,对使用JPA配置了关联关系实体类进行自动填充,主要作用于Service层,对新增关联和移除关联方法参数进行自动填充。
@AutoRelativeComputed字段详解:
**mode**:关联类型,目前支持RelativeMode.MTM(多对多)、RelativeMode.OTM(一对多)、RelativeMode.MTO(多对一)。**PS:一对多、多对一暂时不建议使用**
**inverse**:是否为逆向,false为单向绑定,true为双向绑定,默认为false。
**source**:参数源类型。
**target**:参数目标类型。
**joinField**:关联字段。
**inverseField**:逆向关联字段。
**exComputed**:扩展的填充字段。
#### 1.3.2 使用案例
##### 1). 多对多关系
这里举例为用户和角色的多对多关系。
用户表(user):
| 字段名称 | 类型 | 备注 |
| --------- | ------ | ---------------- |
| id | String | 用户ID,唯一标识 |
| user_name | String | 用户名 |
角色表(role):
| 字段名称 | 类型 | 备注 |
| --------- | ------ | ---------------- |
| id | String | 角色ID,唯一标识 |
| role_name | String | 角色名称 |
用户角色关联表(user_role):
| 字段名称 | 类型 | 备注 |
| -------- | ------ | ------------ |
| id | String | ID,唯一标识 |
| user_id | String | 用户ID |
| role_id | String | 角色ID |
1. 使用JPA配置实体类关联关系
**User.java**
```java
/**
* 与角色的多对多关联
*/
@JsonIgnoreProperties("userInfos")//避免互相关联死循环
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role", //中间表的名称
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
private Set<Role> roleInfos = new HashSet<>();
```
**Role.java**
```java
@JsonIgnoreProperties("roleInfos")
@ManyToMany(mappedBy = "roleInfos", fetch = FetchType.EAGER, targetEntity = User.class)
private Set<User> userInfos = new HashSet<>(0);
```
2. 在**UserServiceImpl**中添加方法和注解
```java
/**
* 添加角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed(mode = RelativeMode.MTM, source = Role.class, target = UserRole.class, joinField = "userId", inverseField = "roleId")
public void addRoleToUser(String id, List list, Map<String, Object> param) {
LinkedList<TranscationEntity> entityList = new LinkedList<>();
// 删除重复数据
entityList.add(new TranscationEntity(TransItemExeType.DELETE_ENTITY, UserRole.class, param));
// 新增
entityList.add(new TranscationEntity(TransItemExeType.INSERT_ENTITY, list));
genericDaoService.executeTrans(entityList);
}
/**
* 移除角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed(mode = RelativeMode.MTM, source = Role.class, target = UserRole.class,joinField = "userId", inverseField = "roleId")
public void removeRoleFromUser(String id, List list, Map<String, Object> param) {
genericDaoService.deleteList(UserRole.class, param);
}
```
#### 1.3.3 扩展填充算法
**@AutoRelativeComputed** 注解主要填充中间关联数据,有部分数据和关联无关,但是又必须填充,这种时候就需要 **exComputed** 来扩展填充算法,可以通过算法来填充其它字段。例如,岗位和组织多对多关联自动填充, exComputed字段值为**orgPosition** :
```java
@AutoRelativeComputed(mode = RelativeMode.MTM, source = LdpMcsOrganization.class, target = LdpMcsPositionOrganization.class,joinField = "postId", inverseField = "orgId", exComputed = "orgPosition")
```
编写扩展算法需要实现 **CustomComputer** 接口,并在computed方法中编写自动填充逻辑。computed方法有一个参数,这个参数是注解中target类的对象,可以在方法中拿到对象,并对对象中的其它字段进行填充。这里对manager字段进行填充:
```java
public class ComputerOrgPosition implements CustomComputer {
@Override
public void computed(Object target) {
LdpMcsPositionOrganization positionOrganization = (LdpMcsPositionOrganization) target;
positionOrganization.setManager(false);
}
}
```
在有 **@Configuration** 注解类中完成Bean的装配:
```java
@Configuration
public class ExtraComputer {
/**
* 关联表其它业务数据填充
*
* @return
*/
@Bean(name = "orgPosition")
public CustomComputer getUserPositionComputer() {
return new ComputerOrgPosition();
}
}
`
``
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment