Versions:
Hibernate: 5.6.3.Final
Hibernate Search with Elasticsearch: 6.0.8.Final
Problem:
We have a bidirectional OneToOne relation between two classes Person
and HumanName
, where HumanName
is the “parent” side with the mappedBy
-annotation. Person
is @Indexed
, with an @IndexedEmbedded
on its name field.
Due to the way Hibernate deals with these cases, this results in an N+1-problem every time a List<Person>
with their names is returned in a query. We tried this workaround, implementing PersistentAttributeInterceptable
in our HumanName
class. This successfully deals with the N+1 problem; however, the creation of the search index then fails with the following error. Removing the methods $$_hibernate_getInterceptor()
and $$_hibernate_setInterceptor
fixes the Hibernate Search error, but reintroduces the N+1 error. Is there a way to to make this work with Hibernate Search?
Caused by: org.hibernate.search.util.common.SearchException: HSEARCH000520: Hibernate Search encountered failures during bootstrap. Failures:
Hibernate ORM mapping:
type 'com.example.entitygraphtest.model.Person':
path '.name<no value extractors>.firstName':
failures:
- HSEARCH700079: Exception while retrieving property type model for 'firstName' on 'com.example.entitygraphtest.model.HumanName'.
The model classes look like this:
@Indexed
@Getter
@Setter
@Entity
@NoArgsConstructor
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person {
@Id
@GeneratedValue
UUID id;
@IndexedEmbedded(includeEmbeddedObjectId = true)
@AssociationInverseSide(inversePath = @ObjectPath(@PropertyValue(propertyName = "person")))
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
HumanName name;
}
@Getter
@Setter
@Entity
public class HumanName implements PersistentAttributeInterceptable {
@Id
@GeneratedValue
UUID id;
@KeywordField
String firstName;
@ElementCollection
private List<String> middleNames;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "name")
@LazyToOne(LazyToOneOption.NO_PROXY)
Person person;
public Person getPerson() {
if (interceptor != null) {
return (Person) interceptor.readObject(this, "person", person);
}
return person;
}
public void setPerson(Person person) {
if (interceptor != null) {
this.person = (Person) interceptor.writeObject(this, "person", this.person, person);
} else {
this.person = person;
}
}
@Transient
private PersistentAttributeInterceptor interceptor;
@Override
public PersistentAttributeInterceptor $$_hibernate_getInterceptor() {
return interceptor;
}
@Override
public void $$_hibernate_setInterceptor(final PersistentAttributeInterceptor interceptor) {
this.interceptor = interceptor;
}
}