文章目录
-
只需要改造一下实体类,以后再也不用写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 ];
}