问题描述
如何将json对象数据保存到数据库对应的表中。
分析问题
json数据内容不固定,json数据格式是固定的(name:value),数据库表结构是固定的,在json数据格式 与 数据库表结构之间建立一套对应规则,写一套逻辑解析这套对应规则。
1、json对象对应的数据库表需要确定。
2、json对象的节点对应数据库表的列需要确定。
3、json对象的value类型需要确定下来,才能对value值做相应的操作。
4、数据库表的主键列比较特殊,一般是自增长列,需要确定。
5、数据库记录一般是假删除,有一个删除状态列,需要确定。
6、json对象的value的类型是json时,此列为外键,确定主表,获取主表主键。
7、json对象的value的类型是array是,json此节点的数据为从表的数据,确定从表,对从表操作。
解决问题实例
一、自定义特性
定义了两个特性,EntityAttribute特性用于实体,PropertyAttribute特性用于属性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Standard.Frame.BEntity
{
/// <summary>
/// 实体特性
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class EntityAttribute : Attribute
{
public EntityAttribute(String entityName, String tableName)
{
EntityName = entityName;
TableName = tableName;
}
/// <summary>
/// 实体名称
/// </summary>
public string EntityName { get; set; }
/// <summary>
/// 表名称
/// </summary>
public string TableName { get; set; }
}
/// <summary>
/// 属性特性
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class PropertyAttribute : Attribute
{
public PropertyAttribute() { }
/// <summary>
/// json节点名称
/// </summary>
public string FieldName { get; set; }
/// <summary>
/// 列名称
/// </summary>
public string ColumnName { get; set; }
/// <summary>
/// 节点类型
/// </summary>
public string NodeType { get; set; }
/// <summary>
/// 是否是主键
/// </summary>
public string IsPK { get; set; }
/// <summary>
/// 是否删除
/// </summary>
public string IsDeletedFlag { get; set; }
/// <summary>
/// 主实体
/// </summary>
public string MainEntity { get; set; }
}
}
二、在实体、属性上添加特性
这里特性的作用:
1、建立json对象与数据库表的对应关系。
建立json节点的name与数据库表列的对应关系。
3、指定json节点value的类型,类型主要有三类:字符串、json对象、数组。
4、标示出主键列。
5、标示出删除标示列。
6、标示出主实体列,类似于关系表的外键关系。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Standard.Frame.Config;
namespace Standard.Frame.BEntity
{
/// <summary>
/// 项目信息
/// </summary>
[Entity("PlanProject", "XMGL_20")]
public class PlanProject:BaseBEntity
{
/// <summary>
/// 项目ID
/// </summary>
[Property(FieldName = "planProjectID", ColumnName = "XMGL_20_COL_10", NodeType = NodeType.String, IsPK = BooleanType.True)]
public string PlanProjectID { get; set; }
/// <summary>
/// 项目名称
/// </summary>
[Property(FieldName = "planProjectName", ColumnName = "XMGL_20_COL_70", NodeType = NodeType.String)]
public string PlanProjectName { get; set; }
/// <summary>
/// 是否已删除
/// </summary>
[Property(FieldName = NodeOperation.Operation, ColumnName = "XMGL_20_COL_200", NodeType = NodeType.String, IsDeletedFlag = BooleanType.True)]
public string IsDeleted { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[Property(FieldName = "createTime", ColumnName = "XMGL_20_COL_210", NodeType = NodeType.String)]
public string CreateTime { get; set; }
/// <summary>
/// 创建人
/// </summary>
[Property(FieldName = "creatorID", ColumnName = "XMGL_20_COL_220", NodeType = NodeType.Json, MainEntity = "User")]
public string CreatorID { get; set; }
/// <summary>
/// 项目组成员
/// </summary>
[Property(FieldName = "projectMember", NodeType = NodeType.Array, MainEntity = "ProjectMember")]
public string ProjectMember { get; set; }
}
/// <summary>
/// 项目组成员信息
/// </summary>
[Entity("ProjectMember", "XMGL_220")]
public class ProjectMember : BaseBEntity
{
/// <summary>
/// 人员ID
/// </summary>
[Property(FieldName = "projectMemberID", ColumnName = "XMGL_220_COL_10", NodeType = NodeType.String, IsPK = BooleanType.True)]
public string ProjectMemberID { get; set; }
/// <summary>
/// 姓名
/// </summary>
[Property(FieldName = "projectMemberName", ColumnName = "XMGL_220_COL_20", NodeType = NodeType.String)]
public string ProjectMemberName { get; set; }
/// <summary>
/// 性别
/// </summary>
[Property(FieldName = "sex", ColumnName = "XMGL_220_COL_30", NodeType = NodeType.String)]
public string Sex { get; set; }
/// <summary>
/// 备注
/// </summary>
[Property(FieldName = "notes", ColumnName = "XMGL_220_COL_110", NodeType = NodeType.String)]
public string Notes { get; set; }
/// <summary>
/// 是否已删除
/// </summary>
[Property(FieldName = NodeOperation.Operation, ColumnName = "XMGL_220_COL_200", NodeType = NodeType.String, IsDeletedFlag = BooleanType.True)]
public string IsDeleted { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[Property(FieldName = "createTime", ColumnName = "XMGL_220_COL_210", NodeType = NodeType.String)]
public string CreateTime { get; set; }
/// <summary>
/// 创建人
/// </summary>
[Property(FieldName = "creatorID", ColumnName = "XMGL_220_COL_220", NodeType = NodeType.Json, MainEntity = "User")]
public string CreatorID { get; set; }
/// <summary>
/// 项目
/// </summary>
[Property(FieldName = "planProject", ColumnName = "XMGL_20_COL_10", NodeType = NodeType.Json, MainEntity = "PlanProject")]
public string PlanProject { get; set; }
}
}
三、反射机制,分析实体特性、属性特性,将json对象数据保存到数据库。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Standard.Frame.BEntity;
using Standard.Frame.Utility;
using Standard.Frame.Config;
using Standard.Frame.DBAccess;
using System.Reflection;
using Standard.Frame.Utility;
namespace Standard.Frame.BLRuler
{
public class JsonToTable
{
public static void Save(object inputJsonObj)
{
try
{
SaveJsonObj(inputJsonObj, "PlanProject", null);
}
catch (Exception e)
{
LogUtility.Write(DateTime.Now.ToString() + " : " + e.Message);
}
}
/// <summary>
/// 保存输入的json对象
/// </summary>
/// <param name="inputJsonObj"></param>
/// <param name="entityName"></param>
private static void SaveJsonObj(object inputJsonObj, string entityName, Tuple<string, object> tupleFk)
{
Dictionary<string, object> dicReq = CommonFacade.ParseJsonObjToDic(inputJsonObj);
Type type = CommonFacade.GetEntityType(entityName);
string tableName = CommonFacade.GetTableNameByType(type);
System.Reflection.PropertyInfo[] properties = type.GetProperties();
//先保存主表数据(string类型的节点)
object recordID = SaveStringNode(dicReq, tableName, properties, tupleFk);
//保存从表数据(array类型的节点)
SaveArrayNode(dicReq, properties, entityName, recordID);
}
/// <summary>
/// 保存json对象中的字符串节点
/// </summary>
/// <param name="dicReq"></param>
/// <param name="tableName"></param>
/// <param name="properties"></param>
/// <param name="tupleFk"></param>
/// <returns></returns>
private static object SaveStringNode(Dictionary<string, object> dicReq, string tableName, System.Reflection.PropertyInfo[] properties,Tuple<string, object> tupleFk)
{
PreSqlAccess sqlAccess = new PreSqlAccess();
Dictionary<string, object> dicTable = new Dictionary<string, object>();
Tuple<string, object> tuplePk = ParseStringNode(dicReq, properties, dicTable);
if (tupleFk != null)
{
dicTable.Remove(tupleFk.Item1);
dicTable.Add(tupleFk.Item1, tupleFk.Item2);
}
object recordID = sqlAccess.InsertOrUpdateDataSync(tableName, dicTable, tuplePk);//保存到数据库
return recordID;
}
/// <summary>
/// 保存从表数据(数据类型为array的节点)
/// </summary>
/// <param name="dicReq"></param>
/// <param name="properties"></param>
/// <param name="recordID"></param>
private static void SaveArrayNode(Dictionary<string, object> dicReq, System.Reflection.PropertyInfo[] properties, string mainEntityName, object recordID)
{
System.Attribute[] attrs = null;
foreach (System.Reflection.PropertyInfo p in properties)
{
attrs = System.Attribute.GetCustomAttributes(p);
foreach (System.Attribute attr in attrs)
{
if (attr is PropertyAttribute)
{
PropertyAttribute propertyAttr = (PropertyAttribute)attr;
if (dicReq.ContainsKey(propertyAttr.FieldName) && propertyAttr.NodeType == NodeType.Array)
{
Tuple<string, object> tupleFk = GetTupleFKID(propertyAttr.MainEntity, mainEntityName, recordID);
List<object> listObj = CommonFacade.ParseJsonObjToList(dicReq[propertyAttr.FieldName]);
listObj.ForEach((inputJsonObj) =>
{
SaveJsonObj(inputJsonObj, propertyAttr.MainEntity, tupleFk);
});
}
}
}
}
}
/// <summary>/// 解析输入json对象中的字符串节点
/// </summary>
/// <param name="dicReq"></param>
/// <param name="properties"></param>
/// <returns></returns>
private static Tuple<string, object> ParseStringNode(Dictionary<string, object> dicReq, System.Reflection.PropertyInfo[] properties, Dictionary<string, object> dicTable)
{
Tuple<string, object> tuplePk = null;
System.Attribute[] attrs = null;
foreach (System.Reflection.PropertyInfo p in properties)
{
attrs = System.Attribute.GetCustomAttributes(p);
foreach (System.Attribute attr in attrs)
{
if (attr is PropertyAttribute)
{
PropertyAttribute propertyAttr = (PropertyAttribute)attr;
if (dicReq.ContainsKey(propertyAttr.FieldName))
{
if (propertyAttr.IsPK == BooleanType.True)
{//记录主键,备用
tuplePk = new Tuple<string, object>(propertyAttr.ColumnName, dicReq[propertyAttr.FieldName]);
break;
}
if (propertyAttr.IsDeletedFlag == BooleanType.True)
{//"是否删除" 这个标示列需要特殊处理
if (dicReq.ContainsKey(NodeOperation.Operation) && Convert.ToString(dicReq[propertyAttr.FieldName]) == NodeOperationType.Deleted)
dicTable.Add(propertyAttr.ColumnName, 1);
break;
}
if (propertyAttr.NodeType == NodeType.String)
dicTable.Add(propertyAttr.ColumnName, dicReq[propertyAttr.FieldName]);
if (propertyAttr.NodeType == NodeType.Json)
dicTable.Add(propertyAttr.ColumnName,CommonFacade.GetPKID(dicReq[propertyAttr.FieldName], propertyAttr.MainEntity));
}
}
}
}
return tuplePk;
}
/// <summary>/// 将主表ID匹配到从表对应的外键
/// </summary>
/// <param name="inputJsonObj"></param>
/// <param name="entityName"></param>
/// <param name="mainEntityName"></param>
/// <param name="recordID"></param>
/// <returns></returns>
private static Tuple<string, object> GetTupleFKID(string entityName, string mainEntityName, object recordID)
{
Tuple<string, object> tupleFK = null;
Type type = CommonFacade.GetEntityType(entityName);
System.Reflection.PropertyInfo[] properties = type.GetProperties();
System.Attribute[] attrs = null;
foreach (System.Reflection.PropertyInfo p in properties)
{
attrs = System.Attribute.GetCustomAttributes(p);
foreach (System.Attribute attr in attrs)
{
if (attr is PropertyAttribute)
{
PropertyAttribute propertyAttr = (PropertyAttribute)attr;
if (propertyAttr.NodeType == NodeType.Json && propertyAttr.MainEntity == mainEntityName)
{
tupleFK = new Tuple<string, object>(propertyAttr.ColumnName,recordID);
goto outer;
}
}
}
}
outer:
return tupleFK;
}
}
}
四、操作数据库
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using Standard.Frame.Config;
namespace Standard.Frame.DBAccess
{
public class PreSqlAccess
{
/// <summary>/// 同步插入记录
/// </summary>
/// <param name="tablename"></param>
/// <param name="dicTable"></param>
/// <returns></returns>
private object InsertRecordSync(string tablename, Dictionary<string, object> dicTable)
{
StringBuilder columns = new StringBuilder();
StringBuilder datas = new StringBuilder();
List<IDataParameter> parameters = new List<IDataParameter>();
foreach (var item in dicTable)
{
columns.Append(item.Key).Append(",");
datas.Append("@" + item.Key).Append(",");
parameters.Add(new SqlParameter("@" + item.Key, item.Value));
}
StringBuilder sqlCommand = new StringBuilder();
sqlCommand.Append(" insert into ");
sqlCommand.Append(tablename).Append(" ( ").Append(columns.ToString().Trim(new char[] { ',' })).Append(" ) ");
sqlCommand.Append(" values ( ").Append(datas.ToString().Trim(new char[] { ',' })).Append(" ) ");
SqlAccess sqlAccess = new SqlAccess();
return sqlAccess.InsertRecordSync(tablename, sqlCommand.ToString(), CommandType.Text, parameters.ToArray(), null);
}
/// <summary>/// 同步更新记录
/// </summary>
/// <param name="tablename"></param>
/// <param name="dicTable"></param>
/// <param name="tuplePk"></param>
/// <returns></returns>
private object UpdateRecordSync(string tablename, Dictionary<string, object> dicTable, Tuple<string, object> tuplePk)
{
StringBuilder columnValues = new StringBuilder();
List<IDataParameter> parameters = new List<IDataParameter>();
foreach (var item in dicTable)
{
columnValues.Append(item.Key).Append("=").Append("@" + item.Key).Append(",");
parameters.Add(new SqlParameter("@" + item.Key, item.Value));
}
StringBuilder sqlCommand = new StringBuilder();
sqlCommand.Append(" update ").Append(tablename);
sqlCommand.Append(" set ");
sqlCommand.Append(columnValues.ToString().Trim(new char[] { ',' }));
sqlCommand.Append(" where ").Append(tuplePk.Item1).Append("=").Append(tuplePk.Item2);
SqlAccess sqlAccess = new SqlAccess();
return sqlAccess.ExecuteNonQuerySync(sqlCommand.ToString(), CommandType.Text, parameters.ToArray(), null);
}
/// <summary>
/// 插入或者更新记录 返回记录ID
/// </summary>
/// <param name="tablename"></param>
/// <param name="dicTable"></param>
/// <param name="tuplePk"></param>
/// <returns></returns>
public object InsertOrUpdateDataSync(string tablename, Dictionary<string, object> dicTable, Tuple<string, object> tuplePk = null)
{
object recordID = default(object);
if (dicTable.ContainsKey(NodeOperation.Operation))
{
switch (Convert.ToString(dicTable[NodeOperation.Operation]))
{
case NodeOperationType.Added:
dicTable.Remove(NodeOperation.Operation);
recordID=InsertRecordSync(tablename, dicTable);
break;
case NodeOperationType.Changed:
case NodeOperationType.Deleted:
dicTable.Remove(NodeOperation.Operation);
recordID = tuplePk.Item2;
UpdateRecordSync(tablename, dicTable, tuplePk);
break;
default: ; break;
}
}
return recordID;
}
}
}