创建灵活的可扩展的键值存储表

创建灵活的可扩展的键值存储表

上一期写的是关于POJO类与json串互转的三种通用方法 JSON转换工具类, 这一期依据建立的jackson工具类实现可扩展的键值存储表,具体从以下几部分入手
第一部分 : 讲解可扩展的键值存储表使用背景
第二部分 : 实际演示
第三部分 : 讲解可扩展的键值存储表存储表结构
第四部分 : 讲解可扩展的键值存储表具体代码实现

第一部分 可扩展的键值存储表使用背景

可扩展的键值存储表使用背景其实本质上就是为可变业务内容服务的 ,
假使现在我们是一个填写科技方面的问卷的被调查者,这次问卷的题目是20道,单选15道,多选5道 , 而在存储的时候,我们需要把题目和答案一起打包存储 . 而下次我们是一个人文类问卷 , 题目是25道,多选题与单选题数量也与之前的不同. 对于这种变化的内容业务,我们不可能用一个entity把数据定死,20条就定义20个字段Key , 25条就定义25个字段Key ,而且 科技类题目和人文类题 Key还不一样 , 显然这样做是非常不合理的,而用可扩展的键值存储表可以很好地解决这个问题(以上的解释可能不够形象 ,但是这种问卷的形式的不可定数据非常适合可扩展的键值存储)

第二部分 可扩展的键值存储表使用背景

1.1 存储数据 前端传值

请求地址:http://localhost:8080/saveConfigKey
请求参数:
{
	"objCode" : "reserveTime",
	"data" : {
		"reserveDay" : {
			"weekCanBook" : false,
			"201811" : [12, 10, 20]
		},
		"receptUpper" : 20,
		"reserveTime" : "8:00-12:00,14:00-18:00",
		"openReservPosition" : [1, 3, 5, 7]
	}
}

1.2 数据库存储结果

select * from configKey where objcode = ‘reserveTime’;
创建灵活的可扩展的键值存储表

2.1. 查询数据 前端传值

请求地址:http://localhost:8080/getConfigKey/reserveTime

返回的参数:
{
	"objCode" : "reserveTime",
	"data" : {
		"reserveDay" : {
			"weekCanBook" : false,
			"201811" : [12, 10, 20]
		},
		"receptUpper" : 20,
		"reserveTime" : "8:00-12:00,14:00-18:00",
		"openReservPosition" : [1, 3, 5, 7]
	}
}

第二部分 可扩展的键值存储表存储表结构

1. 表结够设计

/*创立键值表*/
CREATE TABLE configKey (  
ID NUMBER(10) ,  
configKey VARCHAR2(50) ,  
configValue VARCHAR2(50) ,
configGroup VARCHAR2(50) , 
status VARCHAR2(50) , 
createTime  DATE ,
lastUpdate DATE ,
objCode VARCHAR2(50)
)
comment on table configKey is '基础配置键值对表'
comment on column  configKey.ID  is '主键';
comment on column  configKey.configKey  is 'Key值';
comment on column  configKey.configValue  is 'value值';
comment on column  configKey.configGroup  is '存储keyValue值所属位置范围 预留字段';
comment on column  configKey.status  is '状态 有效 VALID  无效 INVALID';
COMMENT ON COLUMN  configKey.createTime  IS '创建时间';
COMMENT ON COLUMN  configKey.lastUpdate  IS '最后更新时间';
COMMENT ON COLUMN  configKey.objCode  IS '所属业务外键';
commit;

--自定义一个序列  
 create sequence configKey_sequence    
       increment by 1 --每次增加几个,我这里是每次增加1  
       start with 1   --从1开始计数  
       nomaxvalue      --不设置最大值  
       nocycle         --一直累加,不循环  
       nocache        --不建缓冲区

2. entity结构设计

package com.example.modules.configKey.entity;
import java.util.Date;
import java.util.Map;

public class ConfigKey {
    //主键
    private Long id;
    //存储Key值
    private String configKey;
    //存储value值
    private String configValue;
    //存储keyValue值所属位置范围 预留字段
    private String configGroup;
    //状态
    private String status;
    //创建时间
    private Date createTime;
    //最后更新时间
    private Date lastUpdate;
    //配置所属业务外键
    private String objCode;

    private Map<String , Object> data;
    public Map<String, Object> getData() {
        return data;
    }

    public void setData(Map<String, Object> data) {
        this.data = data;
    }
省略部分get set 方法
}

第三部分 可扩展键值存储表具体代码实现

1建立Dao层

package com.example.modules.configKey.dao;

import com.example.modules.configKey.entity.ConfigKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@Mapper
public interface ConfigKeyDao {

    List<ConfigKey> queryByObjCode (String objCode);

    void insertConfigKey ( @Param("configKey") List<ConfigKey> configKey);

    int updateConfigKey (ConfigKey configKey);
}

2. 映射XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.modules.configKey.dao.ConfigKeyDao">
    <select id="queryByObjCode" resultType="com.example.modules.configKey.entity.ConfigKey">
        SELECT *
        FROM configKey
        WHERE objCode = #{ objCode ,jdbcType=VARCHAR}
    </select>

    <insert id="insertConfigKey" parameterType="com.example.modules.configKey.entity.ConfigKey" >
        <if test="configKey != null and configKey.size() > 0">
            <foreach collection="configKey" item="item" index="index"  open="begin" close=" ; end ;" separator=";">
             INSERT INTO configKey
             (
             id,
             configKey,
             configValue,
             configGroup,
             status,
             createTime,
             objCode
             )
             VALUES
             (
             configKey_sequence.NEXTVAL,
             #{item.configKey,jdbcType=VARCHAR},
             #{item.configValue,jdbcType=VARCHAR},
             #{item.configGroup,jdbcType=VARCHAR},
             #{item.status,jdbcType=VARCHAR},
             sysdate,
             #{item.objCode,jdbcType=VARCHAR}
            )
         </foreach>
        </if>


    </insert>

    <update id="updateConfigKey" parameterType="com.example.modules.configKey.entity.ConfigKey">
        update configKey
        <set>
            <if test="configKey != null">configKey = #{configKey,jdbcType=VARCHAR},</if>
            <if test="configValue != null">configValue = #{configValue,jdbcType=VARCHAR},</if>
            <if test="configGroup != null">configGroup =  #{configGroup,jdbcType=VARCHAR},</if>
            <if test="status != null">status = #{status,jdbcType=VARCHAR},</if>
            lastUpdate =  sysdate
        </set>
        where objCode =  #{objCode,jdbcType=VARCHAR}
    </update>

</mapper>

3. 建立Service

该service中调用的JsonTools.obj2String( value );和JsonTools.string2Obj( value , Map.class )均为调用我们前一篇文章封装的jackson解析工具类

package com.example.modules.configKey.service;

import com.example.modules.configKey.dao.ConfigKeyDao;
import com.example.modules.configKey.entity.ConfigKey;
import core.utils.JsonTools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ConfigKeyService {

    @Autowired
    private ConfigKeyDao configKeyDao;

    public ConfigKey  insertConfigKey (ConfigKey configKey){
        String objCode = configKey.getObjCode();
        Map<String , Object> data = configKey.getData();
        List< ConfigKey > configKeyList = mapToJson( data , objCode );
        configKeyDao.insertConfigKey( configKeyList );
        return configKey;
    }

    public ConfigKey queryByObjCode (String objCode){
        List<ConfigKey> configKeyList = configKeyDao.queryByObjCode( objCode );
        if( null == configKeyList || configKeyList.isEmpty() ){
            return null;
        }
        return jsonToMap( configKeyList );
    }

    /**
     *
     * @param data
     * @return
     */
    /**
     * Map转换json方法
     * @param data 代转换的数据
     * @param objCode 数据所属业务标志位
     * @return
     */
    public List< ConfigKey >  mapToJson( Map<String , Object> data , String objCode){
        List< ConfigKey > result = new ArrayList< ConfigKey >();
        if( null == data){ return result;}
        Object value = "";
        for( String key : data.keySet() ){
            value = data.get( key );
            if( null == value ){
                value = "";
            }else if( value instanceof  Map){
                //调用创建的jackson解析map转json工具
                value = JsonTools.obj2String( value );
            }
            ConfigKey configKey = new ConfigKey();
            configKey.setObjCode( objCode );
            configKey.setConfigKey( key );
            configKey.setConfigValue( String.valueOf( value ) );
            configKey.setStatus( "VALID" );
            result.add( configKey );
          }
          return result;
    }


    /**
     * json转Map方法
     * @param configKeys 数据库查出的数据
     * @return
     */
    public ConfigKey jsonToMap ( List< ConfigKey > configKeys ){
        ConfigKey configKey = new ConfigKey();
        if( null == configKeys || configKeys.isEmpty() ){
            return  configKey ;
        }
        Map<String , Object > map = new HashMap<>();
        configKey.setObjCode( configKeys.get(0).getObjCode() );
        for( ConfigKey config : configKeys ){
            String key = config.getConfigKey();
            String value = config.getConfigValue();
            if( null == value || "".equals( value )){
                map.put( key , "");
            }else if ( value.contains( "{" ) ){
                //调用创建的jackson解析json转map工具
                map.put( key , JsonTools.string2Obj( value , Map.class ) );
            }else {
                map.put( key , value );
            }
        }
        configKey.setData( map );
        return  configKey ;
    }

}

3.请求控制层

package com.example.modules.configKey.controller;

import com.example.modules.configKey.entity.ConfigKey;
import com.example.modules.configKey.service.ConfigKeyService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
public class ConfigKeyController {

    @Resource
    private ConfigKeyService configKeyService;

    @RequestMapping(value = "/saveConfigKey",method = RequestMethod.POST)
    public ConfigKey saveConfigKey(@RequestBody ConfigKey configKey){
        configKeyService.insertConfigKey( configKey );
        return configKey;
    }

    @RequestMapping(value = "/getConfigKey/{objCode}",method = RequestMethod.GET)
    public ConfigKey getConfigKey( @PathVariable String objCode ){
        return configKeyService.queryByObjCode( objCode );
    }
}