只需要改造一下实体类,以后再也不用写SQL了

news2024/11/20 10:22:54

文章目录

  • 只需要改造一下实体类,以后再也不用写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)
})
  

传统的操作方式是:

  1. 前端需要把表单参数字段组成串传入进来,(JSON或者带分割的字符串)。
  2. 后端在JSON或分割的字符串,解析成变量。
  3. 在把对应的字段变量,按照写的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获取该行数据信息。一般用于更新数据前读取该条数据。

在这里插入图片描述

传统的操作方式是:

  1. 从表格获取到该行ID
  2. 传入后端查询到行条数据的所有字段信息。
  3. 把查到的字段组装成JSON或拼串返回给前端。
  4. 前端根据返回的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传入后端进行匹配。

在这里插入图片描述

传统的操作方式是:

  1. 获取要更新的数据信息和ID。把表单参数字段组成串传入进来,(JSON或者带分割的字符串)。
  2. 后端在JSON或分割的字符串,解析成变量。
  3. 在把对应的字段变量,按照写的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删除数据。

在这里插入图片描述

传统的操作方式是:

  1. 获取要删除的行ID
  2. 后端根据ID执行SQL
ClassMethod Delete(pJson As %Library.DynamicObject)
{
	s id = pJson.id
	&sql(delete M_T.Person where ID = :id)
	q SQLCODE
}

6. 查询列表数据

在这里插入图片描述

传统的操作方式是:

  1. 后端查询前端需要的字段。
  2. 把字段拼成JSON数组返回到前端。
  3. 前端根据返回的数组,解析对应的字段填充到表格。
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执行。

思考

前端标识名称年龄身份证
前端idnameageno
后端IDMT_NameMT_AgeMT_No

我们能否通过对照字段关系把SQL组装一下,从而到达不用每次前后端交互,都要组装,解析,编写SQL呢,避免这些耗时有无意义的操作呢?

方案

建立映射

  1. 为了作为对照我们建立一个分数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 ];

}
  1. 通过别名建立对应映射字段。

注:这里的映射字段指的是前端需要的属性。因为我们的字段都带有前缀,所以前端对照时一般会不带前缀取更规范的命名。所以需要对照。

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 ];

  1. 建立映射类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"}
  1. 因为我们需要根据实体表自动生成对照,所里需要代码生成器构造一个方法。
  • '%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
}
  1. 改造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"}

查询单条数据

逻辑:

  1. 先获取映射。
  2. 根据映射遍历生成组装的SQL
  3. 执行SQL得到映射好的JSON
  4. 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语句。

逻辑:

  1. 根据映射字段找到属性字段。
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"}
  1. 获取到表的属性字段后组装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')
  1. 把得到的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
    }
}
  1. 调用时可直接将前端所需要的插入字段组成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
  1. 根据映射字段找到属性字段。(同INSERT
  2. 获取到表的属性字段后组装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 _"'"
}
  1. 把得到的SQL语句执行。(同INSERT
  2. 调用时可直接将前端所需要的更新字段组成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

  1. 利用单条数根据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)
}

  1. 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
}
  1. 直接传入表名进行调用,返回给前端表格所需的数据。
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了。

适用情况:

  • 适合不复杂的表操作,查询。
  • 效率上也在可接受范围之内。

最后思考

  1. 查询时,需要WHERE查询时,根据表单筛选进行WHERESQL语句自动编写对照呢,自动生成筛选条件?
  2. 根据映射做字段的表单验证?

理论上:都是可行的,留给大家思考完善。

参考

  • 此方案由云海宝云工通用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 ];
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/29174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

经济师十大专业通过人数分析!选专业有谱了!

最近,很多同学在后台咨询,哪个专业的好通过,自己要如何选专业…… 小编特别整理了已经公布的各省市2021年初、中级经济师各专业通过情况数据,给大家参考! 2022年中级经济专业技术资格考试设《经济基础知识》和《专业知…

让学前端不再害怕英语单词(三)

前两章直通车↓↓↓ 让学前端不再害怕英语单词(一) 让学前端不再害怕英语单词(二) 由于前两章都反响强烈,都上了全站的热榜,所以今天打算把第三章也写出来 第三章我们主要讲一下css中的伪类的英语单词还有…

TResNet: ResNet改进,实现高精度的同时保持高 GPU 利用率

终于开题,抓紧发文,然后放飞​​​​​​,来由就是想搞一篇论文,但是增加了某个东西之后吧,速度变慢了,所以导师提议加个这玩意看看能不能快点。 论文题目:TResNet: High Performance GPU-Dedi…

深度剖析商业智能BI中的多维分析

数据在这些年的时间中,也逐渐成长为了个人、机构、企业乃至国家的战略资源,被很多人放到“新时代的石油”这一位置上。虽然这个说法也引起了一些争议,但更多只是讨论数据和石油的差异性,却并没有多少人否认数据的价值,…

开发速查表,一个值得每个程序员收藏的网站

在工作过程中,虽然我们程序员,主要是使用一门语言开发,但免不了会用到其他语言参与其他项目;或者很多全栈工程师,会参与前端的开发调试;总的来说,我们工作过程中,都会涉及到多门编程…

Fluent 嵌套网格(overset)功能讲解与实例操作

作者 | 张杨 在流体仿真中,我们经常会遇到边界运动的问题,如: 生物医疗行业中血管的运动 航空航天行业中飞行器的分离 容积泵中齿轮的相对运动 在ANSYS Fluent 17.0之前的版本中,我们通常采用传统的MDM(Moving/D…

【模型训练】YOLOv7车辆和行人检测

YOLOv7车辆和行人检测 1、车辆和行人检测模型训练2、模型评估3、模型和数据集下载网盘链接1、本项目采用YOLOv7算法实现对车辆和行人检测,在几千多张车辆和行人检测中能训练得到,我们训练了YOLOv7、,所有指标都是在同一个验证集上得到; 2、目标类别数:2 ;类别名:person、…

5、网络配置

文章目录5、网络配置5.1 VMware三种模式5.1.1 桥连模式5.1.2 NAT模式5.1.3 仅主机模式5.2 查看网络IP和网关5.2.1 查看虚拟网络编辑器5.2.2 修改虚拟网卡 Ip5.2.3 查看网关5.2.4 查看 windows 环境的中 VMnet8 网络配置5.3 配置网络ip地址5.3.1 ifconfig查看网络接口配置1 基本…

Windows内核--系统调用参数验证(5.1)

内核参数验证的重要性 内核模式之所以有别于用户模式,在于内核模式应该是安全、可信的。用户系统调用可以传入各式各样的参数,可能是代码无意写错或因不预期的内存覆盖"暗地修改"参数,也可能是Hack有意传入,内核都应当妥…

【强化学习论文合集】ICRA-2022 强化学习论文 | 2022年合集(六)

强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现…

生成者(建造者)模式

思考生成者模式 生成者模式就是将对象构建和对象内部构建分离 对象构建:手机的构建 对象内部构建:手机中屏幕和电池的构建 1.生成者模式的本质 生成器模式的本质:分离整体对象构建算法和对象的部件构造。 构建一个复杂的对象,本来就有构建的过…

前端程序员辞掉朝九晚五工作成为独立开发者一年开发出6款软件的故事

一个前端程序员的梦想 作为一个程序员,陈明福的梦想是: 自主自由的工作内容和方式。在全球范围内发展个人品牌和影响力。学习技术和经验,成为 SaaS 软件方面的专家。对世界产生积极影响。财务自由,能提前退休。 他的故事 1、他…

JS 根据某个字段进行排序或分组

JS 数组中根据某个字段进行排序 const arr [ { name: "崔喻琪", age: 32 }, { name: " 王忱景", age: 18 }, { name: " 房真睿", age: 27 }, { name: "姬泉孝", age: 20 }, { name: "余嘉芳", age: 16 }, { na…

Deep Few-Shot Learning for Hyperspectral Image Classification-浅读

这里写目录标题Deep Few-Shot Learning for Hyperspectral Image ClassificationIntroductionMethodExperimentDeep Few-Shot Learning for Hyperspectral Image Classification 我看的第一篇 few-shot learning 文章,记录一下,看看能不能说明few-shot …

学生家乡网页设计作品静态HTML网页—— HTML+CSS+JavaScript制作辽宁沈阳家乡主题网页源码(11页)

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有…

Python绘制三维图详解

利用Python绘制三维图 目标: 绘制图像z2x2y2z^2 x^2 y^2z2x2y2 import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3D #绘制3D图案画曲面的第一步是就是要创建一个二维平面的网格,在Python当中,…

设备树_基础知识

设备树 格式 /dts-v1/; // 版本号 / { // /表示根节点string-property "xxx"; // string 类型string-list-property "xxx", "yyy"; // strin…

Linux下的Framebuffer编程

文章目录前言一、LCD操作原理二、代码解析及编写程序的步骤0.定义各类参数1.打开LCD设备节点2.获取触摸屏数据3.mmap映射Framebuffer,在Framebuffer中写入数据三、LCD操作函数解析1.描点函数2.显示字符函数总结前言 本篇文章将会介绍Linux下的Framebuffer编程&…

html中的定位知识点如何使用

目录 系列文章目录 文章目录 前言 一、定位是什么?有什么用? 二、定位方式有哪些?怎么使用? 1、静态定位:就是默认的定位方式,意思就是没有定位; 2、相对定位: 3、绝对定位&…

PDF怎么转成Word?安利几个转换小技巧

平时我们工作学习的时候,经常要跟文件打交道,并且接触最多的文件形式就是PDF与Word两种文件格式,它们各有各的好处,PDF的保密性以及兼容性好,便于我们进行文件分享查阅,而Word就方便我们进行编辑。如果我们…