springboot 버전은 2.3.4입니다.RELEASE
지속성 계층은 JPA를 채택합니다
Saas 애플리케이션의 모든 테넌트는 테넌트 데이터를 격리하기 위해 동일한 서비스와 데이터베이스를 사용하기 때문에 BaseSaasEntity
public abstract class BaseSaasEntity { @JsonIgnore @Column(nullable = false, updatable = false) protected Long tenantId; }
@MappedSuperclass @Data @FilterDef(name = "tenantFilter", parameters = {@ParamDef(name = "tenantId", type = "long")}) @Filter(condition = "tenant_id=:tenantId", name = "tenantFilter") public abstract class BaseSaasEntity { @JsonIgnore @Column(nullable = false, updatable = false) protected Long tenantId; @PrePersist public void onPrePersist() { if (getTenantId() != null) { return; } Long tenantId = TenantContext.getTenantId(); Check.notNull(tenantId, "租户不存在"); setTenantId(tenantId); } }
의 일부 기능을 활용할 수 있습니다. Hibernate3은 "가시성" 규칙, 즉 Hibernate 필터를 사용하여 데이터를 처리하는 혁신적인 방법을 제공합니다. Hibernate 필터는 매개변수를 취할 수 있는 전역적으로 유효한 명명된 필터입니다. 특정 Hibernate 세션에 대해 필터를 활성화(또는 비활성화)할지 여부를 선택할 수 있습니다.여기에서는 @FilterDef 및 @Filter를 통해 SQL 필터 조건을 미리 정의합니다. 그런 다음 @TenantFilter 주석을 사용하여 인터페이스에 데이터 필터링이 필요하다는 것을 식별합니다
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Transactional public @interface TenantFilter { boolean readOnly() default true; }
@Aspect @Slf4j @RequiredArgsConstructor public class TenantSQLAspect { private static final String FILTER_NAME = "tenantFilter"; private final EntityManager entityManager; @SneakyThrows @Around("@annotation(com.lvjusoft.njcommon.annotation.TenantFilter)") public Object aspect(ProceedingJoinPoint joinPoint) { Session session = entityManager.unwrap(Session.class); try { Long tenantId = TenantContext.getTenantId(); Check.notNull(tenantId, "租户不存在"); session.enableFilter(FILTER_NAME).setParameter("tenantId", tenantId); return joinPoint.proceed(); } finally { session.disableFilter(FILTER_NAME); } } }
public class IdentityInterceptor extends HandlerInterceptorAdapter { public IdentityInterceptor() { log.info("IdentityInterceptor init"); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader(AuthConstant.USER_TOKEN_HEADER_NAME); UserContext.setToken(token); String tenantId = request.getHeader(AuthConstant.TENANT_TOKEN_HEADER_NAME); TenantContext.setTenantUUId(tenantId); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { UserContext.clear(); TenantContext.clear(); } }
public class DynamicRoutingDataSource extends AbstractRoutingDataSource { private Map<Object, Object> targetDataSources; public DynamicRoutingDataSource() { targetDataSources =new HashMap<>(); DruidDataSource druidDataSource1 = new DruidDataSource(); druidDataSource1.setUsername("username"); druidDataSource1.setPassword("password"); druidDataSource1.setUrl("jdbc:mysql://localhost:3306/db?useSSL=false&useUnicode=true&characterEncoding=utf-8"); targetDataSources.put("db1",druidDataSource1); DruidDataSource druidDataSource2 = new DruidDataSource(); druidDataSource2.setUsername("username"); druidDataSource2.setPassword("password"); druidDataSource2.setUrl("jdbc:mysql://localhost:3306/db?useSSL=false&useUnicode=true&characterEncoding=utf-8"); targetDataSources.put("db2",druidDataSource1); this.targetDataSources = targetDataSources; super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } public void addDataSource(String key, DataSource dataSource) { if (targetDataSources.containsKey(key)) { throw new IllegalArgumentException("dataSource key exist"); } targetDataSources.put(key, dataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return DataSourceContext.getSource(); } }
위 내용은 Springboot 다중 테넌트 SaaS를 구축하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!