我使用EJB 3.0和Hibernate 4和PostgreSQL作为我的数据库服务器来创建多租户系统,其中每个租户将具有单独但相同的架构。我仍处于试用阶段,我有3个方案public
,company1
,company2
所有人都有一个人。现在我想要做的是根据用户在运行时更改架构,以便他只能查看他/她公司的数据。
这是我的示例代码: 实体对象:
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> T unwrap(Class<T> 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
文件:
<?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 name="testEJB">
<jta-data-source>jdbc/testpgsql</jta-data-source>
<properties>
<property name="javax.persistence.provider" value="org.hibernate.ejb.HibernatePersistence" />
<property name="hibernate.connection.username" value="postgres" />
<property name="hibernate.connection.password" value="root" />
<property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/test" />
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.multiTenancy" value="SCHEMA" />
<property name="hibernate.tenant_identifier_resolver" value="com.neebal.util.multitenancy.SchemaResolver" />
<property name="hibernate.multi_tenant_connection_provider"
value="com.neebal.util.multitenancy.MultiTenantProvider" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>
</persistence>
最后是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<Person> getAll() {
Person person = entityManager.find(Person.class, 2L);
System.out.println(person.getName());
return null;
}
}
在此示例中,我将模式硬编码为company1
,但它仍然存在或从公共模式中检索数据。所以在这个例子中我错了。
答案 0 :(得分:1)
问题已经有1年了,但我认为根据某些运行时条件使用不同模式的问题很常见,所以无论如何我都会回答。如果我理解你的权利并且租户很少,那么我认为最简单的方法就是为你的persistence.xml中的每个租户定义一个单独的持久性单元
<persistence-unit name="public">
.. settings for first schema
</persistence-unit>
<persistence-unit name="company1">
.. settings for first schema
</persistence-unit>
<persistence-unit name="company2">
.. settings for first schema
</persistence-unit>
然后为每个人分别拥有一个entityManager:
@PersistenceContext(unitName = "public")
private EntityManager emPublic;
@PersistenceContext(unitName = "company1")
private EntityManager emComp1;
@PersistenceContext(unitName = "company2")
private EntityManager emComp1;
现在,您可以在给定当前授权用户的情况下在实体管理器之间切换。
根据您的确切基础设施等,也可能有其他方法。例如,如果所有模式都在同一台服务器上,那么您也可以尝试将模式名称直接传递给查询。
这是纯粹的JPA,因此是可移植的,不依赖于任何持久性提供程序,如hibernate和DBMS。