Problems when using multi-tenancy and lazy loading

Hello,
1.We used org.hibernate : hibernate-core : 5.2.12.Final, and we use multi-tenancy and lazy loading in our project.

2.We use objects that have a longer life cycle,not suitable for solving the problem by extending the session time,So we used the method of enabling hibernate.enable_lazy_load_no_trans to handle it.

3.We may switch the tenant identifier in the thread, so we did not put the tenant identifier in the CurrentTenantIdentifierResolver.

We think of a modification:
We intend to place the tenant identifier in the proxy object when lazy loading,then the tenant identifier is passed to the session when the lazy loading automatically creates the session, and the hibernate source code is modified as follows:
1.Add multi-tenant identifier attributes in the AbstractLazyInitializer class

//Add the properties of the multi-tenant identifier in this class.
private String multiTenanctKey;

public String getMultiTenanctKey()
{
return multiTenanctKey;
}

public void setMultiTenanctKey(String multiTenanctKey)
{
	this.multiTenanctKey = multiTenanctKey;
}

2.Add a tenant identifier when automatically creating a session in the AbstractLazyInitializer class

/*
*Pass in the tenant identifier when the session is automatically *created.
*/
SharedSessionContractImplementor Session = (SharedSessionContractImplementor)sf.withOptions()
.tenantIdentifier(multiTenanctKey).openSession();

3.Adding a tenant identifier to a parent class when a proxy object is automatically created in the JavassistLazyInitializer class invoke method

/*
*When creating a proxy object, assign values to the multi-tenant identity *of the parent class in the subclass.
*/
setMultiTenanctKey(session.getTenantIdentifier());

We use objects that have a longer life cycle,not suitable for solving the problem by extending the session time,So we used the method of enabling hibernate.enable_lazy_load_no_trans to handle it.

This sounds like the source of your problems. You don’t need to use the enable_lazy_load_no_trans. Using this setting might indicate that you are fetching entities even when DTO projections would be more suitable.

Entities are only needed when you plan to modify them. They are not universal data holders that should suit every possible use case in your application.

We may switch the tenant identifier in the thread, so we did not put the tenant identifier in the CurrentTenantIdentifierResolver.

It sounds like you are not using Hibernate properly, as it was designed to be used for multi tenancy.

We think of a modification:
We intend to place the tenant identifier in the proxy object when lazy loading,then the tenant identifier is passed to the session when the lazy loading automatically creates the session

You tell about some Hibernate core modifications without providing the reason for having such change. More, your post says about some problems that you have omitted to elaborate.

Try to restructure your post to explain the actual problem. It might be that you are not not using Hibernate properly, as already indicated.

Hello again,I think I didn’t say it clearly. we use lazy loading in our project.But we need to use the object after the session is closed
for example:

package com.hibernatemaptostringtest;

import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import java.util.*;

@DynamicUpdate
@Entity
@Table (name = "account")
@Inheritance (strategy = InheritanceType.JOINED)
@DiscriminatorColumn (name = "type", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue ("account")
public class SAccount
{
	private Integer accountId;

	private Map<String, Object> additionalInfo = new HashMap<>();

	public SAccount() { }


	@Id
	@GeneratedValue (strategy = GenerationType.IDENTITY)
	public Integer getAccountId()
	{
		return accountId;
	}

	public void setAccountId(Integer accountId)
	{
		this.accountId = accountId;
	}

	@ElementCollection (fetch = FetchType.LAZY, targetClass = String.class)
	@CollectionTable (name = "addiationalinfo")
	@MapKeyColumn (name = "mapkey")
	public Map<String, Object> getAdditionalInfo()
	{
		return additionalInfo;
	}

	public void setAdditionalInfo(Map<String, Object> additionalInfo)
	{
		this.additionalInfo = additionalInfo;
	}
}

@Test
	public void hibernateLazyLoadingTest()
	{
		Session session = sessionFactory.withOptions().tenantIdentifier("").openSession();
		SAccount account = session.get(SAccount.class, 2);
		session.close();
		System.out.println(account.getAdditionalInfo());
	}
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.hibernatemaptostringtest.SAccount.additionalInfo, could not initialize proxy - no Session

	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:582)
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:201)
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:561)
	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:132)
	at org.hibernate.collection.internal.PersistentMap.toString(PersistentMap.java:250)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.hibernatemaptostringtest.SAccount.toString(SAccount.java:252)
	at java.lang.String.valueOf(String.java:2994)
	at java.io.PrintStream.println(PrintStream.java:821)
	at com.hibernatemaptostringtest.HIbernateMapToStringTest.hibernateLazyLoadingTest(HIbernateMapToStringTest.java:42)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

So we used the method of enabling hibernate.enable_lazy_load_no_trans to handle it.
But we used multi tenancy.Show as below

org.hibernate.HibernateException: SessionFactory configured for multi-tenancy, but no tenant identifier specified

	at org.hibernate.internal.AbstractSharedSessionContract.<init>(AbstractSharedSessionContract.java:154)
	at org.hibernate.internal.AbstractSessionImpl.<init>(AbstractSessionImpl.java:29)
	at org.hibernate.internal.SessionImpl.<init>(SessionImpl.java:252)
	at org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1218)
	at org.hibernate.internal.SessionFactoryImpl.openSession(SessionFactoryImpl.java:450)
	at org.hibernate.collection.internal.AbstractPersistentCollection.openTemporarySessionForLoading(AbstractPersistentCollection.java:275)
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:198)
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:561)
	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:132)
	at org.hibernate.collection.internal.PersistentMap.toString(PersistentMap.java:250)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.hibernatemaptostringtest.SAccount.toString(SAccount.java:252)
	at java.lang.String.valueOf(String.java:2994)
	at java.io.PrintStream.println(PrintStream.java:821)
	at com.hibernatemaptostringtest.HIbernateMapToStringTest.hibernateLazyLoadingTest(HIbernateMapToStringTest.java:42)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

One solution is to use CurrentTenantIdentifierResolver, but we may use many tenant in the business. e.g : load one object from db1 , and then load another object from db2. CurrentTenantIdentifierResolver can not resolve this problem.

Why not JOIN FETCH the additionalInfo for the business use case where you need this association being initialized?

This is the easiest solution. LAZY associations are meant to be fetched eagerly if you need them initialized in the UI.

additionalInfo is used in a small part of business processes and most of them are not used, so set additionalInfo to LazyLoad to improve efficiency.

Ok. So just JOIN FETCH using JPQL or entity graphs for those rare use cases when you need it initialized.

Thank you. I discuss it with my partner.