跟我上“云”端(四)使用eclipselink构建企业级多租户应用

上一篇博客中我们介绍了多租户的数据隔离,文中具体的介绍了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中定义了实体的公共属性,其中重点关注两个属性值
    1. id:id使用的是自定义的uuid生成策略,这个在下一篇博客中介绍。
    2. 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实现企业级的多租户应用的数据层隔离也就解决了。

你可能感兴趣的:(跟我上“云”端(四)使用eclipselink构建企业级多租户应用)