After switching to Spring Boot 3.2.0, I'm now facing an issue with Spring Cache. Basically, I'm caching a JPA entity X that has one eagerly loaded ManyToOne entity O, which has a Lazy-loaded entity P. So that entity P is cached/serialized as org.hibernate.proxy.pojo.bytebuddy.SerializableProxy, but is unable to be de-serialized when taken from cache by Spring (throwing "java.lang.IllegalStateException: Could not identify any active SessionFactory having UUID ..."). Although, it all worked fine in Spring Boot 2.7 (Hibernate 5.6).
Debugging showed that that SerializableProxy now has sessionFactoryUuid member in it, which is used in its updated readResolve method during deserialization and such Factory can't be found. It was not the case in Hibernate 5.6 and sessionFactoryUuid was not used, that's why it worked.
For the reference, here are 2 versions of that readResolve method, one that worked in Spring Boot 2.7 (Hibernate 5.6) and one that fails for me in Spring Boot 3.2 (Hibernate 6.4.1):
Older code:
private Object readResolve() {
BytecodeProvider bytecodeProvider = Environment.getBytecodeProvider();
if ( !( bytecodeProvider instanceof BytecodeProviderImpl ) ) {
throw new IllegalStateException( "The bytecode provider is not ByteBuddy, unable to deserialize a ByteBuddy proxy." );
}
HibernateProxy proxy = ( (BytecodeProviderImpl) bytecodeProvider ).getByteBuddyProxyHelper().deserializeProxy( this );
afterDeserialization( (ByteBuddyInterceptor) proxy.getHibernateLazyInitializer() );
return proxy;
}
Newer code:
private Object readResolve() {
final SessionFactoryImplementor sessionFactory = retrieveMatchingSessionFactory( this.sessionFactoryUuid, this.sessionFactoryName );
BytecodeProviderImpl byteBuddyBytecodeProvider = retrieveByteBuddyBytecodeProvider( sessionFactory );
HibernateProxy proxy = byteBuddyBytecodeProvider.getByteBuddyProxyHelper().deserializeProxy( this );
afterDeserialization( (ByteBuddyInterceptor) proxy.getHibernateLazyInitializer() );
return proxy;
}
First line of it uses "this.sessionFactoryUuid", which can't be found giving "java.lang.IllegalStateException: Could not identify any active SessionFactory having UUID 4abdfbc7-d762-4cfa-9a3f-e435fabe489c", where '4abdfbc7-d762-4cfa-9a3f-e435fabe489c' is that sessionFactoryUuid.
Currently, a workaround for me is specifically unproxy that proxy before caching, but it seems to be overkill and defeats the purpose of Lazy Loading. Is there another way? I've found a similar question asked here and Dhaval also mentioned unproxying as the only option. I don't want to spend resources for unproxying it (especially, when there is a collection of entities X) since I don't really care about that part after getting entity X from cache - that's why it's marked to be lazy-loaded. Maybe there are some Spring Cache or Hibernate settings that can help here? What's the best practice for such scenarios?
Thank you very much in advance.
UPDATE 1: Tried to use Andrey's suggestion from comments to specify session_factory_name for Hibernate as: spring.jpa.properties.hibernate.session_factory_name=custom-hibernate-session (had to be mindful of the fact that I'm configuring LocalContainerEntityManagerFactoryBean manually, so used hibernate.session_factory_name). Although, got org.hibernate.engine.jndi.JndiException: Error parsing JNDI name [custom-hibernate-session], caused by javax.naming.NoInitialContextException: Need to specfy class name in enviroment or system property, or in an application resource file: java.naming.factory.initial.
UPDATE 2: I was not sure about setting hibernate.session_factory_name_is_jndi to false, but when I did, that last error was gone and now my deserialization/reading from cache works fine! So that hibernate.session_factory_name did the trick! I just don't understand, why absence of this property breaks caching of entities with lazy-loaded associations completely and why it's like this by default. Anyway, thank you @AndreyB.Panfilov, I would not be able to solve it that quickly without your advice!