上一篇博客中我们介绍了多租户的数据隔离,文中具体的介绍了hibernate和eclipselink对于多租户的实现情况,博客的最后,我也对hibernate实现多租户的细节上做了解释,这次,我想带大家一起来使用eclipselink构建企业级的多租户项目。
eclipselink的三种实现
由于eclipselink完整实现了jpa规范,我们就可以使用ejb构建一个企业级的多租户项目,首先eclipselink支持3中多租户的模式:
- Single-Table Multi-tenancy,依靠租户区分列(tenant discriminator columns)来隔离表的行,实现多租户共享表。
- Table-Per-Tenant Multi-tenancy,依靠表的租户区分(table tenant discriminator)来隔离表,实现一租户一个表,大体类似于上文的共享数据库独立Schema模式。
- Virtual Private Database(VPD ) Multi-tenancy,依靠 Oracle VPD 自身的安全访问策略(基于动态SQL where子句特性),实现多租户共享表。
Table-Per-Tenant Multi-tenancy实现
- 我想将的是第二种方式,因为它在性能、分离度以及灵活性上都是比较合适的。
- 首先是persistence.xml的内容,经过上一篇博客的介绍,我们应该已经配置好eclipselink的基本使用环境。
Persistence.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<!-- 给定persistence-unit名字-->
<persistence-unit name="MT_HOTEL_SERVICE"
transaction-type="JTA">
<!-- 使用eclipselink的持久化实现-->
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<!-- 使用jboss中配置的数据源-->
<jta-data-source>java:jboss/datasources/JcMysqlDS</jta-data-source>
<!-- 由于配置了实体自动扫描此处不用列出系统中的试题-->
<properties>
<!-- 修改第一次加载时间长的问题 -->
<property name="eclipselink.deploy-on-startup" value="True" />
<!-- 运行执行本地sql查询 -->
<property name="eclipselink.jdbc.allow-native-sql-queries"
value="true" />
<!-- 设置服务器类型 -->
<property name="eclipselink.target-server" value="JBoss" />
<!-- logging 级别配置-->
<property name="eclipselink.logging.level" value="FINE" />
<!-- 配置静态织入-->
<property name="eclipselink.weaving" value="static" />
<!-- 设置自定义主键生成策略-->
<property name="eclipselink.session.customizer" value="com.tgb.itoo.base.util.uuid.UUIDSequence" />
</properties>
</persistence-unit>
</persistence>
- 配置中每一行的配置都有做出解释,这里就不再多说了。
BaseEntity配置
- 对于实体的配置:首先是使用的实体继承,BaseEntity的代码如下:
package com.tgb.itoo.base.entity;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
@MappedSuperclass
@EntityListeners(value = { EntityListener.class })
public abstract class BaseEntity implements IdEntity {
private static final long serialVersionUID = 1L;
public BaseEntity() {
super();
}
@Id
@GeneratedValue(generator = "system-uuid")//自定义uuid生成策略
@Column(name = "id")
protected String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/**
* 数据库名称
*/
@Transient //不进行持久化
private String dataBaseName;
public String getDataBaseName() {
return dataBaseName;
}
public void setDataBaseName(String dataBaseName) {
this.dataBaseName = dataBaseName;
}
/**
* 备注
*/
@Column(name = "comment", length = 255)
private String comment;
/**
* 操作人
*/
@Column(name = "operator", length = 20)
private String operator;
……
}
- BaseEntity中定义了实体的公共属性,其中重点关注两个属性值
- id:id使用的是自定义的uuid生成策略,这个在下一篇博客中介绍。
- dataBasename:从名字上来说很明了就是数据库名,但是这个属性不是用来持久化的,所以使用@Transient注解,这个属性主要是为了传递参数的,会在实现多租户的时候用到,因为对于任何实体的每一次操作都应该确定是那个承租者的操作,就要使数据的CRUD都要对应自己的数据库。
具体某个实体的设计
package com.tgb.itoo.authority.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.eclipse.persistence.annotations.Multitenant;
import org.eclipse.persistence.annotations.MultitenantType;
import org.eclipse.persistence.annotations.TenantTableDiscriminator;
import org.eclipse.persistence.annotations.TenantTableDiscriminatorType;
import com.tgb.itoo.base.entity.TimeBaseEntity;
/**
* 角色实体
* @author hanyi
*
*/
@Entity
@Table(name="ta_role")
@Multitenant(value=MultitenantType.TABLE_PER_TENANT)
@TenantTableDiscriminator(type=TenantTableDiscriminatorType.SCHEMA, contextProperty="tenant_id")
public class Role extends TimeBaseEntity {
private static final long serialVersionUID = 598970641717338526L;
/**
* 角色名称
*/
@Column(name = "roleName", length = 32)
private String roleName;
/**
* 角色描述
*/
@Column(name = "roleDescribe", length = 100)
private String roleDescribe;
……
}
- 应该注意实体中对于多租户的注解,其中设置了MultitenantType.TABLE_PER_TENANT也就是使用依靠表的租户区分,TenantTableDiscriminatorType有三种类型,这里使用SCHEMA,也就是每个租户使用独立的SCHEMA,定义了contextProperty=”tenant_id”,这也就设置了entitymanager可以根据tenant_id属性区别不同的租户。
entitymanager的注入
//实体管理器,管理实体持久化操作,通过容器注入
@PersistenceContext(unitName = "MT_HOTEL_SERVICE")
protected EntityManager em;
设置多租户属性
/**
* 根据数据库名字,得到当前操作对应的实体管理器的名字
* @return 实体管理器
*/
protected EntityManager getEntityManager() {
//dataBaseName的值,给实体管理器赋值,此处的tenant_id是在实体注解时规定,只要统一可以是任何值。
if (dataBaseName!=null && !dataBaseName.equals(""))
{
em.setProperty("tenant_id", dataBaseName);
}
//如果dataBaseName的值为空,那么就选择一个默认的库,这里选择的是根库(base)
else
{
em.setProperty("tenant_id", "base);
}
return this.em;
}
具体Eao的方法
/**
* @author hanyi
* @MethodName : save
* @Description : 无返回值的泛型保存方法
* @param t:要保存的实体
*/
public void save(Object entity) {
//从实体类型中拿到当前操作的数据库名字
getDataBaseName(entity);
//取得实体管理器,进行持久化操作
getEntityManager().persist(entity);
}
- 这里忽略了从getDataBaseName(entity)方法的代码,其主要作用是通过反射得到前台设置的databasename的值,之后再getEntityManager的时候使用。
完成配置
到此eclipselink+ejb+jpa实现企业级的多租户应用的数据层隔离也就解决了。
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-04 19:08:40