[Grails] Пару последних граблей с GORM


В последнее время несколько раз получал ошибки из-за неопытности в некоторых аспектах GORM и Hibernate. В этом плане GORM постоянный источник неприятных сюрпризов.
И сегодня увидев отличную статью Advanced GORM Features: Inheritance, Embedded Data, Maps and Lists Storing решил записать пока не забыл.

1. Если вы используете наследование в доменых объектах, то не забывайте миграцию при изменении имён классов

GORM позволяет использовать наследование. Классы наследники могут сохранятся либо в разные таблицы table-per-subclass либо все в одну table-per-hierarchy (по умолчанию).
Обратите внимание, что если у вас в одной таблице (table-per-hierarchy) то у вас будет ещё одно поле discriminator с именем class по которому собственно GORM и будет понимать какого конкретно класса эта запись.
Например для такой иерархии классов

class Content {
    String title
    User author
}

class Blog extends Content {
    Date dateCreated
}

class Book extends Content {
    String isbn
}

Будет такая таблица

+---------------------------------------------------------------------------------------+
| content                                                                               |
+---------------------------------------------------------------------------------------+
|id | class            | title            | author_id | date_created | isbn             |
|1  | com.example.Blog | My post          | 1         | '2014-1-1'   | null             |
|2  | com.example.Book | Grails in action | 2         | null         | '978-1933988931' |
+---------------------------------------------------------------------------------------+

Как вы видите, в поле class сохраняется полное имя класса. В момент выборки GORM по этому полю пытается создать этот класс и гидрировать данные из записи.
И тут кроется опасность: если вы допустим поменяли у себя имя класса или его пакет, то все старые записи не смогут извлечься. И самое противное что об этом вы можете узнать только когда уже задеплоите новую версию на продакшен.
Поэтому, если вы поменяли имя доменного класса, не забудьте добавить в скрипт миграции с помощью migration plugin (надеюсь, вы профессионалы, и используете его).
Например, если вы поменяли пакет класса Content и его наследников с com.example на com.example.content то вам следует написать примерно такой скрипт миграции:

databaseChangeLog = {
    changeSet(author: 'John Doe', id: '033_change_content_class-1') {
        update(tableName: "content") {
            column(name: "class", type: "varchar(255)", value: 'com.example.content.Blog')
            where("class = 'com.example.Blog'")
        }
    }
    changeSet(author: 'John Doe', id: '033_change_content_class-2') {
        update(tableName: "content") {
            column(name: "class", type: "varchar(255)", value: 'com.example.content.Book')
            where("class = 'com.example.Book'")
        }
    }
}

2. Не помечайте абстрактные доменные классы модификатором abstract, глючит

Наткнулся на какой-то хитрый баг. Если допустим вы пометите Content как абстрактный, что так и есть, то биндинг подклассов перестаёт почему то работать. Уже забыл детали, забыл зафайлить багу. Grails 2.2.3, возможно поздние версии работают.

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s