具有Hibernate 4.0和独立模式方法的MultiTenancy

我使用EJB 3.0和Hibernate 4和PostgreSQL作为我的数据库服务器来创建多租户系统,其中每个租户将具有单独但相同的架构。 我还处于试用阶段,我有3个方案publiccompany1company2都有一个单人桌。 现在我想要做的是根据用户在运行时更改模式,以便他只能查看他/她公司的数据。

这是我的示例代码:实体对象:

  package com.neebal.domain; import java.io.Serializable; import java.lang.Long; import java.lang.String; import javax.persistence.*; import org.eclipse.persistence.annotations.Multitenant; import org.eclipse.persistence.annotations.MultitenantType; @Entity //@Table(schema = "company1") public class Person implements Serializable { @Id private Long id; private String name; private static final long serialVersionUID = 1L; public Person() { super(); } public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } } 

MultiTenantConnectionProvider类:

 import java.sql.Connection; import java.sql.SQLException; import java.util.Map; import org.hibernate.HibernateException; import org.hibernate.service.config.spi.ConfigurationService; import org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider; import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.service.spi.ServiceRegistryAwareService; import org.hibernate.service.spi.ServiceRegistryImplementor; public class MultiTenantProvider implements MultiTenantConnectionProvider, ServiceRegistryAwareService { private static final long serialVersionUID = 4368575201221677384L; private C3P0ConnectionProvider connectionProvider = null; @Override public boolean supportsAggressiveRelease() { return false; } @Override public void injectServices(ServiceRegistryImplementor serviceRegistry) { Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings(); connectionProvider = new C3P0ConnectionProvider(); connectionProvider.injectServices(serviceRegistry); connectionProvider.configure(lSettings); } @Override public boolean isUnwrappableAs(Class clazz) { return false; } @Override public  T unwrap(Class clazz) { return null; } @Override public Connection getAnyConnection() throws SQLException { final Connection connection = connectionProvider.getConnection(); return connection; } @Override public Connection getConnection(String tenantIdentifier) throws SQLException { final Connection connection = getAnyConnection(); try { connection.createStatement().execute("SET SCHEMA '" + tenantIdentifier + "'"); } catch (SQLException e) { throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e); } return connection; } @Override public void releaseAnyConnection(Connection connection) throws SQLException { try { connection.createStatement().execute("SET SCHEMA 'public'"); } catch (SQLException e) { throw new HibernateException("Could not alter JDBC connection to specified schema [public]", e); } connectionProvider.closeConnection(connection); } @Override public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException { releaseAnyConnection(connection); } } 

CurrentTenantIdentifierResolver类:

 import org.hibernate.context.spi.CurrentTenantIdentifierResolver; public class SchemaResolver implements CurrentTenantIdentifierResolver { @Override public String resolveCurrentTenantIdentifier() { System.out.println("company1"); return "company1"; //TODO: Implement service to identify tenant like: userService.getCurrentlyAuthUser().getTenantId(); } @Override public boolean validateExistingCurrentSessions() { return false; } } 

persistence.xml文件:

    jdbc/testpgsql              

最后是DAO课程:

 import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import com.neebal.domain.Person; /** * Session Bean implementation class PersonDAO */ @Stateless public class PersonDAO implements PersonDAOLocal { @PersistenceContext EntityManager entityManager; /** * Default constructor. */ public PersonDAO() { // TODO Auto-generated constructor stub } @Override public void save(Person person) { entityManager.persist(person); } @Override public List getAll() { Person person = entityManager.find(Person.class, 2L); System.out.println(person.getName()); return null; } } 

在这个例子中,我将模式硬编码为company1但它仍然存在或从公共模式中检索数据。 所以我在这个例子中错了。

问题已经有1年了,但我认为根据某些运行时条件使用不同模式的问题很常见,所以无论如何我都会回答。 如果我理解你并且租户很小,那么我认为最简单的方法就是为你的persistence.xml中的每个租户定义一个单独的持久性单元。

  .. settings for first schema   .. settings for first schema   .. settings for first schema  

然后为每个人分别拥有一个entityManager:

 @PersistenceContext(unitName = "public") private EntityManager emPublic; @PersistenceContext(unitName = "company1") private EntityManager emComp1; @PersistenceContext(unitName = "company2") private EntityManager emComp1; 

现在,您可以在给定当前授权用户的情况下切换实体管理器。

根据您的确切基础设施等,也可能有其他方法。 例如,如果所有模式都在同一台服务器上,那么您也可以尝试将模式名称直接传递给查询。

这是纯粹的JPA,因此是可移植的,不依赖于任何持久性提供程序,如hibernate和DBMS。