前情提要:此篇文章写的比较详细,可能会给各位读者一些启发。
SQL
编写。50
多个字段。在写CURD
的时候需要对每一个字段进行核对,包括字段名,字段值。一旦位置写错了,再次核对又要花费大量的时间。SQL
语句位置,例如:插入,更新。很容易出错。以上会用大量的时间花费在写SQL,对照字段,绑定字段,增加了许多重复的工作量。
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 ];
}
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
}
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
}
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
}
ID
删除数据。传统的操作方式是:
ID
。ID
执行SQL
。ClassMethod Delete(pJson As %Library.DynamicObject)
{
s id = pJson.id
&sql(delete M_T.Person where ID = :id)
q SQLCODE
}
传统的操作方式是:
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
通用工具类项目。https://github.com/yaoxin521123/IRIS-MAP-SQL.git
https://github.com/yaoxin521123/IRIS-TOOL.git
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
}
}
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)
}
}
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
}
}
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)
}
}
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 ];
}
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 ];
}