我使用以下3个方法解决动态数据数据问题。
一、有限数据由列转行(数据模板)
有限数据指属性的所有可能值,如上面那篇文章中说到的品牌、适用年龄、尺码等属性,这些属性的特点是数据有限,变动频繁,所以,我们不使用固定的数据表列,而是使用数据表行的无限延申来进行动态拓展,数据模板的实体类属性定义如下:
private List<String> preinstallValues=new ArrayList<>();//可能的值选项(单选、多选用到)
@Enumerated(EnumType.STRING)
private ControlClassifyEnum controlClassifyEnum;//控件类型,如前端需要显示成文本框、日历选择框、单选多选框。
@Enumerated(EnumType.STRING)
private DataClassifyEnum dataClassifyEnum;//可能的值Object("Object"),字符串型("java.lang.String"),布尔型("java.lang.Boolean"),日期型("java.util.Date"),整型("int"),长整型("long"),单精度浮点型("float"),双精度浮点型("double"),大字符串型("bigString"),自定义("custom");
@ManyToOne
private DictionaryEntity parentDictionary;//父模板、树形结构需要
@OneToMany(cascade = {CascadeType.REMOVE},mappedBy="parentDictionary")
private List<DictionaryEntity> childDictionarys=new ArrayList<>();//子模板、树形结构需要
@Id
@Column(length=50)
private String uuid;//主键
private String name;//模板名
private String flag;//模板标识
private String intro;//模板描述
private int priority;//排序
以上数据模板我使用jpa实现,其实和实现方式一点关系没有(所以,软件设计不必拘泥于具体实现方案,重点在于设计,好的设计扩展性好,差的设计拓展性差,和具体语言、具体实现方式无关)
采用如上方式可以将无限的数据可能已数据库行的形式呈现,页面添加数据模板(也可叫数据字典)如下图所示
二、无限数据json压缩
由上一步实现的数据模板是一个树形结构,一个项目有共同使用这一颗数据模板数,每个使用的实体类截取其中一个部分(这里涉及到动态的类型及数据绑定,请关注后续文章),截取的部分其实就是一个包含子模版的DictionaryEntity,其中包含了具体的模板值,可以将其json化后转换为一个json字符串,存储到指定的数据库表列字段中,即完成了数据模板的实例化过程。
三、检索字段数据同步
以上两个步骤解决了动态数据的设计和实例化,但存储具体数据的json字符串本压缩成一个字段存储在数据列中,虽然json可以被检索,但效率不高,那么如何处理需要检索的数据,这里使用java的反射来将json字段的值映射到具体的数据库表列字段中。
反射的主要代码如下:
大概的意思就是将resource的数据模板json字段取出,转化为SJONArray,使用递归的方式循环遍历数据模板,使用反射取出resource的类属性,与数据模板的模板flag进行对比,如果相等则对其赋值,大家也可以有自己的实现方式。
public void recursion(JSONArray arr,Resource resource)
{
for(Object obj : arr)
{
JSONObject json=(JSONObject)obj;
Object child=json.get("childDictionaryInstances");
if(child!=null)
{
recursion((JSONArray)child, resource);
}
System.out.println(obj.getClass().getSimpleName());
if(obj.getClass().getSimpleName().equals("JSONArray"))
{
recursion((JSONArray)obj,resource);
}
String dictionaryUuid=json.get("dictionaryUuid").toString();
System.out.println("dictionaryUuid:"+dictionaryUuid);
DictionaryEntity dictionary=em.find(DictionaryEntity.class, dictionaryUuid);
String flag=dictionary.getFlag();
Object dictionaryValue=json.get("dictionaryValue");
if(!dictionaryUuid.isEmpty()&&!flag.isEmpty()&&dictionaryValue!=null)
{
//Field[] fields=Class.forName(targetClassifyEnum.getIntro()).getDeclaredFields();
Field field=null;
try{
System.out.println("Class:"+resource.getClass().getName());
field=resource.getClass().getDeclaredField(flag);
}
catch(NoSuchFieldException e)
{
System.out.println("flag:"+flag+" 字段不存在,将在父类中查找");
try{
System.out.println("parent Class:"+resource.getClass().getSuperclass().getName());
field=resource.getClass().getSuperclass().getDeclaredField(flag);
}
catch(NoSuchFieldException ex)
{
System.out.println("flag:"+flag+" 字段不存在,将在父类中查找");
}
}
//Field[] fieldsx=field.getDeclaringClass().getDeclaredFields();
if(field!=null)
{
System.out.println("同步字段"+flag+" value:"+dictionaryValue.toString());
if(dictionary!=null)
{
DataClassifyEnum dataClassifyEnum=dictionary.getDataClassifyEnum();
if(dataClassifyEnum.equals(DataClassifyEnum.字符串型)||dataClassifyEnum.equals(DataClassifyEnum.大字符串型))
{
dictionaryValue = dictionaryValue.toString();
}
else if(dataClassifyEnum.equals(DataClassifyEnum.整型))
{
dictionaryValue = Integer.parseInt(dictionaryValue.toString());
}
else if(dataClassifyEnum.equals(DataClassifyEnum.日期型))
{
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dictionaryValue = sdf.parse(dictionaryValue.toString());
}
else if(dataClassifyEnum.equals(DataClassifyEnum.布尔型))
{
dictionaryValue = Boolean.parseBoolean(dictionaryValue.toString());
}
field.setAccessible(true);
field.set(resource,dictionaryValue);
this.update=true;
}
}
}
}
}
这样一来,对于需要检索的数据(其实不多),可以通过数据同步的方式将其同步到数据库列字段中。
总体概括一下,对于动态的数据类型,我们归纳为两类,一类是描述型、一类是描述检索型,对于描述性,定义好数据模板并实例化到具体实体类即可,对于描述检索型,额外需要做一次数据同步来解决。
对于实例化时如何截取指定的数据模板,设计动态类型及类型—数据模板绑定,这个在后续的文章进行说明。