Man*_*hah 9 java hibernate jpa jdbc multi-tenant
我使用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;
}
}
Run Code Online (Sandbox Code Playgroud)
本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);
}
}
Run Code Online (Sandbox Code Playgroud)
本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;
}
}
Run Code Online (Sandbox Code Playgroud)
该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>
Run Code Online (Sandbox Code Playgroud)
最后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;
}
}
Run Code Online (Sandbox Code Playgroud)
在这个例子中,我硬编码的模式作为company1,但它仍然存在,要么从公共架构检索数据.所以我在这个例子中错了.
这个问题已经有一岁了,但我认为根据某些运行时条件使用不同模式的问题是常见的,所以我无论如何都会回答。如果我理解正确并且租户集很小,那么我认为实现您想要实现的目标的最简单方法是在 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>
Run Code Online (Sandbox Code Playgroud)
然后为每一个都有一个单独的entityManager:
@PersistenceContext(unitName = "public")
private EntityManager emPublic;
@PersistenceContext(unitName = "company1")
private EntityManager emComp1;
@PersistenceContext(unitName = "company2")
private EntityManager emComp1;
Run Code Online (Sandbox Code Playgroud)
现在,您可以在给定当前授权用户的情况下在实体管理器之间切换。
根据您的具体基础设施等,可能还有其他方法。例如,如果您的所有模式都位于同一服务器上,那么您还可以尝试将模式名称直接传递给您的查询。
这是纯 JPA,因此是可移植的,不依赖于任何持久性提供程序(如 hibernate)或 DBMS。
| 归档时间: |
|
| 查看次数: |
9128 次 |
| 最近记录: |