Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Hibernate+Spring автогенерация филда, Необходимо скопировать ключ в филд 
V
    Опции темы
CPlusPlusFAN
Дата 31.5.2015, 14:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

Репутация: нет
Всего: нет



Приветствую!

Возникла такая проблема. Вот есть энтити:
Код

@Entity
@Table(name = "test")
public class TestEntity {
    @Id
    @GeneratedValue(generator = "uuid_gen")
    @GenericGenerator(name = "uuid_gen", strategy = "ru.test.hiber.entities.UUIDGenerator")
    @Column(name = "id", length = 32, nullable = false, unique = true)
    private String id;
    @Column(name = "data", length = 32, nullable = false)
    private String data;

    // Get & Set
}

public class UUIDGenerator implements IdentifierGenerator {
    @Override
    public Serializable generate(SessionImplementor session, Object obj) {
        UUID uuid = UUID.randomUUID();
        String result = uuid.toString().replaceAll("-", "");
        return result;
    }
}


В его поле data нужно записать значение ключа id этой же энтити. Иными словами, для каждой записи в таблице test должны совпадать значения полей id и data.
У него data NOT NULL, поэтому хорошо бы обойтись одним персистом.
Можно ли в Spring+Hibernate при выполнении персиста перед непосредственно самим персистом, но после генерации ключа скопировать этот ключ?
@EntityListener пробовал - не заработал.

Премного благодарен!
PM MAIL ICQ Jabber   Вверх
AntonSaburov
Дата 1.6.2015, 10:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Штурман
****


Профиль
Группа: Модератор
Сообщений: 5658
Регистрация: 2.7.2002
Где: Санкт-Петербург

Репутация: 8
Всего: 118



Может глянуть @PrePersist ?
PM MAIL WWW ICQ   Вверх
CPlusPlusFAN
Дата 1.6.2015, 10:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

Репутация: нет
Всего: нет



Код

@Entity
@Table(name = "test")
@EntityListeners(PrePersistListener.class)
public class TestEntity {
// Missing
}

class PrePersistListener {
    @PrePersist
    public void prePersist(TestEntity testEntity )
    {
        // pre persist
    }
}

У меня не работало. Может там прототип метода должен быть другим?

Это сообщение отредактировал(а) CPlusPlusFAN - 1.6.2015, 14:37
PM MAIL ICQ Jabber   Вверх
AntonSaburov
Дата 2.6.2015, 10:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Штурман
****


Профиль
Группа: Модератор
Сообщений: 5658
Регистрация: 2.7.2002
Где: Санкт-Петербург

Репутация: 8
Всего: 118



Т.е. делаешь save и листенер не "слышит" ? Это может быть проблема реализации JPA. Какой провайдер для этого используется ?
PM MAIL WWW ICQ   Вверх
CPlusPlusFAN
Дата 2.6.2015, 10:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

Репутация: нет
Всего: нет



Хибер 3 и 4. А может это работает только для энтити менеджера? У меня сессион фактори.

Это сообщение отредактировал(а) CPlusPlusFAN - 2.6.2015, 10:52
PM MAIL ICQ Jabber   Вверх
AntonSaburov
Дата 2.6.2015, 16:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Штурман
****


Профиль
Группа: Модератор
Сообщений: 5658
Регистрация: 2.7.2002
Где: Санкт-Петербург

Репутация: 8
Всего: 118



Это функциональность JPA, так что по идее надо работать через EntityManager. Нужно попробовать.
PM MAIL WWW ICQ   Вверх
CPlusPlusFAN
Дата 2.6.2015, 20:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

Репутация: нет
Всего: нет



AntonSaburov, именно! Если юзать энтити менеджер, то события легко перехватываются.
Эх, генерация ключа происходит после перехвата события.  smile 

ПС В общем, сделал свою реализацию перехвата генерации ключа:
Код

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PostGenerate {
}

public class UUIDGenerator implements IdentifierGenerator, Configurable {
    private Object postGenerateInstance; // Инстанс листенера
    private List<Method> postGenerateMethods = new ArrayList<Method>(); // Колбек методы
    private boolean insideEntity; // Листенер - сам энтити

    static private final Class<?> KEY_CLASS = String.class;

    @Override
    public Serializable generate(SessionImplementor session, Object obj) {
        UUID uuid = UUID.randomUUID();
        String result = uuid.toString().replaceAll("-", "");

        if (insideEntity) {
            postGenerateInstance = obj;
        }
        // Вызов колбеков
        if (postGenerateInstance != null && postGenerateMethods != null) {
            for (Method method : postGenerateMethods) {
                try {
                    method.setAccessible(true);
                    if (insideEntity) {
                        method.invoke(postGenerateInstance, new Object[] { result });
                    } else {
                        method.invoke(postGenerateInstance, new Object[] { obj, result });
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }

        return result;
    }

    @Override
    public void configure(Type type, Properties params, Dialect d) throws MappingException {
        postGenerateInstance = null;
        postGenerateMethods.clear();

        try {
            Class<?> entityClass = Class.forName(params.get("entity_name").toString()); // Класс энтити, задан в пропертях
            Class<?> postGenerateClass = entityClass; // По дефолту листенер - сам энтити

            Object postGenerateValue = params.get("post-generate"); // Получаем класс листенера из параметра
            if (postGenerateValue != null) {
                String postGenerateClassName = postGenerateValue.toString();
                postGenerateClass = Class.forName(postGenerateClassName);
            }
            
            insideEntity = (entityClass == postGenerateClass);
            if (!insideEntity) { // Листенер должен иметь конструктор без параметров
                postGenerateInstance = postGenerateClass.newInstance();
            }
            // Ищем колбеки
            for (Method method : postGenerateClass.getDeclaredMethods()) {
                method.setAccessible(true);
                if (isMethodSuitable(postGenerateClass, entityClass, method)) {
                    postGenerateMethods.add(method);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean isMethodSuitable(Class<?> listenerClass, Class<?> entityClass,
            Method listenerMethod) {
        if (!listenerMethod.isAnnotationPresent(PostGenerate.class)) { // Выбираем аннотированные методы
            return false;
        }

        Class<?>[] parameterTypes = listenerMethod.getParameterTypes();
        // У метода должны быть либо 2 параметра: энтити и ключ (для случая отдельного класса колбека),
        // либо 1 - ключ (для случая колбека внутри энтити)
        return (insideEntity && parameterTypes.length == 1
                && parameterTypes[0].isAssignableFrom(KEY_CLASS) || !insideEntity
                && parameterTypes.length == 2 && parameterTypes[0].isAssignableFrom(entityClass)
                && parameterTypes[1].isAssignableFrom(KEY_CLASS));
    }
}

// 1 Колбек внутри энтити
@Entity
@Table(name = "test")
public class TestEntity {
    @Id
    @GeneratedValue(generator = "uuid_gen")
    @GenericGenerator(name = "uuid_gen", strategy = "ru.test.hiber.entities.UUIDGenerator", parameters = {
            @Parameter(name = "post-generate", value = "ru.test.hiber.entities.TestEntity") // Опционально
    })
    @Column(name = "id", length = 32, nullable = false, unique = true)
    private String id;
    @Column(name = "data", length = 100, nullable = false)
    private String data;

    // Get & Set
    
    @PostGenerate
    public void generatedKey(String key) {
        // Ключ сгенерен, но не записан в энтити
    }
}

// 2 Колбек в отдельном классе
@Entity
@Table(name = "test")
public class TestEntity {
    @Id
    @GeneratedValue(generator = "uuid_gen")
    @GenericGenerator(name = "uuid_gen", strategy = "ru.test.hiber.entities.UUIDGenerator", parameters = {
            @Parameter(name = "post-generate", value = "ru.test.hiber.entities.TestEntityListener")
    })
    @Column(name = "id", length = 32, nullable = false, unique = true)
    private String id;
    @Column(name = "data", length = 100, nullable = false)
    private String data;

    // Get & Set
}

public class TestEntityListener {
    @PostGenerate
    public void postGen(TestEntity entity, String key) {
        // Ключ сгенерен, но не записан в энтити
    }
}


Это сообщение отредактировал(а) CPlusPlusFAN - 3.6.2015, 00:28
PM MAIL ICQ Jabber   Вверх
AntonSaburov
Дата 3.6.2015, 11:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Штурман
****


Профиль
Группа: Модератор
Сообщений: 5658
Регистрация: 2.7.2002
Где: Санкт-Петербург

Репутация: 8
Всего: 118



Было у меня такое подозрение, что ключ к этому времени может и не появится. Ну может тогда проще поставить триггер на базу ? Или вызывать генератор самому при создании объекта прямо в конструкторе.
PM MAIL WWW ICQ   Вверх
CPlusPlusFAN
Дата 3.6.2015, 12:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

Репутация: нет
Всего: нет



AntonSaburov, я делаю так: когда дергается генератор, то ключ уже точно будет сгенерен. При этом, в генераторе есть вся необходимая информация об энтити, в которую нужно записать ключ.
Более того, генераторы поддерживают конфигурирование параметрами, в которые можно передать класс листенера.
ПС Ну, надеюсь, мой снипет хоть кому-то пригодится, кто вдруг столкнется с аналогичной проблемой. 

Это сообщение отредактировал(а) CPlusPlusFAN - 3.6.2015, 21:02
PM MAIL ICQ Jabber   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java EE (J2EE) и Spring | Следующая тема »


 




[ Время генерации скрипта: 0.0518 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.