Today I made another step towards migration end.
The project is compiled and indexing process starts.
At some point I get an exception:
org.hibernate.AssertionFailure: force initializing collection loading
The full indexing log is here.
Another issue I have is with my Provider
class I mentioned before.
Provider’s name
property is embedded immutable class LocalizedField
which holds a Collections::unmodifiableMap
where key is Locale
and value is translation. This map has its own converter which converts to JSON
while storing to repository and converts to Map
when reading from repository.
The reason why I use JSON
and not let Hibernate do it is to avoid creating additional tables for translations (which would happen because of Map
) and also to be able to use json_search that gives me ability to implement autocomplete. I would do it with Lucene if there were option for wildcard search. My whole Map is converted to single field:
{
"hr_HR" : "Mrkvica - Ulica grada Mainza",
"en_US" : "Mrkvica - The street of city of Mainz"
}
LocalizedField.java
@NoArgsConstructor @ToString @EqualsAndHashCode
public class LocalizedField implements Serializable {
private static final long serialVersionUID = -7002190313077107236L;
@Convert( converter = LocaleStringMapConverter.class )
private Map<Locale, String> data;
public LocalizedField( final LocalizedField src ) {
this.data = src.entrySet().stream()
.map( entry -> {
if( entry.getValue() == null ) { entry.setValue(""); }
return entry;
} )
.collect( Collectors.collectingAndThen(
Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ),
Collections::unmodifiableMap
) );
}
public LocalizedField( final LocalizedFieldDTO src ) {
this.data = src.getData().entrySet().stream()
.map( entry -> {
if( entry.getValue() == null ) { entry.setValue(""); }
return entry;
} )
.collect( Collectors.collectingAndThen(
Collectors.toMap(
entry -> Locale.forLanguageTag( entry.getKey() ),
Map.Entry::getValue
),
Collections::unmodifiableMap
) );
}
public String get( final Locale locale ) {
return this.data.get( locale );
}
public Set<Map.Entry<Locale, String>> entrySet() {
return this.data.entrySet();
}
}
To display sorted providers in admin part of application I use transient property sortableTitle
to avoid reading and parsing all the data just to provide a sort.
Provider.java
public class Provider {
@Embedded
@AttributeOverride(name = "data",
column = @Column(name = "name", columnDefinition = "TEXT"))
@FullTextField(projectable = Projectable.NO, searchable = Searchable.YES,
analyzer = "customAnalyzer",
valueBridge = @ValueBridgeRef(type = LocalizedFieldBridge.class))
private LocalizedField name;
@Override @Transient
@IndexingDependency(derivedFrom = {
@ObjectPath(@PropertyValue(propertyName = "name")),
@ObjectPath({
@PropertyValue(propertyName = "parent"),
@PropertyValue(propertyName = "name")
})
})
@KeywordField(name = "sortableTitle", projectable = Projectable.NO,
searchable = Searchable.NO, sortable = Sortable.YES)
public String getSortableTitle() {
final String result = this.name.get(LocaleUtil.SORT_LOCALE);
if (this.parent == null) {
return TextUtil.getFoldedString(result);
}
// FIXME exception is thrown during reindex process
if (this.parent.name == null) {
throw new IllegalStateException(
"Provider parent is NOT NULL, but its name IS NULL; " +
"child provider: " + this.name.get(LocaleUtil.SORT_LOCALE)
);
}
final String parentName = this.parent.name.get(LocaleUtil.SORT_LOCALE);
return TextUtil.getFoldedString(parentName + '.' + result);
}
During indexing process IllegalStateException
is thrown from my method getSortableTitle()
. When I put a breakpoint on a row with throw statement, I can find parent Provider
being initialized partially - its name is null
. I guess the problem is that I use @Converter
to populate LocalizedField name
and this converter is perhaps not triggered during parent Provider
initialization. It could be something else, but so far I don’t know. When using SELECT
statement from my SQL tool, I can see both names are present - for child Provider
and parent Provider
.
Any ideas? Except putting all relations to eager because it would destroy any chance for my application to be fast and responsive.