博客-mybatis类型构造器解决List内嵌复杂类结构

一次mybatis类型构造器的使用实例

背景

存储复杂的数据结构时,存在包含子类,嵌套本类,List存储的复杂情况,写mapper和表结构太费劲,嵌套子类list不想弄子表,嵌套自身list不想弄parentId等等, 于是拟使用类型构造器解决

类结构

base类

public class TestCaseInfo extends CUBaseEntity {
    private Long testCaseId;
    @ApiModelProperty("测试用例名称")
    private String testCaseName;
    @ApiModelProperty("测试用例描述")
    private String testCaseDesc;
    private String testCaseType;
    private Long relationId;
    @ApiModelProperty("测试用例的选择的型号code")
    private String productCode;
    @ApiModelProperty("测试用例的型号名称")
    private String productName;
    private String typeId;
    @ApiModelProperty("测试用例中单元之间的间隔时间")
    private String intervalTime = "0";
    @ApiModelProperty("测试单元间隔时间单位,固定ms")
    private String intervalTimeUnit = "ms";
    @ApiModelProperty("测试用例的测试单元集")
    private List<TestCaseUnit> testCaseUnitList;
}

base类的子类TestCaseUnit

public class TestCaseUnit {
    @ApiModelProperty("测试单元名称描述")
    public String desc;
    @ApiModelProperty("所属测试用例ID")
    private Long testCaseId;
    @ApiModelProperty("用例测试单元ID")
    private Long testCaseUnitId;
    @ApiModelProperty("排序,协议测试时需按此顺序")
    private int sort;
    @ApiModelProperty("单品测试测试单元状态,启用0,停用1")
    private String status;

    @ApiModelProperty("测试单元对应属性Json")
    private JSONObject property;

    @ApiModelProperty("用例测试单元类型,simple简易模式下无testCaseParamList,customize自定义")
    private String testCaseUnitType;

    @ApiModelProperty("测试单元测试动作参数")
    private List<TestCaseParam> testCaseParamList;

    private List<TestCaseUnit> childTestCaseUnitList;
    @ApiModelProperty("子属性集,如果为组命令G简易测试,用到此值")
    private JSONArray childPropertyArray;

    private Long parentTestCaseUnitId;
}

可以看到base类中包含TestCaseUnit类的List, 就需要主表与子表分别存储Base信息与TestCaseUnit信息, 但每一个TestCaseUnit中又会包含复杂结构,比如自身List以及TestCaseParam类的List, 一般会需要更多子表和关联键来关联信息, 比较麻烦, 对于基本不会作为查询条件的自身List以及TestCaseParam类的List,拟整合mysql的json字段来单纯存储读取,那么如何存储读取就成了关键, 本文使用mybatis的类型构造器进行解析。

JsonTypeHandler

public class JsonTypeHandler<T extends Object> extends BaseTypeHandler<T> {

    private static final ObjectMapper mapper = new ObjectMapper();
    private Class<T> clazz;

    public JsonTypeHandler(Class<T> clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.clazz = clazz;
    }


    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, this.toJson(parameter));
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return this.toObject(rs.getString(columnName), clazz);
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return this.toObject(rs.getString(columnIndex), clazz);
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return this.toObject(cs.getString(columnIndex), clazz);
    }

    private T toObject(String content, Class<?> clazz) {
        if (content != null && !content.isEmpty()) {
            try {
                return (T) mapper.readValue(content, clazz);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            return null;
        }
    }

    private String toJson(T object) {
        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

JsonArrayHandler

public class JsonArrayHandler<T> extends BaseTypeHandler<List<T>> {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    private Class<T> type;

    public JsonArrayHandler() {
        // 添加无参构造函数
    }

    public JsonArrayHandler(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
        //objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
        //        ObjectMapper.DefaultTyping.NON_FINAL);
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<T> parameter, JdbcType jdbcType)
            throws SQLException {
        try {
            // 将 List<PropertyCheck> 转换为 JSON 字符串
            ps.setString(i, objectMapper.writeValueAsString(parameter));
        } catch (JsonProcessingException e) {
            throw new SQLException("Failed to convert List<T> to JSON string", e);
        }
    }

    @Override
    public List<T> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return toList(rs.getString(columnName));
    }

    @Override
    public List<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return toList(rs.getString(columnIndex));
    }

    @Override
    public List<T> getNullableResult(java.sql.CallableStatement cs, int columnIndex) throws SQLException {
        return toList(cs.getString(columnIndex));
    }

    public List<T> toList(String json) throws SQLException {
        if (json == null || json.isEmpty()) {
            return null;
        }
        try {
            // 将 JSON 字符串转换为 List<PropertyCheck>
            return objectMapper.readValue(json,
                    objectMapper.getTypeFactory().constructCollectionType(List.class, type));
        } catch (IOException e) {
            throw new SQLException("Failed to convert JSON string to List<T>", e);
        }
    }
}

泛型支持:public class JsonArrayHandler extends BaseTypeHandler<List> 中的 表示这是一个泛型类,可以适用于不同类型的 JSON 数组字段。通过使用泛型,可以在运行时指定具体的类型,使得处理不同类型的 JSON 数组变得更加灵活和通用。

构造函数重载:public JsonArrayHandler() 是一个无参构造函数,而 public JsonArrayHandler(Class type) 是一个有参构造函数。通过提供两个构造函数,可以灵活地创建 JsonArrayHandler 的实例。无参构造函数用于在不知道具体类型时创建实例,而有参构造函数用于在已知类型时创建实例。

参数设置:setNonNullParameter 方法用于将 Java 对象转换为存储在数据库中的 JSON 字符串。将 List 对象转换为 JSON 字符串,并将其设置到 PreparedStatement 对象中。

结果获取:getNullableResult 方法用于从数据库中获取 JSON 字符串,并将其转换回 List 对象。将 JSON 字符串解析为 JSONArray,然后将其转换为 List 对象。

LIST存储构造器使用

public class TestUnitListTypeHandler extends JsonArrayHandler<TestUnit> {
    public TestUnitListTypeHandler() {
        super(TestUnit.class);
    }
}
<resultMap id="TestUnitResultMap" type="com.ruoyi.business.model.testcase.relation.TestUnit">
        <id column="test_unit_id" property="testUnitId"/>
        <result column="relation_id" property="relationId"/>
        <result column="property_name" property="propertyName"/>
        <result column="property" property="property"
                typeHandler="com.ruoyi.common.handler.JsonTypeHandler"/>
        <result column="property_type" property="propertyType"/>
        <result column="property_write_type" property="propertyWriteType"/>
        <result column="test_unit_desc" property="testUnitDesc"/>
        <result column="parent_property_name" property="parentPropertyName"/>
        <result column="test_unit_property_array" property="testUnitPropertyArray" typeHandler="com.ruoyi.common.handler.JsonTypeHandler"/>
        <result column="child_test_unit_list" property="childTestUnitList" typeHandler="com.ruoyi.business.handler.testcase.TestUnitListTypeHandler"/>
    </resultMap>


    <insert id="insertRelationUnitBatch" useGeneratedKeys="true" keyProperty="testUnitList.testUnitId">
        <if test="testUnitList != null and testUnitList.size() != 0">
            insert into test_instruct (relation_id, property_name, property, property_type, property_write_type,
                                       test_unit_desc,
                                       parent_property_name, test_unit_property_array, child_test_unit_list) values
            <foreach collection="testUnitList" item="testUnit" separator=",">
                (#{relationId}, #{testUnit.propertyName},
                 #{testUnit.property, typeHandler=com.ruoyi.common.handler.JsonTypeHandler},
                 #{testUnit.propertyType}, #{testUnit.propertyWriteType},
                 #{testUnit.testUnitDesc}, #{testUnit.parentPropertyName},
                 #{testUnit.testUnitPropertyArray, typeHandler=com.ruoyi.common.handler.JsonTypeHandler},
                 #{testUnit.childTestUnitList, typeHandler=com.ruoyi.business.handler.testcase.TestUnitListTypeHandler})
            </foreach>
        </if>
    </insert>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值