Skip to content

Commit c4f4ec7

Browse files
committed
feat(database\mybatis-plus-jdk8): JSON 包装 TypeHandler(支持泛型) 的使用
1 parent 126b55d commit c4f4ec7

4 files changed

Lines changed: 439 additions & 9 deletions

File tree

database/mybatis-plus-jdk8/README.md

Lines changed: 268 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,11 +2069,11 @@ Page{records=[{"id":1,"name":"阿腾","age":25,"score":99.99,"birthday":"2025-01
20692069

20702070

20712071

2072-
## 🌟 Mapper XML常用标签整理
2072+
## Mapper XML常用标签整理
20732073

20742074
------
20752075

2076-
### 🟣 `#{}``${}` 的主要差异
2076+
### `#{}``${}` 的主要差异
20772077

20782078
| | `#{}` | `${}` |
20792079
| ---- | ----------------------------------------------- | --------------------------------- |
@@ -2084,7 +2084,7 @@ Page{records=[{"id":1,"name":"阿腾","age":25,"score":99.99,"birthday":"2025-01
20842084

20852085
------
20862086

2087-
### 🟣 基本标签(适用增删改查)
2087+
### 基本标签(适用增删改查)
20882088

20892089
🔹`<select>` — 定义**数据的读取语句**
20902090

@@ -2138,7 +2138,7 @@ Page{records=[{"id":1,"name":"阿腾","age":25,"score":99.99,"birthday":"2025-01
21382138

21392139
------
21402140

2141-
### 🟣 动态标签(适用条件拼接)
2141+
### 动态标签(适用条件拼接)
21422142

21432143
🔹`<![CDATA[]]>` —转义操作
21442144

@@ -2251,7 +2251,7 @@ Page{records=[{"id":1,"name":"阿腾","age":25,"score":99.99,"birthday":"2025-01
22512251

22522252
------
22532253

2254-
### 🟣 resultMap —列与对象的高度自由映射
2254+
### resultMap —列与对象的高度自由映射
22552255

22562256
✅适用条件:列名与对象属性不一一对应时,或者需要进行关联时。
22572257
✅作用:可以进行一对一、一对多甚至是有参赋值。
@@ -2266,7 +2266,7 @@ Page{records=[{"id":1,"name":"阿腾","age":25,"score":99.99,"birthday":"2025-01
22662266

22672267
------
22682268

2269-
#### 🟣 一对一
2269+
**一对一**
22702270

22712271
association
22722272

@@ -2298,7 +2298,7 @@ association
22982298

22992299
------
23002300

2301-
#### 🟣 一对多(collection)
2301+
**一对多(collection)**
23022302

23032303
✅适用条件:需要获取**一对多**的数据时(如一个用户有多个购买记录)。
23042304
✅作用:可以将关联的数据按 List 映射到对象中。
@@ -3591,13 +3591,272 @@ public class MyBatisPlusConfiguration {
35913591

35923592

35933593

3594+
3595+
3596+
### JSON 包装 TypeHandler(支持泛型)
3597+
3598+
轻量包装 + 抽象基类:**用“强类型包装类”替代裸泛型,但通过抽象类统一行为,避免重复代码**
3599+
3600+
---
3601+
3602+
#### 定义一个抽象 JSON 包装基类
3603+
3604+
这个类只负责承载 value,并提供基础能力
3605+
3606+
```java
3607+
package local.ateng.java.mybatisjdk8.entity;
3608+
3609+
import java.io.Serializable;
3610+
3611+
/**
3612+
* JSON 字段包装基类
3613+
* <p>
3614+
* 用于承载数据库 JSON 字段的泛型结构,避免直接使用裸泛型导致的类型擦除问题。
3615+
*
3616+
* @author Ateng
3617+
* @since 2026-04-12
3618+
*/
3619+
public abstract class JsonWrapper<T> implements Serializable {
3620+
3621+
/**
3622+
* 实际数据
3623+
*/
3624+
private T value;
3625+
3626+
/**
3627+
* 无参构造方法
3628+
*/
3629+
public JsonWrapper() {
3630+
}
3631+
3632+
/**
3633+
* 带参构造方法
3634+
*
3635+
* @param value 实际数据
3636+
*/
3637+
public JsonWrapper(T value) {
3638+
this.value = value;
3639+
}
3640+
3641+
/**
3642+
* 获取实际数据
3643+
*
3644+
* @return value
3645+
*/
3646+
public T getValue() {
3647+
return value;
3648+
}
3649+
3650+
/**
3651+
* 设置实际数据
3652+
*
3653+
* @param value 数据
3654+
*/
3655+
public void setValue(T value) {
3656+
this.value = value;
3657+
}
3658+
}
3659+
```
3660+
3661+
---
3662+
3663+
#### 针对具体泛型定义“无逻辑子类”
3664+
3665+
例如:
3666+
3667+
```java
3668+
package local.ateng.java.mybatisjdk8.entity;
3669+
3670+
import java.util.List;
3671+
3672+
/**
3673+
* MyData 列表包装
3674+
*
3675+
* @author Ateng
3676+
* @since 2026-04-12
3677+
*/
3678+
public class MyDataList extends JsonWrapper<List<MyData>> {
3679+
3680+
public MyDataList() {
3681+
}
3682+
3683+
public MyDataList(List<MyData> value) {
3684+
super(value);
3685+
}
3686+
}
3687+
```
3688+
3689+
再比如:
3690+
3691+
```java
3692+
public class UserMap extends JsonWrapper<Map<String, User>> {
3693+
3694+
public UserMap() {
3695+
}
3696+
3697+
public UserMap(Map<String, User> value) {
3698+
super(value);
3699+
}
3700+
}
3701+
```
3702+
3703+
---
3704+
3705+
#### TypeHandler 只处理 JsonWrapper
3706+
3707+
重点来了:**不再处理 List,而是处理 JsonWrapper**
3708+
3709+
```java
3710+
package local.ateng.java.mybatisjdk8.handler;
3711+
3712+
import cn.hutool.core.util.ObjectUtil;
3713+
import com.alibaba.fastjson2.JSON;
3714+
import com.alibaba.fastjson2.JSONReader;
3715+
import com.alibaba.fastjson2.JSONWriter;
3716+
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
3717+
import local.ateng.java.mybatisjdk8.entity.JsonWrapper;
3718+
import local.ateng.java.mybatisjdk8.entity.MyDataList;
3719+
import org.apache.ibatis.type.JdbcType;
3720+
import org.apache.ibatis.type.MappedJdbcTypes;
3721+
import org.apache.ibatis.type.MappedTypes;
3722+
import org.slf4j.Logger;
3723+
import org.slf4j.LoggerFactory;
3724+
3725+
import java.lang.reflect.ParameterizedType;
3726+
import java.lang.reflect.Type;
3727+
3728+
/**
3729+
* JsonWrapper 通用 TypeHandler
3730+
*
3731+
* @author Ateng
3732+
* @since 2026-04-12
3733+
*/
3734+
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.LONGVARCHAR, JdbcType.OTHER}) // 数据库字段类型
3735+
@MappedTypes({MyDataList.class}) // Java 类型
3736+
public class JsonWrapperTypeHandler<T extends JsonWrapper>
3737+
extends AbstractJsonTypeHandler<T> {
3738+
3739+
private static final Logger log = LoggerFactory.getLogger(JsonWrapperTypeHandler.class);
3740+
3741+
private final Class<T> clazz;
3742+
3743+
public JsonWrapperTypeHandler(Class<T> clazz) {
3744+
this.clazz = clazz;
3745+
}
3746+
3747+
/**
3748+
* 解析 JSON → 包装类
3749+
*/
3750+
@Override
3751+
protected T parse(String json) {
3752+
if (ObjectUtil.isEmpty(json)) {
3753+
return null;
3754+
}
3755+
3756+
try {
3757+
T wrapper = clazz.newInstance();
3758+
3759+
// 获取泛型 T
3760+
Type superType = clazz.getGenericSuperclass();
3761+
3762+
if (superType instanceof ParameterizedType) {
3763+
Type actualType = ((ParameterizedType) superType)
3764+
.getActualTypeArguments()[0];
3765+
3766+
Object value = JSON.parseObject(
3767+
json, actualType,
3768+
// 默认下是camel case精确匹配,打开这个后,能够智能识别camel/upper/pascal/snake/Kebab五中case
3769+
JSONReader.Feature.SupportSmartMatch,
3770+
// 允许字段名不带引号
3771+
JSONReader.Feature.AllowUnQuotedFieldNames,
3772+
// 忽略无法序列化的字段
3773+
JSONReader.Feature.IgnoreNoneSerializable
3774+
);
3775+
3776+
wrapper.setValue(value);
3777+
}
3778+
3779+
return wrapper;
3780+
3781+
} catch (Exception e) {
3782+
log.error("JsonWrapper 反序列化失败", e);
3783+
return null;
3784+
}
3785+
}
3786+
3787+
/**
3788+
* 包装类 → JSON
3789+
*/
3790+
@Override
3791+
protected String toJson(T obj) {
3792+
if (ObjectUtil.isEmpty(obj)) {
3793+
return null;
3794+
}
3795+
3796+
try {
3797+
return JSON.toJSONString(
3798+
obj.getValue(),
3799+
// 序列化输出空值字段
3800+
JSONWriter.Feature.WriteNulls,
3801+
// 基于字段反序列化
3802+
JSONWriter.Feature.FieldBased
3803+
);
3804+
} catch (Exception e) {
3805+
log.error("JsonWrapper 序列化失败", e);
3806+
return null;
3807+
}
3808+
}
3809+
}
3810+
3811+
```
3812+
3813+
---
3814+
3815+
#### 使用方式
3816+
3817+
**全局使用**
3818+
3819+
```java
3820+
@Bean
3821+
public ConfigurationCustomizer configurationCustomizer() {
3822+
return configuration -> {
3823+
3824+
configuration.getTypeHandlerRegistry().register(JsonWrapperTypeHandler.class);
3825+
3826+
};
3827+
}
3828+
```
3829+
3830+
**局部使用**
3831+
3832+
```java
3833+
@TableField(typeHandler = JsonWrapperTypeHandler.class)
3834+
private MyDataList mydataList;
3835+
```
3836+
3837+
写入数据
3838+
3839+
```
3840+
project.setMyDataList(new MyDataList(list));
3841+
```
3842+
3843+
数据库存储:
3844+
3845+
```json
3846+
[
3847+
{"id":1,"name":"A"}
3848+
]
3849+
```
3850+
3851+
3852+
35943853
## 拦截器Interceptor
35953854

35963855
参考:[官网文档](https://baomidou.com/plugins/)
35973856

35983857
------
35993858

3600-
### 🔹 常用的 SQL 拦截器场景
3859+
### 常用的 SQL 拦截器场景
36013860

36023861
1. **分页拦截器**
36033862
- 自动对查询语句追加分页逻辑,返回分页数据。
@@ -3624,7 +3883,7 @@ public class MyBatisPlusConfiguration {
36243883

36253884
------
36263885

3627-
### 🔹 MyBatis-Plus 默认内置的常用拦截器
3886+
### MyBatis-Plus 默认内置的常用拦截器
36283887

36293888
- `PaginationInnerInterceptor`(分页)
36303889
- `OptimisticLockerInnerInterceptor`(乐观锁)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package local.ateng.java.mybatisjdk8.entity;
2+
3+
import java.io.Serializable;
4+
5+
/**
6+
* JSON 字段包装基类
7+
* <p>
8+
* 用于承载数据库 JSON 字段的泛型结构,避免直接使用裸泛型导致的类型擦除问题。
9+
*
10+
* @author Ateng
11+
* @since 2026-04-12
12+
*/
13+
public abstract class JsonWrapper<T> implements Serializable {
14+
15+
/**
16+
* 实际数据
17+
*/
18+
private T value;
19+
20+
/**
21+
* 无参构造方法
22+
*/
23+
public JsonWrapper() {
24+
}
25+
26+
/**
27+
* 带参构造方法
28+
*
29+
* @param value 实际数据
30+
*/
31+
public JsonWrapper(T value) {
32+
this.value = value;
33+
}
34+
35+
/**
36+
* 获取实际数据
37+
*
38+
* @return value
39+
*/
40+
public T getValue() {
41+
return value;
42+
}
43+
44+
/**
45+
* 设置实际数据
46+
*
47+
* @param value 数据
48+
*/
49+
public void setValue(T value) {
50+
this.value = value;
51+
}
52+
}

0 commit comments

Comments
 (0)