- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在开发支持 Multi-Tenancy 的 POC java 应用程序。我使用 JHipster 生成器启动我的 POC,并在 spring boot 上启动 OAUTH2 身份验证。每个租户都有自己的 SCHEMA,但租户和 OAUTH2 表是公开的。 JHipster 使用 hibernate 和 Spring Data 连接数据库。在我的示例中,我使用 Mysql 作为数据库。
我想用单个数据源和单个连接池实现解决方案。作为连接池,JHipster 使用 HikariCP。在 MultiTenantConnectionProvider 中,我想以类似 Hibernate 文档描述的方式更改 SCHEMA(参见示例 16.3。)
http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch16.html#d5e4866
当 hibernate 调用 getConnection(String tenantIdentifier) 时,我想在 MYSQL 数据库中设置正确的 SCHEMA。我的实现使用 Mysql 命令来更改方案“USE sample_tenant_identifier”。我必须使用名称为“user”和“admin”的用户。每个用户都有自己的 SCHEMA。我遇到的问题很奇怪。例如,所有 SELECT 操作都使用“user”模式,但 INSERT 或 UPDATE 使用“admin”模式。结果“admin”在“user”SCHEMA 中看到数据,但将数据插入“admin”SCHEMA。
包 com.mycompany.myapp.tenancy.hibernate;
import org.hibernate.HibernateException;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
/**
* Created by AdamS on 2015-04-02.
*/
public class SchemaMultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider, ServiceRegistryAwareService {
private final Logger log = LoggerFactory.getLogger(SchemaMultiTenantConnectionProviderImpl.class);
DataSource dataSource;
@Override
public Connection getAnyConnection() throws SQLException {
return this.dataSource.getConnection();
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
try {
connection.createStatement().execute("USE jhipster;");
}
catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [public]", e);
}
connection.close();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
log.debug("Tenatd is:"+tenantIdentifier);
final Connection connection = getAnyConnection();
try {
connection.createStatement().execute("USE " + tenantIdentifier + ";");
// connection.setCatalog(tenantIdentifier);
// connection.setSchema(tenantIdentifier);
}
catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
}
return connection;
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
// try {
// connection.createStatement().execute("USE "+tenantIdentifier+";");
// }
// catch (SQLException e) {
// throw new HibernateException("Could not alter JDBC connection to specified schema [public]", e);
// }
connection.close();
}
@Override
public boolean supportsAggressiveRelease() {
return false;
}
@Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}
@Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings();
DataSource localDs = (DataSource) lSettings.get("hibernate.connection.datasource");
dataSource = localDs;
}
}
我创建了第二个工作示例,我在其中为每个租户创建了新的 DataSource 并将其存储在 Map 中。这个例子工作正常,但并发映射这不是我想要的。
package com.mycompany.myapp.tenancy.hibernate;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* Created by AdamS on 2015-03-12.
*/
public class MyMultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider, ServiceRegistryAwareService {
private final Logger log = LoggerFactory.getLogger(MyMultiTenantConnectionProviderImpl.class);
DataSource dataSource;
private Map<String, DataSource> dataSourceMap = new HashMap<String, DataSource>();
public MyMultiTenantConnectionProviderImpl() {
getSource("main");
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
connection.close();
}
@Override
public Connection getAnyConnection() throws SQLException {
//return this.dataSource.getConnection();
log.info("get eny connection return main");
return getSource("jhipster").getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
log.info("Tenatd is:" + tenantIdentifier);
return getSource(tenantIdentifier).getConnection();
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
log.info("releaseConnection " + tenantIdentifier);
connection.close();
}
@Override
public boolean supportsAggressiveRelease() {
return false;
}
@Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}
@Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings();
DataSource localDs = (DataSource) lSettings.get("hibernate.connection.datasource");
dataSource = localDs;
}
public DataSource getSource(String tentant) {
if(dataSourceMap.containsKey(tentant)){
return dataSourceMap.get(tentant);
} else {
DataSource ds = dataSource(tentant);
dataSourceMap.put(tentant,ds);
return ds;
}
}
public DataSource dataSource(String tentant) {
log.info("Create Datasource "+tentant);
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
config.addDataSourceProperty("url", "jdbc:mysql://localhost:3306/"+tentant);
config.addDataSourceProperty("user", "root");
config.addDataSourceProperty("password", "");
//MySQL optimizations, see https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
config.addDataSourceProperty("cachePrepStmts", true);
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.addDataSourceProperty("useServerPrepStmts", "true");
return new HikariDataSource(config);
}
}
我的配置类:
package com.mycompany.myapp.config;
...
@Configuration
@EnableJpaRepositories(basePackages = {"com.mycompany.myapp.repository"},entityManagerFactoryRef = "entityManagerFactory",transactionManagerRef = "transactionManager")
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
//@EnableTransactionManagement()
//@EnableAutoConfiguration(exclude = HibernateJpaAutoConfiguration.class)
public class DatabaseConfiguration implements EnvironmentAware {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
private RelaxedPropertyResolver propertyResolver;
private Environment env;
private DataSource dataSource;
@Autowired(required = false)
private MetricRegistry metricRegistry;
@Override
public void setEnvironment(Environment env) {
this.env = env;
this.propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
}
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingClass(name = "com.mycompany.myapp.config.HerokuDatabaseConfiguration")
@Profile("!" + Constants.SPRING_PROFILE_CLOUD)
public DataSource dataSource() {
log.debug("Configuring Datasource");
if (propertyResolver.getProperty("url") == null && propertyResolver.getProperty("databaseName") == null) {
log.error("Your database connection pool configuration is incorrect! The application" +
"cannot start. Please check your Spring profile, current profiles are: {}",
Arrays.toString(env.getActiveProfiles()));
throw new ApplicationContextException("Database connection pool is not configured correctly");
}
HikariConfig config = new HikariConfig();
config.setDataSourceClassName(propertyResolver.getProperty("dataSourceClassName"));
if (propertyResolver.getProperty("url") == null || "".equals(propertyResolver.getProperty("url"))) {
config.addDataSourceProperty("databaseName", propertyResolver.getProperty("databaseName"));
config.addDataSourceProperty("serverName", propertyResolver.getProperty("serverName"));
} else {
config.addDataSourceProperty("url", propertyResolver.getProperty("url"));
}
config.addDataSourceProperty("user", propertyResolver.getProperty("username"));
config.addDataSourceProperty("password", propertyResolver.getProperty("password"));
//MySQL optimizations, see https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(propertyResolver.getProperty("dataSourceClassName"))) {
config.addDataSourceProperty("cachePrepStmts", propertyResolver.getProperty("cachePrepStmts", "true"));
config.addDataSourceProperty("prepStmtCacheSize", propertyResolver.getProperty("prepStmtCacheSize", "250"));
config.addDataSourceProperty("prepStmtCacheSqlLimit", propertyResolver.getProperty("prepStmtCacheSqlLimit", "2048"));
config.addDataSourceProperty("useServerPrepStmts", propertyResolver.getProperty("useServerPrepStmts", "true"));
}
if (metricRegistry != null) {
config.setMetricRegistry(metricRegistry);
}
dataSource = new HikariDataSource(config);
return dataSource;
}
@Bean
public SpringLiquibase liquibase(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:config/liquibase/master.xml");
liquibase.setContexts("development, production");
if (env.acceptsProfiles(Constants.SPRING_PROFILE_FAST)) {
if ("org.h2.jdbcx.JdbcDataSource".equals(propertyResolver.getProperty("dataSourceClassName"))) {
liquibase.setShouldRun(true);
log.warn("Using '{}' profile with H2 database in memory is not optimal, you should consider switching to" +
" MySQL or Postgresql to avoid rebuilding your database upon each start.", Constants.SPRING_PROFILE_FAST);
} else {
liquibase.setShouldRun(false);
}
} else {
log.debug("Configuring Liquibase");
}
return liquibase;
}
@Bean
public MultiTenantSpringLiquibase liquibaseMt(DataSource dataSource) throws SQLException {
MultiTenantSpringLiquibase multiTenantSpringLiquibase = new MultiTenantSpringLiquibase();
multiTenantSpringLiquibase.setDataSource(dataSource);
Statement stmt = null;
stmt = dataSource.getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT tu.tentantId FROM t_user tu WHERE tu.tentantId IS NOT NULL");
ArrayList<String> schemas = new ArrayList<>();
while(rs.next()) {
String schemaName = rs.getString("tentantId");
dataSource.getConnection().createStatement().executeUpdate("CREATE DATABASE IF NOT EXISTS "+schemaName);
schemas.add(schemaName);
}
multiTenantSpringLiquibase.setSchemas(schemas);
multiTenantSpringLiquibase.setChangeLog("classpath:config/liquibase/mt_master.xml");
multiTenantSpringLiquibase.setContexts("development, production");
if (env.acceptsProfiles(Constants.SPRING_PROFILE_FAST)) {
if ("org.h2.jdbcx.JdbcDataSource".equals(propertyResolver.getProperty("dataSourceClassName"))) {
multiTenantSpringLiquibase.setShouldRun(true);
log.warn("Using '{}' profile with H2 database in memory is not optimal, you should consider switching to" +
" MySQL or Postgresql to avoid rebuilding your database upon each start.", Constants.SPRING_PROFILE_FAST);
} else {
multiTenantSpringLiquibase.setShouldRun(false);
}
} else {
log.debug("Configuring MultiTenantSpringLiquibase");
}
return multiTenantSpringLiquibase;
}
@Bean
public Hibernate4Module hibernate4Module() {
return new Hibernate4Module();
}
}
和 EntityManagerConfiguration:
package com.mycompany.myapp.config;
....
/**
* Created by AdamS on 2015-03-31.
*/
@Configuration
@EnableTransactionManagement
public class EntityManagerConfiguration {
@Autowired
private DataSource dataSource;
@Autowired
private JpaVendorAdapter jpaVendorAdapter;
@Bean(name = "entityManagerFactory")
//@DependsOn("transactionManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws Throwable {
HashMap<String, Object> properties = new HashMap<String, Object>();
//properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
//properties.put("javax.persistence.transactionType", "JTA");
properties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory");
properties.put("hibernate.cache.use_second_level_cache", "false");
properties.put("hibernate.cache.use_query_cache", "false");
properties.put("hibernate.generate_statistics", "true");
properties.put("hibernate.tenant_identifier_resolver", "com.mycompany.myapp.tenancy.hibernate.MyCurrentTenantIdentifierResolver");
/* MANY DATASOURCES. WORKING SOLUTION */
//properties.put("hibernate.multi_tenant_connection_provider", "com.mycompany.myapp.tenancy.hibernate.MyMultiTenantConnectionProviderImpl");
/*SCHEMA CONFIG THAT IS NOT WORKING*/
properties.put("hibernate.multi_tenant_connection_provider", "com.mycompany.myapp.tenancy.hibernate.SchemaMultiTenantConnectionProviderImpl");
properties.put("hibernate.multiTenancy", "SCHEMA");
LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setDataSource(dataSource);
entityManager.setJpaVendorAdapter(jpaVendorAdapter);
//entityManager.setPackagesToScan("com.mycompany.myapp.domain");
entityManager.setPackagesToScan(new String[] { "com.mycompany.myapp.domain","com.mycompany.myapp.tenancy.domain" });
entityManager.setPersistenceUnitName("persistenceUnit");
entityManager.setJpaPropertyMap(properties);
return entityManager;
}
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
有人知道为什么我的 Hibernate 实现可以那样工作吗?您可以在 github 上找到整个项目:
最佳答案
替换
connection.createStatement().execute("USE " + tenantIdentifier + ";");
与
connection.setCatalog(tenantIdentifier);
代码中的主要问题是(我认为)使用 sql USE ...;
而不是使用 setCatalog()
函数作为 explained in the mysql connector documentation
我遇到了同样的问题。首先,我想到了 hibernate 问题并构建了一个 new jhipster project根据你的代码。即使使用较新的 hibernate 模式,我也重现了这个问题。
一旦我在连接上使用了 setCatalog,它就开始工作了。
关于java - Multi-Tenancy 应用程序 Java Spring Hibernate Mysql OAuth2 Spring Security,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29755928/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!