实现Burt Beckwith式GORM的性能-没有集合

来源:互联网 发布:excel sql语句 编辑:程序博客网 时间:2024/06/07 06:07

不再使用GORM/Hibernate集合?

Burt Beckwithhttp://burtbeckwith.com/ 先生讲述了在 Grails中提高GORM的性能。他的演讲在 http://www.infoq.com/presentations/GORM-Performance

他的观点是在你的域对象中使用集合因为在对象被添加之前,集合可能需要从数据库中被完全加载

我决定在我的工作项目中使用它。这个项目有18个域类。我遇到了一些问题,这些问题让我头疼几个小时,然后我才解决这些问题。更新整个项目从“hasMany/belongsTo“风格到没有集合风格它花费了我半天的时间 这里是一个非常简单的域类采用典型的 GORM 一对多的级联关系

购物车程序示例

让我们用一个购物车示例代码开始。我们有一个购物车对象包含许多Item对象

典型的GORM 关系

grails-app\domain\shopping\Cart.groovy

package shoppingclass Cart {String namestatic hasMany = [ items : Item ]static mapping = {sort "name"items sort:"name"}static constraints = {    name nullable:false, blank:false, unique:true    }    String toString() {    "Cart $name"    }}

grails-app\domain\shopping\Item.groovy

package shoppingclass Item {String nameInteger quantitystatic belongsTo = [ cart : Cart ]static mapping = {sort "name"}static constraints = {    name nullable:false, blank:false, unique:['cart']    quantity nullable:false    }    String toString() {    "$quantity $name"    }}

grails-app\conf\BootStrap.groovy

import shopping.*class BootStrap {    def init = { servletContext ->        def cart = new Cart(name:"alpha").save()    cart.addToItems new Item(name:"apples", quantity:10)    cart.addToItems new Item(name:"milk", quantity:2)    cart.addToItems new Item(name:"bread", quantity:3)    cart.save()    }    def destroy = {    }}

删除 hasMany 和 belongsTo

现在,遵照Burt Beckwith的建议,我们删除 belongsTo 和 hasMany 关系,并且在子类中嵌入父对象

现在我们的代码看起来像这样:

BelongsTo 和HasMany 被删除

grails-app\domain\shopping\Cart.groovy

package shoppingclass Cart {String namestatic mapping = {sort "name"}    static constraints = {    name nullable:false, blank:false, unique:true    }        String toString() {    "Cart $name"    }}

grails-app\domain\shopping\Item.groovy

package shoppingclass Item {String nameInteger quantityCart cartstatic mapping = {sort "name"}    static constraints = {    name nullable:false, blank:false, unique:['cart']    quantity nullable:false    }        String toString() {    "$quantity $name"    }}

grails-app\conf\BootStrap.groovy

import shopping.*class BootStrap {    def init = { servletContext ->    def cart = new Cart(name:"alpha").save()    new Item(cart:cart, name:"apples", quantity:10).save()    new Item(cart:cart, name:"milk", quantity:2).save()    new Item(cart:cart, name:"bread", quantity:3).save()    }        def destroy = {    }}

这是所有的修改。

在 Cart.groovy 文件

  • 删除了static hasMany = [ items : Item ]
  • 从mappings中删除了 items sort:”name” 

在 Item.groovy 文件

  • 删除了 belongsTo
  • 增加了 Cart cart

在 BootStrap.groovy 文件

  • 修改了我们创建子类对象的方式.
    • 从: cart.addToItems new Item(name:”apples”, quantity:10)
    • 到 : new Item(cart:cart, name:”apples”, quantity:10)

新的问题和解决方案

我们已经完成了吗?当然不是–它并非那么简单。还有一些其他的问题需要解决::

  1. 子类对象items访问不再那么容易。
  2. 集合排序(在mapping中定义)不再发生。
  3. 不能删除父对象因为外键关系.
  4. 脚手架不再显示子类的对象.

让我们一次解决这些问题。

子类对象items访问不再那么容易

这个问题很容易修改。我们在Cart类中只是增加一个新的方法到父对象(Cart),它返回子对象的集合(Item):

grails-app\domain\shopping\Cart.groovy

    ....    def getItems() {    Items.findAllByCart(this)    }    ....

集合排序(在mapping中定义)不再发生.

这个问题也很容易。我们只需要稍微修改上面的代码. 我们增加了一个排序的参数到findAllBy*方法中。

grails-app\domain\shopping\Cart.groovy

    ....    def getItems() {        Item.findAllByCart(this, [sort:"name"])    }    ....

现在,我们能访问购物车中的items就像我们有了一个hasMany的关系.

    println cart.items

不能删除父对象因为外键关系.

这是令人头痛的问题。不是因为它很困难,但它需要做一些小的研究找到一个优雅的解决方案.

这个问题是如果我做一个购物车的删除操作,它将会失败因为购物车中有items。在老的hasMany / belongsTo方案中将会自动级联删除items和cart。现在我们没有了,因此我们必须手动做删除. 

这是我知道的最好的解决方案(如果有一个更好的方案让我知道)是在Cart对象上使用beforedelete事件对象。

在一个对象被删除之前,GORM会触发 beforeDelete事件。你能将代码放置在beforeDelete方法中,做你想做的任何事情。在我们的例子中,我们将删除子类的item对象。想看一看beforeDelete详细文档请访问 这里.

让我们修改Cart.groovy 的代码和增加beforeDelete方法.

grails-app\domain\shopping\Cart.groovy

    ....    def beforeDelete() {        Item.withNewSession { items*.delete() }    }    ....

这是我喜欢的Groovy的原因一行代码能那么多的工作

首先,我们建立了一个Hibernate的session。如果你读了关于beforeDelete的文档,你应该知道(这种情况)使用一个新的session为删除操作是为了避免产生StackOverflow异常。

其次,我们删除子类items的所有对象。我们使用groovy的扩展操作符调用列表中所有项的方法。

所以这一行代码父对象(Cart)被删除时,所有的子对象(Item将被删除

脚手架不再显示子类的对象.

自从父类对象不再知道关于子类对象以后,默认的Grails的脚手架不再显示购物车中的项目。我不知道解决的方式,除了不使用脚手架。

最终Cart 和 Item 的代码

grails-app\domain\shopping\Cart.groovy

package shoppingclass Cart {String namestatic mapping = {sort "name"}static constraints = {name nullable:false, blank:false, unique:true}String toString() {"Cart $name"}def getItems() {Item.findAllByCart(this, [sort:"name"])}def beforeDelete() {Item.withNewSession { items*.delete() }}    }

grails-app\domain\shopping\Item.groovy

package shoppingclass Item {String nameInteger quantityCart cartstatic mapping = {sort "name"}static constraints = {name nullable:false, blank:false, unique:['cart']quantity nullable:false}String toString() {"$quantity $name"}}

我的观点

实现困难(Implementation Difficulty)

一旦我知道了怎样去实施这一方案,部署它在我的域类里那是相当简单。记住你需要更新任何使用addTo* 和removeFrom*方法的代码。

复杂性/维护(Complexity / Maintenance

这种解决方案使我的应用程序理解更复杂吗?我不这么认为。一个有经验的Groovy编程者能够理解的cart.getItems()方法。beforeDelete需要一点时间来理解,但它很快就学会了。

同样使用belongsTo / hasMany也很复杂。请看GORM Gotchas Part 2。我不认为这一方案比使用belongsTo / hasMany更复杂.

性能(Performance)

还不知道。我没有在一个真实的应用中考虑过子类对象加载的性能问题。

我会再次这么做(Would I Do It Again

肯定。我有一个项目崩溃了,因为它有超过20个对象和 超过30个关系。Hibernate 和GORM 导致各种异常,当时,对于如何解决问题我太缺乏经验我考虑尝试使用“没有集合”技术,看看我是否能完成这个项目。我有一个好的预感,这个方法行不通,但我需要证明这一点。

英文原文:Implementing Burt Beckwith’s GORM Performance – No Collections

备注:英文原文回复更精彩,有兴趣的朋友可以看英文回复。


0 0
原创粉丝点击