Mandantenfähigkeit mit Hibernate 4.0 mit Separaten Schema-Ansatz
Ich bin mit EJB 3.0 und Hibernate 4 mit PostgreSQL als mein Datenbank server zu erstellen Multi-Tenant - system, wo jeder Mieter haben getrennte, aber identische schema. Ich bin noch in der Testphase, wo ich 3 Systeme public
, company1
, company2
alle mit einer einzigen Tabelle person. Nun, was ich tun möchten, ist ändern Sie das schema in der Laufzeit als pro Benutzer, so dass er die Daten anzeigen können seine/Ihre Firma nur.
Hier ist mein Beispielcode:
Entity-Objekt:
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;
}
}
Den MultiTenantConnectionProvider
Klasse:
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);
}
}
Den CurrentTenantIdentifierResolver
Klasse:
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;
}
}
Den persistence.xml
Datei:
<?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>
Und schließlich die DAO
Klasse:
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;
}
}
In diesem Beispiel habe ich hardcoded das schema als company1
aber noch besteht oder ruft die Daten aus dem öffentlichen schema. Also, wo bin ich falsch in diesem Beispiel.
- Yupp, ich habe es in meiner Art und Weise aber. Ich werde meine Lösung hier in kurzer Zeit.
- Ich dachte, es war schon arbeiten wie diese, hmmm. Warten auf Ihre Lösung, einfach nur zu vergleichen.vielen Dank!
- Kannst du bitte die Lösung posten, die Sie erwähnt?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ist die Frage schon 1 Jahr alt, aber ich denke, das problem der Verwendung unterschiedlicher schemas je nach einiger Laufzeit-Zustand ist Häufig ein, so werde ich dennoch Antworten. Wenn ich verstehe, dass Sie die Rechte und die Menge der Mieter ist klein, dann denke ich, der einfachste Weg, das zu tun, was Sie zu erreichen versuchen, ist die Definition eines eigenen Persistenz-Einheiten für jeden Mieter in Ihrem persistence.xml
Dann für jeden eine separate entityManager:
Nun können Sie wechseln zwischen den entity-Managern, angesichts der derzeit autorisierten Benutzer.
Je nach Ihren genauen Infrastruktur etc., möglicherweise gibt es andere Ansätze auch. Zum Beispiel, wenn alle Ihre schemas sind auf dem gleichen server, dann könnten Sie auch versuchen, geben Sie den schema-Namen, die direkt auf Ihre Fragen.
Ist das Reine PPV und somit tragbar und nicht abhängig Persistenz-provider, wie hibernate noch auf Ihrem DBMS.