至于效率问题,就我的看法,ORM是一个mapping,从业务逻辑mapping到sql。
作为mapping,是可以做时间和空间的均衡。比如做缓存什么的,并不像IO,CPU一样是硬件瓶颈。
俺用的是delphi,做梦都想搞一个ORM。。。可惜静态语言的ORM大多ugly,不敢用。
On Jul 3, 6:17 pm, 朱涛 <zhutao.is...@gmail.com> wrote:
> 大家好,最近一直困扰我的一个问题是关于ORM(对象关系映射)的。ORM的提出是具有相当牛的意义的,它封装了底层的sql,让开发人员无需了解DB,只需要操作熟悉的类即可,可是问题来了。无论何种的封装都会给性能带来一定的损失,而作为DB应用场景很重要的一个方面即是WEB应用,其对于DB的性能要求显得尤其高。很多PV很大的网站最终的性能瓶颈都是DB的读写,那么作为在sql之上的ORM更使得性能降低不少。
> ORM的已有的意义是明确的(可能不够),即
>
> 1. 简化OO程序员的编程
> 2. 便于数据库迁移
On Jul 3, 6:35 pm, "Jeffrey Zhao" <je...@live.com> wrote:
> ORM会略为降低性能,但是降低很少,不会成为性能瓶颈。
>
> Web应用的瓶颈往往在于DB查询,例如一个查询使用0.1秒,ORM耗费0.0001秒,可以忽略不计,不会"降低不少"。
>
> 所以说ORM会带来性能损失这句话可谓是"无稽之谈",如果真有很多性能损失,那么应该检查ORM的方式是否有问题。
>
> ORM的作用并不一定是数据迁移,而是在领域模型和DB Schema中进行了一次映射。
>
> 领域模型是一个应用中最为关键的概念,它和存储方式是无关的。
>
> 一个领域模型进行修改之后(这点是很常见的),而DB Schema是不用修改的,使用ORM就可以用很少的代价完成这次改变。
>
> Jeffrey Zhao
> Blog:http://www.cnblogs.com/JeffreyZhao
> Twitter:http://twitter.com/jeffz_cn
>
> From: 朱涛
> Sent: Friday, July 03, 2009 6:17 PM
> To: pon...@googlegroups.com
> Subject: [TL] {讨论}ORM的意义何在?
>
> 大家好,最近一直困扰我的一个问题是关于ORM(对象关系映射)的。ORM的提出是具有相当牛的意义的,它封装了底层的sql,让开发人员无需了解DB,只需要操作熟悉的类即可,可是问题来了。无论何种的封装都会给性能带来一定的损失,而作为DB应用场景很重要的一个方面即是WEB应用,其对于DB的性能要求显得尤其高。很多PV很大的网站最终的性能瓶颈都是DB的读写,那么作为在sql之上的ORM更使得性能降低不少。
>
> ORM的已有的意义是明确的(可能不够),即
> 1.. 简化OO程序员的编程
> 2.. 便于数据库迁移
On Jul 3, 7:35 pm, 朱涛 <zhutao.is...@gmail.com> wrote:
> 我不认可jeffrey的说法,ORM的一个目标即是对各种主流的DB的支持,但显然各种DB对sql的支持情况是不同的,而且对于某个特定的DB会有更好的、更优性能的处理方式,比如mysql中一些specific
> sql性能是可观的,而sql server or oracle就没有对应的东西。这也就是写raw
> sql的好处,你可以把一个DB的性能发挥到尽量高的程度。
> 另外,所谓降低很少这是对一部分的封装而言的,对于更多其它的封装并非如此,比如聚合,连接等,使用raw
> sql可能只需要一条sql语句,而倘若使用ORM则会转换为多条sql语句。
>
> 所以,ORM
>
> 1. 没有发挥特定DB的优势
> 2. 为了为各种DB提供统一的接口,只能在特定的DB以更多的sql语句来牺牲性能
> 3. ORM往往并不提供sql所有的功能,如django直到1.0还没有聚合等
>
> 希望和大家继续讨论,请指正。
> --
>
> 朱涛 Tower Joohttp://twitter.com/towerjoohttp://sites.google.com/site/towerjoo
>
> 2009/7/3 Jeffrey Zhao <je...@live.com>
>
> > ORM会略为降低性能,但是降低很少,不会成为性能瓶颈。
>
> > Web应用的瓶颈往往在于DB查询,例如一个查询使用0.1秒,ORM耗费0.0001秒,可以忽略不计,不会"降低不少"。
>
> > 所以说ORM会带来性能损失这句话可谓是"无稽之谈",如果真有很多性能损失,那么应该检查ORM的方式是否有问题。
>
> > ORM的作用并不一定是数据迁移,而是在领域模型和DB Schema中进行了一次映射。
>
> > 领域模型是一个应用中最为关键的概念,它和存储方式是无关的。
>
> > 一个领域模型进行修改之后(这点是很常见的),而DB Schema是不用修改的,使用ORM就可以用很少的代价完成这次改变。
>
> > Jeffrey Zhao
> > Blog:http://www.cnblogs.com/JeffreyZhao
> > Twitter:http://twitter.com/jeffz_cn
>
> > *From:* 朱涛 <zhutao.is...@gmail.com>
> > *Sent:* Friday, July 03, 2009 6:17 PM
> > *To:* pon...@googlegroups.com
> > *Subject:* [TL] {讨论}ORM的意义何在?
>
> > 大家好,最近一直困扰我的一个问题是关于ORM(对象关系映射)的。ORM的提出是具有相当牛的意义的,它封装了底层的sql,让开发人员无需了解DB,只需要操作熟悉的类即可,可是问题来了。无论何种的封装都会给性能带来一定的损失,而作为DB应用场景很重要的一个方面即是WEB应用,其对于DB的性能要求显得尤其高。很多PV很大的网站最终的性能瓶颈都是DB的读写,那么作为在sql之上的ORM更使得性能降低不少。
>
> > ORM的已有的意义是明确的(可能不够),即
>
> > 1. 简化OO程序员的编程
> > 2. 便于数据库迁移
复杂的还是写sql
orm的方便在于查询条件的组合
比如
a=XXX.where(xx=xxx)
if xxxx:
a=XXX.where("xxx=%s",xx)
for i in a:
....
如果拼接sql就麻烦的多
还容易有注入
同前所述,ORM 毕竟也是现实需求产生的,
节省直接写SQL 出错,加速80% 一般性的数据操作代码编写,
这就足够了,
如果出现频繁要手工写SQL 来完成数据提取的话,
应该质疑:
- DB 设计不合理
- 应用设计不合理
而不是质疑ORM 的作用...
想 GAE, 干脆就只给 键值形式的 BigTable ,不用"成熟","宏大"的关系DB,
一样出彩 ;-)
http://zoomquiet.org 人生苦短,Pythonic!-)
usage 7-zip to replace WinRAR/WinZip; You can get the truely Freedom 4
software.
企业系统还是离不开RDBMS的,毕竟RDBMS已经非常成熟,实践很多,而且现有项目都是构建在这上面的。
云计算要深入企业,还是逃不开RDBMS。
说到这里可能值得补充一句,理论上灵活的ORM其实不一定是O“R”M,换一个实现方式,可以变成O*M,这也是使用ORM(理论上)的好处之一吧。
Jeffrey Zhao
Blog: http://www.cnblogs.com/JeffreyZhao
Twitter: http://twitter.com/jeffz_cn
--------------------------------------------------
From: "Zoom.Quiet" <zoom....@gmail.com>
Sent: Friday, July 03, 2009 9:07 PM
To: <pon...@googlegroups.com>
Subject: [TL] Re: {讨论}ORM的意义何在?
与使用RAW SQL 相对的是使用ORM框架
而不是ORM。 即使不用ORM框架 我们常常要做ORM只不过是通过写SQL来手工完成。
所以我建议把标题改为 "ORM框架的意义何在?"
ORM框架是有性能损失。 但它还是被经常使用有2点原因。
1. 虽然性能有损失 但它不是系统性能的瓶颈
2. 它太方便了 :) 损失些性能就损失吧 :p
在一个项目中有相当多的数据库操作都是十分简单的手工完成这些操作枯燥无聊而且这些操作从很少需要从SQL上进行优化(它们太简单了没有什么优化的余
地) , 就算进行优化也是在数据库上优化(比如调整索引之类)。 对于这种操作不找个框架来做那就是对自己的折磨( 我曾经受了很长时间的这种折
磨.......)
至于你说"在一个应用中既写ORM,也写raw sql" 这个我认为不对 ORM框架本来就不是用来处理所有的情况的混着用没什么不可.
我认为互联网应用的业务逻辑相对简单 而且它的性能问题是通过cache之类的方式来解决。 这点性能损失算不了什么。
(我没做过互联网应用猜测而已。。。 不对请拍砖。 谢谢)
事实上
我们公司
没有用的Orm的
分歧点
主要是
有同学认为
用Orm代码风格不统一
性能没什么区别.......
---------------
张沈鹏 <zsp007> 2009年6月25日 下午10:14
Orm(或者叫DAL?)好处是不需要拼接sql
可以避免注入
用起来也很有快感
现在我正式推出震撼的整合mc,sqlstore的orm brach
分支地址
URL: https://xxx/svn//branches/zuroc/zzzorm
部分用法演示
In [12]: from luzong.autumn import Model
In [13]: class User(Model):pass
....:
init User
In [14]: for i in User.where()[:3]:print i.screen_name
....:
席殊
斯曼
旌旗
In [15]: print User.count()
163
In [16]: print User.where("id>%s",3).where("id<40").order_by("id desc").count()
0
In [17]: print User.where("id>%s",3).where("id<10000").order_by("id
desc").count()
32
In [18]: for i in User.where("id>%s",3).where("id<10000").order_by("id
desc")[:10]:print i,
....:
<__main__.User object at 0x866882c> <__main__.User object at
0x866826c> <__main__.User object at 0x86685cc> <__main__.User object
at 0x86686cc> <__main__.User object at 0x866896c> <__main__.User
object at 0x86687ac> <__main__.User object at 0x8668a2c>
<__main__.User object at 0x866878c> <__main__.User object at
0x866858c> <__main__.User object at 0x86687ec>
In [19]: User.mc.get
KeyboardInterrupt
In [19]: for i in User.where("id>%s",3).where("id<10000").order_by("id
desc")[:10]:print i.id
....:
2001
1068
1067
1066
1065
1064
1063
1062
1061
1060
In [20]: for i in User.where("id>%s",3).where("id<10000").order_by("id
desc")[:10]:print i.id
KeyboardInterrupt
In [20]: User.mc_get(2001)
Out[20]: <__main__.User object at 0x86c6a4c>
In [21]: User.mc_get_list([2001,2002])
Out[21]: [<__main__.User object at 0x86c6d8c>, None]
In [22]: User.mc_get_list([2001,1060])
Out[22]: [<__main__.User object at 0x86c672c>, <__main__.User object
at 0x86c6e8c>]
In [23]: User.mc_get_multi([2001,1060])
Out[23]:
{1060: <__main__.User object at 0x8668b2c>,
2001: <__main__.User object at 0x8668b4c>}
CJ 2009年6月26日 上午12:41
回复: xxx
收件人: xxx
再给个性能分析?
CJ
2009/6/25 张沈鹏 <zsp007>
B
回复: xxx
收件人: xxx
性能?这个就是自动做escape的字符串拼接吧, 如果一样的sql, 性能不会有差吧, 应该是没有手写sql灵活
2009/6/26 CJ <>
CJ <> 2009年6月26日 上午10:01
回复: xxx
收件人: xxx
如 User.where()[:10] 也是吗?
CJ
2009/6/26 bear tung <>
张沈鹏 <zsp007> 2009年6月26日 上午10:33
收件人: xxx
https://xxx/svn//branches/zuroc/zzzorm/luzong/autumn/db/query.py
def __getitem__(self, k):
if self.cache != None:
return self.cache[k]
if isinstance(k, (int, long)):
self.limit = (k, 1)
lst = self.get_data()
if not lst:
return None
return lst[0]
elif isinstance(k, slice):
if k.start == 0 and k.stop is None:
self.limit = ()
elif k.start is not None:
assert k.stop is not None, "Limit must be set when an
offset is present"
assert k.stop >= k.start, "Limit must be greater than
or equal to offset"
self.limit = k.start, (k.stop - k.start)
elif k.stop is not None:
self.limit = 0, k.stop
return self.get_data()
QH 2009年6月26日 上午10:44
回复: xxx
收件人: xxx
我倾向的不是一个ORM,也不是DAL,而是一个sql generator
sql和object绑定这件事情有点重,而且也和旧代码的风格有挺大差异,如果要做ORM的话,会导致代码前后风格不一致,增加维护难度。
而一个sql generator可以比较好的解决手写sql麻烦和不安全的问题(其实我并不这么觉得),比如
sql = sqlgen.select('user').where('id>%s', 3).where('id<%s',
40).order_by('id desc').count().to_sql()
cursor.execute(sql)
这样我就觉得足够了(其实和写原始sql比较起来,也差不了多少)。
这样的sql_generate的代码,可以看看web.py或者sqlalchemy,应该会有类似的实现。
2009/6/25 张沈鹏 <zsp007>:
--
QH
张沈鹏 <zsp007> 2009年6月26日 上午11:16
收件人: xxx
2009/6/26 QH:
> 我倾向的不是一个ORM,也不是DAL,而是一个sql generator
zuroc@frodo ~/dev/xxx_xss/luzong/autumn $ vi db/query.py
你说的就是这个
We can also chain the ``where`` method::
q.where(name='John').where(age=30)
In both cases the ``WHERE`` clause will become::
WHERE `name` = 'John' AND `age` = 30
You can also order using ``order_by`` to sort the results::
# The second arg is optional and will default to ``ASC``
q.order_by('column', 'DESC')
You can limit result sets by slicing the Query instance as if it were a
list. Query is smart enough to translate that into the proper ``LIMIT``
clause when the query hasn't yet been run::
q = Query(model=MyModel).where(name='John')[:10] # LIMIT 0, 10
q = Query(model=MyModel).where(name='John')[10:20] # LIMIT 10, 10
q = Query(model=MyModel).where(name='John')[0] # LIMIT 0, 1
Simple iteration::
for obj in Query(model=MyModel).where(name='John'):
# Do something here
Counting results is easy with the ``count`` method. If used on a ``Query``
> sql和object绑定这件事情有点重,而且也和旧代码的风格有挺大差异,如果要做ORM的话,会导致代码前后风格不一致,增加维护难度。
我到不觉得难懂
正如mako和ptl并存
没有人觉得mako难懂
QH 2009年6月26日 上午11:23
回复: xxx
收件人: xxx
2009/6/26 张沈鹏 <zsp007>:
> 2009/6/26 QH:
>> 我倾向的不是一个ORM,也不是DAL,而是一个sql generator
>
>
> zuroc@frodo ~/dev/xxx_xss/luzong/autumn $ vi db/query.py
> 你说的就是这个
>
>
> We can also chain the ``where`` method::
>
> q.where(name='John').where(age=30)
>
> In both cases the ``WHERE`` clause will become::
>
> WHERE `name` = 'John' AND `age` = 30
>
> You can also order using ``order_by`` to sort the results::
>
> # The second arg is optional and will default to ``ASC``
> q.order_by('column', 'DESC')
>
> You can limit result sets by slicing the Query instance as if it were a
> list. Query is smart enough to translate that into the proper ``LIMIT``
> clause when the query hasn't yet been run::
>
> q = Query(model=MyModel).where(name='John')[:10] # LIMIT 0, 10
> q = Query(model=MyModel).where(name='John')[10:20] # LIMIT 10, 10
> q = Query(model=MyModel).where(name='John')[0] # LIMIT 0, 1
>
> Simple iteration::
>
> for obj in Query(model=MyModel).where(name='John'):
> # Do something here
>
> Counting results is easy with the ``count`` method. If used on a ``Query``
看起来似乎就是这个。如果有人喜欢用这样的库去生成sql,我没有意见,虽然我自己还是会直接写sql,但是至少我很容易理解别人的代码究竟干了些什么。
>> sql和object绑定这件事情有点重,而且也和旧代码的风格有挺大差异,如果要做ORM的话,会导致代码前后风格不一致,增加维护难度。
>
> 我到不觉得难懂
> 正如mako和ptl并存
> 没有人觉得mako难懂
我也没有说mako难懂。我是说一个项目里又有mako又有ptl难维护。我不止一次的干过为了改某个页面元素要到ptl里去改一遍,再到mako里去改一遍的事情。
--
张沈鹏 <zsp007> 2009年6月26日 上午11:28
收件人: xxx
> 看起来似乎就是这个。如果有人喜欢用这样的库去生成sql,我没有意见,虽然我自己还是会直接写sql,但是至少我很容易理解别人的代码究竟干了些什么。
orm 主要是修改起来方便
user = User.get(1324)
user.name = name
user.age =age
user.save()
User(name="xxxx",age="xxx").save()
User.where(age=10).update(name="xxxx")
这样写起来比写sql方便很多
QH 2009年6月26日 上午11:32
回复: xxx
收件人: xxx
2009/6/26 张沈鹏 <zsp007>:
是方便,但只方便一点点。用sql就是这样两条语句
update user set name='xxxx', age='xxx' where id=1324;
update user set name='xxxx' where age=10;
张沈鹏 <zsp007> 2009年6月26日 下午12:24
收件人: xxx
>> User.where(age=10).update(name="xxxx")
>>
>> 这样写起来比写sql方便很多
>
> 是方便,但只方便一点点。用sql就是这样两条语句
User.where(age=age).update(name=name)
VS
cursor = store.get_cursor(table="user")
cursor.execute("update user set name=%s where age=%s",name,age)
cursor.connection.commit()
>
> update user set name='xxxx', age='xxx' where id=1324;
> update user set name='xxxx' where age=10;
>
> --
> QH
>
--
张沈鹏 <zsp007> 2009年6月26日 下午1:07
收件人: xxx
def register_subject_image(subject_id, label, user_id, x, y, filesize):
cursor = store.get_cursor()
cursor.execute('select id, x, y, filesize from subject_image where
subject_id=%s', (subject_id, ))
for r in cursor.fetchall():
if -2 < filesize-r[3] < 2 and x == r[1] and y == r[2]:
return r[0]
cursor.execute("insert into subject_image
(subject_id,user_id,label,x,y,filesize) values (%s,%s,%s,%s,%s,%s)",
(subject_id, user_id, label, x, y, filesize))
pid = cursor.lastrowid
if pid:
cursor.connection.commit()
return pid
else:
cursor.connection.rollback()
def register_subject_image(subject_id, label, user_id, x, y, filesize):
for img in SubjectImage.where(subject_id)
if img and abs(img.filesize-filesize)<2 and x == img.x and y == img.y:
return img.id
img = SubjectImage()
img.subject_id = subject_id
img.user_id=user_id
img.label=label
img.x=x
img.y=y
#也可以这么写 img =
SubjectImage(x=x,y=y,label=label,user_id=user,subject_id=subject)
img.save()
return img.id
我不清楚原来这里为什么要rollback,但如果要rollback可以这样写
img.begin()
try:
img.save()
img.commit()
except:
img.rollback()
张沈鹏 <zsp007> 2009年6月26日 下午1:07
收件人: xxx
哦 上面是举一个例子
张沈鹏 <zsp007> 2009年6月26日 下午1:09
收件人: xxx
for img in SubjectImage.where(subject_id)
这一句应该是
for img in SubjectImage.where(subject_id=subject_id)
QH 2009年6月26日 下午1:28
回复: xxx
收件人: xxx
2009/6/26 张沈鹏 <zsp007>:
> User.where(age=age).update(name=name)
难道你这里就直接commit了?要做trasaction怎么办?
如果想auto commit的话,封装一个函数就行:
store.commit_sql("update user set name=%s where age=%s", (name, age),
table='user')
def commit_sql(self, sql, paras=(), table=None):
if table is None:
table = find_table_from_sql(sql)
cursor = self.get_cursor(table=table)
cursor.execute(sql, paras)
cursor.commit()
> VS
> cursor = store.get_cursor(table="user")
> cursor.execute("update user set name=%s where age=%s",name,age)
> cursor.connection.commit()
--
张沈鹏 <zsp007> 2009年6月26日 下午1:38
收件人: xxx
2009/6/26 QH:
>
> 2009/6/26 张沈鹏 <zsp007>:
>> User.where(age=age).update(name=name)
>
> 难道你这里就直接commit了?要做trasaction怎么办?
见上面的那个例子
> 如果想auto commit的话,封装一个函数就行:
>
> store.commit_sql("update user set name=%s where age=%s", (name, age),
> table='user')
>
> def commit_sql(self, sql, paras=(), table=None):
> if table is None:
> table = find_table_from_sql(sql)
> cursor = self.get_cursor(table=table)
> cursor.execute(sql, paras)
> cursor.commit()
>
>> VS
>> cursor = store.get_cursor(table="user")
>> cursor.execute("update user set name=%s where age=%s",name,age)
>> cursor.connection.commit()
>
>
>
>
> --
> QH
>
> --~--~---------~--~----~------------~-------~--~----~
> 您收到此信息是由于您订阅了 Google 论坛“xxx-dev”论坛。
> 要在此论坛发帖,请发电子邮件到 xxx
> 要退订此论坛,请发邮件至 xxx-dev+u...@googlegroups.com
> 更多选项,请通过 http://groups.google.com/group/xxx-dev?hl=zh-CN 访问该论坛
> -~----------~----~----~----~------~----~------~--~---
>
>
bear tung <> 2009年6月26日 下午1:45
回复: xxx
收件人: xxx
不赞成auto commit, commit应该都是显式调用的
2009/6/26 QH
2009/6/26 张沈鹏 <zsp007>:
> User.where(age=age).update(name=name)
难道你这里就直接commit了?要做trasaction怎么办?
如果想auto commit的话,封装一个函数就行:
store.commit_sql("update user set name=%s where age=%s", (name, age),
table='user')
def commit_sql(self, sql, paras=(), table=None):
if table is None:
table = find_table_from_sql(sql)
cursor = self.get_cursor(table=table)
cursor.execute(sql, paras)
cursor.commit()
> VS
> cursor = store.get_cursor(table="user")
> cursor.execute("update user set name=%s where age=%s",name,age)
> cursor.connection.commit()
--
QH
--~--~---------~--~----~------------~-------~--~----~
QH 2009年6月26日 下午1:55
回复: xxx
收件人: xxx
那你上一封信就应该变成
x = User.where(age=age)
x.begin()
x.update(name=name)
x.commit()
VS
cursor = store.get_cursor(table="user")
cursor.execute("update user set name=%s where age=%s",name,age)
cursor.connection.commit()
复杂度还是相差不大
而且,如果是跨表的transaction怎么办?
2009/6/26 张沈鹏 <zsp007>:
> 2009/6/26 QH:
>>
>> 2009/6/26 张沈鹏 <zsp007>:
>>> User.where(age=age).update(name=name)
>>
>> 难道你这里就直接commit了?要做trasaction怎么办?
>
> 见上面的那个例子
>
>> 如果想auto commit的话,封装一个函数就行:
>>
>> store.commit_sql("update user set name=%s where age=%s", (name, age),
>> table='user')
>>
>> def commit_sql(self, sql, paras=(), table=None):
>> if table is None:
>> table = find_table_from_sql(sql)
>> cursor = self.get_cursor(table=table)
>> cursor.execute(sql, paras)
>> cursor.commit()
>>
>>> VS
>>> cursor = store.get_cursor(table="user")
>>> cursor.execute("update user set name=%s where age=%s",name,age)
>>> cursor.connection.commit()
>>
>>
>>
>>
>> --
>> QH
>>
>> >
>>
>
>
>
> --
> 弓长
> 孝文
> 、
> 王
> http://zsp.javaeye.com/
>
> >
>
--
张沈鹏 <zsp007> 2009年6月26日 下午2:38
收件人: xxx
2009/6/26 QH:
> 那你上一封信就应该变成
>
> x = User.where(age=age)
> x.begin()
> x.update(name=name)
> x.commit()
默认是auto commit
我喜欢auto commit
> VS
>
> cursor = store.get_cursor(table="user")
> cursor.execute("update user set name=%s where age=%s",name,age)
> cursor.connection.commit()
>
> 复杂度还是相差不大
>
> 而且,如果是跨表的transaction怎么办?
对一个begin,commit就可以了
--
QH 2009年6月26日 下午2:47
回复: xxx
收件人: xxx
2009/6/26 张沈鹏 <zsp007>:
> 2009/6/26 QH:
>> 那你上一封信就应该变成
>>
>> x = User.where(age=age)
>> x.begin()
>> x.update(name=name)
>> x.commit()
>
> 默认是auto commit
> 我喜欢auto commit
auto commit 的情况见我上一封信。直接用sql仅仅是一个函数封装的问题。
>> VS
>>
>> cursor = store.get_cursor(table="user")
>> cursor.execute("update user set name=%s where age=%s",name,age)
>> cursor.connection.commit()
>>
>> 复杂度还是相差不大
>>
>> 而且,如果是跨表的transaction怎么办?
> 对一个begin,commit就可以了
举个例子?
--
张沈鹏 <zsp007> 2009年6月26日 下午3:06
收件人: xxx
1.innodb事务是自动开启
所以只需要不提交就是一个事物
2.同一个数据库中的表的begin是同样的作用
db = new_class.db = sqlstore.get_farm_by_table(new_class.Meta.table)
...........
@classmethod
def begin(cls):
"""
begin() and commit() let you explicitly specify an SQL transaction.
Be sure to call commit() after you call begin().
"""
db = cls.db
db.b_commit = False
@classmethod
def commit(cls):
db = cls.db
try:
cursor = db.get_cursor()
cursor.connection.commit()
finally:
db.b_commit = True
@classmethod
def rollback(cls,db=None):
db = cls.db
try:
cursor = db.get_cursor()
cursor.connection.rollback()
finally:
db.b_commit = True
QH 2009年6月26日 下午3:20
回复: xxx
收件人: xxx
举个例子吧:
两张表,user和location_stats。一个transaction:
把id为12345的user的location设置成'beijing',并把id为'beijing'的location_stats的user_count列加一。
直接用sql就是这样:
c = store.get_cursor(tables=['user', 'location_stats'])
c.execute("update user set location=%s where id=%s", ('beijing', 12345))
c.execute("update location_stats set user_count=user_count+1 where
id=%s", 'beijing')
c.connection.commit()
ORM呢?
2009/6/26 张沈鹏 <zsp007>:
>
张沈鹏 <zsp007> 2009年6月26日 下午3:26
收件人: xxx
User.begin()
User.where(id=1345).update(location="beijing")
LocationStats.where(id="beijing").update("user_count=user_count+1")
User.commit()
--
张沈鹏 <zsp007> 2009年6月26日 下午3:35
收件人: xxx
update 的参数是这样拼出来的
def update(self,*args,**kwds):
values = self.extract_condition_values()
update_set=[]
if args:
update_set.append(args[0])
values=args[1:]+values
if kwds:
update_set.append(
','.join(
"%s=%%s"%(
escape(k)
)
for k in kwds.keys()
)
)
values = list(kwds.values())+values
if update_set:
Query.raw_sql(
'UPDATE %s SET %s %s' % (
self.model.Meta.table_safe,
','.join(update_set),
self.extract_condition_keys() or ''
),
values,
self.db
)
也就是可以写
update("sss=xxx")
或
update("sss=%s",xxx)
或
update(ss=xxx)
或
update("sss=%s",xxx,xxx,xxxx=xxx)
张沈鹏 <zsp007> 2009年6月26日 下午3:45
收件人: xxx
本orm另外一大好处是集成了memcache
可以方便的mc_get_list([id_1,id_2]),mc_get_mulit([id_1,id_2]),mc_get(id)
并可以通过设置Meta中的mc_key,mc_ver来更换mc
mc_key默认是 表名@版本号:%s
# Assume id is the default
if not getattr(new_class.Meta, 'pk', None):
new_class.Meta.pk = 'id'
if not getattr(new_class.Meta,'mc_key',None):
mc_ver = getattr(new_class.Meta,"mc_ver","")
new_class.Meta.mc_key = "%s@%s:%%s"%(name,mc_ver)
QH 2009年6月26日 下午4:23
回复: xxx
收件人: xxx
2009/6/26 张沈鹏 <zsp007>:
> User.begin()
> User.where(id=1345).update(location="beijing")
> LocationStats.where(id="beijing").update("user_count=user_count+1")
> User.commit()
>
>> c = store.get_cursor(tables=['user', 'location_stats'])
>> c.execute("update user set location=%s where id=%s", ('beijing', 12345))
>> c.execute("update location_stats set user_count=user_count+1 where
>> id=%s", 'beijing')
>> c.connection.commit()
看起来和直接写sql的复杂度还是差不多。从代码上看不出来LocationStats和User是在同一个transaction内,个人感觉还不如sql代码来得直观。
另外,这里也没有判断LocationStats和User是否在同一个mysql server上的检查。
到目前为止,ORM还没有足够的好处驱动我迁移。我觉得一个sql generator加稍许常用调用pattern的封装足矣。
其他人的意见?
--
张沈鹏 <zsp007> 2009年6月26日 下午4:29
收件人: xxx
cursor.execute("insert into deleted_note_photo "
"(`id`,seq_id,note_id,author_id,title,layout,album_id,"
"album_photo_id,`time`,delete_user_id,flag)"
" values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
(p.id, p.seq_id, p.note_id, p.author_id, p.title,
p.layout, p.album_id, p.album_photo_id,
p.creation_date, user.id, self.domain))
比起这样的代码
用orm显然好看的多
flycondor <flycondor> 2009年6月26日 下午4:32
回复: xxx
收件人: xxx
如果我们在数据库访问层遵循类似friendfeed的schemaless那样很简单的范式
那么orm价值比较大
在此之前,差别不大
基本上是学习路径决定个人喜好
2009/6/26 QH
2009/6/26 张沈鹏 <zsp007>:
张沈鹏 <zsp007> 2009年6月26日 下午4:34
收件人: xxx
在比如这样的代码 就是手工写了一个orm
sql ="""select `id`, seq_id, note_id, author_id, flag, title,
album_id, album_photo_id,
`time` from note_photo order by `id` desc limit %d, %d""" %
(start, start+limit)
cursor.execute(sql)
rs = cursor.fetchall()
ps = [NotePhoto(r[0],r[1],r[2],r[3],r[4],r[5],None,r[6],r[7],r[8])
for r in rs]
张沈鹏 <zsp007> 2009年6月26日 下午4:40
收件人: xxx
2009/6/26 flycondor <F>:
> 如果我们在数据库访问层遵循类似friendfeed的schemaless那样很简单的范式
> 那么orm价值比较大
可以在新的项目中试试
QH 2009年6月29日 上午11:06
回复: xxx
收件人: xxx
2009/6/26 flycondor <flycondor>:
> 如果我们在数据库访问层遵循类似friendfeed的schemaless那样很简单的范式
> 那么orm价值比较大
即使在那种情况下,我也会建议把ORM和DAL分离开来。DAL是必须的,ORM是可选的。ORM构建在DAL之上。
> 在此之前,差别不大
> 基本上是学习路径决定个人喜好
这不是喜好的问题。我自己的个人项目里经常会使用ORM来定义数据结构,因为ORM更适合从程序员的角度考虑问题,而不是从数据存储的角度考虑问题。
我反对在代码中引入ORM的最主要理由是代码维护难度。从mako和权限框架两次重构经验我们看到,一旦代码中同时出现两种风格,会使得代码整体的易读性和可维护性变低,学习成本也会大大提高。因此除了能够带来明显的好处以外(mako和权限框架我认为是属于有明显好处的例子),我不赞成在代码中再次引入新风格。要么把所有代码都重构成新风格,要么能够说服所有开发者愿意接受在有两种风格的code
base上进行开发。这也是为什么上次石头讨论前端代码重构时我强调这件事情要么不做,要么就要做完的原因。
是只要一层
另外就是
类似friendfeed那种用法
用orm比较方便
但是, 从我个人的角度来看, 在性能很critical的情况下,raw sql是更合适的.
On 7/3/09, 朱涛 <zhutao...@gmail.com> wrote:
> 大家好,最近一直困扰我的一个问题是关于ORM(对象关系映射)的。ORM的提出是具有相当牛的意义的,它封装了底层的sql,让开发人员无需了解DB,只需要操作熟悉的类即可,可是问题来了。无论何种的封装都会给性能带来一定的损失,而作为DB应用场景很重要的一个方面即是WEB应用,其对于DB的性能要求显得尤其高。很多PV很大的网站最终的性能瓶颈都是DB的读写,那么作为在sql之上的ORM更使得性能降低不少。
> ORM的已有的意义是明确的(可能不够),即
>
> 1. 简化OO程序员的编程
> 2. 便于数据库迁移
>
> 但是一个网站或者服务它对数据库迁移(如从mysql到oracle)的迁移要求有多高呢?我觉得绝大多数的应用是没有这样的需求的。
> 至于1的简化编程,我觉得意义也不大,因为对于一个合格的程序员来讲sql也应是其技能的一部分,即使代码看起来ugly些(很多人说raw
> sql写入程序中是很ugly的,我不以为然)。
>
> 到此是否可以推断ORM对于没有数据库迁移需求的合格程序员而讲是没有任何意义的呢?或者说我的理解是错误的呢?
>
> 那么ORM的意义究竟何在?欢迎大家指正和讨论。
>
> ps 近期一个使用django(python的一种framework)的朋友每天的工作竟是将已有的ORM代码改回raw sql,我不禁想起了上面这些。
> --
>
> 朱涛 Tower Joo
> http://twitter.com/towerjoo
> http://sites.google.com/site/towerjoo
>
--
Sent from my mobile device
Regards,
Linker Lin
linker...@gmail.com
2009/7/9 Jeffrey Zhao <je...@live.com>:
> 一个项目又有多少复杂SQL,使用ORM也没有阻止这个项目使用RAW SQL。
>
> 怀疑Oracle的什么呢?Oracle,SQL
> Server这样的商业RDBMS的性能和功能,都不是MySQL,PostgreSQL这样的开源数据库可以比拟的----价格除外。
--
wing
wing9...@gmail.com
Hope is a good thing, maybe the best of things.