Подтвердить что ты не робот

Есть ли способ объявить конечные поля для объектов, управляемых 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, конечно, фиктивная, но не кажется, что она должна быть вне досягаемости.

4b9b3361

Ответ 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;
    }
}