1.什么是多租户
在Java中,每个租户通常是一个独立的公司或组织,他们希望在共享的环境中隔离自己的数据和配置,多租户之间可以实现资源隔离,也可以共享某个共同的资源数据,在逻辑层面上来看,他们是隔离的,但是底层上还是使用的相同的硬件资源
多租户的架构,通常应用在Saas程序当中,允许服务提供商通过单一实例为多个客户提供服务,从而提高资源利用率并降低成本。
2.常见的租户数据模型
- 独立数据库模式(Separate Database Model):每个租户拥有独立的数据库。
- 共享数据库,独立Schema模式(Shared Database, Separate Schema Model):多个租户共享一个数据库,但每个租户有独立的Schema。
- 共享数据库,共享Schema模式(Shared Database, Shared Schema Model):多个租户共享一个数据库和一个Schema,租户数据通过逻辑隔离(如租户ID)来区分。
(1)独立数据库模式实现
配置多数据源
@Configuration
public class DataSourceConfig {
@Bean(name = "tenantDataSource")
@Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public DataSource tenantDataSource() {
// 动态获取当前租户ID (通常从请求中获取)
String tenantId = TenantContext.getTenantId();
// 根据租户ID选择对应的数据源
if (tenantId.equals("tenant1")) {
return DataSourceBuilder.create()
.url("jdbc:mysql://localhost:3306/tenant1_db")
.username("root")
.password("password")
.build();
} else if (tenantId.equals("tenant2")) {
return DataSourceBuilder.create()
.url("jdbc:mysql://localhost:3306/tenant2_db")
.username("root")
.password("password")
.build();
}
throw new RuntimeException("Unknown tenant");
}
}
使用TenanContext类去获取租客信息
public class TenantContext {
private static ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static void setTenantId(String tenantId) {
currentTenant.set(tenantId);
}
public static String getTenantId() {
return currentTenant.get();
}
public static void clear() {
currentTenant.remove();
}
}
(2)共享数据库,独立Schema模式实现
这里用Hibernate实现
配置类
@Configuration
public class HibernateConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, MultiTenantConnectionProvider connectionProvider, CurrentTenantIdentifierResolver tenantResolver) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setPackagesToScan("com.example.demo.model");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(vendorAdapter);
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.multiTenancy", MultiTenancyStrategy.SCHEMA);
properties.put("hibernate.tenant_identifier_resolver", tenantResolver);
properties.put("hibernate.multi_tenant_connection_provider", connectionProvider);
emf.setJpaPropertyMap(properties);
return emf;
}
}
自定义多租户连接器
@Component
public class SchemaMultiTenantConnectionProvider implements MultiTenantConnectionProvider {
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
Connection connection = dataSource.getConnection();
connection.createStatement().execute("SET SCHEMA '" + tenantIdentifier + "'");
return connection;
}
@Override
public Connection getAnyConnection() throws SQLException {
return dataSource.getConnection();
}
// 释放连接
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
connection.close();
}
}
自定义租户解析器
@Component
public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
return TenantContext.getTenantId(); // 返回当前租户的Schema名称
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
(3)共享数据库,共享Schema模式实现
实体类
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "tenant_id")
private String tenantId; // 用于区分租户
private String productName;
private int quantity;
// getters and setters
}
Service层
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public List<Order> getOrdersForCurrentTenant() {
String tenantId = TenantContext.getTenantId();
return orderRepository.findByTenantId(tenantId);
}
public Order createOrder(Order order) {
String tenantId = TenantContext.getTenantId();
order.setTenantId(tenantId);
return orderRepository.save(order);
}
}
JPA
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByTenantId(String tenantId);
}
3.多租户的实现
常见的方法包括:
- Hibernate的多租户支持:Hibernate提供了内置的多租户支持,可以通过配置来实现不同的数据隔离策略。
- Spring Data JPA:与Spring Boot结合,提供了灵活的多租户数据访问层的实现。
- 数据库层实现:通过在SQL查询中加入租户ID过滤条件,确保每个租户只能访问自己的数据。