天天看點

jxTMS使用示例--資料庫操作

使用本示例需通过docker容器,请先下拉jxTMS的docker镜像并按说明启动tms容器,并从helloWorld开始尝试。

jxTMS的数据库操作

对于编程来说,数据库操作包括:

  • 数据表的定义
  • ORM【对象关系映射】操作,即通常的增删改查
  • 条件查询

下面我们来演示jxTMS是如何操作前两项,条件查询请参考下一节。

数据表的定义

jxTMS中是通过在功能模块目录下的data文件中定义一个数据类来实现数据表的定义的。大家可以在demo1目录中创建data文件,然后输入:

class demoData:
	#jxTMS的数据对象必须有主键才能保存,一般建议使用long型的ID作为主键,否则可能会出现各种意外
	field ID long primaryKey
	field CreateTime datetime

	field Type string len=32 index 2
	field Name string len=126 fulltext
	
	field NoUsed bool
;
           

然后将该文件按用sftp管理jxTMS的代码所述拷贝到tms容器的/home/tms/codeDefine/demo/demo/demo1目录中。

此时可以查看数据库,看看是否有了名为demoData的数据表:

#切换目标数据库为组织的私有数据库:demo_6288
mysql> use demo_6288;
#查看数据表demoData的描述
mysql> desc demoData;
           

然后执行一次热机刷新。

再次查看数据库,看看是否有了名为demoData的数据表:

#再次查看数据表demoData的描述
mysql> desc demoData;
+------------+--------------+------+-----+---------+-------+
| Field      | Type         | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| ID         | bigint(20)   | NO   | PRI | NULL    |       |
| NoUsed     | tinyint(4)   | NO   |     | NULL    |       |
| Type       | varchar(32)  | NO   | MUL | NULL    |       |
| CreateTime | datetime     | NO   |     | NULL    |       |
| Name       | varchar(126) | NO   | MUL | NULL    |       |
+------------+--------------+------+-----+---------+-------+
5 rows in set (0.00 sec)
           

可以看到,demo组织所对应的数据库中自动多出了一张demoData的数据表,而且该数据中的列和我们在data文件中的定义一一对应。然后我们再看一下demoData的建表语句:

mysql> show create table demoData;
| demoData | CREATE TABLE `demoData` (
  `ID` bigint(20) NOT NULL,
  `NoUsed` tinyint(4) NOT NULL,
  `Type` varchar(32) NOT NULL,
  `CreateTime` datetime NOT NULL,
  `Name` varchar(126) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `index_demoData_2` (`Type`),
  FULLTEXT KEY `index_demoData_F0` (`Name`) /*!50100 WITH PARSER `ngram` */ 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
           

我们进一步看到:demoData表中ID是主键,而在class demoData的定义中:

#ID属性有一个primaryKey的定义
field ID long primaryKey
           

以Type建立了一个索引,而在class demoData的定义中:

#Type属性有一个2号索引,请注意一下数据库表相关索引的名字恰恰是:index_demoData_2
field Type string len=32 index 2
           

还以Name建立了一个全文索引,而在class demoData的定义中:

#Name属性有一个全文索引
field Name string len=126 fulltext
           

注1:全文索引最小分词单位是两个字符【支持中文】,所以在使用全文索引时不能只送入一个字符,必须大于等于两个字符

注2:由于mysql的限制,一个查询语句中只能使用一个全文索引,如果有两个以上的全文索引会什么也查不到

大家对照一下data文件中demoData类的定义和show create table demoData命令的输出,就很容易理解如何用data文件定义一个数据表了。具体的说明,请参考自定义数据类.。

ORM操作

我们现在通过data文件定义了demoData数据类,jxTMS还自动为我们创建好了demoData数据表,那我们就试一下如何操作它们吧。

注:从本节开始,所有对capa.py的修改,都是在demo1类的定义中增加或修改其中的某一个对象函数,大家一定要注意这些函数的缩进,务必使得其一定要是demo1的对象函数

我们修改capa.py中的sayHello函数,创建一个新的demoData对象【对应demoData表中的一行】:

@myModule.event('cmd', 'sayHello')
def sayHello(self, db, ctx):
	dd = pyORM.create(db,'demoData')
	dd.Type = 'demo'
	dd.Name = 'Hello world!'
           

然后执行一次热机刷新。

然后点击快捷栏中的【演示->helloWorld】,点击【点我】按钮。然后看看demoData表:

mysql> select * from demoData;
+-----------------+--------+------+---------------------+--------------+
| ID              | NoUsed | Type | CreateTime          | Name         |
+-----------------+--------+------+---------------------+--------------+
| 103897269075998 |      0 | demo | 2021-06-11 07:00:30 | Hello world! |
+-----------------+--------+------+---------------------+--------------+
1 row in set (0.00 sec)
           

注:ID和CreateTime不会和演示中的一样

会发现确实插入了一行数据,其中的Type和Name也确实是我们在sayHello中所指定的,但ID和CreateTime我们并没有指定啊?这是由于jxTMS会默认如果一个数据类定义了ID和CreateTime会自动帮我们初始化。其中的ID会赋予一个唯一值,而CreateTime则赋予create函数调用时的时间值。

注:ID的生成依靠主机号与时间,支持255个主机、每1024毫秒可产生16M个唯一的ID

所有在data文件中定义的数据类,都是pyORM类,然后jxTMS根据data文件中的定义,将之虚拟为demoData等,但对开发者来说,不需要关心这一点,只要给create函数指定定义过的数据类的类名,jxTMS就会自动创建一个虚拟的数据类,然后直接对其属性进行赋值,jxTMS就会将这些值翻译为对应的数据库类型并保存到数据库中。

记住你从数据库中查出来的dataData的ID值【我查出来的是:103897269075998,你要换成你的】,然后我们再次修改capa.py,增加一个helloWorld界面的prepareDisp响应函数:

#helloWorld界面显示后,为其执行界面初始化
@myModule.event('prepareDisp', 'helloWorld')
def helloWorld(self, db, ctx):
	#读取刚创建的数据对象
	#请将103897269075998替换为你从数据库中查询到的ID值
	dd = pyORM.getByID(db.get,'demoData',103897269075998)
	#向绑定了outText的控件写信息
	self.setOutput('outText',utils.getMsg('{}.{}/from:{}',dd.Type,dd.Name,dd.ID)
           

然后执行一次热机刷新后,再次点击快捷栏中的【演示->helloWorld】,看看和前一次有何不同?

jxTMS通过getByID函数可以直接从数据库中读取相应的数据行,然后创建一个虚拟的demoData对象,并用读到的数据为该对象的同名属性赋值。然后就可以直接使用该对象的各个属性值了。

现在,我们继续修改sayHello函数:

@myModule.event('cmd', 'sayHello')
def sayHello(self, db, ctx):
	#请将103897269075998替换为你从数据库中查询到的ID值
	dd = pyORM.getByID(db.getDBConn(),'demoData',103897269075998)
	dd.Type = 'test'
	dd.Name = 'ok'
	db.update(dd,'Type','Name')
           

然后执行一次热机刷新。

再次点击快捷栏中的【演示->helloWorld】,点击【点我】按钮。然后再次查看数据库中的demoData表:

mysql> select * from demoData;
           

看看和上一个select有什么变化。

开发者在读到数据对象后,可以修改除ID这个主键之外的任何属性,只要在修改后执行:

db.update(数据对象变量,'被修改的属性名1','被修改的属性名2',......)
           

jxTMS即会自动将修改提交到数据库中。

注1:所有被修改的属性,必须在update函数中逐一列举,否则jxTMS不会提交该属性的变化

注2:不管是cmd、还是prepareDisp函数,在整个函数的执行过程中,所有对数据库的变动,包括create、update,都被纳入到同一个数据库事务中,在函数执行过程中不管因为任何原因导致的异常,都会导致该数据库事务的回滚,不会对数据库产生实质的影响

注3:由于异常会导致数据库操作的回滚,而cmd、prepareDisp函数都是功能模块中的对象函数,所以如果开发者将数据对象设置为了python对象的对象属性,则在异常时会导致缓存下来的数据对象和数据库中的数据不一致,如:

@myModule.event('cmd', 'sayHello')
def sayHello(self, db, ctx):
	#请将103897269075998替换为你从数据库中查询到的ID值
	self.cachedORM = pyORM.getByID(db.getDBConn(),'demoData',103897269075998)
	self.cachedORM.Type = 'updated 123'
	self.cachedORM.Name = 1 / 0
	db.update(self.cachedORM,'Type','Name')
           

则由于【1/0】会掷出除零异常,这时用户点击了同一界面上的其它cmd按钮时执行的响应函数中,就会出现self.cachedORM.Type和数据库中的值不一致的错误。

注:每次通过点击【演示->helloWorld】打开的标签页,都是对应于一个单独的功能对象【调用New函数新创建出来的功能对象】,所以两个同时打开的helloWorld完全独立

所以呢,对于连续的业务操作,使用对象变量作为业务数据的缓存,当然会显著降低用getByID读取数据库的操作,但很可能会诱发数据不一致的严重错误,而如果要化解这一错误,还必须捕获异常然后进行恢复,既大大增加了工作量,也未必能百分百的保证恢复的正确性。所以笔者强烈建议:不要用对象变量跨操作作为业务数据的缓存,需要的时候直接从数据库中读取就好了。

通过其它属性来读取数据对象

上面我们都是通过getByID函数来读取数据对象的,除此之外,我们还可以通过其它属性来读取数据对象:

dd = pyORM.get(db.getDBConn(),'数据类名','属性名','属性值')
           

但是,请务必牢记:以这种方式读取的该数据类的该属性,必须具有唯一值【如流水号、学号、工号等】,否则读到的数据很可能是随机选择的一个。

如果需要通过多个属性来读取,或读取多个,则必须采用条件查询的方法进行读取,请参考下一节中的说明。

关于删除的说明

由于jxTMS设计的目的之一就是尽可能的降低开发者的门槛,所以开发者的经验未必足够。那么误删除就会成为一个非常恐怖的隐藏炸弹,所以jxTMS非常干脆的取消了所有ORM数据对象的删除能力。

请牢记:所有ORM数据对象都无法删除,除了jxTMS平台内部的两个特殊的ORM数据类:

  • Relation:用于刻画两个ORM对象之间的关系的数据类
  • tag:用于对某ORM数据对象进行标记的数据类

也就是说,只有这两个类提供了删除能力,其它所有的jxTMS的ORM数据类,包括jxTMS内部用java定义的以及用户在data文件中定义的,都是一经创建就无法删除。

那么,如果确实需要删除某数据对象怎么办呢?就如同demoData所演示的,定义一个NoUsed的布尔属性,当需要删除时,将该属性设置为True,然后在查询时过滤掉所有NoUsed=true的数据行即可。