文章目录
- 
   
只需要改造一下实体类,以后再也不用写SQL了  - 现状分析
 - 现状示例
 - 1. 建立人员表`M.T.Person`表,包含三个字段名称,年龄,身份证。
 - 2. 新增一条数据,也就是插入的情况。
 - 3. 获取单条数据内容,也是就是根据`ID`获取该行数据信息。一般用于更新数据前读取该条数据。
 - 4. 更新数据,也就是`UPDATE`也需要把对应的字段和`ID`传入后端进行匹配。
 - 5. 删除数据,也就是根据`ID`删除数据。
 - 6. 查询列表数据
 - 现状总结
 
- 思考
 - 
   
方案  - 建立映射
 - 查询单条数据
 - 增加 `INSERRT`
 - 更新 `UPDATE`
 - 删除 `DELETE`
 - 查询 `QUERY`
 - 总结
 
- 再次思考
 - 改造映射类
 - 查询列表数据
 - 新增与更新与单条数据查询
 - 删除
 - 总结
 
- 最后
 - 最后思考
 - 参考
 - 附
 - Github
 
- 完整代码
 
 
  只需要改造一下实体类,以后再也不用写SQL了 
  
 
前情提要:此篇文章写的比较详细,可能会给各位读者一些启发。
现状分析
- 在写业务时,每新增一个表,都要对该表进行增删改查的
SQL编写。 - 如果一个表的字段非常多,例如:
50多个字段。在写CURD的时候需要对每一个字段进行核对,包括字段名,字段值。一旦位置写错了,再次核对又要花费大量的时间。 - 前端表单传入的参数字段值,到后端时需要重新解析,在填入对应的
SQL语句位置,例如:插入,更新。很容易出错。 - 前端绑定表格又要重新又要重新对照字段。
 
以上会用大量的时间花费在写SQL,对照字段,绑定字段,增加了许多重复的工作量。
现状示例
1. 建立人员表M.T.Person表,包含三个字段名称,年龄,身份证。
 
- 通常前缀
M.T包名代表功能架构名,Person代表具体功能表名。 - 通常字段前缀习惯加上包名
+表名缩写的前缀,例如,这里MT只代前缀。(为了模拟大部分表的现状,所以这里加了前缀,实际上没有必要加。) 
Class M.T.Person Extends %Persistent [ SqlTableName = Person ]
{
Property MTName As %String [ SqlFieldName = MT_Name ];
Property MTAge As %Integer [ SqlFieldName = MT_Age ];
Property MTNo As %String [ SqlFieldName = MT_No ];
}
 
2. 新增一条数据,也就是插入的情况。

- 前端
 

this.$axios.get("", {
  params: {
    ClassName: "M.Person",
    MethodName: "Insert",
    pJson: JSON.stringify(this.editForm)
  }
}).then(res => {
  this.queryBySql()
  this.$message({
    showClose: true,
    message: '恭喜你,操作成功',
    type: 'success'
  });
  this.dialogVisible = false
  this.resetForm(formName)
})
  
 
传统的操作方式是:
- 前端需要把表单参数字段组成串传入进来,(
JSON或者带分割的字符串)。 - 后端在
JSON或分割的字符串,解析成变量。 - 在把对应的字段变量,按照写的
SQL语句字段顺序,填入对应的INSERT值。 
- 后端
 
ClassMethod Insert(pJson As %Library.DynamicObject)
{
	s name = pJson.name
	s age = pJson.age
	s no = pJson.no
	&sql(insert into M_T.Person (MT_Name, MT_Age, MT_No) values(:name, :age, :no))
	q SQLCODE
}
 
3. 获取单条数据内容,也是就是根据ID获取该行数据信息。一般用于更新数据前读取该条数据。
 

传统的操作方式是:
- 从表格获取到该行
ID。 - 传入后端查询到行条数据的所有字段信息。
 - 把查到的字段组装成
JSON或拼串返回给前端。 - 前端根据返回的
JSON或字符串在写入到对应表单。 
ClassMethod GetDataById(pJson As %Library.DynamicObject = {{}})
{
	s id = pJson.id
	s data = ^M.T.PersonD(id)
	s name = $lg(data, 2)
	s age = $lg(data, 3)
	s no = $lg(data, 4)
	s obj = {}
	s obj.name = name
	s obj.age = age
	s obj.no = no
	s obj.id = id
	q obj
}
 
4. 更新数据,也就是UPDATE也需要把对应的字段和ID传入后端进行匹配。
 

传统的操作方式是:
- 获取要更新的数据信息和
ID。把表单参数字段组成串传入进来,(JSON或者带分割的字符串)。 - 后端在
JSON或分割的字符串,解析成变量。 - 在把对应的字段变量,按照写的
SQL语句字段顺序,填入对应的UPDATE值。 
ClassMethod Update(pJson As %Library.DynamicObject)
{
	s id = pJson.id
	s name = pJson.name
	s age = pJson.age
	s no = pJson.no
	&sql(update M_T.Person set MT_Name = :name, MT_Age = :age, MT_No = :no where %ID = :id)
	q SQLCODE
}
 
5. 删除数据,也就是根据ID删除数据。
 

传统的操作方式是:
- 获取要删除的行
ID。 - 后端根据
ID执行SQL。 
ClassMethod Delete(pJson As %Library.DynamicObject)
{
	s id = pJson.id
	&sql(delete M_T.Person where ID = :id)
	q SQLCODE
}
 
6. 查询列表数据

传统的操作方式是:
- 后端查询前端需要的字段。
 - 把字段拼成
JSON数组返回到前端。 - 前端根据返回的数组,解析对应的字段填充到表格。
 
ClassMethod QueryBySql(pJson As %Library.DynamicObject = {{}})
{
	s name = pJson.name
	s sql = "SELECT ID,MT_Name, MT_Age, MT_No FROM M_T.Person "
	if (name '= "") {
		s sql = sql _ " where MT_Name = '" _ name _"'"
	}
	s mStatement = ##class(%SQL.Statement).%New()
	s sc = mStatement.%Prepare(.sql)
	s rs = mStatement.%Execute()
	
	s array = []
	while rs.%Next() {
		s name = rs.%Get("MT_Name")
		s age = rs.%Get("MT_Age")
		s no = rs.%Get("MT_No")
		s id = rs.%Get("ID")
		s obj = {}
		s obj.name = name
		s obj.age = age
		s obj.no = no
		s obj.id = id
		d array.%Push(obj)
	}
	q array
}
 
现状总结
首先以上6步,介绍了传统方式的表的基本操作,新增,修改,删除,查询单条数据,查询列表数据。
我们也发现前几乎每一步都涉及到组装SQL,对照字段。
- 前端根据对照的内容绑定表单或表格。
 - 后端根据前端的字段,解析,然后组装
SQL执行。 
思考
| 前端 | 标识 | 名称 | 年龄 | 身份证 | 
|---|---|---|---|---|
| 前端 | id | name | age | no | 
| 后端 | ID | MT_Name | MT_Age | MT_No | 
我们能否通过对照字段关系把SQL组装一下,从而到达不用每次前后端交互,都要组装,解析,编写SQL呢,避免这些耗时有无意义的操作呢?
 
  方案 
  
 
建立映射
- 为了作为对照我们建立一个分数
Score表。 
Class M.T.Score Extends %Persistent [ SqlTableName = Score ]
{
Property MTMath As %String [ SqlFieldName = MT_Math ];
Property MTChinese As %String [ SqlFieldName = MT_Chinese ];
Property MTEnglish As %String [ SqlFieldName = MT_English ];
}
 
- 通过别名建立对应映射字段。
 
注:这里的映射字段指的是前端需要的属性。因为我们的字段都带有前缀,所以前端对照时一般会不带前缀取更规范的命名。所以需要对照。
Property MTMath As %String [ Aliases = {math}, SqlFieldName = MT_Math ];
Property MTChinese As %String [ Aliases = {chinese}, SqlFieldName = MT_Chinese ];
Property MTEnglish As %String [ Aliases = {english}, SqlFieldName = MT_English ];
 
- 建立映射类
M.Map通过表名,字段的别名与字段的属性名写个方法对照起来。 
目的:
- 给前端传入后端的找到对应属性写
SQL。 - 后端返回给前端所以的表单字段名。
 
ClassMethod QueryFieldMap(className)
{
	#; 包名
	s schema = $p(className, ".", 1, * - 1)
	
	#; 表名
	s table = $p(className, ".", *)
	
	#; 将包名替换为带下划线的架构名
	s schema = $replace(schema, ".", "_")
	s obj = ##class(%Library.DynamicObject).%New()  
	s sql = "SELECT * FROM INFORMATION_SCHEMA.Columns where TABLE_NAME  = '"_table _"' AND TABLE_SCHEMA = '"_schema_"'"
	s mStatement = ##class(%SQL.Statement).%New()
	s status = mStatement.%Prepare(.sql)
	$$$ThrowOnError(status)
	s result = mStatement.%Execute()
	
	while result.%Next() {
		
		#; 取字段名称
		s fieldName =  result.%Get("COLUMN_NAME")
		
		#; 取字别名
		s propertyName = $replace(fieldName, "_", "")
		s alies = $g(^oddDEF(className, "a",propertyName, 58))
		#; 别名不为空取别名,否则取字段名
		s key = $s(alies '= "" : alies, 1 : fieldName)
		d obj.%Set(key, fieldName)
	}
    q obj
}
        
 
IMP>w ##class(M.Map).QueryFieldMap("M.T.Score").%ToJSON()
{"ID":"ID","chinese":"MT_Chinese","english":"MT_English","math":"MT_Math"}
 
- 因为我们需要根据实体表自动生成对照,所里需要代码生成器构造一个方法。
 
'%class.Abstract因为代码生成器为编译时自动生成。所以映射的类要建立为抽象类,不为自身生成代码。%class.ClassType '= "persistent",这只是个标识,用于控制哪些实体类生成映射。
ClassMethod MapField() [ CodeMode = objectgenerator ]
{
	#; 不是抽象类直接退出
	if '%class.Abstract {
		#; 获取类名,如果类不声明关键字 类型为 ClassType = persistent 则退出
		s className = %class.Name
		q:(%class.ClassType '= "persistent") $$$OK
		
		d %code.WriteLine(" s obj = ..QueryFieldMap("""_ className _""")") 
	    d %code.WriteLine(" q obj")   
	}
    q $$$OK
}
 
- 改造
Score类,这里要注意增加类型ClassType = persistent,与继承M.Map映射类。 
- 因为
Score类不是抽象类,并且ClassType = persistent,所以编译时会自动为Score生成映射类代码。 
Class M.T.Score Extends (%Persistent, M.Map) [ ClassType = persistent, SqlRowIdName = id, SqlTableName = Score ]
{
Property MTMath As %String [ Aliases = {math}, SqlFieldName = MT_Math ];
Property MTChinese As %String [ Aliases = {chinese}, SqlFieldName = MT_Chinese ];
Property MTEnglish As %String [ Aliases = {english}, SqlFieldName = MT_English ];
 
- 我们调用一下方法验证一下。
 
IMP>w ##class(M.T.Score).MapField().%ToJSON()
{"id":"id","chinese":"MT_Chinese","english":"MT_English","math":"MT_Math"}
 
查询单条数据
逻辑:
- 先获取映射。
 - 根据映射遍历生成组装的
SQL。 - 执行
SQL得到映射好的JSON。 - 把
JSON转对象返回。 
ClassMethod Data(id As %Integer, tableName As %String) As %DynamicObject [ SqlProc ]
{
	#; 获取类名
	s className = $replace(tableName, "_", ".")
	
	#; 获取映射
	s mapJson = $classmethod(className, "MapField")
	#; 组装查询SQL字符串
	s str = "json_object("
	s iter = mapJson.%GetIterator()
	while iter.%GetNext(.key, .value) {
		continue:(mapJson.%GetTypeOf(key) = "unassigned") 
		s str = str _ "'" _key _ "'" _ ":" _ value _ ","
	} 
	
	#; 删除最后一个逗号
	s str = $e(str, 1, * - 1)
	s str = str _ ")  as json "
	s sql = "SELECT  " _ str _ " FROM " _ tableName _ " where %ID = ?"
	
	#; 执行查询
	s mStatement = ##class(%SQL.Statement).%New()
	s status = mStatement.%Prepare(.sql)
	s result = mStatement.%Execute(id)
	
	#; 获取表的数据
	while result.%Next() {
		s jsonStr = result.%Get("json")
		s json = {}.%FromJSON(jsonStr)
	}
	q json
}
 

现在有一条数据,我们通过上述方法查询。
ClassMethod QueryScoreById(pJson As %Library.DynamicObject)
{
	#; 只需要传入对应ID与表明
    q ##class(M.Common.Sql).Data(pJson.id, "M_T.Score")
}
 
- 返回对照的
JSON数组,可直接作用前端表单。 
IMP> w ##class(M.Score).QueryScoreById({"id" : 1}).%ToJSON()
{"id":1,"chinese":"90","english":"100","math":"80"}
 
增加 INSERRT
 
- 由于前端新增数据,可能只是某一些字段进行插入,所以要根据前端的映射字段找到后端表的属性字段,再组装
SQL,并非把所有字段写成INSERT语句。 
逻辑:
- 根据映射字段找到属性字段。
 
ClassMethod MapJson2SqlFields(map As %DynamicObject, json As %DynamicObject) As %DynamicObject
{
    #dim ret as %DynamicObject = {}
    q:(map.%ToJSON() = "{}") json
    
    #; 遍历insert或update的字段
    s iter = json.%GetIterator()
    while iter.%GetNext(.key, .value) {
        continue:(map.%GetTypeOf(key) = "unassigned") 
        
        #; 根据映射字段,获取属性字段
        s newKey = map.%Get(key)   
        d ret.%Set(newKey, value)       
    } 
    q ret
}
 
IMP 2e1> w ##class(IMP.Common.Sql).MapJson2SqlFields(##class(M.T.Score).MapField(), ##class(M.Score).QueryScoreById({"id":1})).%ToJSON()
{"id":1,"MT_Chinese":"90","MT_English":"100","MT_Math":"80"}
 
- 获取到表的属性字段后组装
INSERT语句。 
ClassMethod Json2SqlInsert(zJson)
{
    s data = zJson.data
    s table = zJson.table
    
    #; 逗号分割的字段串
    s nameStr = ""
    
    #; 带引号的分割的值串
    s dataStr = ""
    
    #; 把data中的字段与值分组
    s iter = data.%GetIterator()
    while iter.%GetNext(.key, .value) {
        s nameStr = $s(nameStr = "" : key, 1 : nameStr _ "," _ key)
        if ($listvalid(value)){
            if (value = ""){
                s fmtValue = "NULL"
            }else{
                s fmtValue = "$LISTFROMSTRING('" _ $lts(value) _ "')"
            }
        }else{
            s fmtValue = $s(value = "" : "NULL" , 1 : "'" _ value _ "'")
        }
        s dataStr = $s(dataStr = "" : fmtValue, 1 : dataStr _ "," _ fmtValue)
    }
    q "INSERT INTO " _ table _ "(" _ nameStr _ ") VALUES (" _ dataStr _ ")"
}
 
w ##class(M.Common.Sql).Json2SqlInsert({"type":"INSERT","table":"M_T.Score","id":"","data":{"MT_Math":90,"MT_Chinese":90,"MT_English":90}})
INSERT INTO M_T.Score(MT_Math,MT_Chinese,MT_English) VALUES ('90','90','90')
 
- 把得到的
SQL语句执行。 
ClassMethod XSQL(sqlStr)
{
	#define ThrowSqlException(%str)  throw:((SQLCODE '= 0)&&(SQLCODE '= 100)) ##class(%Exception.SystemException).%New("SQL错误", SQLCODE, , %str _ ":" _ " SQLCODE:"_ SQLCODE  _ " %msg:"_ $g(%msg))
    s sqlStatement = ##class(%SQL.Statement).%New()
    s sqlStatus = sqlStatement.%Prepare(sqlStr)
    $$$ThrowOnError(sqlStatus)
    s sqlResult = sqlStatement.%Execute()
    s stateType = sqlStatement.%Metadata.statementType
    if (sqlResult.%Message '= "") {
	    $$$ThrowSqlException(sqlResult.%Message)
    }else {
        return sqlResult.%ROWID
    }
}
 
- 调用时可直接将前端所需要的插入字段组成
JSON传入后端即可INSERT。 
ClassMethod MainInsert()
{
	s obj = {}
	s obj.math = 90
	s obj.chinese = 90
	s obj.english = 90
	#; 模拟前端传进来的json传
	q ..SaveScore(obj)
}
ClassMethod SaveScore(pJson As %DynamicObject)
{
	#; 直接将json传如即可返回结果
	s result = ##class(M.Common.Sql).Save(pJson, "M_T.Score")
	q result
}
 
IMP>w ##class(M.Score).MainInsert()
3
 

更新 UPDATE
 
- 与插入
INSERT类似前端更新数据,可能只是某一些字段进行更新,所以要根据前端的映射字段找到后端表的属性字段,在组装SQL。 
- 根据映射字段找到属性字段。(同
INSERT) - 获取到表的属性字段后组装
UPDATE语句,这里需要传入ID进行更新。 
ClassMethod Json2SqlUpdate(zJson)
{
    s data = zJson.data
    s table = zJson.table
    s rowID = zJson.id
    
    s iter = data.%GetIterator()
    s fieldDataStr = ""
    while iter.%GetNext(.key, .value) {
        if ($listvalid(value)){
            if (value = ""){
                s fmtValue = "NULL"
            }else{
                s fmtValue = "$LISTFROMSTRING('" _ $lts(value) _ "')"
            }
        }else{
            s fmtValue = $s(value = "" : "NULL" , 1 : "'" _ value _ "'")
        }
        s fieldData = key _ " = " _ fmtValue
        s fieldDataStr = $s(fieldDataStr = "" : fieldData, 1 : fieldDataStr _ "," _ fieldData)
    }
    q "UPDATE " _ table _ " SET " _ fieldDataStr _ " WHERE %ID = '" _ rowID _"'"
}
 
- 把得到的
SQL语句执行。(同INSERT) - 调用时可直接将前端所需要的更新字段组成
JSON传入后端即可UPDATE。 
ClassMethod SaveScore(pJson As %DynamicObject)
{
	#; 直接将json传如即可返回结果
	s result = ##class(M.Common.Sql).Save(pJson, "M_T.Score")
	q result
}
/// w ##class(M.Score).MainUpdate()
ClassMethod MainUpdate()
{
	s obj = {}
	s obj.id = 1
	s obj.math = 10
	s obj.chinese = 10
	s obj.english = 10
	#; 模拟前端传进来的json传,update需要根据ID
	q ..SaveScore(obj)
}
 
IMP>w ##class(M.Score).MainUpdate()
1
 

删除 DELETE
 
ClassMethod Json2SqlDelete(zJson)
{
    s table = zJson.table
    s rowID = zJson.id
    q "DELETE FROM " _ table _ " WHERE %ID = '" _ rowID _ "'"
}
 
- 删除最简单只需要传入,需要的删除
ID即可。 
ClassMethod MainDelete()
{
	s obj = {}
	s obj.id = 2
	q ..DeleteScore(obj)
}
ClassMethod DeleteScore(pJson As %DynamicObject)
{
	#; 直接将json传如即可返回结果
	s result = ##class(M.Common.Sql).Delete(pJson, "M_T.Score")
	q result
}
 
IMP>w ##class(M.Score).MainDelete()
2
 

查询 QUERY
 
- 利用单条数根据
ID获取JSON对象的特性,将它转成存储过程,在查询里调用。 
ClassMethod Query(pJson As %Library.DynamicObject, tableName)
{
    s sqlCode($i(sqlCode)) = " SELECT IMP_Common.Sql_Data(%ID,'" _ tableName _ "')"
    s sqlCode($i(sqlCode)) = " FROM " _ tableName _ ""
    q ##class(IMP.Common.Sql).DynamicSql2Array(.sqlCode)
}
 
- 将
SQL进行执行,这里判断如果是JSON对象则直接Push到数组里。 
ClassMethod DynamicSql2Array(ByRef sqlCode) As %DynamicArray
{
    s ret = []
    s sqlStatement = ##class(%SQL.Statement).%New()
    s sqlStatus = sqlStatement.%Prepare(.sqlCode)
    s sqlResult = sqlStatement.%Execute() 
	$$$ThrowOnError(sqlStatus)
    s columns = sqlStatement.%Metadata.columns
    s colCount = sqlResult.%ResultColumnCount
    for {
        q:('sqlResult.%Next())
        s rowData = sqlResult.%GetData(1)
        if (colCount = 1)&&($isobject(rowData)){
	        #; 如果是json对象则直接添加
            d ret.%Push(rowData)
        }else{
            #; 兼容多列
            s rowData = {}
            for i = 1 : 1 : colCount{
                s val = sqlResult.%GetData(i)
                s colName = columns.GetAt(i).colName
                d rowData.%Set(colName, val)
            }
            d ret.%Push(rowData)
        }
    }
    q ret
}
 
- 直接传入表名进行调用,返回给前端表格所需的数据。
 
ClassMethod Query(pJson As %Library.DynamicObject, tableName)
{
    s sqlCode($i(sqlCode)) = " SELECT IMP_Common.Sql_Data(%ID,'" _ tableName _ "')"
    s sqlCode($i(sqlCode)) = " FROM " _ tableName _ ""
    q ..DynamicSql2Array(.sqlCode)
}
 
IMP> d ##class(M.Score).QueryScore({}).%ToJSON()
[{"id":1,"chinese":"10","english":"10","math":"10"},{"id":3,"chinese":"90","english":"90","math":"90"}]
 
总结
最后我们总结一下调用方法。
- 查询单条数据
 
##class(M.Common.Sql).Data(pJson.id, "M_T.Score")
 
- 插入或更新
 
##class(M.Common.Sql).Save(pJson, "M_T.Score")
 
- 删除数据
 
##class(M.Common.Sql).Delete(pJson, "M_T.Score")
 
- 查询列表数据
 
##class(M.Common.Sql).Query(pJson, "M_T.Score")
 
综上所述,只要对表建立映射,我们可以不用书写任何一条SQL语句,只输入表名,就可以进行CURD。
回过头看我们的方案是不是有一些类似简单版本的MyBatis。
再次思考
我们是否可以通过实体类直接来调用如上增删改查的方式,连表名都不用去传参,直接调用实体类就可以直接去执行?
下面我们结合前端页面来实践一下。
改造映射类
我们可以利用$this指定当前的上下文环境的类名,来直接用实体类来调用对应方法
在M.Map增加如下四个方法
ClassMethod Save(pJson As %Library.DynamicObject)
{
	s tableName = ..GetTableNameByClassName($this)
	q ##class(M.Common.Sql).Save(pJson, tableName)
}
ClassMethod Query(pJson As %Library.DynamicObject)
{
	s tableName = ..GetTableNameByClassName($this)
	q ##class(M.Common.Sql).Query(pJson, tableName)
}
ClassMethod Delete(pJson As %Library.DynamicObject)
{
	s tableName = ..GetTableNameByClassName($this)
	q ##class(M.Common.Sql).Delete(pJson, tableName)
}
ClassMethod Data(pJson As %Library.DynamicObject)
{
	s tableName = ..GetTableNameByClassName($this)
	q ##class(M.Common.Sql).Data(pJson.id, tableName)
}
ClassMethod GetTableNameByClassName(className)
{
	&sql(SELECT SqlQualifiedNameQ into :tableName FROM   %Dictionary.CompiledClass WHERE ID = :className)
	q tableName
}
 
查询列表数据
- 前端
 
HTML:

JS:

- 后端
 
ClassMethod QueryRaw(pJson As %Library.DynamicObject)
{
	q ##class(M.T.Score).Query(pJson)
}
 

新增与更新与单条数据查询
- 前端
 
HTML:

JS:


- 后端
 
ClassMethod InsertRaw(pJson As %Library.DynamicObject)
{
	q ##class(M.T.Score).Save(pJson)
}
 

ClassMethod DataRaw(pJson As %Library.DynamicObject)
{
	q ##class(M.T.Score).Data(pJson)
}
 

ClassMethod UpdateRaw(pJson As %Library.DynamicObject)
{
	q ##class(M.T.Score).Save(pJson)
}
 


删除
- 前端
 

- 后端
 
ClassMethod DeleteRaw(pJson As %Library.DynamicObject)
{
	q ##class(M.T.Score).Delete(pJson)
}
 


总结
最后我们再次总结一下调用方法。
- 查询单条数据
 
##class(实体类名).Data(pJson.id)
 
- 插入或更新
 
##class(实体类名).Save(pJson)
 
- 删除数据
 
##class(实体类名).Delete(pJson)
 
- 查询列表数据
 
##class(实体类名).Query(pJson)
 
最后
综上所诉:我们只需要对实体表,增加别名映射,继承M.Map映射类。就可以在也不用写,解析,组装,写SQL了。
适用情况:
- 适合不复杂的表操作,查询。
 - 效率上也在可接受范围之内。
 
最后思考
- 查询时,需要
WHERE查询时,根据表单筛选进行WHERE的SQL语句自动编写对照呢,自动生成筛选条件? - 根据映射做字段的表单验证?
 
理论上:都是可行的,留给大家思考完善。
参考
- 此方案由云海宝云工通用
SQL基础上演化而来。 
附
分享,开源,利他,创造价值,分享学习,一起成长,相伴前行。
- 开源此 
MAP SQL项目 和TOOL通用工具类项目。 - 欢迎大家提出自己想法和建议,共同完善项目。
 
Github
MAP SQL项目GitHub
https://github.com/yaoxin521123/IRIS-MAP-SQL.git
 
TOOL通用工具类项目GitHub
https://github.com/yaoxin521123/IRIS-TOOL.git
 
完整代码
- M.Person
 
Class M.Person Extends %RegisteredObject
{
ClassMethod Insert(pJson As %Library.DynamicObject)
{
	s name = pJson.name
	s age = pJson.age
	s no = pJson.no
	&sql(insert into M_T.Person (MT_Name, MT_Age, MT_No) values(:name, :age, :no))
	q SQLCODE
}
ClassMethod Update(pJson As %Library.DynamicObject)
{
	s id = pJson.id
	s name = pJson.name
	s age = pJson.age
	s no = pJson.no
	&sql(update M_T.Person set MT_Name = :name, MT_Age = :age, MT_No = :no where %ID = :id)
	q SQLCODE
}
ClassMethod Delete(pJson As %Library.DynamicObject)
{
	s id = pJson.id
	&sql(delete M_T.Person where ID = :id)
	q SQLCODE
}
/// w ##class(M.Sql).QueryBySql().%ToJSON()
ClassMethod QueryBySql(pJson As %Library.DynamicObject = {{}})
{
	s name = pJson.name
	s sql = "SELECT ID,MT_Name, MT_Age, MT_No FROM M_T.Person "
	if (name '= "") {
		s sql = sql _ " where MT_Name = '" _ name _"'"
	}
	s mStatement = ##class(%SQL.Statement).%New()
	s sc = mStatement.%Prepare(.sql)
	s rs = mStatement.%Execute()
	
	s array = []
	while rs.%Next() {
		s name = rs.%Get("MT_Name")
		s age = rs.%Get("MT_Age")
		s no = rs.%Get("MT_No")
		s id = rs.%Get("ID")
		s obj = {}
		s obj.name = name
		s obj.age = age
		s obj.no = no
		s obj.id = id
		d array.%Push(obj)
	}
	q array
}
ClassMethod GetDataById(pJson As %Library.DynamicObject = {{}})
{
	s id = pJson.id
	s data = ^M.T.PersonD(id)
	s name = $lg(data, 2)
	s age = $lg(data, 3)
	s no = $lg(data, 4)
	s obj = {}
	s obj.name = name
	s obj.age = age
	s obj.no = no
	s obj.id = id
	q obj
}
}
 
- M.Score
 
Class M.Score Extends %RegisteredObject
{
ClassMethod Insert()
{
	&sql(insert into M_T.Score (MT_Math, MT_Chinese, MT_English) values(:name, :age, :no))
	q SQLCODE
}
/// w ##class(M.Score).QueryScoreById({"id" : 1}).%ToJSON()
ClassMethod QueryScoreById(pJson As %Library.DynamicObject)
{
	#; 只需要传入对应ID与表明
    q ##class(M.Common.Sql).Data(pJson.id, "M_T.Score")
}
/// w ##class(M.Score).MainInsert()
ClassMethod MainInsert()
{
	s obj = {}
	s obj.math = 90
	s obj.chinese = 90
	s obj.english = 90
	#; 模拟前端传进来的json传
	q ..SaveScore(obj)
}
ClassMethod SaveScore(pJson As %DynamicObject)
{
	#; 直接将json传如即可返回结果
	s result = ##class(M.Common.Sql).Save(pJson, "M_T.Score")
	q result
}
/// w ##class(M.Score).MainUpdate()
ClassMethod MainUpdate()
{
	s obj = {}
	s obj.id = 1
	s obj.math = 10
	s obj.chinese = 10
	s obj.english = 10
	#; 模拟前端传进来的json传,update需要根据ID
	q ..SaveScore(obj)
}
/// w ##class(M.Score).MainDelete()
ClassMethod MainDelete()
{
	s obj = {}
	s obj.id = 2
	q ..DeleteScore(obj)
}
ClassMethod DeleteScore(pJson As %DynamicObject)
{
	#; 直接将json传如即可返回结果
	s result = ##class(M.Common.Sql).Delete(pJson, "M_T.Score")
	q result
}
/// d ##class(M.Score).QueryScore({}).%ToJSON()
ClassMethod QueryScore(pJson As %Library.DynamicObject)
{
    q ##class(M.Common.Sql).Query(pJson, "M_T.Score")
}
/// w ##class(M.Score).InsertRaw()
ClassMethod InsertRaw(pJson As %Library.DynamicObject)
{
#;	s obj = {}
#;	s obj.math = 90
#;	s obj.chinese = 80
#;	s obj.english = 70
	q ##class(M.T.Score).Save(pJson)
}
/// w ##class(M.Score).UpdateRaw()
ClassMethod UpdateRaw(pJson As %Library.DynamicObject)
{
#;	s obj = {}
#;	s obj.id = 5
#;	s obj.math = 20
#;	s obj.chinese = 20
#;	s obj.english = 20
	q ##class(M.T.Score).Save(pJson)
}
/// w ##class(M.Score).DeleteRaw()
ClassMethod DeleteRaw(pJson As %Library.DynamicObject)
{
#;	s obj = {}
#;	s obj.id = 4
	q ##class(M.T.Score).Delete(pJson)
}
/// w ##class(M.Score).DataRaw().%ToJSON()
ClassMethod DataRaw(pJson As %Library.DynamicObject)
{
#;	s obj = {}
#;	s obj.id = 5
	q ##class(M.T.Score).Data(pJson)
}
/// w ##class(M.Score).QueryRaw().%ToJSON()
ClassMethod QueryRaw(pJson As %Library.DynamicObject)
{
#;	s obj = {}
	q ##class(M.T.Score).Query(pJson)
}
}
 
- M.Map
 
Class M.Map Extends %RegisteredObject [ Abstract ]
{
/// desc:映射字段
/// 没有写到%code里的代码是直接判断来执行的
/// %class 是 %Dictionary.ClassDefinition 的对象
/// %code 是 %Stream.MethodGenerator 的对象
/// w ##class(CT.IMP.SysRole).MapField().%ToJSON()
ClassMethod MapField() [ CodeMode = objectgenerator ]
{
	#; 不是抽象类直接退出
	if '%class.Abstract {
		#; 获取类名,如果类不声明关键字 类型为 ClassType = persistent 则退出
		s className = %class.Name
		q:(%class.ClassType '= "persistent") $$$OK
		
		d %code.WriteLine(" s obj = ..QueryFieldMap("""_ className _""")") 
	    d %code.WriteLine(" q obj")   
	}
    q $$$OK
}
/// desc:查询映射字段
/// w ##class(M.Map).QueryFieldMap("CT_IMP.SysRole").%ToJSON()
/// w ##class(M.Map).QueryFieldMap("M.T.Score").%ToJSON()
ClassMethod QueryFieldMap(className)
{
	#; 包名
	s schema = $p(className, ".", 1, * - 1)
	
	#; 表名
	s table = $p(className, ".", *)
	
	#; 将包名替换为带下划线的架构名
	s schema = $replace(schema, ".", "_")
	s obj = ##class(%Library.DynamicObject).%New()  
	s sql = "SELECT * FROM INFORMATION_SCHEMA.Columns where TABLE_NAME  = '"_table _"' AND TABLE_SCHEMA = '"_schema_"'"
	s mStatement = ##class(%SQL.Statement).%New()
	s status = mStatement.%Prepare(.sql)
	$$$ThrowOnError(status)
	s result = mStatement.%Execute()
	
	while result.%Next() {
		
		#; 取字段名称
		s fieldName =  result.%Get("COLUMN_NAME")
		
		#; 取字别名
		s propertyName = $replace(fieldName, "_", "")
		s alies = $g(^oddDEF(className, "a",propertyName, 58))
		#; 别名不为空取别名,否则取字段名
		s key = $s(alies '= "" : alies, 1 : fieldName)
		d obj.%Set(key, fieldName)
	}
    q obj
}
ClassMethod Save(pJson As %Library.DynamicObject)
{
	s tableName = ..GetTableNameByClassName($this)
	q ##class(M.Common.Sql).Save(pJson, tableName)
}
ClassMethod Query(pJson As %Library.DynamicObject)
{
	s tableName = ..GetTableNameByClassName($this)
	q ##class(M.Common.Sql).Query(pJson, tableName)
}
ClassMethod Delete(pJson As %Library.DynamicObject)
{
	s tableName = ..GetTableNameByClassName($this)
	q ##class(M.Common.Sql).Delete(pJson, tableName)
}
ClassMethod Data(pJson As %Library.DynamicObject)
{
	s tableName = ..GetTableNameByClassName($this)
	q ##class(M.Common.Sql).Data(pJson.id, tableName)
}
ClassMethod GetTableNameByClassName(className)
{
	&sql(SELECT SqlQualifiedNameQ into :tableName FROM   %Dictionary.CompiledClass WHERE ID = :className)
	q tableName
}
}
 
- M.Common.Sql
 
Class M.Common.Sql Extends %RegisteredObject
{
ClassMethod DynamicSql2Array(ByRef sqlCode) As %DynamicArray
{
    s ret = []
    s sqlStatement = ##class(%SQL.Statement).%New()
    s sqlStatus = sqlStatement.%Prepare(.sqlCode)
    s sqlResult = sqlStatement.%Execute() 
	$$$ThrowOnError(sqlStatus)
	
    s columns = sqlStatement.%Metadata.columns
    s colCount = sqlResult.%ResultColumnCount
    for {
        q:('sqlResult.%Next())
        s rowData = sqlResult.%GetData(1)
        if (colCount = 1)&&($isobject(rowData)){
            d ret.%Push(rowData)
        }else{
            #; 兼容多列
            s rowData = {}
            for i = 1 : 1 : colCount{
                s val = sqlResult.%GetData(i)
                s colName = columns.GetAt(i).colName
                d rowData.%Set(colName, val)
            }
            d ret.%Push(rowData)
        }
    }
    q ret
}
ClassMethod Json2Sql(zJson)
{
    s type = zJson.type
    q $case(type,
                "UPDATE": ..Json2SqlUpdate(zJson),
                "INSERT": ..Json2SqlInsert(zJson),
                "DELETE": ..Json2SqlDelete(zJson),
                :""
                )
}
///  w ##class(M.Common.Sql).Json2SqlInsert({"type":"INSERT","table":"M_T.Score","id":"","data":{"MT_Math":90,"MT_Chinese":90,"MT_English":90}}).%ToJSON()
ClassMethod Json2SqlInsert(zJson)
{
    s data = zJson.data
    s table = zJson.table
    
    s iter = data.%GetIterator()
    s nameStr = ""
    s dataStr = ""
    while iter.%GetNext(.key, .value) {
        s nameStr = $s(nameStr = "" : key, 1 : nameStr _ "," _ key)
        if ($listvalid(value)){
            if (value = ""){
                s fmtValue = "NULL"
            }else{
                s fmtValue = "$LISTFROMSTRING('" _ $lts(value) _ "')"
            }
        }else{
            s fmtValue = $s(value = "" : "NULL" , 1 : "'" _ value _ "'")
        }
        s dataStr = $s(dataStr = "" : fmtValue, 1 : dataStr _ "," _ fmtValue)
    }
    q "INSERT INTO " _ table _ "(" _ nameStr _ ") VALUES (" _ dataStr _ ")"
}
ClassMethod Json2SqlUpdate(zJson)
{
    s data = zJson.data
    s table = zJson.table
    s rowID = zJson.id
    
    s iter = data.%GetIterator()
    s fieldDataStr = ""
    while iter.%GetNext(.key, .value) {
        if ($listvalid(value)){
            if (value = ""){
                s fmtValue = "NULL"
            }else{
                s fmtValue = "$LISTFROMSTRING('" _ $lts(value) _ "')"
            }
        }else{
            s fmtValue = $s(value = "" : "NULL" , 1 : "'" _ value _ "'")
        }
        s fieldData = key _ " = " _ fmtValue
        s fieldDataStr = $s(fieldDataStr = "" : fieldData, 1 : fieldDataStr _ "," _ fieldData)
    }
    q "UPDATE " _ table _ " SET " _ fieldDataStr _ " WHERE %ID = '" _ rowID _"'"
}
ClassMethod Json2SqlDelete(zJson)
{
    s table = zJson.table
    s rowID = zJson.id
    q "DELETE FROM " _ table _ " WHERE %ID = '" _ rowID _ "'"
}
ClassMethod XJson2Sql(zJson)
{
    q ..XSQL(..Json2Sql(zJson))
}
ClassMethod XSQL(sqlStr)
{
	#define ThrowSqlException(%str)  throw:((SQLCODE '= 0)&&(SQLCODE '= 100)) ##class(%Exception.SystemException).%New("SQL错误", SQLCODE, , %str _ ":" _ " SQLCODE:"_ SQLCODE  _ " %msg:"_ $g(%msg))
    s sqlStatement = ##class(%SQL.Statement).%New()
    s sqlStatus = sqlStatement.%Prepare(sqlStr)
    $$$ThrowOnError(sqlStatus)
    s sqlResult = sqlStatement.%Execute()
    s stateType = sqlStatement.%Metadata.statementType
    if (sqlResult.%Message '= "") {
	    $$$ThrowSqlException(sqlResult.%Message)
    }else {
        return sqlResult.%ROWID
    }
}
ClassMethod MapJson2SqlFields(map As %DynamicObject, json As %DynamicObject) As %DynamicObject
{
    #dim ret as %DynamicObject = {}
    q:(map.%ToJSON() = "{}") json
    
    s iter = json.%GetIterator()
    while iter.%GetNext(.key, .value) {
        continue:(map.%GetTypeOf(key) = "unassigned") 
        s newKey = map.%Get(key)   
        d ret.%Set(newKey, value)       
    } 
    
    q ret
}
ClassMethod MapJson2SelectInto(mapJson) As %List
{
    #dim ret as %List = ""
    s iter = mapJson.%GetIterator()
    s mapList = ""
    s fieldList = "" 
    while iter.%GetNext(.mapKey, .sqlField) {
        s fieldList = fieldList _ $lb(sqlField)
        s mapList = mapList _ $lb(":" _ mapKey)
    }
    
    q $lb($lts(fieldList), $lts(mapList))
}
ClassMethod Save(pJson As %Library.DynamicObject, tableName As %String) As %Integer
{
    s id = pJson.id
    
    #; 将表名替换为类名
    s className = $replace(tableName, "_", ".")
	
	 #; 获取对照
    s dataMap = $classmethod(className, "MapField")  
    s result =  ..XJson2Sql({
        "type": ($s(id '= "" : "UPDATE" , 1 : "INSERT")),
        "table": (tableName),
        "id": (id),
        "data": (..MapJson2SqlFields(dataMap, pJson))
    })
    q result
}
ClassMethod Delete(pJson As %Library.DynamicObject, tableName As %String) As %Integer
{
	s id = pJson.id
	q:(id = "") $$$OK
	q ..XJson2Sql({
	    "type": "DELETE",
	    "table": (tableName),
	    "id": (id)
	})
}
ClassMethod Data(id As %Integer, tableName As %String) As %DynamicObject [ SqlProc ]
{
	#; 获取类名
	s className = $replace(tableName, "_", ".")
	
	#; 获取映射
	s mapJson = $classmethod(className, "MapField")
	#; 组装查询SQL字符串
	s str = "json_object("
	s iter = mapJson.%GetIterator()
	while iter.%GetNext(.key, .value) {
		continue:(mapJson.%GetTypeOf(key) = "unassigned") 
		s str = str _ "'" _key _ "'" _ ":" _ value _ ","
	} 
	
	#; 删除最后一个逗号
	s str = $e(str, 1, * - 1)
	s str = str _ ")  as json "
	s sql = "SELECT  " _ str _ " FROM " _ tableName _ " where %ID = ?"
	
	#; 执行查询
	s mStatement = ##class(%SQL.Statement).%New()
	s status = mStatement.%Prepare(.sql)
	s result = mStatement.%Execute(id)
	
	#; 获取表的数据
	while result.%Next() {
		s jsonStr = result.%Get("json")
		s json = {}.%FromJSON(jsonStr)
	}
	q json
}
ClassMethod Query(pJson As %Library.DynamicObject, tableName)
{
    s sqlCode($i(sqlCode)) = " SELECT IMP_Common.Sql_Data(%ID,'" _ tableName _ "')"
    s sqlCode($i(sqlCode)) = " FROM " _ tableName _ ""
    q ..DynamicSql2Array(.sqlCode)
}
}
 
- M.T.Person
 
Class M.T.Person Extends %Persistent [ SqlTableName = Person ]
{
Property MTName As %String [ SqlFieldName = MT_Name ];
Property MTAge As %Integer [ SqlFieldName = MT_Age ];
Property MTNo As %String [ SqlFieldName = MT_No ];
}
 
- M.T.Score
 
Class M.T.Score Extends (%Persistent, M.Map) [ ClassType = persistent, SqlRowIdName = id, SqlTableName = Score ]
{
Property MTMath As %String [ Aliases = {math}, SqlFieldName = MT_Math ];
Property MTChinese As %String [ Aliases = {chinese}, SqlFieldName = MT_Chinese ];
Property MTEnglish As %String [ Aliases = {english}, SqlFieldName = MT_English ];
}
                


















