访问grails/hibernate生成的域类SQL

Jon*_*ess 11 grails hibernate grails-orm jdbctemplate

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

例如

class Book {
    String title
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Wil*_*ill 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
}
Run Code Online (Sandbox Code Playgroud)

如果您需要多个映射(例如,为了支持您自己的较旧的 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(","))
}
Run Code Online (Sandbox Code Playgroud)

哪个打印:

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)
Run Code Online (Sandbox Code Playgroud)

它非常原始,需要一些改进,比如引用和一些反对 sql 注入的东西,但它有效,更像是一个概念证明。

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

此链接显示了一篇在 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()             
    }
Run Code Online (Sandbox Code Playgroud)

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

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

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