问题描述    

     如何将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;
      }
    }
}