IllegalStateException in multithread Session/EntityManager is closed

I have this error, I am running hibernate version 5.5.3.Final in tomcat 10.0.7.
I am using hibernate Session, not EntityManager

[29-06-2021 15:45:12,482] [pool-125-thread-1] FATAL lt.cm.util.Reflection - java.lang.reflect.InvocationTargetException
java.lang.reflect.InvocationTargetException
	at jdk.internal.reflect.GeneratedConstructorAccessor137.newInstance(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at lt.cm.util.Reflection.getNewInstance(Reflection.java:106)
	at lt.cm.json.LTJsonParse.toJson(LTJsonParse.java:55)
	at lt.json.LTJsonModel.toJson(LTJsonModel.java:91)
	at lt.model.Device.toJson(Device.java:408)
	at lt.websocket.ModelObj.getJson(ModelObj.java:44)
	at lt.websocket.LTWSMessage.create(LTWSMessage.java:112)
	at lt.websocket.LTWSManager.lambda$publish$0(LTWSManager.java:74)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1603)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
	at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:408)
	at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:736)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:661)
	at lt.websocket.LTWSManager.publish(LTWSManager.java:54)
	at lt.websocket.LTWSWrapper.lambda$newEvent$0(LTWSWrapper.java:37)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
	at lt.websocket.LTWSWrapper.newEvent(LTWSWrapper.java:36)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalStateException: Session/EntityManager is closed
	at org.hibernate.internal.AbstractSharedSessionContract.checkOpen(AbstractSharedSessionContract.java:384)
	at org.hibernate.engine.spi.SharedSessionContractImplementor.checkOpen(SharedSessionContractImplementor.java:148)
	at org.hibernate.internal.AbstractSharedSessionContract.checkOpenOrWaitingForAutoClose(AbstractSharedSessionContract.java:390)
	at org.hibernate.internal.SessionImpl.getEntityUsingInterceptor(SessionImpl.java:563)
	at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.hydrateEntityState(EntityReferenceInitializerImpl.java:188)
	at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.readRow(AbstractRowReader.java:102)
	at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractRows(ResultSetProcessorImpl.java:157)
	at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:94)
	at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:105)
	at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87)
	at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710)
	at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:93)
	at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163)
	at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589)	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264)
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
	at org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:178)
	at org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:163)
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264)
	at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:162)
	at org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:168)
	**at lt.json.model.DeviceJson.<init>(DeviceJson.java:61)**
	... 33 more

The class raising this error is as follow (line 61)

package lt.json.model;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

import org.hibernate.LazyInitializationException;

import com.google.gson.annotations.Expose;

import lt.json.LTJsonModel;
import lt.json.type.LTTypeParse;
import lt.locale.DateUtils;
import lt.model.Device;
import lt.model.DeviceGroup;
import lt.model.LTTable;
import lt.model.User;

public class DeviceJson extends lt.cm.json.model.DeviceJson<DeviceDataJson> implements LTJsonModel {
	private static final long serialVersionUID = 4387177204893976858L;

	@Expose
	private Integer id;
	
	@Expose
	protected String customId = null;

	@Expose
	private Set<Integer> familyIds = null;

	@Expose
	private String syncTime = null;

	@Expose
	private Map<Integer, Set<Integer>> groupsIds = null;

	@Expose
	private Integer parentId = null;

	@Expose
	private Integer nodeModelId = null;

	public DeviceJson(Device device, User user) {
		id = device.getId();
		name = device.getName();
		customId = device.getCustomId();
		if (user != null) {
			type = (device.getType() != null) ? device.getType().getType() : null;
			online = device.isOnline();
			syncTime = DateUtils.getDateTime(device.getSynctime(), device.getDeployment());
			location = LTTypeParse.getPointJson(device.getLocation());
			onMap = device.isOnmap();
			hasList = device.isHaslist();
			nodeModelId = device.getNodeModel().getId();
			if (device.getParent() != null)
				parentId = device.getParent().getId();
			try {
				if (device.getDevicedata() != null && device.getDevicedata().size() != 0) {
					data = LTJsonModel.getEntities(DeviceDataJson.class, device.getDevicedata(),
							device.getDeployment());
				}
			} catch (LazyInitializationException e) {
				data = null;
			}
		}
		try {
			if (device.getFamilies() != null && device.getFamilies().size() != 0) {
				familyIds = new HashSet<>();
				device.getFamilies().forEach(family -> familyIds.add(family.getId()));
			}
		} catch (LazyInitializationException e) {
			familyIds = null;
		}
		try {
			Map<DeviceGroup, Set<DeviceGroup>> groups = device.getGroups();
			groupsIds = new HashMap<>();
			for (Entry<DeviceGroup, Set<DeviceGroup>> entity : groups.entrySet()) {
				Set<Integer> subGroupsIds = new HashSet<>();
				for (DeviceGroup subGroup : entity.getValue())
					subGroupsIds.add(subGroup.getId());
				groupsIds.put(entity.getKey().getId(), subGroupsIds);
			}
		} catch (LazyInitializationException e) {
			groupsIds = null;
		}
	}

	@Override
	public Integer getId() {
		return id;
	}
	
	public String getCustomId() {
		return customId;
	}

	public void setCustomId(String customId) {
		this.customId = customId;
	}
	
	public Boolean isOnMap() {
		return onMap;
	}
	
	public Boolean isHasList() {
		return hasList;
	}

	public DeviceJson(Device device) {
		this(device, null);
	}

	@Override
	public LTTable toModel() {
		Device device = new Device();
		device.setId(id);
		return device;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || !getClass().equals(o.getClass()))
			return false;
		DeviceJson device = (DeviceJson) o;
		return Objects.equals(name, device.name);
	}

	@Override
	public int hashCode() {
		return Objects.hash(name);
	}
}

Here is the Entity used by this class

package lt.model;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
import org.postgresql.geometric.PGpoint;

import com.google.gson.JsonElement;

import lt.LTPlugin;
import lt.controller.CacheController.CacheTemplate;
import lt.database.LTDBConfig;
import lt.database.mapping.DeviceType;
import lt.ehcache.Heap;
import lt.ehcache.LTEhcache;
import lt.ehcache.OffHeap;
import lt.json.LTJsonModel;
import lt.json.model.DeviceJson;
import lt.websocket.ModelObj.LTEventObject;

@LTPlugin
@Entity(name = "Device")
@Table(name = "lt_device", schema = LTDBConfig.LTSchema)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@NaturalIdCache
@LTEhcache(template = CacheTemplate.LONG_TIME)
public class Device extends LTTable implements LTEventObject {
	private static final long serialVersionUID = 3028650185302870665L;

	@ManyToOne
	@Fetch(FetchMode.JOIN)
	@JoinColumn(name = "deploymentID", nullable = false, updatable = false, insertable = false)
	private Deployment deployment;
	
	@Column(name = "deploymentID", nullable = false)
	private int deploymentId;
	
	@ManyToOne
	@Fetch(FetchMode.JOIN)
	@JoinColumn(name = "nodeModelId")
	private NodeModel nodeModel;
	
	@NaturalId(mutable = false)
	@Column(name = "name", length = 100, nullable = false)
	private String name;
	
	@Column(name = "customID")
	private String customId;
	
	@Column(name = "online",  nullable = false)
	private boolean online = false;
	
	@Column(name = "synctime", nullable = true)
    private LocalDateTime synctime;
	
	@Column(name = "onmap", nullable = false)
	private boolean onmap = true;
	
	@Column(name = "haslist", nullable = false)
	private boolean haslist = false;
	
	@ManyToOne
	@Fetch(FetchMode.SELECT)
	@JoinColumn(name = "bridgeID", nullable = true)
	private Bridge bridge;

	@Column(name = "location", nullable = false)
	private PGpoint location = new PGpoint();
	
	@ManyToOne
	@Fetch(FetchMode.SELECT)
	@JoinColumn(name = "parentID", nullable = true, updatable = false)
	private Device parent;
	
	@OneToMany(cascade = { 
			CascadeType.PERSIST, 
			CascadeType.MERGE,
			CascadeType.REFRESH}, fetch = FetchType.LAZY)
	@JoinColumn(name = "parentID", insertable = false, updatable = false)
	@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
	@LTEhcache(template = CacheTemplate.LONG_TIME)
	private Set<Device> children = new HashSet<>();
	
	@Column(name = "deviceType", columnDefinition = LTDBConfig.LTSchema + ".lt_devicetype", nullable = true, updatable = false)
	private DeviceType type;
	
	@Column(name = "devicetypekey", length = 100, nullable = true)
	private String typeKey;
	
	@OneToMany(cascade = { 
			CascadeType.PERSIST, 
			CascadeType.MERGE,
			CascadeType.REFRESH},
			fetch = FetchType.LAZY,
			mappedBy = "device", orphanRemoval = true)
	@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
	@LTEhcache(template = CacheTemplate.LONG_TIME_SMALL)
	private Set<DgLink> groups = new HashSet<>();

	@ManyToMany(cascade = { 
			CascadeType.PERSIST, 
			CascadeType.MERGE,
			CascadeType.REFRESH},
			fetch = FetchType.LAZY)
	@JoinTable(name = "lt_linkdevicefamily", schema = LTDBConfig.LTSchema,
		joinColumns = @JoinColumn(name = "deviceID", nullable = false, updatable = false),
		inverseJoinColumns = @JoinColumn(name = "familyID", nullable = false, updatable = false))
	@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
	@LTEhcache(template = CacheTemplate.LONG_TIME_SMALL)
	private Set<Family> families = new HashSet<>();
	
	@OneToMany(fetch = FetchType.LAZY)
	@JoinColumn(name = "deviceID")
	@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
	@LTEhcache(template = CacheTemplate.ONE_DAY, heap = @Heap(size = 10000), offHeap = @OffHeap(size = 30))
	private Set<DeviceData> devicedata = new HashSet<>();
	
	@OneToMany(fetch = FetchType.LAZY)
	@JoinColumn(name = "deviceID")
	@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
	@LTEhcache(template = CacheTemplate.LONG_TIME_SMALL)
	private Set<DeviceSubGroup> subGroups = new HashSet<>();

	public Deployment getDeployment() {
		return deployment;
	}
	
	public int getDepId() {
		return deployment.getId();
	}

	public void setDeployment(Deployment deployment) {
		this.deployment = deployment;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public String getCustomId() {
		return customId;
	}

	public void setCustomId(String customId) {
		this.customId = customId;
	}

	public Bridge getBridge() {
		return bridge;
	}

	public void setBridge(Bridge bridge) {
		this.bridge = bridge;
	}

	public PGpoint getLocation() {
		return location;
	}

	public void setLocation(PGpoint location) {
		this.location = location;
	}
	
	public Set<Family> getFamilies() {
		return families;
	}

	public void setFamilies(Set<Family> family) {
		this.families = family;
	}

	public Set<DeviceData> getDevicedata() {
		return devicedata;
	}

	public void setDevicedata(Set<DeviceData> devicedata) {
		this.devicedata = devicedata;
	}
	
	public boolean isOnline() {
		return online;
	}

	public int getDeploymentId() {
		return deploymentId;
	}

	public void setDeploymentId(int deploymentId) {
		this.deploymentId = deploymentId;
	}

	public void setOnline(boolean online) {
		this.online = online;
	}

	public LocalDateTime getSynctime() {
		return synctime;
	}

	public void setSynctime(LocalDateTime synctime) {
		this.synctime = synctime;
	}

	public NodeModel getNodeModel() {
		return nodeModel;
	}

	public void setNodeModel(NodeModel nodeModel) {
		this.nodeModel = nodeModel;
	}
	
	public Set<DgLink> getGroupsLink() {
		return groups;
	}
		
	public Device getParent() {
		return parent;
	}

	public void setParent(Device parent) {
		this.parent = parent;
	}

	public DeviceType getType() {
		return type;
	}

	public void setType(DeviceType type) {
		this.type = type;
	}

	public String getTypeKey() {
		return typeKey;
	}

	public void setTypeKey(String typeKey) {
		this.typeKey = typeKey;
	}

	public boolean isOnmap() {
		return onmap;
	}

	public void setOnmap(boolean onmap) {
		this.onmap = onmap;
	}
	
	public boolean isHaslist() {
		return haslist;
	}

	public void setHaslist(boolean haslist) {
		this.haslist = haslist;
	}

	public Set<DeviceSubGroup> getSubGroups() {
		return subGroups;
	}
	
	public Set<Device> getChildren() {
		return children;
	}

	public void setChildren(Set<Device> children) {
		this.children = children;
	}

	public Map<DeviceGroup, Set<DgLink>> getGroupsInfo() {
		Map<DeviceGroup, Set<DgLink>> groups = new HashMap<>();
		for (DgLink group : this.groups) {
			if(!groups.containsKey(group.getGroup())) {
				Set<DgLink> subGroups = new HashSet<>();
				if(!group.getGroup().equals(group.getSubGroup()))
					subGroups.add(group);
				groups.put(group.getGroup(),subGroups);
			} else if(!group.getGroup().equals(group.getSubGroup()))
				groups.get(group.getGroup()).add(group);
		}
		return groups;
	}
	
	public Map<DeviceGroup, Set<DeviceGroup>> getGroups() {
		Map<DeviceGroup, Set<DeviceGroup>> groups = new HashMap<>();
		for(Map.Entry<DeviceGroup, Set<DgLink>> entry : getGroupsInfo().entrySet())
			groups.put(entry.getKey(), entry.getValue()
					.stream().map(dgl -> dgl.getSubGroup()).collect(Collectors.toSet()));
		return groups;
	}
	
	public void addGroups(Map<DeviceGroup, Set<DeviceGroup>> groups) {
		for (Entry<DeviceGroup, Set<DeviceGroup>> entity : groups.entrySet()) {
			DgLink dgLinkGroup = new DgLink(this, entity.getKey(), entity.getKey());
			if(!this.groups.contains(dgLinkGroup)) {
				this.groups.add(dgLinkGroup);
				entity.getKey().getDevicesLink().add(dgLinkGroup);
			}
			if(entity.getValue() != null)
				for (DeviceGroup subGroup : entity.getValue())
					if(!subGroup.equals(entity.getKey())) {
						DgLink dgLinkSubGroup = new DgLink(this, entity.getKey(), subGroup);
						if(!this.groups.contains(dgLinkSubGroup)) {
							this.groups.add(dgLinkSubGroup);
							entity.getKey().getDevicesLink().add(dgLinkSubGroup);
						}
					}
		}
    }
	
	public void removeGroups(Map<DeviceGroup, Set<DeviceGroup>> groups) {
		Set<DgLink> newSubGroups = new HashSet<>();
		Set<DgLink> newGroups = new HashSet<>();
		Set<DgLink> delGroups = new HashSet<>();
		for (Entry<DeviceGroup, Set<DeviceGroup>> entity : groups.entrySet()) {
			newSubGroups.add(new DgLink(this, entity.getKey(), entity.getKey()));
			if(entity.getValue() != null)
				for (DeviceGroup subGroup : entity.getValue())
					if(!subGroup.equals(entity.getKey()))
						newSubGroups.add(new DgLink(this, entity.getKey(), subGroup));
		}
		for (DgLink dgLink : this.groups)
			if(!newGroups.contains(dgLink) 
						|| !newSubGroups.contains(dgLink))
					delGroups.add(dgLink);
		for (DgLink dgLink : delGroups) {
			this.groups.remove(dgLink);
			dgLink.getGroup().getDevicesLink().remove(dgLink);
			dgLink.setDevice(null);
			dgLink.setGroup(null);
			dgLink.setSubGroup(null);
		}
	}
    
	public void addFamily(Family family) {
		if(families.add(family))
			family.getDevices().add(this);
    }
	
    public void removeFamily(Family family) {
    	if(families.remove(family))
    		family.getDevices().remove(this);
    }
    
    public ModelProperty getModelProperty(String tag) {
		for(ModelProperty modelProperty : nodeModel.getModelProperties())
			if(modelProperty.getTag().equals(tag))
				return modelProperty;
		return null;
	}
    
    public FamilyProperty getFamilyProperty(ModelProperty modelProperty) {
    	if(modelProperty == null) return null;
    	for(FamilyProperty familyProperty : modelProperty.getFamilyProperties())
			if(families.contains(familyProperty.getFamily()))
				return familyProperty;
		return null;
	}
    
    public FamilyProperty getFamilyProperty(String tag) {
    	return getFamilyProperty(getModelProperty(tag));
	}

	@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || !getClass().equals(o.getClass())) return false;
        Device device = (Device) o;
        return Objects.equals(name, device.name);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

	@Override
	public JsonElement toJson(User user) {
		return LTJsonModel.toJson(DeviceJson.class, this, user);
	}
}

The Set has FetchType.LAZY.

I have this error expecially when I have in the DB more than 100 devices, keep in mind that I share the Hibernate Session among threads and the DeviceData are updated quiet often.

Any help will be appreciated

If you read the java doc of Session, you will realize that it is not thread safe. You need a Session per thread or synchronize access to it.