JPA EntityManager#getReference()
возвращает что-то очень ленивое, обычно прокси.Он поступает в базу данных только при обращении к свойствам.Согласно документации:
Получить экземпляр, состояние которого может быть лениво извлечено
Что я хочу знать, так это то, имеет ли смысл изучать полятакой прокси.
У меня есть следующие два класса, которые формируют отношение один ко многим:
@Entity
@Table(name = "parent")
public class Parent {
@Id
@GeneratedValue
public UUID id;
public String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
public List<Child> children = new ArrayList<>();
public void addChild(Child child) {
children.add(child);
child.parent = this;
}
public String getName() {
return name;
}
public List<Child> getChildren() {
return children;
}
}
@Entity
@Table(name = "child")
public class Child {
@Id
@GeneratedValue
public UUID id;
public String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
public Parent parent;
}
Обратите внимание, что оба класса используют стратегию доступа к полю (так как @Id
на поле, а не на получателе).
Я добавляю одну Parent
с двумя дочерними элементами в базу данных во время настройки тестового набора через JDBC, чтобы избежать кэширования экземпляров Parent
в EntityManager
.Я не показываю этот код, так как он громоздкий и простой.Если это необходимо, я могу добавить его позже.
Затем я запускаю следующие тесты:
@Test
@Transactional
public void testWithFind() {
Parent parent = entityManager.find(Parent.class, parentId);
assertThat(parent.name, is("Joe"));
assertThat(parent.children, hasSize(2));
}
@Test
@Transactional
public void testWithGetReference() {
Parent parent = entityManager.getReference(Parent.class, parentId);
assertNull(parent.name);
assertThat(parent.children, hasSize(0));
assertThat(parent.getName(), is("Joe"));
assertThat(parent.getChildren(), hasSize(2));
// ... but still ...
assertNull(parent.name);
assertThat(parent.children, hasSize(0));
}
Оба теста успешны.
Я вижу вконсоль, которая JPA фактически запрашивает базу данных.Для find()
связанного теста это
Hibernate: select parent0_.id as id1_2_0_, parent0_.name as name2_2_0_ from parent parent0_ where parent0_.id=?
Hibernate: select children0_.parent_id as parent_i3_0_0_, children0_.id as id1_0_0_, children0_.id as id1_0_1_, children0_.name as name2_0_1_, children0_.parent_id as parent_i3_0_1_ from child children0_ where children0_.parent_id=?
Для getReference()
это
Hibernate: select parent0_.id as id1_2_0_, parent0_.name as name2_2_0_ from parent parent0_ where parent0_.id=?
Hibernate: select children0_.parent_id as parent_i3_0_0_, children0_.id as id1_0_0_, children0_.id as id1_0_1_, children0_.name as name2_0_1_, children0_.parent_id as parent_i3_0_1_ from child children0_ where children0_.parent_id=?
Как видно, find()
заполняет как String
, так и коллекциюполе ок.Но для «сущности», возвращаемой getReference()
, это не так: поля остаются в своем исходном состоянии даже после вызова получателей.
Я понимаю, что невозможно перехватить доступ к полю с помощью прокси(или, возможно, любой другой «юридический» механизм), поэтому прокси-сервер в основном работает на уровне получателей.Но сущности аннотированы для использования стратегии доступа к полям, поэтому пользователь явно ожидает, что поля будут использоваться для получения значений, а не для получения.Я не нашел никаких разъяснений в спецификации, и она ничего не говорит о семантике «только для получателя» для таких прокси (или я просто не нашел таких упоминаний).
Мои вопросы:
Это несоответствие спецификации JPA, или я просто не понимаю ее правильно?
Можно ли использовать поля для доступа к компонентам возвращаемой сущности
Я использую Hibernate 5.3.9 в качестве поставщика JPA.