Есть ли способ объявить конечные поля для объектов, управляемых Hibernate?
Я только начинаю с Hibernate, и все примеры, которые я вижу до сих пор, выглядят так же, как учебник в документации Hibernate:
package org.hibernate.tutorial.domain;
import java.util.Date;
public class Event {
private Long id;
private String title;
private Date date;
public Event() {}
/* Accessor methods... */
}
В частности: ни одно из полей не объявлено как final
, и должен существовать конструктор без аргументов, чтобы структура Hibernate могла создавать экземпляр класса и устанавливать его поля.
Но здесь вещь - я действительно не нравится, когда мои классы изменяются каким-либо образом, когда я могу ее избежать (Java Practices: Immutable Objects делают довольно сильный аргумент для этого). Итак, есть ли способ заставить Hibernate работать, даже если я должен объявить каждое из полей "final"?
Я понимаю, что Hibernate использует Reflection для создания экземпляров своих классов и, следовательно, должен иметь возможность ссылаться на какой-то конструктор, не рискуя, что он выберет неправильный конструктор или передаст неправильное значение одному из своих параметров, поэтому он вероятно, безопаснее вызывать конструктор no-arg и устанавливать каждое поле по одному за раз. Однако не следует ли предоставлять необходимую информацию в Hibernate, чтобы она могла безопасно создавать неизменяемые объекты?
public class Event {
private final Long id;
private final String title;
private final Date date;
public Event(@SetsProperty("id") Long id,
@SetsProperty("title") String title,
@SetsProperty("date") Date date) {
this.id = id;
this.title = title;
this.date = new Date(date.getTime());
}
/* Accessor methods... */
}
Аннотация @SetsProperty
, конечно, фиктивная, но не кажется, что она должна быть вне досягаемости.
Ответы
Ответ 1
Похоже, что это не используется для Hibernate, поскольку многие операции, которые он выполняет, относятся к изменяемому состоянию:
- слияние объектов
- проверка грязного состояния
- изменения промывки
При этом, если вы обеспокоены неизменностью, вы можете выбрать оболочку вокруг своих объектов с помощью конструкторов-копий:
public class FinalEvent {
private final Integer id;
public FinalEvent(Event event) {
id = event.id;
}
}
Однако это означает дополнительную работу.
Теперь, когда я думаю об этом, сеансы спящего режима обычно связаны с потоком, и это по крайней мере одно исключает преимущества окончательных полей - безопасную публикацию.
Какие еще преимущества конечных полей вы ищете?
Ответ 2
Неизменяемый объект означает объект без методов, которые изменяют его состояние (т.е. его поля). Поля не обязательно должны быть окончательными. Таким образом, вы можете удалить все мутаторы и настроить Hibernate для использования полей acces вместо accessors или вы можете просто отметить конструктор no-arg и мутаторы как устаревшие. Это немного обходное решение, но лучше, чем ничего.
Ответ 3
На самом деле в JDK 1.5+ спящий режим может обрабатывать (через отражение) изменение конечных полей. Создайте защищенный конструктор по умолчанию(), который устанавливает поля в значения по умолчанию /null и т.д. Hibernate может и будет переопределять эти значения при создании объекта.
Это все возможно благодаря изменениям в модели памяти Java 1.5 - изменения интереса (позволяющие окончательному быть не столь окончательным), где сделаны для обеспечения сериализации/десериализации.
public class Event {
private final Long id;
private final String title;
private final Date date;
// Purely for serialization/deserialization
protected Event() {
id = null;
title = null;
date = null;
}
public Event(Long id, String title, Data date) {
this.id = id;
this.title = title;
this.date = date;
}
/* Accessor methods... */
}
Ответ 4
Этот тоже давно искал меня. Одна из идей, которую я недавно тестировал, - определить интерфейсы только для чтения для ваших классов моделей, а ваши DAO и любые фабрики возвратят их вместо этого на объект. Это означает, что, хотя реализация изменена, как только она покинет объект DAO/ factory, она больше не может быть изменена.
Так же:
public interface Grape {
public Color getColor();
}
public class HibernateGrapeDao {
public Grape findGrape(int grapeId) {
HibernateGrape grape = hibernate.find(...
return grape;
}
}
class HibernateGrape implements Grape {
....
}
Возможно, даже захочет, чтобы классы внедрения были закрытыми для пакета dao, поэтому никто не может с ними поиграть. Немного больше работы, но, вероятно, помогает держать вещи более чистыми в долгосрочной перспективе. И, очевидно, будьте осторожны со всем бизнесом по обеспечению равенства/идентичности.
Ответ 5
Вы можете выполнить желаемый результат, используя шаблон Builder. Я прочитал публикацию некоторое время назад на форумах Hibernate, обсуждая эту идею (хотя я никогда не реализовал ее сам).
Ответ 6
Аннотировать свой класс с помощью @Access (AccessType.FIELD), тогда вы можете сделать свои поля окончательными. Вот так:
@Access(AccessType.FIELD)
public final class Event {
private final Long id;
private final String title;
private final Date date;
private Event() {
id = null;
title = null;
date = null;
}
public Event(Long id, String title, Date date) {
this.id = id;
this.title = title;
this.date = date;
}
}