@@ -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
22712271association
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
360238611 . ** 分页拦截器**
36033862 - 自动对查询语句追加分页逻辑,返回分页数据。
@@ -3624,7 +3883,7 @@ public class MyBatisPlusConfiguration {
36243883
36253884------
36263885
3627- ### 🔹 MyBatis-Plus 默认内置的常用拦截器
3886+ ### MyBatis-Plus 默认内置的常用拦截器
36283887
36293888- ` PaginationInnerInterceptor ` (分页)
36303889- ` OptimisticLockerInnerInterceptor ` (乐观锁)
0 commit comments