Доступ к серверу Grails/hibernate для SQL-класса

В моем приложении grails (1.3.7) я использую шаблон JDBC для массового импорта 1000-х записей из файла CSV (поскольку он намного быстрее, чем использование ванильного 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 insert.

Так как стек 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)

Ответ 1

Я не знаю, достаточно ли грааля, чтобы сказать, возможно ли это в нем. Я попытался создать поля вставки 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 и те части, которые мы работали с пакетом jpa, т.е. ручная очистка, когда все persist() были выполнены. Вы уверены, что нет ничего подобного в grails для использования с gorm?

Эта ссылка показывает сообщение в блоге об использовании пакетного обновления в grails, используя withTransaction, в то время как регулярное использование сеанса clear:

    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 camel и подчеркиванием в db. Парень за этим переводом в спящем режиме - это ImprovedNamingStrategy. Может быть, вы можете что-то получить от него. Или добавьте имя столбца в аннотацию @CSV. Похоже на переработку JPA: -).

Существует также log4jdbc, но я думаю, что это не решит вашу проблему: вам нужно прокрасться в hibernate sql поколение.