我在Quarkus中使用hibernate6的TenantResolver来进行多租户过滤,当然,在springboot中使用CurrentTenantIdentifierResolver也是同样的问题。我发现TenantResolver在一个请求中只执行一次
自定义租户解析器.java
@PersistenceUnitExtension
@RequestScoped
public class CustomTenantResolver implements TenantResolver {
@Override
public String getDefaultTenantId() {
return TenantUtil.NONE.toString();
}
@Override
public String resolveTenantId() {
var tenantId = TenantUtil.getTenantId().toString();
System.out.println("resolve tenantId " + tenantId);
return tenantId;
}
}
租户实用程序
public class TenantUtil {
private static final ThreadLocal<Serializable> tl = new ThreadLocal<>();
public static final Serializable ROOT = "-1";
public static final Serializable NONE = "-999";
public static void clear() {
tl.remove();
}
public static Serializable getTenantId() {
return tl.get();
}
public static void setTenantId(Serializable tenantId) {
tl.set(tenantId);
}
}
租户过滤器
@Priority(0)
@Provider
public class TenantFilter implements ContainerRequestFilter, ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
String tenantId = requestContext.getUriInfo().getQueryParameters().getFirst("tenantId");
TenantUtil.setTenantId(Objects.requireNonNullElse(tenantId, TenantUtil.NONE));
}
@Override
public void filter(
ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
TenantUtil.clear();
}
}
角色资源
@Path("/role")
public class RoleResource {
@Inject RoleRepository roleRepository;
@GET
@Path("list")
public List<SysRole> list() {
return roleRepository.select().fetch();
}
@GET
@Path("test")
public void test() {
TenantUtil.setTenantId("123");
System.out.println(roleRepository.select().fetch());
TenantUtil.setTenantId("456");
System.out.println(roleRepository.select().fetch());
}
}
curl http://127.0.0.1/role/list?tenantId=123
[{"id":"1", "tenantId": "123"}]
curl http://127.0.0.1/role/list?tenantId=456
[{"id":"2", "tenantId": "456"}]
curl http://127.0.0.1/role/test
[{"id":"1", "tenantId": "123"}]
[]
CustomTenantResolver.resolveTenantId
只执行一次
租户是在 EntityManager 创建时确定的,EntityManager 通常绑定到您的事务(或者默认绑定到您的请求,但不要依赖它,事务是您的朋友,Quarkus 中的 Hibernate ORM 在外部是只读的)无论如何,交易)。
所以我认为你必须为每个租户使用不同的交易:
public class RoleResource {
@Inject RoleRepository roleRepository;
@GET
@Path("list")
@Transactional // You really should use transactions
public List<SysRole> list() {
return roleRepository.select().fetch();
}
@GET
@Path("test")
public void test() {
QuarkusTransaction.requiringNew().run(() -> {
TenantUtil.setTenantId("123");
System.out.println(roleRepository.select().fetch());
});
QuarkusTransaction.requiringNew().run(() -> {
TenantUtil.setTenantId("456");
System.out.println(roleRepository.select().fetch());
});
}
}
请参阅本文档了解如何使用事务,特别是有关
QuarkusTransaction
的部分。或者 您可以使用标准的 UserTransaction
,但在我看来它不太实用。