Force initializing collection loading

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.

This looks like a bug in Hibernate ORM 6.1. If you can report it on Jira with a reproducer, we will have a look. Maybe try with Hibernate ORM 6.2.0.CR1 first, in case this has already been fixed?

I think your problem is there:

You are accessing a field directly, which sidesteps Hibernate proxies and lazy initialization. Use a getter instead and everything should be fine:

(use getName() in the code below too, of course).

Thanks!
If I were thinking, I would have to come to this on my own.
Changed it to use getter and now this part is working as expected.
During the day I’ll try to file a bug report for the other issue, but only if 6.2 shows same exceptions.

Tried to update dependencies to give a try 6.2.0.CR1, but no luck - app won’t start.
Exception is thrown in early stage of boot attempt:

java.lang.IllegalStateException: Cannot apply class transformer without LoadTimeWeaver specified

Log is available here.


CurrencyRepositoryImpl.java

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

public class CurrencyRepositoryImpl implements CurrencyExtendRepository {

	@PersistenceContext
	private EntityManager entityManager;

	@Override
	public CustomCurrency findByIso4217(final String iso4217code) {
		java.util.Currency jc = java.util.Currency.getInstance(iso4217code);
		final QCustomCurrency customCurrency = QCustomCurrency.customCurrency;
		return
			new JPAQueryFactory(this.entityManager)
				.select(customCurrency)
				.from(customCurrency)
				.where(customCurrency.javaCurrency.eq(jc))
				.fetchFirst();
	}

	// ... more methods
}

pom.xml


parent

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>3.0.1</version>
</parent>

versions

<properties>
	<java.version>18</java.version>
	<hibernate-core.version>6.2.0.CR1</hibernate-core.version>
	<org.hibernate.search.version>6.1.7.Final</org.hibernate.search.version>
</properties>

Spring Boot starter

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
	<exclusions>
		<exclusion>
			<groupId>org.hibernate.orm</groupId>
			<artifactId>hibernate-core</artifactId>
		</exclusion>
	</exclusions>
</dependency>

Hibernate core

<dependency>
	<groupId>org.hibernate.orm</groupId>
	<artifactId>hibernate-core</artifactId>
	<version>${hibernate-core.version}</version>
</dependency>

Hibernate Search

<dependency>
	<groupId>org.hibernate.search</groupId>
	<artifactId>hibernate-search-mapper-orm-orm6</artifactId>
	<version>${org.hibernate.search.version}</version>
	<exclusions>
		<exclusion>
			<groupId>org.hibernate.orm</groupId>
			<artifactId>hibernate-core</artifactId>
		</exclusion>
	</exclusions>
</dependency>

<dependency>
	<groupId>org.hibernate.search</groupId>
	<artifactId>hibernate-search-backend-lucene</artifactId>
	<version>${org.hibernate.search.version}</version>
</dependency>

Looks like a Spring problem, probably unrelated. You’d better report your previous problem on JIRA then, even if you can only reproduce it on 6.1. Please include a (minimal) reproducer though (ZIP file containing the source code or link to a github repository). Ideally you’d use the templates available there: hibernate-test-case-templates/search/hibernate-search-6 at main · hibernate/hibernate-test-case-templates · GitHub

To create reproducer it would take days - perhaps the only way would be to copy the whole project to new one and then cut out class by class to minimize it.
I’ll contact you with message if I find how to and suggest another way.

Just for test purposes I replaced all fetch = FetchType.LAZY with fetch = FetchType.EAGER (I have it on 29 places in project) and the exception is still the same:

org.hibernate.AssertionFailure: force initializing collection loading

Now I’m thinking that could be endles loop because I have back references in entities. For example, Article has a collection of PriceInfo and each PriceInfo has back reference to Article.

Still trying to think of a plan how to create a minimal reproducer.

@yrodiere Can’t understand why, but when I forced all my relations to LAZY, indexing is finished successfully. Before I had 29 LAZY relations and 3 EAGER relations (user roles to user and two similar).

As I switched to new Spring Boot and new Hibernate also, I have some issues with Thymeleaf and web mvc security which makes my app unusable, but here I’m showing an index from Luke which looks like somethint usable.

Yes, this is most likely a bug in Hibernate ORM 6.1 not handling association loading correctly in your particular case. As is obvious from the ORM 6 migration guide, a lot of things changed in ORM 6 and thus different bugs were to be expected.

If you want this fixed, you will need to identify the problematic association and provide a reproducer.

@beikov might have already fixed this in 6.2, but since you cannot test your application with 6.2 yet, that’s hard to say.

Reminder for @beikov , this is the error message. Rings a bell?

A post was split to a new topic: Numeric enum mapping

I have filed a Jira here [HHH-16199] - Hibernate JIRA, it includes a link to a reproducer project.