数据字典项 设计实现方案

最近被安排做数据字典的设计,本人一头雾水啊,没弄过啊,网上搜索一下,这个老兄写的很好,原文链接:http://blog.youkuaiyun.com/stevene/article/details/575142?reload

在应用开发中,总会遇到许多数据字典项,比如对象状态、对象类型等等,这些项一般都是固定的若干可选值选项,比如对象状态可能有新建、修改、删除等状态,这些数据字典项一旦定义完毕改动的频率非常低;在应用开发中,为了处理方便,一般要对这些数据字典项值选项进行数字编码(例如: 0表示新建,1表示修改,2表示删除等),以方便应用程序中使用。而UI显示对象信息时不能显示对象状态等的编码,对于编码值设计人员知道代表什么意思,但用户就不明白了,所以需要进行编码转换,从编码转换为文字描述(名称),也就是需要把状态编码0转换为“新建”,把1转换为“修改”,把2转换为“删除”等显示给用户,用户才明白对象当前的状态是什么。


下面介绍一下常用的实现方法:


实现方案:


一、在java文件中定义数据字典项


我们习惯上把应用中遇到的数据字典项都定义到一个java文件中,这是最常用的方法,实现起来比较简单,但维护起来就非常繁琐,特别是数据字典项比较多的情况下,相应的java文件就会比较大,一旦数据字典项有更新那么维护起来就比较费时费力。

java文件中定义数据字典项通常情况下定义为static,举例来说,类ReportConstants中定义了以下数据字典项:

    public static final int CODE_USERINF_TECHELEVEL_GJ = 1;

 

 

    public static final String CODE_USERINF_TECHELEVEL_GJ_KEY = "高级";

 

 

    public static final int CODE_USERINF_TECHELEVEL_ZJ = 2;

 

 

    public static final String CODE_USERINF_TECHELEVEL_ZJ_KEY = "中级";

 

 

    public static final int CODE_USERINF_TECHELEVEL_CJ = 3;

 

 

    public static final String CODE_USERINF_TECHELEVEL_CJ_KEY = "初级";

 

 

    public static final int CODE_USERINF_TECHELEVEL_WJ = 4;

 

 

public static final String CODE_USERINF_TECHELEVEL_WJ_KEY = "无职称";


那么我们在实现中就可以直接引用相应的数据字典项编码及名称,另外,一般情况下需要定义数据字典项编码和名称的转换方法,比如:


    public static String getCodeName(int lCode)

       {

              //初始化返回值

              String strReturn = "未知";

        switch (lCode)

              {

                     case CODE_USERINF_TECHELEVEL_GJ :

                            strReturn = CODE_USERINF_TECHELEVEL_GJ_KEY;

                            break;

                     case CODE_USERINF_TECHELEVEL_ZJ :

                            strReturn = CODE_USERINF_TECHELEVEL_ZJ_KEY;

                            break;

                     case  CODE_USERINF_TECHELEVEL_CJ :

                            strReturn = CODE_USERINF_TECHELEVEL_CJ_KEY;

                            break;

                     case  CODE_USERINF_TECHELEVEL_WJ :

                            strReturn = CODE_USERINF_TECHELEVEL_WJ_KEY;

                            break;

              }

              return strReturn;

       }


这个方法实现了通过数据字典项编码获得数据字典项名称的功能。那么还需要实现一个对应的方法,getCodeByName(String name),即通过数据字典项名称获取数据字典项编码功能(代码这里省略,请读者自己完成)。这样就可以实现数据字典项编码和名称的相互转换。

但是一旦出现数据字典项名称或编码需要更改(“无职称”项编码需要由“4”改为“0),或增加减少数据字典项,都需要更新java文件代码。是否有简便的方法在满足上述需求的情况下又不更新java文件代码?答案是肯定的。下面我们来介绍两种实现方法:一中使用xml文件,一种在数据库定义。


二、在xml文件中定义


第一种方案是应用xml配置文件来定义数据字典项。使用xml配置文件,以便最大限度的减小维护的工作量,避免java代码的频繁修改。

下面我们分步骤详细介绍一下使用xml配置文件的实现方案

 

 

第一步:定义xml数据字典项配置文件


首先新建一个xml文件,命名为DataDictionaryConfig.xml(名字可以自己定义),把应用的用到的数据字典项分组定义到xml文件中,举例如下,我们定义了下列数据字典项:

<?xml version="1.0" encoding="GB2312"?>

<data-dictionaries>

    <data-dictionary>

      <group value = "0" name="ObjectStatus">

         <option value="0" name="detached"/>

         <option value="1" name="new"/>

         <option value="2" name="updated"/>

         <option value="3" name="deleted"/>

      </group>

      <group value = "1" name="ObjectTypes">

         <option value="0" name="对象类型选项0"/>

         <option value="1" name="对象类型选项1"/>

         <option value="2" name="对象类型选项2"/>

         <option value="3" name="对象类型选项3"/>

<option value="4" name="对象类型选项4"/>

      </group>

    </data-dictionary>

</data-dictionaries>

这个xml文件可以根据需要进行扩展,满足更复杂应用的需要。


第二步,定义数据字典项对象类和数据字典项分组对象类:


    对于数据字典项这里我们定义了一个数据字典项对象类,一组数据字典选项集我们定义了一个数据字典项分组对象类,如下:

1)、数据字典项类:

public class DataDictionaryItem

{

  public DataDictionaryItem()

  {

  }

 

 

  private String code;

  private String name;

 

 

  public void setCode(String code)

  {

    this.code = code;

  }

 

 

  public String getCode()

  {

    return this.code;

  }

 

 

  public void setName(String name)

  {

    this.name = name;

  }

 

 

  public String getName()

  {

    return this.name;

  }

 

 

}

 

 

2)、数据字典项分组类

public class DataDictionaryItems

{

  public DataDictionaryItems()

  {

  }

  //数据字典项分组编码

  private String groupCode;

  //数据字典项分组名称

  private String groupName;

  //数据字典项详细

  private java.util.ArrayList items;

 

 

  public void setGroupCode(String code)

  {

    this.groupCode = code;

  }

  public String getGroupCoude()

  {

    return this.groupCode;

  }

 

  public void setGroupName(String name)

  {

    this.groupName = name;

  }

  public String getGroupName()

  {

    return this.groupName;

  }

 

  //设置数据字典项

  public void setDataDictionaryItem(DataDictionaryItem item)

  {

    if(this.items == null)

      this.items = new java.util.ArrayList();

    this.items.add(item);

  }

 

  //设置数据字典项

  public void setDataDictionaryItem(String itemName, String itemCode)

  {

    if(this.items == null)

      this.items = new java.util.ArrayList();

    DataDictionaryItem item = new DataDictionaryItem();

    item.setCode(itemCode);

    item.setName(itemName);

    this.items.add(item);

  }

 

  //获得数据字典项组对象

  public java.util.ArrayList getDataDictioanryItems()

  {

    return this.items;

  }

 

 

第三步,定义Xml数据字典项配置文件解析类,这里我们使用Dom4J,相应的jar可以在http://www.dom4j.org/上找到


import org.dom4j.*;

import org.dom4j.io.*;

import java.util.*;

 

 

public class XMLDDItemParser {

 

 

  //数据字典项结构

  public static DataDictionaryItems dataItems ;

  private static String GROUP_NAME = "name";

  private static String GROUP_CODE = "value";

  private static String ITEM_NAME = "name";

  private static String ITEM_CODE = "value";

 

 

  public XMLDDItemParser() {

  }

 

    /**

   * 获得分组数据字典项集

   * @param groupName String

   * @return DataDictionaryItems

   */

  public static DataDictionaryItems getDataDictionaryItems(String groupName)

  {

    if(dataItems == null)

      dataItems = parseXML(groupName);

    return dataItems;

  }

 

  /**

   * 根据分组名称解析xml文件,获得该分组下数据字典项集

   * @param gName String

   * @return DataDictionaryItems 数据字典项分组对象

   */

  public static DataDictionaryItems parseXML(String gName)

  {

    try

    {

      org.dom4j.io.SAXReader saxReader = new org.dom4j.io.SAXReader();

      Document document = saxReader.read("DataDictionaryConfig.xml");

 

 

      dataItems = new DataDictionaryItems();

 

 

      List list = document.selectNodes("//group");

      Iterator iter = list.iterator();

      while (iter.hasNext())

      {

        Node node = (Node) iter.next();

        if (node instanceof Element)

        {

          //document

          Element element = (Element) node;

          String GroupName = element.attributeValue(GROUP_NAME);

          String GroupValue = element.attributeValue(GROUP_CODE);

          //设置分组名称编码

          dataItems.setGroupName(GroupName);

          dataItems.setGroupCode(GroupValue);

          //取组内数据字典项

          if (gName.equals(GroupName))

          {

            //取数据字典项名称编码

            Iterator elemIter = element.elementIterator();

            while (elemIter.hasNext())

            {

              Element elem = (Element) elemIter.next();

              dataItems.setDataDictionaryItem(elem.attributeValue(ITEM_NAME), elem.attributeValue(ITEM_CODE));

            }

          }

        }

      }

    }

    catch (Exception ex) {

      ex.printStackTrace();

    }

    return dataItems;

  }


第四步,提供数据字典项编码转换方法类:


public class DataDictionaryUtils {

  public DataDictionaryUtils() {

  }

 

 

  /**

   * 根据数据项名称转换为数据项编码

   * @param groupName String

   * @param itemName String

   * @return String  数据项编码

   */

  public static String getItemCode(String groupName, String itemName)

  {

    String code = "-1";

    DataDictionaryItems dataItems = XMLDDItemParser.getDataDictionaryItems(groupName);

   

    java.util.ArrayList items = dataItems.getDataDictioanryItems();

    if(items != null)

    {

      DataDictionaryItem item;

      for(int i = 0; i < items.size(); i++)

      {

        item = (DataDictionaryItem) items.get(i);

        if(item != null)

        {

          String name = item.getName();

          if(name.equals(itemName))

          {

            code = item.getCode();

            break;

          }

        }

      }

    }

    return code;

  }

  /**

   * 根据数据项编码转换为数据项名称

   * @param groupName String

   * @param itemCode String

   * @return String

   */ 

  public static String getItemName(String groupName, String itemCode)

  {

    String name = "未知";

    DataDictionaryItems dataItems = XMLDDItemParser.getDataDictionaryItems(groupName);

   

    java.util.ArrayList items = dataItems.getDataDictioanryItems();

    if (items != null)

    {

      DataDictionaryItem item;

      for (int i = 0; i < items.size(); i++)

      {

        item = (DataDictionaryItem) items.get(i);

        if (item != null)

        {

          String code = item.getCode();

          if (code.equals(itemCode))

          {

            name = item.getName();

            break;

          }

        }

      }

    }

    return name;

  }

 

 

至此,我们已经完成了该方案的设计。使用xml文件,增加删除数据字典项等只需要更新xml文件即可,不涉及java文件的更新。

Xml可以根据应用的具体需要进行扩展设计。这里仅仅抛砖引玉,提供一种思路。


三、使用数据库表


上一种方法我们使用xml文件定义数据字典项,现在我们把数据字典项定义在数据库表中,下面我们来详细介绍实现方式:


第一步:定义数据字典项数据表结构


根据前面xml文件定义,这里我们定义两张表,一张是数据字典分组信息表,一张是数据字典项详细信息表。如下:

drop table datadic_groups;

create table datadic_groups(

    group_code varchar2(20) primary key,

    group_name varchar2(50) 

);

 

drop table datadic_items;

create table datadic_items(

    dataitem_code varchar2(20) primary key,

    dataitem_name varchar2(50),

    group_code varchar2(20)

);

 

alter table datadic_items 

add constraint dataitem_foreignkey foreign key (group_code) 

references datadic_groups(group_code);

 

 

 

 

这两张表可以根据应用的具体需求进行扩充,这里不再赘述。


第二步:根据定义的数据字典表结构定义数据字典实体类。

(请参照二、在xml文件中定义的第二步)


第三步:实现数据库表中数据字典项的查询功能


  /**

   * 实现从数据库查询数据字典项

   * @param gName String

   * @return DataDictionaryItems

   */

  public static DataDictionaryItems getFromDB(String gName)

  {

     dataItems = new DataDictionaryItems();

    

     try

     { 

        //获取数据连接

        java.sql.Connection conn = getConnection();

        if(conn != null)

        {

           //查询数据库,根据组名称查询组编号,根据组编号获取该组内数据字典项信息

           String strSql = "select items.dataitem_code, items.dataitem_name, items.group_code, dgroups.group_name from datadic_items items, datadic_groups dgroups where items.group_code = dgroups.group_code and dgroups.group_name='"+gName+"'";

           java.sql.Statement stmt = conn.createStatement();

           java.sql.ResultSet rs = stmt.executeQuery(strSql);

           while(rs.next())

           {

               String dataitem_code = rs.getString(1);

               String dataitem_name = rs.getString(2);

               dataItems.setDataDictionaryItem(dataitem_name, dataitem_code);

 

 

               String group_code = rs.getString(3);

               String group_name = rs.getString(4);

               dataItems.setGroupCode(group_code);

               dataItems.setGroupName(group_name);

           }

        }

     }

     catch(Exception ex)

     {

       ex.printStackTrace();

     }

     return dataItems;

  }

 

 

第四步:提供数据字典项编码转换方法类:

    (请参照二、在xml文件中定义的第四步)

 

 

四、进一步完善

1、两种方式都可以提供数据字典项维护界面,直接在维护界面上操作数据字典项,避免由于误操作导致xml文件或数据库数据错误。具体的实现也是比较简单,不再详细说明。

2、使用数据库表方式时,如果想减少频繁查询数据库,可以将数据字典项信息在系统启动后第一次访问时加载内存中,如果数据字典项数据量比较大,可实现一自维护线程,采用最近最少使用算法,将频繁使用的数据字典项驻留内存,将长期不用的数据字典项从内存中删除,每次自动检查内存中的数据字典项,如果存在则从内存中读取,如果不存在则查询数据库,替换内存中最少使用的数据字典项。

3、增加运行日志记录,可以使用log4J来记录运行日志

<think>我们正在讨论后端数据字典表的设计方法,目标是寻找最佳实践。根据引用内容,我们可以总结出几个关键点: 1. 引用[1]提到数据字典系统中应用后,能让数据更加严谨。说明数据字典的重要性在于维护数据的规范性和一致性。 2. 引用[2]讨论了数据字典表的设计,建议将多个字典表合并,避免每个业务模块都设计单独的字典表。这样可以减少表的数量,便于管理。 3. 引用[3]指出将数据字典都定义在一个Java文件中虽然简单,但维护困难,特别是当字典很多时。这暗示了我们需要一个更灵活、易于维护的方案。 4. 引用[4]提出了枚举字典的两种方案:前端写死字典表或由后端提供字典接口。这涉及到前后端交互中字典数据的使用问题。 结合这些引用,我们可以设计一个后端数据字典表的最佳实践方案: ### 一、数据字典设计 采用“大字典表”设计,将系统中所有类型的字典存储在一张表中,通过类型字段区分不同字典。表结构如下: ```sql CREATE TABLE sys_dict ( id BIGINT PRIMARY KEY AUTO COMMENT '主键', dict_type VARCHAR(50) NOT NULL COMMENT '字典类型(如:gender, user_status)', dict_code VARCHAR(50) NOT NULL COMMENT '字典编码(如:1, 2, "MALE")', dict_value VARCHAR(100) NOT NULL COMMENT '字典值(如:男, 女, 正常)', sort INT DEFAULT 0 COMMENT '排序', remark VARCHAR(200) COMMENT '备注', UNIQUE KEY (dict_type, dict_code) ) COMMENT='系统字典表'; ``` **字段说明**: - `dict_type`:字典类型,用于区分不同业务场景(如性别、状态等) - `dict_code`:字典编码,后端存储和传输的值(通常为数字或短字符串) - `dict_value`:字典值,展示给用户看的值 **示例数据**: | dict_type | dict_code | dict_value | sort | |-----------|-----------|------------|------| | gender | 1 | 男 | 1 | | gender | 2 | 女 | 2 | | user_status| 0 | 禁用 | 1 | | user_status| 1 | 启用 | 2 | ### 二、后端服务设计 1. **字典缓存**:系统启动时加载全量字典到缓存(如Redis),避免频繁查询数据库 2. **接口设计**: - 获取所有字典类型:`GET /dict/types` - 按类型获取字典:`GET /dict/items?type=gender` - 批量获取字典:`POST /dict/batch-items`(传递多个类型) ### 三、前后端交互方案 采用引用[4]的**方案二**:后端只传递字典编码,前端通过字典接口获取完整字典数据 ```json // 用户接口返回 { "id": 1, "name": "张三", "gender": 1 // 只返回字典编码 } // 前端单独调用字典接口缓存数据 // 初始化时调用 GET /dict/items?type=gender [ {"code": "1", "value": "男"}, {"code": "2", "value": "女"} ] ``` ### 四、性能优化 1. **缓存策略**:使用Redis Hash存储字典,Key为`sys_dict:${dict_type}`,结构: ``` HSET sys_dict:gender 1 "男" 2 "女" ``` 2. **更新机制**:字典变更时同步更新缓存(通过消息队列或数据库触发器) ### 五、枚举映射(Java示例) 创建枚举类与字典类型关联: ```java public enum GenderEnum { MALE(1, "男"), FEMALE(2, "女"); private final int code; private final String value; // 构造方法、getter public static String getValueByCode(int code) { // 从缓存获取,避免硬编码 return DictCache.get("gender", code); } } ``` ### 六、最佳实践总结 | 设计要点 | 实现方案 | 优势说明 | |------------------|-------------------------------------|----------------------------------| | **表结构** | 单表存储所有字典类型 | 减少表数量,统一管理[^2] | | **前后端交互** | 后端只返回code,前端维护字典映射 | 降低网络传输量[^4] | | **缓存机制** | Redis Hash按类型存储 | 快速查询,减轻数据库压力 | | **枚举支持** | 动态关联字典数据 | 避免硬编码,便于维护[^3] | | **变更通知** | 数据库触发器+消息队列 | 保证缓存一致性 | ### 注意事 1. 字典类型命名采用`snake_case`风格(如`user_status`) 2. 禁止删除字典(只做禁用标记),避免历史数据关联失效 3. 国际化场景增加`language`字段支持多语言 --- §§相关问题§§ 1. 如何设计支持多语言的数据字典表? 2. 数据字典变更后如何实时通知前端更新? 3. 在微服务架构中如何共享数据字典? 4. 数据字典与枚举类如何实现自动映射?</think>### 后端数据字典设计最佳实践 根据引用内容和行业实践,后端数据字典表的设计核心在于**平衡灵活性与规范性**,以下是经过验证的最佳实践方案: --- #### 一、基础表结构设计(推荐方案) ```sql CREATE TABLE sys_dict ( id BIGINT PRIMARY KEY AUTO COMMENT '主键', dict_type VARCHAR(50) NOT NULL COMMENT '字典类型(如:gender, order_status)', dict_code VARCHAR(50) NOT NULL COMMENT '字典编码(如:1, "A")', dict_value VARCHAR(100) NOT NULL COMMENT '字典值(如:男, 已支付)', sort INT DEFAULT 0 COMMENT '排序', is_enable TINYINT DEFAULT 1 COMMENT '是否启用', remark VARCHAR(200) COMMENT '备注', UNIQUE KEY (dict_type, dict_code) ) COMMENT='系统字典表'; ``` **设计要点**: 1. **单表存储所有字典** - 合并类型相近的字典(如用户状态、订单状态)避免多表管理[^2] - 通过`dict_type`字段区分业务场景 2. **编码规范** ```java // 枚举类示例(Java) public enum DictType { GENDER("gender"), USER_STATUS("user_status"), ORDER_STATUS("order_status"); } ``` 3. **索引优化** - 唯一索引:`(dict_type, dict_code)` 防止重复编码 - 覆盖索引:`dict_type` 提高查询效率 --- #### 二、前后端交互方案 **方案对比**: | 方案 | 实现方式 | 适用场景 | 缺点 | |---------------------|----------------------------------|-------------------------|--------------------------| | **前端维护字典** | 前端写死字典映射关系 | 小型目/静态字典 | 修改需发版[^4] | | **后端接口返回** | 后端提供`/api/dict/{type}`接口 | 中大型目/动态字典 | 增加网络请求 | | **混合方案** | 基础字典前端维护+业务字典接口获取 | 推荐方案[^4] | 实现略复杂 | **接口示例**: ```json // GET /api/dict/gender [ {"code": "1", "value": "男"}, {"code": "2", "value": "女"}, {"code": "0", "value": "未知"} ] ``` --- #### 三、性能优化策略 1. **缓存机制** ```java // Spring Boot伪代码 @Cacheable(value = "dictCache", key = "#dictType") public List<DictItem> getDictItems(String dictType) { return dictMapper.selectByType(dictType); } ``` - 使用Redis缓存字典数据,降低DB压力 - 缓存更新:通过`@CacheEvict`监听字典变更 2. **懒加载优化** - 首次访问时加载全量字典到内存 - 定时任务刷新变更字典 --- #### 四、枚举集成方案 ```java // 枚举与字典集成(Java示例) public enum GenderEnum { MALE("1", "男"), FEMALE("2", "女"); private final String code; private final String value; // 从字典表动态初始化 static { DictService.loadEnum(GenderEnum.class); } } ``` **优势**: - 编译时检查编码正确性 - 业务代码直接使用枚举值 - 保持与字典表同步更新[^3] --- #### 五、高级特性实现 1. **多语言支持** ```sql ALTER TABLE sys_dict ADD language VARCHAR(10) DEFAULT 'zh-CN'; ``` - 通过`language`字段实现国际化 2. **层级字典设计** ```sql ALTER TABLE sys_dict ADD parent_code VARCHAR(50) DEFAULT '0'; ``` - 实现树形结构(如地区字典) 3. **变更审计** ```sql ALTER TABLE sys_dict ADD create_time DATETIME DEFAULT CURRENT_TIMESTAMP, ADD update_time DATETIME ON UPDATE CURRENT_TIMESTAMP; ``` --- #### 六、最佳实践总结 | 设计原则 | 实现方案 | 收益说明 | |-------------------|----------------------------------|--------------------------| | **统一存储** | 单表存储所有字典类型 | 降低维护成本[^2] | | **编码规范** | 使用枚举约束业务代码 | 避免魔法值[^3] | | **缓存优化** | Redis+本地内存二级缓存 | 提升访问性能 | | **前后端解耦** | 提供标准化字典接口 | 独立更新字典[^4] | | **可扩展性** | 预留多语言/层级字段 | 适应业务发展 | > **关键提示**:禁止在业务表中直接存储字典文本值(如`varchar '男'`),应始终存储字典编码(如`tinyint 1`),通过关联字典表获取显示值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值