uliorm调用非ORM sql语句的注意事项
一句话简单说,在执行view处理时使用do_来执行非ORM的语句,如:
from uliweb.orm import do_
result = do_(select(User.c, User.c.username=='limodou'))
复杂的解释
uliorm是基于sqlalchemy开发的。所以可以直接使用sqlalchemy的核心sql功能,比如常见的:select, update, insert, update。其中select可以单独调用。而update, insert, update一般是和一个Table对象连接使用。而使用uliorm你一般要先创建一个Model,然后这个Model就会有一个table属性,它就是真正的Table对象,所以只要使用Model.table就可以调用Table相关的方法了。那么Table中还有.c属性,它是所有字段的一个类字典属性。你可以使用Model.table.c或直接使用Model.c,它们是一样的。
因此我们已经知道了,如何找到相应的Table和字段集合,这样我们就可以执行sqlalchemy提供的各种方法。比如先定义一个User表:
class User(Model):
username = Field(str, max_length=80, verbose_name='用户名称', index=True, unique=True, required=True)
email = Field(str, max_length=80, verbose_name='邮箱')
上面定义了两个字段。那么我们不用uliorm提供的方法,而是使用sqlalchemy提供的sql语句来操作一下:
select(User.c, User.c.username=='limodou').execute() #查询
User.table.insert().values(username='new user', email='newuser@abc.com').execute() #插入
User.table.update().values(username='test').where(User.c.username=='new user').execute() #更新
User.table.delete(User.c.username=='new user').execute() #删除
注意到后面的'.execute',这样会使用数据库引擎自动创建的连接。然后在结果处理完毕后自动关闭。如果结果还没有处理完,可以直接调用result.close()来关闭。上述的描述可以在sqlalchemy的文档《Working with Engines and Connections》中找到。而这里自动创建的连接,其实缺省是连接池的处理。关闭也只是返回到连接池中。但是这样一来,有可能无法保证多条sql语句使用同一个连接,并且处于一个事务之下。所以带事务的处理要先创建连接,然后在连接上调用begin()来开启一个事务,然后所有的操作都在这个连接上,然后根据事务的提交或回滚来调用相应的commit()或rollback()。
同时sqlalchemy在创建引擎时还提供了一个strategy的参数,而uliorm就是使用了它,如:
db=create_engine('mysql://localhost/test',strategy='threadlocal')
这样,它的作用就是当你执行.execute()(在sqlalchemy中叫“Connectionless”,即非显示连接处理)时自动创建线程级的连接。这样的好处是保证不同的sql语句共离一个连接,从而使得事务的处理比较简单。但是uliorm也是在这个地方出了问题。所以经过重构,uliorm单独提供了Begin(), Commit(), Rollback()来保持自已维护的线程级的连接。但是connectionless在uliorm中还是可以使用,也因此会造成它们使用的连接有可能不是同一个,所以为了保证在一个请求的处理过程中使用同一个连接,你要使用uliorm提供的do_()函数来执行非ORM操作。如:
do_(select(User.c, User.c.username=='limodou'))
其实就是把sql语句最后的.execute()去掉,用do_()包含起来就行了。它会自动查找当前线程中已经存在的连接对象,如果没有则使用线程连接对象(适用于批量处理程序)。那么这个线程对象应如何创建呢?它是通过Begin()来创建的。而Begin()会在每个请求进来之后,在执行你的view函数之前被调用,从而保证已经有一个可用的线程连接。并且在返回响应和抛出异常时,它也会被正确回收。
当然,如果你不想使用缺省的线程连接,而是自已来创建新的连接,你还是可以通过:
db = get_connection() #获得缺省的engine对象
conn = db.connect() #获得新的连接
来创建新的连接。注意,这还是连接池的处理。sqlalchemy在缺省情况下会自动创建5个连接数的连接池。
如果你还想使用线程连接,但是同时想自已控制事务,那么可以在你的view中显示调用Begin(), Commit(), Rollback()来复用当前的线程连接,并进行事务控制。这里Begin()会先查找是不是已经存在一个线程连接,如果不存在,则会先创建,如果存在则复用。