Есть ли способ объявить окончательные поля для объектов, управляемых Hibernate? - PullRequest
23 голосов
/ 26 мая 2009

Я только начинаю работать с 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: неизменные объекты приводят довольно веские аргументы для этого). Так что есть ли способ заставить Hibernate работать, даже если бы я объявил каждое из полей 'final' ?

Я понимаю, что Hibernate использует Reflection для создания экземпляров своих классов и поэтому должен иметь возможность вызывать какой-либо конструктор без риска того, что он выберет неправильный конструктор или передаст неправильное значение одному из его параметров, поэтому вероятно, безопаснее вызывать конструктор без аргументов и устанавливать каждое поле по одному. Однако разве нельзя предоставить 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, конечно, вымышленная, но не кажется, что она должна быть вне досягаемости.

Ответы [ 6 ]

14 голосов
/ 28 мая 2009

Неизменяемый объект означает объект без методов, которые изменяют его состояние (то есть его поля). Поля не должны быть окончательными. Таким образом, вы можете удалить все мутаторы и настроить Hibernate на использование доступа к полям вместо методов доступа или просто пометить конструктор без аргументов и мутаторы как устаревшие. Это немного обходной путь, но лучше, чем ничего.

12 голосов
/ 10 сентября 2010

На самом деле в JDK 1.5+ Hibernate может обрабатывать (через отражение) изменение конечных полей. Создайте защищенный конструктор по умолчанию (), который устанавливает для полей значения по умолчанию / 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... */

}

7 голосов
/ 28 мая 2009

Похоже, это не сценарий использования Hibernate, так как многие операции, которые он выполняет, касаются изменчивого состояния:

  • объединение объектов
  • проверка грязного состояния
  • изменения промывки

При этом, если вас беспокоит неизменность, вы можете выбрать обертку вокруг ваших объектов с помощью конструкторов копирования:

public class FinalEvent {
    private final Integer id;

    public FinalEvent(Event event) {
        id = event.id;
    }
}

Хотя это означает дополнительную работу.


Теперь, когда я думаю об этом, сеансы гибернации обычно связаны с потоками, и это лишает по крайней мере одного преимущества последних полей - безопасной публикации.

Какие еще преимущества конечных полей вы ищете?

4 голосов
/ 24 декабря 2009

Этот тоже давал мне покоя. Одна идея, которую я недавно опробовал, заключается в следующем: определить интерфейсы только для чтения для ваших классов моделей и заставить ваши 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, чтобы никто не мог напрямую с ними возиться. Немного больше работы, но, вероятно, поможет сохранить вещи чище в долгосрочной перспективе. И, очевидно, будьте осторожны со всем бизнесом равенства / идентичности.

2 голосов
/ 28 мая 2009

Вы можете достичь желаемого результата, используя шаблон Builder. Я прочитал сообщение некоторое время назад на форумах Hibernate, где обсуждалась эта идея (хотя я сам никогда не реализовывал это ...)

0 голосов
/ 13 августа 2014

Аннотируйте свой класс с помощью @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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...