【问题标题】:Accessing grails/hibernate generated SQL for domain class访问 grails/hibernate 为域类生成的 SQL
【发布时间】:2012-09-11 08:29:58
【问题描述】:

在我的 grails (1.3.7) 应用程序中,我使用 JDBC 模板从 CSV 文件批量导入 1000 条记录(因为它比使用普通 GORM/hibernate 快得多,正如您所期望的那样)。

例如

class Book {
    String title
}

// For each CSV record...
insertStatementList.add("insert into book (id, title) values (nextval('hibernate_sequence'), thetitle)")
...
JdbcTemplate bulkInsert = ...
bulkInsert.batchUpdate(insertStatementList)

这种方法的问题在于域类的更改(例如添加subject 属性)需要同时更改域类和 SQL 插入语句。

由于 GORM/hibernate 堆栈最终必须从域类定义派生 SQL,有没有办法访问此功能,以便我不必单独维护 SQL 插入语句?或者在伪代码中,可能类似于以下内容:

// would return something like:
// "insert into book (id, title) values (nextval('hibernate_sequence'), 'thetitle')"
def insertStatement = hibernate.getSqlInsertForClass(Book, book.properties)

【问题讨论】:

  • 我不知道你的优化要求是什么,但你仍然可以使用hibernate,保持gorm映射的灵活性并获得惊人的性能。考虑一下:naleid.com/blog/2009/10/01/… --- 至于获取休眠插入语句,我认为他们不会为此公开 api,因此您必须拦截日志或 jdbc 驱动程序。挺麻烦的。
  • @Raphael 我已经尝试过链接中的方法(刷新休眠缓存等),但我发现使用 JDBC 模板与 GORM 相比快 3 倍,我的分析器显示大部分额外的时间花在构建 GORM(和相关的)对象上。
  • 我应该补充一点,首先使用链接中的方法确实提供了显着的加速 - 但仍然不如 JDBC 模板。
  • 你云尝试使用从域映射的持久属性来构建你的 sql 语句。此方法返回这样的列表:new DefaultGrailsDomainClass(YourDomainClazz).persistantProperties
  • @Raphael 感谢您的建议 - 但我认为它并不能真正回答我的问题。是的,我可以编写一些代码来从域类 def 动态构建 SQL,但我的观点是 hibernate 必须已经在这样做(也许不能公开访问,但尽管如此,它正在发生)

标签: hibernate grails grails-orm jdbctemplate


【解决方案1】:

我对 grails 的了解还不足以说明其中是否有可能。我尝试通过列出类属性并动态组合它来生成 SQL 插入字段,但它出现了乱序。

您可以创建一个注释来定义该字段在 CSV 中的位置:

import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME) @interface CSV { int position() }

class Product {
  @CSV(position=1) String description
  @CSV(position=3) BigDecimal price
  @CSV(position=4) Integer type
  @CSV(position=2) boolean soldout
}

如果您需要多个映射(例如,为了支持您自己的旧 CSV),您应该考虑映射结构或 XML,这将与实体分离。

然后你需要迭代字段来组成查询:

def csv = '''rice;10.0;3;false
beet;12.0;2;false
mango;22.0;2;true'''

def properties = Product.declaredFields
  .findAll { it.declaredAnnotations }
  .sort { it.declaredAnnotations[0].position() }

def templateQuery = "INSERT INTO product(#fields) VALUES (#values)"

csv.eachLine { line ->
  def fields = line.split( /;/ )
  def sqlFields = [:]

  fields.eachWithIndex { field, i ->
    sqlFields[properties[i].name] = field
  }

  println templateQuery
    .replace('#fields', sqlFields.keySet().join(","))
    .replace('#values', sqlFields.values().join(","))
}

哪些打印:

INSERT INTO product(description,price,type,soldout) VALUES (rice,10.0,3,false)
INSERT INTO product(description,price,type,soldout) VALUES (beet,12.0,2,false)
INSERT INTO product(description,price,type,soldout) VALUES (mango,22.0,2,true)

它非常原始,需要一些修饰,例如引号和一些反对 sql 注入的东西,但它可以工作,更像是概念证明。

在我工作的旧系统中,我们使用 jboss seam 并且在我们使用 jpa 批处理的那些散装东西上,即在所有 persist() 完成后手动刷新。你确定 grails 上没有类似的东西可以与 gorm 一起使用吗?

This link 展示了一篇在 grails 中使用批量更新的博文,使用 withTransaction 同时定期应用会话清除:

    List <Person> batch =[]
    (0..50000).each{
       Person person= new Person(....)
        batch.add(person)
        println "Created:::::"+it
        if(batch.size()>1000){
            Person.withTransaction{
                for(Person p in batch){
                    p.save()
                }
            }
            batch.clear()
        }
      session = sessionFactory.getCurrentSession()
      session.clear()             
    }

你确定这不起作用?如果没有,那么注释可能是一个解决方案。

由于java的驼峰大小写和db中的下划线之间的差异,列命名也存在问题。在休眠状态下,该翻译背后的人是ImprovedNamingStrategy。也许你能从他那里得到一些东西。或者在 @CSV 注释中添加列名。听起来像是回收 JPA :-)。

还有log4jdbc,但我认为它不会解决你的问题:你需要潜入休眠sql生成。

【讨论】:

  • 谢谢,但请参阅我在 Raphael 的回答中的最新评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-20
  • 1970-01-01
  • 2021-10-30
  • 2015-08-30
  • 2015-10-12
  • 2011-04-13
  • 2014-10-19
相关资源
最近更新 更多