I have one master db. After login with master db I have some another db. Is it possible to connect at runtime to second db and also have instanace of first db also(master db) application using spring-jdbc or hibernate,
thanks in advance.
Yes, sure. You can create as many data sources as you need. Just define them in the Spring Context and autowire in you classes. This question might help you with defining components with the same type but different names.
UPD1: you can create a datasource at runtime just like that:
DataSource ds = new DataSource();
ds.setUsername("username");
ds.setPassword("password");
ds.setDriverClassName("com.mysql.jdbc.Driver"); // or another driver
ds.setUrl("jdbc:mysql://{hostname}:{port}/{dbName}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false");
ds.setTestWhileIdle(true);
ds.setTestOnBorrow(true);
ds.setTestOnReturn(false);
ds.setValidationQuery("/* ping */ SELECT 1");
ds.setValidationQueryTimeout(1);
ds.setValidationInterval(30000);
ds.setTimeBetweenEvictionRunsMillis(30000);
ds.setMinIdle(1);
ds.setMaxWait(10000);
ds.setMaxIdle(10);
ds.setInitialSize(10);
ds.setMinEvictableIdleTimeMillis(30000);
Related
I am building a REST-API with spring boot and I would like to implement a Multi-Tenant Structure to handle data.
I want to have one Database called Main that will have the User table, which will hava data about users (Username, password ... and a field database that will denote which database is appointed to this user).
Everytime a user signs up his respective DB will be created (This is one of the points where I am facing difficulties).
I have read different Tutorials and they all are with predefined Datasources in the application.properties file. Clearly this is not the case here, since the DB for each user will be created "on the fly", or accessed if it is already created.
The workflow is like this ( explained as simple as possible ):
User signs-up
The app creates the User Entity saves it to Main DB and creates the respective DB for the user
The app checks for each call if the user is authenticated, if he is, then go and fetch data from his DB
Then there are a lot of questions regarding filling the DBs when they are automatically created.
But first things first :)
My stack : POSTGRESQL, Spring Boot
Thank you in advance.
multi-tenancy can be achieved as you required by following steps.
Add 2 configuration classes one for shared database and one for tenant database, which configurs LocalContainerEntityManagerFactoryBean. This bean should set the required multitenancy properties for LocalContainerEntityManagerFactoryBean
e.g.
Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
this.properties.getProperties(), new HibernateSettings());
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, this.connectionProvider);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, this.resolver);
properties.put(Environment.DIALECT, "org.hibernate.dialect.MySQLDialect");
This class should also implement named bean transactionManager for each type. e.g.
#Bean(name = "tenantTransactionManager")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(this.entityManagerFactory().getObject());
return tm;
}
implement interface CurrentTenantIdentifierResolver and method resolveCurrentTenantIdentifier. This should return the database name of the tenant based on the current logged-in user. Or default database name if no user is logged-in
A thread safe context holder to remember the current tenant name
Annotate the services implementations for the entity classes with #Transactional annotation and pass the bean name of appropriate entity manager e.g.
#Transactional("tenantTransactionManager") // for tenant database
and
#Transactional("transactionManager") // for shared database.
Setup a database schema creation method when a new users signs up. and maintain the tenant database name as one of the column in user table in shared schema.
If you are using spring security, implement UserDetailsService interface and implement method loadUserByUsername such that it returns an object of TenantUser class which contains additional information ( tenant database name) for the user logging in.
public class TenantUser extends org.springframework.security.core.userdetails.User {
/** The tenand id. */
private String tenantId;
Hope these steps help you to achieve what you want. There are many articles available which explains all these steps in detail. My implementation is deep embedded in my project hence it is not in a state which can be shared as working example.
Happy to answer any further questions
I found the complete solution to my problem here:
Multi-tenancy: Managing multiple datasources with Spring Data JPA
Big thanks to the author #Cepr0.
The only thing that is missing is creating the DB on the fly.
I will update the answer here when I finish my implementation.
UPDATE
I created the Database with the following code, it was recommended by #Milind Barve. So thank you.
Class.forName("org.postgresql.Driver");
Connection con = DriverManager.getConnection("jdbc:postgresql://localhost:5432/","postgres", "password");
Statement smt = con.createStatement();
smt.executeUpdate("CREATE DATABASE [name_of_db_here] WITH OWNER DEFAULT");
UPDATE :
Initializing the schema of each newly created DB,
I created a .sql file with all the table creations and use FlyWay to initialize each newly created DB
// INITIALIZE THE DB
Flyway flyway = Flyway.configure()
.dataSource(dataSource)
.target(MigrationVersion.LATEST)
.load();
flyway.migrate();
Spring Boot : How to add new Datasource at runtime
My project want to connect two datasource.
The first datasource I can Config in application.properties but the second datasource can't config because this config is in the tableConfig from DB of the first datasource.
So,
config the 1st datasource.
query data from the 1st datasource for get config of 2nd datasource (url, username, password).
add new 2nd datasource
Now, I config two Datasource from application.properties and it's work.
But the requirement want to change the 2nd datasource from table of 1st datasource. T.T
Please, gives me some suggestions.
Thank you.
A Spring configuration like this should work (consider it pseudo code):
#Bean("secondDatasource")
public Datasource secondDatasource(#Qualifier("firstDatasource") Datasource ds){
// use `ds` to obtain the necessary information to obtain a datasource ...
return DataSourceBuilder
.create()
.username(username)
.password(pwd)
.url(url)
.driverClassName(driver)
.build();
}
I would at least start without using Spring Data JPA in the configuration class and operate directly on the data source to keep things simple.
You already got pointers, how to set up Spring Data JPA to then use the different data sources: http://www.baeldung.com/spring-data-jpa-multiple-databases
The code above is mainly just copied from: https://stackoverflow.com/a/28822145
I have two datasources (JPA) in my project. Both are on hsql server. I need to recreate the schema each time. For the first datasource I have schema-hsql.sql and import.sql files. Where to put initial HSQL script for second datasource?
My datasources configuration is based on http://www.baeldung.com/spring-data-jpa-multiple-databases
Is it even possible?
In this since it is a special case where two DataSources are available you can not use the default way of creating tables and inserting initial data using schema-hsql.sql and import.sql.
In this case you need to do some programming using DatabasePopulatorUtils
DataSource dataSource1() { ... }
DataSource dataSource2() { ... }
DatabasePopulatorUtils.execute(new ResourceDatabasePopulater(new ClasspathResource("schema-datasource1.sql"), new ClasspathResource("import-datasource1.sql")), dataSource1());
DatabasePopulatorUtils.execute(new ResourceDatabasePopulater(new ClasspathResource("schema-datasource2.sql"), new ClasspathResource("import-datasource2.sql")), dataSource2());
In a very large project where do we set up the database connection so that it is available across all the modules?
Suppose the requirement is like this:
LoginPage.html -> LoginServlet.java -> LoginService.java ==> Takes DB help to check the credentials.
Now, since the actual credentials are stored in DB, where do we set up the database so that the connection is available to all the modules?
In big projects, is database connection made as and when needed or database connections setup at the time when application is run and made available across all the modules.
If DB connections are made available to all the modules (which need DB connectivity), how is this achieved?
Thanks for your help and inputs.
Since you're not using an IoC approach (Spring), the alternative would be to have a static class (or a singleton) that has a reference to the DataSource. Whenever you need a Connection you only have to get it from that class:
public class JdbcUtils{
private static DataSource dataSource;
static{
dataSource = new DB2SimpleDataSource();
dataSource.setDatabaseName("DBNAME");
dataSource.setServerName("xxx.xxx.xxx.xxx");
dataSource.setPortNumber(447);
dataSource.setUser("USER");
dataSource.setPassword("PASS");
dataSource.setDriverType(4);
dataSource.setCurrentSchema("SCHEMA");
//OR even better get the DataSource through JNDI lookup if defined on server
}
public static Connection getConnection() throws SQLException{
return dataSource.getConnection()
}
}
I am using spring & hibernate. my application has 3 modules. Each module has a specific database. So, Application deals with 3 databases. On server start up, if any one of the databases is down, then server is not started. My requirement is even if one of the databases is down, server should start as other module's databases are up, user can work on other two modules. Please suggest me how can i achieve this?
I am using spring 3.x and hibernate 3.x. Also i am using c3p0 connection pooling.
App server is Tomcat.
Thanks!
I would use the #Configuration annotation to make an object who's job it is to construct the beans and deal with the DB down scenario. When constructing the beans, test if the DB connections are up, if not, return a Dummy Version of your bean. This will get injected into the relevant objects. The job of this dummy bean is to really just throw an unavailable exception when called. If your app can deal with these unavailable exceptions for certain functions and show that to the user while continuing to function when the other datasources are used, you should be fine.
#Configuration
public class DataAccessConfiguration {
#Bean
public DataSource dataSource() {
try {
//create data source to your database
....
return realDataSource;
} catch (Exception) {
//create dummy data source
....
return dummyDataSource;
}
}
}
This was originally a comment:
Have you tried it? You wouldn't know whether a database is down until you connect to it, so unless c3p0 prevalidates all its connections, you wouldn't know that a particular database is down until you try to use it. By that time your application will have already started.