英文:
how to Resolve "could not initialize proxy - no session" error when using Spring repository
问题
I'm working on a mutitenant project it maintains different schema for each tenant, followed Project
由于我们动态切换租户,似乎缺少某些配置,导致关闭会话或不保持会话打开以获取延迟加载的对象。这会导致“无法初始化代理 - 无会话”错误。
请查看以下链接以访问完整项目和数据库架构脚本,按照自述文件中提供的步骤操作。
Project
如果有人能指出代码中的问题,将会很有帮助。
我尝试将服务方法放在 @Transactional 注解中,但没有奏效。
我期望它能再次调用延迟加载的对象。这个项目是复杂项目的简化版本,实际上我有更多延迟加载的对象。
问题:
我遇到了“无法初始化代理 [com.amran.dynamic.multitenant.tenant.entity.Tenant#1] - 无会话”的无会话错误,位于第26行(/dynamicmultitenant/src/main/java/com/amran/dynamic/multitenant/tenant/service/ProductServiceImpl.java)。
英文:
I'm working on a mutitenant project it maintains different schema for each tenant, followed Project
As we are dynamically switching the tenants so it looks like some configuration is missed which is closing the session or not keeping the session open to fetch the LAZY loaded objects. Which results in "could not initialize proxy - no session" error.
Please check below link to access the complete project and db schema scripts, please follow the steps given in Readme file.
Project
It will be helpful if someone can point out the issue in the code.
i tried to put service methods in @Transactional annotation but that didn't work.
I'm expecting it to make another call to the LAZY loaded object, This project is simplefied verson of the complex project, actually i have lot more lazy loaded objects.
Issue:-
I'm getting no Session error "could not initialize proxy [com.amran.dynamic.multitenant.tenant.entity.Tenant#1] - no Session"
at line 26 (/dynamicmultitenant/src/main/java/com/amran/dynamic/multitenant/tenant/service/ProductServiceImpl.java)
答案1
得分: 1
The issue is that your transaction boundaries are not correct. In TenantDatabaseConfig
and MasterDatabaseConfig
you've correctly added @EnableTransactionManagement
, which will set up transactions when requested.
However - the outermost component that has an (implicit) @Transactional
annotation is the ProductRepository
(by virtue of it being implemented by the SimpleJpaRepository
class - which has the annotation applied to it - link
and so your productRepository.findAll();
call will start a transaction, create a JPA session, run the query, close the session, close the transaction, which means that there is no longer any transaction / session open in which to perform the lazy-loading.
Therefore, your original attempt of
i tried to put service methods in @Transactional annotation but that didn't work.
IS the correct thing to do.
You don't say exactly what you tried to do, and where, but there are a few things that could have gone wrong. Firstly, make sure you're adding a org.springframework.transaction.annotation.Transactional
and not a javax.transaction.Transactional
annotation.
Secondly (and the more likely problem in this scenario), you'll need to configure the annotation with which transaction manager the transaction should be bound to, otherwise it may use an existing / new transaction created against the master DB connection, not the tenant one.
In this case, I think that:
@Service
@Transactional(transactionManager = "tenantTransactionManager")
public class ProductServiceImpl implements ProductService {
should work for you, and make all the methods of the service be bound to a transaction on the tenant DB connection.
EDIT: Answering a follow-up question:
can you please also suggest a better way to inject my tenantTransactionManager in all my service classes, as I don't want to mention tenantTxnManger in all service classes if there is any better way to do it ?
Yes, sure. You can create a meta-annotation that applies multiple other annotations, so you could create:
/**
* Marks class as being a service operating on a single Tenant
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
@Transactional("tenantTransactionManager")
public @interface TenantService {
}
and then you can simply annotate your service classes with @TenantService
instead of @Service
:
@TenantService
public class ProductServiceImpl implements ProductService {
英文:
The issue is that your transaction boundaries are not correct. In TenantDatabaseConfig
and MasterDatabaseConfig
you've correctly added @EnableTransactionManagement
, which will setup transactions when requested.
However - the outermost component that has an (implicit) @Transactional
annotation is the ProductRepository
(by virtue of it being implemented by the SimpleJpaRepository
class - which has the annotation applied to it - https://github.com/spring-projects/spring-data-jpa/blob/864c7c454dac61eb602674c4123d84e63f23d766/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java#L95 )
and so your productRepository.findAll();
call will start a transaction, create a JPA session, run the query, close the session, close the transaction, which means that there is no longer any transaction / session open in which to perform the lazy-loading.
Therefore, your original attempt of
> i tried to put service methods in @Transactional annotation but that didn't work.
IS the correct thing to do.
You don't say exactly what you tried to do, and where, but there are a few things that could have gone wrong. Firstly, make sure you're adding a org.springframework.transaction.annotation.Transactional
and not a javax.transaction.Transactional
annotation.
Secondly (and the more likely problem in this scenario), you'll need to configure the annotation with which transaction manager the transaction should be bound to, otherwise it may use an existing / new transaction created against the master DB connection, not the tenant one.
In this case, I think that:
@Service
@Transactional(transactionManager = "tenantTransactionManager")
public class ProductServiceImpl implements ProductService {
should work for you, and make all the methods of the service be bound to a transaction on the tenant DB connection.
EDIT: Answering a follow-up question:
> can you please also suggest a better way to inject my tenantTransactionManager in all my service classes, as I don't want to mention tenantTxnManger in all service classes if there is any better way to do it ?
Yes, sure. You can create a meta-annotation that applies multiple other annotations, so you could create:
/**
* Marks class as being a service operating on a single Tenant
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
@Transactional("tenantTransactionManager")
public @interface TenantService {
}
and then you can simply annotate your service classes with @TenantService
instead of @Service
:
@TenantService
public class ProductServiceImpl implements ProductService {
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论