Spring Boot多租户设置与实现

打印 上一主题 下一主题

主题 966|帖子 966|积分 2898

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
在现代应用程序中,多租户架构是一种非常盛行的设计模式。多租户架构答应多个客户(租户)共享同一个应用程序实例,同时确保数据的隔离性和安全性。本文将详细介绍如安在Spring Boot应用程序中设置和实现多租户支持,并提供丰富的Java代码示例,帮助你更加深入地理解多租户架构的实现。
1. 什么是多租户架构?

多租户架构是一种软件架构模式,其中单个应用程序实例为多个租户(客户)提供服务。每个租户的数据和设置都是隔离的,确保不同租户之间的数据安全和隐私。多租户架构通常有三种实现方式:


  • 共享数据库,独立数据表:所有租户共享同一个数据库,但每个租户有独立的数据表。
  • 共享数据库,共享数据表:所有租户共享同一个数据库和数据表,通过区分租户标识来隔离数据。
  • 独立数据库:每个租户有独立的数据库实例。
本文将重点介绍共享数据库,共享数据表的多租户实现方式。
2. Spring Boot多租户设置

我们将使用Spring Boot和Hibernate来实现多租户支持。以下是实现步骤:

  • 设置基本项目结构。
  • 设置数据源和Hibernate拦截器。
  • 创建租户解析器。
  • 设置实体和存储库。
  • 测试多租户实现。
3. 设置基本项目结构

首先,创建一个Spring Boot项目,并添加以下依赖:
  1. <dependencies>
  2.     <dependency>
  3.         <groupId>org.springframework.boot</groupId>
  4.         <artifactId>spring-boot-starter-data-jpa</artifactId>
  5.     </dependency>
  6.     <dependency>
  7.         <groupId>org.springframework.boot</groupId>
  8.         <artifactId>spring-boot-starter-web</artifactId>
  9.     </dependency>
  10.     <dependency>
  11.         <groupId>com.h2database</groupId>
  12.         <artifactId>h2</artifactId>
  13.         <scope>runtime</scope>
  14.     </dependency>
  15. </dependencies>
复制代码
4. 设置数据源和Hibernate拦截器

在application.properties中设置数据源:
  1. spring.datasource.url=jdbc:h2:mem:testdb
  2. spring.datasource.driverClassName=org.h2.Driver
  3. spring.datasource.username=sa
  4. spring.datasource.password=password
  5. spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
  6. spring.jpa.show-sql=true
  7. spring.jpa.hibernate.ddl-auto=update
复制代码
创建一个TenantInterceptor类,用于拦截数据库利用并添加租户上下文:
  1. import org.hibernate.EmptyInterceptor;
  2. import org.hibernate.type.Type;
  3. import org.springframework.stereotype.Component;
  4. import java.io.Serializable;
  5. @Component
  6. public class TenantInterceptor extends EmptyInterceptor {
  7.     @Override
  8.     public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
  9.         // 在保存实体时添加租户标识
  10.         for (int i = 0; i < propertyNames.length; i++) {
  11.             if ("tenantId".equals(propertyNames[i])) {
  12.                 state[i] = TenantContext.getCurrentTenant();
  13.                 return true;
  14.             }
  15.         }
  16.         return false;
  17.     }
  18.     @Override
  19.     public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
  20.         // 在更新实体时添加租户标识
  21.         for (int i = 0; i < propertyNames.length; i++) {
  22.             if ("tenantId".equals(propertyNames[i])) {
  23.                 currentState[i] = TenantContext.getCurrentTenant();
  24.                 return true;
  25.             }
  26.         }
  27.         return false;
  28.     }
  29. }
复制代码
设置TenantContext,用于生存当前租户标识:
  1. public class TenantContext {
  2.     private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
  3.     public static void setCurrentTenant(String tenantId) {
  4.         currentTenant.set(tenantId);
  5.     }
  6.     public static String getCurrentTenant() {
  7.         return currentTenant.get();
  8.     }
  9.     public static void clear() {
  10.         currentTenant.remove();
  11.     }
  12. }
复制代码
5. 创建租户解析器

租户解析器用于从请求中提取租户标识,并设置到TenantContext中。我们可以通过Spring的过滤器来实现这一功能。
  1. import javax.servlet.Filter;
  2. import javax.servlet.FilterChain;
  3. import javax.servlet.FilterConfig;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.ServletRequest;
  6. import javax.servlet.ServletResponse;
  7. import javax.servlet.http.HttpServletRequest;
  8. import java.io.IOException;
  9. public class TenantFilter implements Filter {
  10.     @Override
  11.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  12.             throws IOException, ServletException {
  13.         HttpServletRequest httpRequest = (HttpServletRequest) request;
  14.         String tenantId = httpRequest.getHeader("X-TenantID");
  15.         if (tenantId != null) {
  16.             TenantContext.setCurrentTenant(tenantId);
  17.         }
  18.         try {
  19.             chain.doFilter(request, response);
  20.         } finally {
  21.             TenantContext.clear();
  22.         }
  23.     }
  24.     @Override
  25.     public void init(FilterConfig filterConfig) throws ServletException {
  26.     }
  27.     @Override
  28.     public void destroy() {
  29.     }
  30. }
复制代码
在设置类中注册这个过滤器:
  1. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. public class TenantConfiguration {
  6.     @Bean
  7.     public FilterRegistrationBean<TenantFilter> tenantFilter() {
  8.         FilterRegistrationBean<TenantFilter> registrationBean = new FilterRegistrationBean<>();
  9.         registrationBean.setFilter(new TenantFilter());
  10.         registrationBean.addUrlPatterns("/*");
  11.         return registrationBean;
  12.     }
  13. }
复制代码
6. 设置实体和存储库

创建一个示例实体,并在其中包含租户标识字段:
  1. import javax.persistence.Entity;
  2. import javax.persistence.GeneratedValue;
  3. import javax.persistence.GenerationType;
  4. import javax.persistence.Id;
  5. @Entity
  6. public class Product {
  7.     @Id
  8.     @GeneratedValue(strategy = GenerationType.IDENTITY)
  9.     private Long id;
  10.     private String tenantId;
  11.     private String name;
  12.     private Double price;
  13.     // Getter和Setter方法
  14. }
复制代码
创建一个Spring Data JPA存储库接口:
  1. import org.springframework.data.jpa.repository.JpaRepository;
  2. import java.util.List;
  3. public interface ProductRepository extends JpaRepository<Product, Long> {
  4.     List<Product> findByTenantId(String tenantId);
  5. }
复制代码
7. 测试多租户实现

创建一个简单的控制器来测试多租户功能:
  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.web.bind.annotation.*;
  3. import java.util.List;
  4. @RestController
  5. @RequestMapping("/products")
  6. public class ProductController {
  7.     @Autowired
  8.     private ProductRepository productRepository;
  9.     @PostMapping
  10.     public Product createProduct(@RequestBody Product product) {
  11.         return productRepository.save(product);
  12.     }
  13.     @GetMapping
  14.     public List<Product> getProducts() {
  15.         String tenantId = TenantContext.getCurrentTenant();
  16.         return productRepository.findByTenantId(tenantId);
  17.     }
  18. }
复制代码
测试多租户功能可以通过以下步骤进行:

  • 启动Spring Boot应用程序。
  • 使用不同的X-TenantID头发送请求来创建和获取产品。
比方,使用cURL下令创建和获取产品:
  1. # 创建产品,租户ID为tenant1
  2. curl -H "X-TenantID: tenant1" -X POST -d '{"name": "Product A", "price": 10.99}' -H "Content-Type: application/json" http://localhost:8080/products
  3. # 创建产品,租户ID为tenant2
  4. curl -H "X-TenantID: tenant2" -X POST -d '{"name": "Product B", "price": 15.99}' -H "Content-Type: application/json" http://localhost:8080/products
  5. # 获取产品,租户ID为tenant1
  6. curl -H "X-TenantID: tenant1" http://localhost:8080/products
  7. # 获取产品,租户ID为tenant2
  8. curl -H "X-TenantID: tenant2" http://localhost:8080/products
复制代码
通过以上步骤,你可以看到不同租户具有自己的产品列表,实现了数据隔离的多租户架构。
8. 结论

本文详细介绍了如安在Spring Boot应用程序中设置和实现多租户支持。通过使用Spring Boot、Hibernate拦截器和自界说过滤器,我们可以实现共享数据库、共享数据表的多租户架构。盼望通过本文的详细解释和代码示例,能帮助你更好地理解和实现多租户支持的Spring Boot应用程序。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

梦见你的名字

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表