Spring web application set datasource based on customer/user - java

I have a Spring web application that uses multiple databases. There are multiple customers and each customer has his own database. One master database (customer auth DB) has customer information and connections strings(or metadata) to the customer databases stored. I have created 2 datasource bean definitions in Spring configuration files. So, I would like to set the second datasource based on customer.
I have tried setting the 2nd datasource based on customer.
The problem is see is that when another customer logs in, the 2nd datasource switches to the new customers database. I think my approach is completely wrong. Any ideas?? or can we set some kind of scope to avoid the problem. My Spring config files have the following definitions:
<bean id="customerDS" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
<property name="driverClassName" value="${property.cust.db.driver}" />
<property name="url" value="${property.cust.db.url}" />
<property name="username" value="${property.cust.db.username}" />
<property name="password" value="${property.cust.db.password}" />
</bean>
<bean id="userAuthDS" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${property.userauth.db.driver}" />
<property name="url" value="${property.userauth.db.url}" />
<property name="username" value="${property.userauth.db.username}" />
<property name="password" value="${property.userauth.db.password}" />
</bean>
I have the following code to set the second datasource based on customer who logs in:
#Autowired
private DriverManagerDataSource customerDS;
if(customer.equals("cust1")){
customerDS.setUrl("jdbc:sybase:Tds:127.0.0.1:2679?ServiceName=cust1");
customerDS.setUsername("dba");
customerDS.setPassword("sql");
}
if(customer.equals("cust2")){
customerDS.setUrl("jdbc:sybase:Tds:127.0.0.1:2689?ServiceName=cust1");
customerDS.setUsername("dba");
customerDS.setPassword("sql");
}

The following link helped me with the problem:
http://spring.io/blog/2007/01/23/dynamic-datasource-routing/
If you carefully read and understand the spring configuration, you should be able to achieve it.

Related

Multi connection pool configurations for different modules in application

Our application has multiple modules, each module use its own schema in the same mysql database. Now I need to make different connection pool configurations for each module because of their different db resource consuming nature, i.e. some module may have 20 active connections at a point of time, but others may just have 1 max. I have searched here and other forums, couldn't find a solution, just this topic is not about multi-tenancy or multi-database, all schemas are in the same db.
Here's the config we have:
<bean id="dataSource" class="our.own.package.RoutingDataSource"> <!-- RoutingDataSource extends spring AbstractDataSource -->
<property name="master" ref="masterDS"/>
</bean>
<bean id="abstractDataSource" abstract="true">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="initialPoolSize" value="#initial.pool.size#" />
<property name="minPoolSize" value="#min.pool.size#" />
<property name="maxPoolSize" value="#max.pool.size#" /> <!-- I want to have different configs for each module in our application -->
</bean>
<bean id="masterDS" class="com.mchange.v2.c3p0.ComboPooledDataSource" parent="abstractDataSource">
<property name="jdbcUrl" value="jdbc:mysql://#host#/" />
<property name="user" value="#user#" />
<property name="password" value="#pwd#" />
<property name="dataSourceName" value="#dbName#" />
</bean>
So now I have two questions:
1) Is it possible to have different connection pool configurations for one datasource in Spring?
2) If I have to go with the multiple datasource way(one datasource for one module), is implementing Spring's AbstractRoutingDataSource the correct way to go?
Thank you!
Ad.1 Your data source is in the fact connection pool so you want to have multiple pools on the top of another pool. You can do it but you will face many other problems.
Ad.2. Yes definitely. You already have RoutingDataSource so this one should be implementation of AbstractRoutingDataSource. Probably you already have logic there to determine current the data source routing key which should be used to do the lookup.

To write Junit Test cases for spring based REST API

For Spring based Rest API, in order to write JUnit test cases, do I need an established database connection or can i have a mock database. Can I get some suggestions.
Technology Stack used si:
Spring MVC,
Hibernate
It depends on what you are testing. If you are testing the service layer, you should not establish actual connections to the database. Instead, you can mock up repository objects, using Mockito, for example. Here is an example of how to wire up mock Repository objects.
If you want to test your data access logic, you might consider using an in-memory database like hsqldb and define it as a test data source in the test configuration.
For more sophisticated, integration testing, you might want to connect to the actual database.
Under your root-context.xml you may consider setting up different bean profiles. For example:
<beans profile="dev">
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.1.2:3306/mydb"/>
<property name="username" value="admin"/>
<property name="password" value="password"/>
<property name="initialSize" value="3"/>
</bean>
...
</beans>
...
<beans profile="prod">
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.1.3:3306/mydb"/>
<property name="username" value="admin"/>
<property name="password" value="password"/>
<property name="initialSize" value="3"/>
</bean>
...
</beans>
And then under your JUnit tests you may consider using the #Profile annotation above your tests:
#Profile(value="dev")
Alternatively, you may set the spring.profiles.active environment property in your application.properties file or as a launch configuration for your application server via -Dspring.profiles.active=dev
For more information, you may read the Spring docs on Profiles.

How to handle Spring application configuration?

I have standalone Spring application that has its setting in DMBS. I have an idea to use only one code (that specifies particular instance) when application is starting and application reads own setting from database. Setting values are then used for creating beans in applicationContext XML file and later in beans itself.
So far (developing phase) I used one properties file and read it in such way:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/taskproducer.properties</value>
</list>
</property>
</bean>
What is the best way how to handle setting from database in Spring application?
You are heading the right way. Property files should indeed contain the database configuration properties.
If you want to go one-step further, you can have profile-specific property files, e.g. development-specific configuration and production-specific.
Take a look at this video for some nice instructions on this subject.
EDIT: in case I misunderstood, and you wanted some guidance on how to setup your database using these properties, here is an example of a Spring XML configuration, based on properties from a configuration file.
Short mention: for example, you would setup your DataSource like this:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</bean>
Then in your property file you would have defined the following properties:
database.url=http://localhost:3306/mydb
database.username=sa
database.password=
Hope this is helpful
You can create a configurer bean which reads props from DB
class DbProperties extends java.util.Properties {
DbProperties() {
String v1 = ... // read prop from db
setProperty("p1", "v1");
}
}
add it to Spring context
...
<context:annotation-config />
<bean id="c1" class="DbProperties" />
<bean id="b1" class="B1"/>
<!--
<context:property-placeholder location="taskproducer.properties" />
-->
<context:property-placeholder properties-ref="c1"/>
and use it
class B1 {
#Value("${p1}")
int x;
}

Configure two PostgreSQL database(fail over mechanism) using Spring 3

I am using Tomcat JDBC API(org.apache.tomcat.jdbc.pool.DataSource) to connect to my PostgreSQL database from Spring configuration file as shown below. I got a new requirement to configure two databases which should act as a fail over mechanism, Like When one database is down application should automatically switch back to another database.
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource"
destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost/dbname?user=postgres" />
<property name="username" value="postgres" />
<property name="password" value="postgres" />
<property name="maxActive" value="5" />
<property name="maxIdle" value="5" />
<property name="minIdle" value="2" />
<property name="initialSize" value="2" />
</bean>
Can anyone suggest how this can be achieved using Spring configuration file.
The normal way this is done is by using virtual IP addresses (with possible forwarding), checking for activity, a shoot-the-other-node-in-the-head approach and proper failover. Spring is exactly the wrong solution to this if you want to avoid things like data loss.
A few recommendations.
repmgr from 2ndquadrant will manage a lot of the process for you.
Use identical hardware and OS and streaming replication.
Use virtual IP addresses, and the like. Use a heartbeat mechanism to trigger failover via rempgr
Then from this perspective your spring app doesn't need reconfiguring.

How to initialize in-memory HSQLDB using script via Spring

I am attempting to do unit testing of my DAO (using Spring and Hibernate). I am using HSQLDB per this tutorial. The tutorial states that the in-memory HSQLDB database can be initialized using a SQL script but I cannot find information on how to do so in Spring. Here is the pertinent Spring context config:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:mydb" />
<property name="username" value="sa" />
<property name="password" value="" />
<property name="initialSize" value="5" />
<property name="maxActive" value="10" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="10" />
</bean>
Any help would be appreciated. Thanks.
If you are trying to work with in-memory databases and Spring, there is a new jdbc namespace for Spring 3 that makes working with embedded databases very easy.
The best part is that it acts as a DataSource, so it can easily be dropped in to replace your existing dataSource bean.
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>
If you are more interested in doing this with Java Config, take a look at the EmbeddedDatabaseBuilder (new in Spring 3.0).
#Configuration
public class DatabaseTestConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
}
Nicholas answer is perfectly fine, but you can use jdbc namespace to initialize external database as well:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/DS"/>
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:/META-INF/database/init.sql"/>
</jdbc:initialize-database>
In the tutorial you link to, one of the ways of setting things up is this (after obvious correction):
In-memory from a script: jdbc:hsqldb:file:path-to-file
I think that that would appear to be relevant. I suggest replacing path-to-file with something that looks like a fully-qualified filename…
You could get around this by creating a subclass of BasicDataSource with getters/setters for two new properties, initExecuteSqlFile and destroyExecuteSqlFile, that can have a comma-seperated list of SQL files to execute. The subclass would have init() and destroy() methods that handle the init/destroy SQL files.
Then use the following bean definition:
<bean
id="datasource"
class="com.example.MyBasicDataSource"
destroy-method="destroy"
init-method="init"
>
<property name="destroyExecuteSqlFile">
<value>h2-destroy-01.sql</value>
</property>
<property name="initExecuteSqlFile">
<value>h2-init-01.sql,h2-init-02.sql,h2-init-03.sql</value>
</property>
<!-- Other properties -->
</bean>
With embedded-database we would only be able to connect to the database from the same JVM. If we have two JVMs, for performance or other constraints, we can:
Instead of using an embedded-database, you can use the datasource suggested in this answer.
Then initialize like Poitrek De suggested (and suggested in previous answer too). You may want to create tables only if they do not exist (as suggested here).

Categories