Browse Source

支持处理Jackson类型处理器泛型.

https://github.com/baomidou/mybatis-plus/issues/5978
nieqiurong 1 year ago
parent
commit
4f8a41152a

+ 34 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/IJsonTypeHandler.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011-2023, baomidou (jobob@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baomidou.mybatisplus.core.handlers;
+
+import java.lang.reflect.Field;
+
+/**
+ * Json类型处理器接口(实现类确保为多例状态).
+ *
+ * @author nieqiurong 2024年3月4日
+ * @since 3.5.6
+ */
+public interface IJsonTypeHandler<T> {
+
+    void init(Field field);
+
+    T parse(String json);
+
+    String toJson(Object obj);
+
+}

+ 10 - 1
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/TableFieldInfo.java

@@ -23,6 +23,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.Version;
 import com.baomidou.mybatisplus.annotation.Version;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
+import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
 import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
@@ -41,6 +42,7 @@ import org.apache.ibatis.type.TypeHandlerRegistry;
 import org.apache.ibatis.type.UnknownTypeHandler;
 import org.apache.ibatis.type.UnknownTypeHandler;
 
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Field;
+import java.lang.reflect.Type;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
@@ -560,8 +562,15 @@ public class TableFieldInfo implements Constants {
         }
         }
         if (typeHandler != null && typeHandler != UnknownTypeHandler.class) {
         if (typeHandler != null && typeHandler != UnknownTypeHandler.class) {
             TypeHandler<?> typeHandler = registry.getMappingTypeHandler(this.typeHandler);
             TypeHandler<?> typeHandler = registry.getMappingTypeHandler(this.typeHandler);
-            if (typeHandler == null) {
+            if (IJsonTypeHandler.class.isAssignableFrom(this.typeHandler)) {
+                // 保证每次实例化
                 typeHandler = registry.getInstance(propertyType, this.typeHandler);
                 typeHandler = registry.getInstance(propertyType, this.typeHandler);
+                IJsonTypeHandler<?> jsonTypeHandler = (IJsonTypeHandler<?>) typeHandler;
+                jsonTypeHandler.init(this.field);
+            } else {
+                if (typeHandler == null) {
+                    typeHandler = registry.getInstance(propertyType, this.typeHandler);
+                }
             }
             }
             builder.typeHandler(typeHandler);
             builder.typeHandler(typeHandler);
         }
         }

+ 14 - 4
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/AbstractJsonTypeHandler.java

@@ -15,10 +15,13 @@
  */
  */
 package com.baomidou.mybatisplus.extension.handlers;
 package com.baomidou.mybatisplus.extension.handlers;
 
 
+import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import org.apache.ibatis.type.BaseTypeHandler;
 import org.apache.ibatis.type.BaseTypeHandler;
 import org.apache.ibatis.type.JdbcType;
 import org.apache.ibatis.type.JdbcType;
 
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
 import java.sql.CallableStatement;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSet;
@@ -28,7 +31,17 @@ import java.sql.SQLException;
  * @author miemie
  * @author miemie
  * @since 2019-11-28
  * @since 2019-11-28
  */
  */
-public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> {
+public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> implements IJsonTypeHandler<T> {
+
+    /**
+     * @since 3.5.6
+     */
+    protected Type genericType;
+
+    @Override
+    public void init(Field field) {
+        this.genericType = field.getGenericType();
+    }
 
 
     @Override
     @Override
     public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
     public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
@@ -53,7 +66,4 @@ public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> {
         return StringUtils.isBlank(json) ? null : parse(json);
         return StringUtils.isBlank(json) ? null : parse(json);
     }
     }
 
 
-    protected abstract T parse(String json);
-
-    protected abstract String toJson(T obj);
 }
 }

+ 2 - 2
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/Fastjson2TypeHandler.java

@@ -46,12 +46,12 @@ public class Fastjson2TypeHandler extends AbstractJsonTypeHandler<Object> {
 
 
 
 
     @Override
     @Override
-    protected Object parse(String json) {
+    public Object parse(String json) {
         return JSON.parseObject(json, this.type);
         return JSON.parseObject(json, this.type);
     }
     }
 
 
     @Override
     @Override
-    protected String toJson(Object obj) {
+    public String toJson(Object obj) {
         return JSON.toJSONString(obj, JSONWriter.Feature.WriteMapNullValue,
         return JSON.toJSONString(obj, JSONWriter.Feature.WriteMapNullValue,
             JSONWriter.Feature.WriteNullListAsEmpty, JSONWriter.Feature.WriteNullStringAsEmpty);
             JSONWriter.Feature.WriteNullListAsEmpty, JSONWriter.Feature.WriteNullStringAsEmpty);
     }
     }

+ 3 - 2
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/FastjsonTypeHandler.java

@@ -44,13 +44,14 @@ public class FastjsonTypeHandler extends AbstractJsonTypeHandler<Object> {
     }
     }
 
 
     @Override
     @Override
-    protected Object parse(String json) {
+    public Object parse(String json) {
         return JSON.parseObject(json, type);
         return JSON.parseObject(json, type);
     }
     }
 
 
     @Override
     @Override
-    protected String toJson(Object obj) {
+    public String toJson(Object obj) {
         return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue,
         return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue,
             SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty);
             SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty);
     }
     }
+
 }
 }

+ 2 - 2
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/GsonTypeHandler.java

@@ -44,12 +44,12 @@ public class GsonTypeHandler extends AbstractJsonTypeHandler<Object> {
     }
     }
 
 
     @Override
     @Override
-    protected Object parse(String json) {
+    public Object parse(String json) {
         return getGson().fromJson(json, type);
         return getGson().fromJson(json, type);
     }
     }
 
 
     @Override
     @Override
-    protected String toJson(Object obj) {
+    public String toJson(Object obj) {
         return getGson().toJson(obj);
         return getGson().toJson(obj);
     }
     }
 
 

+ 24 - 3
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/JacksonTypeHandler.java

@@ -17,6 +17,7 @@ package com.baomidou.mybatisplus.extension.handlers;
 
 
 import com.baomidou.mybatisplus.core.toolkit.Assert;
 import com.baomidou.mybatisplus.core.toolkit.Assert;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.type.JdbcType;
 import org.apache.ibatis.type.JdbcType;
@@ -24,6 +25,9 @@ import org.apache.ibatis.type.MappedJdbcTypes;
 import org.apache.ibatis.type.MappedTypes;
 import org.apache.ibatis.type.MappedTypes;
 
 
 import java.io.IOException;
 import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
 
 
 /**
 /**
  * Jackson 实现 JSON 字段类型处理器
  * Jackson 实现 JSON 字段类型处理器
@@ -47,16 +51,33 @@ public class JacksonTypeHandler extends AbstractJsonTypeHandler<Object> {
     }
     }
 
 
     @Override
     @Override
-    protected Object parse(String json) {
+    public Object parse(String json) {
         try {
         try {
-            return getObjectMapper().readValue(json, type);
+            if (List.class.isAssignableFrom(this.type)) {
+                return getObjectMapper().readValue(json, new TypeReference<Object>() {
+                    @Override
+                    public Type getType() {
+                        return genericType;
+                    }
+                });
+            } else if (Map.class.isAssignableFrom(this.type)) {
+                return getObjectMapper().readValue(json, new TypeReference<Map<?, ?>>() {
+                    @Override
+                    public Type getType() {
+                        return genericType;
+                    }
+                });
+            } else {
+                return getObjectMapper().readValue(json, this.type);
+            }
+
         } catch (IOException e) {
         } catch (IOException e) {
             throw new RuntimeException(e);
             throw new RuntimeException(e);
         }
         }
     }
     }
 
 
     @Override
     @Override
-    protected String toJson(Object obj) {
+    public String toJson(Object obj) {
         try {
         try {
             return getObjectMapper().writeValueAsString(obj);
             return getObjectMapper().writeValueAsString(obj);
         } catch (JsonProcessingException e) {
         } catch (JsonProcessingException e) {

+ 67 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JsonEntity.java

@@ -0,0 +1,67 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName(value = "t_json_entity", autoResultMap = true)
+public class JsonEntity {
+
+    @TableId(type = IdType.INPUT)
+    private String id;
+
+    private String name;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Card card;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private List<Attr> attr;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, Attr> attr2;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, String> attr3;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, Object> attr4;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Attr {
+
+        private String name;
+
+        private String value;
+
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Card {
+
+        private String id;
+
+        private String value;
+
+    }
+
+
+}

+ 10 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JsonEntityMapper.java

@@ -0,0 +1,10 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public interface JsonEntityMapper extends BaseMapper<JsonEntity> {
+
+}

+ 57 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JsonTest.java

@@ -0,0 +1,57 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.test.BaseDbTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author nieqiurong 2024年3月4日
+ */
+public class JsonTest extends BaseDbTest<JsonEntityMapper> {
+
+    @Override
+    protected List<String> tableSql() {
+        return Arrays.asList("drop table if exists t_json_entity", "CREATE TABLE IF NOT EXISTS t_json_entity (" +
+            "id VARCHAR(32) NOT NULL," +
+            "name VARCHAR(30) NULL DEFAULT NULL," +
+            "card VARCHAR(255) NULL DEFAULT NULL," +
+            "attr VARCHAR(255) NULL DEFAULT NULL," +
+            "attr2 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr3 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr4 VARCHAR(255) NULL DEFAULT NULL," +
+            "PRIMARY KEY (id))");
+    }
+
+
+    @Test
+    void test() {
+        doTest(mapper -> {
+            var entity = new JsonEntity("123", "秋秋", new JsonEntity.Card("1", "1111"),
+                Arrays.asList(new JsonEntity.Attr("age", "18"), new JsonEntity.Attr("sex", "女")),
+                new HashMap<>(Map.of("test", new JsonEntity.Attr("小红", "1"))),
+                new HashMap<>(Map.of("name", "1", "test2", "2")),
+                new HashMap<>(Map.of("test1", "1", "test2", 2))
+            );
+            mapper.insert(entity);
+            JsonEntity jsonEntity = mapper.selectById(entity.getId());
+            Assertions.assertEquals("秋秋", jsonEntity.getName());
+            Assertions.assertEquals(2, jsonEntity.getAttr().size());
+            Assertions.assertNotNull(jsonEntity.getCard());
+            Assertions.assertEquals(1, jsonEntity.getAttr2().size());
+            Assertions.assertEquals("小红", jsonEntity.getAttr2().get("test").getName());
+            Assertions.assertEquals(2, jsonEntity.getAttr3().size());
+            Assertions.assertEquals("1", jsonEntity.getAttr3().get("name"));
+            Assertions.assertEquals("2", jsonEntity.getAttr3().get("test2"));
+            Assertions.assertEquals(2, jsonEntity.getAttr4().size());
+            Assertions.assertEquals(2, jsonEntity.getAttr4().get("test2"));
+        });
+    }
+
+
+}