Pull to refresh

Способ проксирования JPA сущностей для клиента (борьба с lazy initialization)

Reading time 2 min
Views 5.8K
Недавно, увидев на Хабре пост про борьбу с lazy initialization в Hibernate, я заинтересовался – прочитал сам пост и ждал пока наберется побольше комментариев – не предложит ли кто-нибудь способ, которым данную проблему решили мы. Ничего похожего я не увидел. Способ под катом.

Опишу на примере. Допустим, есть сущность:
@Entity
public class Person {
  @Id
  private long id;
  private String firstName;
  private String lastName;

  @OneToMany
  private List<Person> children;

  // getters-setters
}

Мы хотим гарантировать, чтобы, когда представление этой сущности будет отправляться на клиент, список children был проинициализирован и, более того, не тянул за собой никаких Hibernate'овских подвязок. Для этого объявляем следующий интерфейс:
public interface IPerson {
  public long getId();
  public void setId(long id);

  // getters-setters for firstName/lastName

  public List<IPerson> getChildren();
  public void setChildren(List<IPerson> children);
}

Именно такое представление и будет отправляться клиенту. Обратите внимание, что Person не реализует IPerson. И клиент вообще не знает о существовании Person. Для него существуют лишь только различные его представления в виде интерфейсов. Проксирование выгладит так:
Person examplePerson = getPersonFromDb();
IPerson personProxy = ProxyFactory.getProxy(IPerson.class, examplePerson);

Как это работает? Метод ProxyFactory.getProxy() создает с помощью механизма динамических прокси имплементацию интерфейса IPerson, в хэндлере которого лежит Map со значениями полей. Для заполнения этого мэпа ProxyFactory читает рефлексией поля сущности examplePerson. Соответственно, когда на интерфейсе вызывается какой-нибудь геттер/cеттер хэндлер лезет в этот мэп.

Естественно, у сущности может быть несколько интерфейсов, а прокси могут содержать только часть полей. Для ситуации со списком (как в примере List<IPerson>) также можно конкретно указывать из каких прокси состоит список (например, List<IPersonOnlyWithName>).

Для того чтобы проверка интерфейсов прокси (что интерфейс действительно соответствует сущности) происходила на этапе компиляции для эклипса был написан простенький Ant-билдер, который проходит по проаннотированным следующим образом сущностям:
@Entity
@ProxyBinding(interfaceClass = IPerson.class)
public class Person {
// …
}

Данный подход доказал свою жизнеспособность на практике и оброс большим количеством разнообразных дополнений. Было бы интересно увидеть какую-нибудь конструктивную критику и комментарии.
Tags:
Hubs:
+17
Comments 29
Comments Comments 29

Articles