本文讲解4.0版jxTMS中python服务的ORM能力,整个系列的文章请查看:docker版jxTMS使用指南:4.0版升级内容
docker版本的使用,请参考:docker版jxTMS使用指南
jxTMS通过jxMysql模块提供了对mysql的ORM形式的访问,使得python服务扩展中访问mysql数据库非常简单。
引用:
from jx.jxMysql import ORM
为便于说明,先展示一个实际的数据类,位于app目录下的data_vrs20.py中的VRS20Data:
dbName = jxGo.getSysConfig('dbName')
class VRS20Data(ORM):
_ormAttr = {
'ID':'int',
'CreateTime':'datetime',
'DevID':'int',
'Ranges':'string',
'Level':'string',
'Unit':'string',
'Status':'string',
'SD':'string'
}
_key = ['ID']
_excludeReset = ['DevID']
def __init__(self,data=None):
#data是初始化数据,即使用data来初始化本数据对象
super(VRS20Data , self).__init__(dbName,'VRS20Data',data=data)
继承ORM的类实质上就对应数据库中的一个数据表【最好类表同名】。该数据表应先在jxTMS的data文件中定义,然后通过热机刷新来自动创建。数据表必须放入到具体的数据库中,所以一般应先了解清楚需要访问的组织别名,然后在配置文件中添加系统配置dbName。然后在数据类定义前就执行:
dbName = jxGo.getSysConfig('dbName')
继承自ORM的数据类可以定义三种类属性:
1、_ormAttr
数据表的各数据列,应和data文件中的同名数据类一一对应。
注:如果少定义了数据列,不影响读取以及update操作,但无法insert新的数据行。这是由于jxTMS根据data文件中数据类的定义创建数据表时会自动将各列创建为Not Null
一般来说,jxTMS中的数据类都有Long型的ID和日期形式的CreateTime,如果定义了这两个属性,ORM会在生成数据对象时自动调用jxUtils中的CID和Now来初始化两属性。
2、_key
数据表的主键,可以有多个,即联合主键。
注:如果不定义key,则update时会掷出异常拒绝执行,因为没有用主键来设置where字句的update语句,一旦执行就会将整个表的所有行修改为相同的数据
3、_excludeReset
日志、需连续保存的设备实时数据等,有些字段是不需要更新的,如日志的对象、数据所属于的设备ID等,所以就没必要每次生成全新的ORM对象,只要reset一下再写入新值即可。而excludeReset指示reset时哪些字段不需要清除,reset时就不会清除这些字段了。
和之前的jxTMS的python服务基础类不同,ORM既定义了类函数也定义了对象函数。
register(cls, clsName, ormCls)
注册数据类
参数:
clsName:数据类名
ormCls:相应的数据类
返回值:
无
示例:
#定义完VRS20Data后,执行
ORM.register('VRS20Data',VRS20Data)
说明:
getBy、listBy、searchBy三个类查询语句是定义在ORM类中的,所以想使用这些函数来查询ORM派生出来的数据类就必须先注册这些数据类
virtual(cls,bv)
在执行insert、update操作时只在日志中打印sql语句而不实际执行
参数:
bv:是否开启全局virtual功能
返回值:
无
说明:
主要用于初始阶段的调试,以避免插入太过乱七八糟的数据,导致查询功能混乱
debug(cls,bv)
在执行数据库访问时在日志中打印sql语句
参数:
bv:是否开启全局debug功能
返回值:
无
说明:
开启后,在执行getBy、getByID、listBy、searchBy、insert、update这六个函数时都将打印要执行的sql语句
virtual和debug都是全局性影响,即只要打开,所有ORM操作都受影响,所以一般只用于调试阶段。
getBy(cls,dbName,clsName,attr,value)
根据属性attr的值从clsName表中读取一行,并转换为dict数据返回
参数:
dbName:数据库名
clsName:数据类名,也即要查询的数据表名,必须使用register注册过
attr:该数据类在_ormAttr中定义的属性,如果不是会掷出异常
value:想查询的数据对象的属性值
返回值:
result:符合条件的数据行,转换为dict。如果有多个符合条件的行,则只会返回一个,具体返回哪个无法确定
rc:执行是否成功
示例:
rs,rc = ORM.getBy(dbName,'VRS20Data','DevID',123456)
if rc:
...
注:所有对数据库的增删改查操作,返回的都是(result,rc)
getByID(cls,dbName,clsName,id)
根据id值从clsName表中读取一行,并转换为dict数据返回
参数:
dbName:数据库名
clsName:数据类名,也即要查询的数据表名
id:数据对象的id值,整数
返回值:
result:符合条件的数据行,转换为dict
rc:执行是否成功
listBy(cls,dbName,clsName,attr,value,limit=15,offset=0)
根据属性attr的值从clsName表中读取所有符合条件的行,并转换为dict数据的list返回
参数:
dbName:数据库名
clsName:数据类名,也即要查询的数据表名
attr:该数据类在_ormAttr中定义的属性,如果不是会掷出异常
value:想查询的数据对象的属性值
limit:一次最多查出多少行,默认15行,尽量不要设的太大
offset:从哪一行开始取出
返回值:
result:符合条件的所有数据行,转换为由dict组成的list
rc:执行是否成功
**searchBy(cls,dbName,clsName,*args,kw)
和listBy类似,但可以一次性指定多个查询条件
参数:
dbName:数据库名
clsName:数据类名,也即要查询的数据表名
args:忽略
kw:key=value的键-值对,key要么是limit或offset,要么是在_ormAttr中定义的属性
返回值:
result:符合条件的所有数据行,转换为由dict组成的list
rc:执行是否成功
说明:
limit默认是15、offset默认是0
示例:
#查询VRS20Data中指定dev的数据,一次最多三条,从符合条件的0行开始读取
rs,rc = ORM.searchBy('demoOrg_2255','VRS20Data',DevID=123456789,limit=3)
#查询VRS20Data中指定Level的数据,一次最多15条,从符合条件的7行开始读取
rs,rc = ORM.searchBy('demoOrg_2255','VRS20Data',Level='随便指定的数',offset=7)
#查询VRS20Data中指定dev和Level的数据,一次最多15条,从符合条件的0行开始读取
rs,rc = ORM.searchBy('demoOrg_2255','VRS20Data',DevID=123456789,Level='xxx')
注:直接使用searchBy较为繁琐,所以jxTMS特意提供了其可迭代的封装格式:searchIter
searchIter是searchBy的可迭代的封装,即支持for循环。其定义为:
class searchIter:
def __init__(self,dbname,clsName,limit=15):
#dbname:数据库名
#clsName:数据表名
#limit:一次最大查询行数
def total(self):
#返回符合条件的总行数
def limit(self):
#返回一次查询的最大行数
def offset(self):
#返回待查询的下一个数据序号
#list或for如果触发数据库查询,则从哪一个序号开始从数据库中查询
#以limit为步进,如:0,15,30,45,...
def nextOrder(self):
#仅用于for循环遍历
#返回下一个将被for循环读取的数据序号
#以1为步进,如:1,2,3,4,...
def reset(self):
#重置,可再次遍历
def condition(self,*args,**kw):
#设置查询条件
#args:忽略
#kw:key=valu形式的查询条件组
def list(self, offset=None):
#手动执行一次查询并获得查询结果列表
#offset:如果不为None则从此处开始查询,一次查询最多limit条
一般使用for来使用searchIter。示例:
from jx.jxMysql import searchIter
import app.data_vrs20
dbName = jxGo.getSysConfig('dbName')
s = searchIter(dbName,'VRS20Data')
#设置查询条件
s.condition(Status='007')
print(s.total())
for vd in s:
print(s.nextOrder())
print(vd)
#再查一次
s.reset()
n = s.total()
i = s.offset()
while i < n:
rs = s.list(offset=i)
print(rs)
i = i + s.limit()
需要注意的是,如果符合条件的数据非常多,直接用for来遍历是非常恐怖的,这也是为什么要提供list函数的原因,因为其可以配合分页来使用。
注:searchIter只能查询单表,所以更新版本的jxMysql增加了多表联合查询,会合并到下一版本中发布
数据对象的初始化
创建一个继承自ORM的数据对象时,会自动初始化ID和CreateTime,如果该数据类定义了这两个属性的话。
ORM的对象初始化函数为:
def __init__(self,dbname,dbtable,data=None):
...
data是一个dict,如果送入,则会用其来初始化ORM对象。
所以VRS20Data的初始化函数就是:
def __init__(self,data=None):
#指示本数据对象访问的是dbName库的VRS20Data表
super(VRS20Data , self).__init__(dbName,'VRS20Data',data=data)
data(self)
返回初始化时送入的dict
参数:
无
返回值:
初始化时送入的dict
**set(self,*args,kw)
设置属性值
参数:
args:忽略
kw:key=value的键-值对,key应是在_ormAttr中定义的属性
返回值:
对象自身
示例:
#设置DevID和Level
do.set(DevID=123456789,Level='xxx').update()
reset(self)
重新初始化
参数:
无
返回值:
对象自身
说明:
reset包含两个步骤:
1、除了在_excludeReset中指定的属性,其它属性全部被清除
2、重新初始化,重置ID和CreateTime
示例:
#重置后设置Level,ID是新的,所以是插入
do.reset().set(Level='xxx').insert()
insert(self)
将本数据对象插入到数据库中
参数:
无
返回值:
对象自身
说明:
如果某属性未赋值,会自动插入其默认值
示例:
do = VRS20Data()
do.set(DevID=123456789,Level='xxx').insert()
update(self)
将修改后的本数据对象更新到数据库中
参数:
无
返回值:
对象自身
说明:
根据key来修改指定的对象
示例:
#读取数据
dict,rc = ORM.getBy(dbName,'VRS20Data','Level','xx')
if rc:
#用读取到的数据创建数据对象
do = VRS20Data(data=dict)
#修改后保存到数据库中
do.set(DevID=123456789,Level='xxx').update()
getInfoValue(self,vn)
读取json格式的Info属性对应的dict中的vn名的值
参数:
vn:Info属性的dict中的键名
返回值:
Info属性的dict中的值
说明:
jxTMS中经常使用json格式的Info字段来保存一些额外的数据,这主要是为了应对不断变化的小需求所需要的额外的数据保存点
setInfoValue(self,vn,v)
设置json格式的Info属性对应的dict中的vn名的值
参数:
vn:Info属性的dict中的键名
v:值
返回值:
无
参考资料:
jxTMS设计思想
jxTMS编程手册
下面的系列文章讲述了如何用jxTMS开发一个实用的业务功能:
如何用jxTMS开发一个功能
下面的系列文章讲述了jxTMS的一些基本开发能力:
jxTMS的HelloWorld