How to configure databases in springboot interchangeably - database

I need to enable code to interchangeably uses a different database by changing configuration. I have Oracle SQL and Azure SQL Server. By changing helm chart (or configuration), I would like to choose which database to use. Things I know are:
Datasource is configured in helm chart. I have a yaml file that declares driver, url, username and password for database.
env:
- name: datasource.project.driverClassName
value: 'oracle.jdbc.OracleDriver'
- name: datasource.project.url
value: 'url'
- name: datasource.project.username
value: 'username'
- name: datasource.project.password
value: 'password'
In my project, I create bean for database:
#Configuration
#EnableConfigurationProperties
public class ProjectDataSourceConfig {
public static final String DB_TX_MANAGER = "";
#Bean
#Primary
#ConfigurationProperties("datasource.project")
public DataSourceProperties projectDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
public DataSource projectDataSource() {
return projectDataSourceProperties().initializeDataSourceBuilder().type(ComboPooledDataSource.class).build();
}
#Bean
public NamedParameterJdbcTemplate projectJdbcTemplate() {
return new NamedParameterJdbcTemplate(projectDataSource());
}
#Bean(name = DB_TX_MANAGER)
public DataSourceTransactionManager projectDbtransactionManager() {
return new DataSourceTransactionManager(projectDataSource());
}
}
My goal is: to find a way to load either Oracle SQL OR Azure SQL Server by modifying configuration file. I am not sure if just changing driverClassName, url, username and password is sufficient enough.

I found this very easy. Since I don't use Hibernate, I can just simply change driver, url, username and password to Azure SQL's, then it works.

Related

Easily switching between connection strings in .NET Core

I've got a code base that uses EF Core and Dapper to perform actions on a database.
I want to set up a new copy of the site to develop some features and I want this site to connect to a new isolated copy of the database (dbConnectionDEBUG).
At the moment, I use the following setup:
startup.cs
...
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("dbConnectionMain")));
services.Configure<ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));
...
I have a ConnectionStrings class which is being populated correctly via the DI in startup:
public class ConnectionStrings
{
public string dbConnectionMain { get; set; }
public string dbConnectionDEBUG { get; set; }
public ConnectionStrings()
{
this.dbConnectionMain = "";
this.dbConnectionDEBUG = "";
}
}
Then, throughout my controllers/services I have access to ConnectionStrings and 99% of the time I'm doing the following to make DB calls:
using (var conn = new SqlConnection(_connectionStrings.dbConnectionMain))
{
conn.Open();
...
This would amount to a lot of code changes if I were to want to switch over to the 'DEBUG' db.
How do I easily switch between the connection strings in my code depending on what version of the system I'm working on.
If I could somehow do this dynamically that'd be great. The obvious determining factor would be the URL the site is operating on.
Alternatively, (as a single change) do I just manually change the connection string at the source (e.g keystore/appsettings). I'm not keen on this as it leaves room for human error.
Update (2)
Based on what #Nkosi mentioned I am pursuing this path:
Have one connection string 'Id' (i.e. dbConnection) used throughout
Differentiate the connection string value within this based on the environment the app is running/deployed in
I have another question:
If I have the following...
"MYAPPNAME": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:12345/;https://myapptestdomain.com/"
}
and:
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
IHostingEnvironment env = context.HostingEnvironment;
config.AddJsonFile($"appsettings.{env.EnvironmentName.ToLower()}.json", optional: true);
})
.UseStartup<Startup>();
...will this automatically pick up my site is in the Development mode based on the applicationUrl values OR will I have to manually add ASPNETCORE_ENVIRONMENT with a value Development on the server I deploy the app to?
Additional: My app is running in an Azure App Service.
Update (3) - Mission Complete
Just to finalise this question (in case anyone needs to know this), I have the following setup based on recommendations made by #Nkosi.
Connection String - I have one connection string Id/name dbConnection which is used in all appSettings (see below)
App Settings
I have a default appSettings.json with dbConnection that looks at the live database
I have an additional appSettings.Playground.json file with dbConnection that looks at my testing database
Azure - App Service - On my playground development slot I have added an App Setting for ASPNETCORE_ENVIRONMENT with the value 'Playground'
In my Program.cs file I have:
config.AddJsonFile($"appsettings.json", optional: true,reloadOnChange: true);
and
config.AddJsonFile($"appsettings.{env.EnvironmentName.ToLower()}.json", optional: true,reloadOnChange: true);
Just to note, I do also initialise a Vault on Azure which stores all my Keys and Secrets for the Azure based apps. Locally User Secrets is used.
ASP.NET Core reads the environment variable ASPNETCORE_ENVIRONMENT at app startup and stores the value in IHostingEnvironment.EnvironmentName.
Since the environment is being loaded, then it should be available from the hosting environment via the builder context
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) => {
string environment = context.HostingEnvironment.EnvironmentName; //get current environment
//load config based on environment.
config.AddJsonFile($"appsettings.{environment}.json", optional: true);
//...
})
//...
Reference Use multiple environments in ASP.NET Core
For simple apps and to keep the connection strings away from my repository I use preprocessor statements plus PATH/System Variables and for release I provide a connection string within the settings.json.
#define USE_FEATURE_X
using System;
namespace MyNamespace {
internal static class StaticConnectionStringFactory {
public static string GetConnectionString() {
#if DEBUG && !USE_FEATURE_X
var connectionString = Environment.GetEnvironmentVariable("CNNSTR_SQL_XYZ", EnvironmentVariableTarget.User);
#elif DEBUG && USE_FEATURE_X
var connectionString = Environment.GetEnvironmentVariable("CNNSTR_SQL_ABC", EnvironmentVariableTarget.User);
#else
var connectionString = Environment.GetEnvironmentVariable("SqlConnectionString", EnvironmentVariableTarget.Process);
#endif
return connectionString;
}
}
}
I think if you add 2 connection for debug and main then you will have face some difficulty because more member working in you team. may be some own wrongly use release mode for code development.
you can try this webconfig method:
public class ConnectionStrings
{
public string dbConnection { get; set; }
public ConnectionStrings()
{
bool Ismain = bool.Parse(System.Configuration.ConfigurationManager.AppSettings["HasLive"]);
if (Ismain)
{
dbConnection = "";// get Main connectionstring
}
else
{
dbConnection = "";// get Debug connectionstring
}
}
}
web.config:
<connectionStrings>
<add name="dbConnectionMain" connectionString="" providerName="System.Data.SqlClient" />
<add name="dbConnectionDEBUG" connectionString="" roviderName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="HasLive" value="false"/>
</appSettings>
</connectionStrings>

Change SQL Server Connection String Dynamically inside an ASP.Net Core application

I open one database at the start, then need to open another database based on user selecting two values. The database selection has to be at run-time and will change every time.
Have tried to access the Connection String using the Connection String class and have tried other options like Singleton which I do not understand. I am running this on a local Windows 10 system running SQL Server Express. Am coding using Asp.Net Core 2.1
> ASP.Net Core v2.1
Building multi tenant, multi year application
Every client will have one SQL DATABASE per year
I hope to have a table with the following structure
COMPANY_CODE VARCHAR(3),
COMPANY_YEAR INT,
COMPANY_DBNAME VARCHAR(5)
Sample Data
COMPANY_CODE: AAD
COMPANY_YEAR: 19
COMPANY_DB: AAD19
COMPANY_CODE: AAD
COMPANY_YEAR: 18
COMPANY_DB: AAD18
COMPANY_CODE: AAD
COMPANY_YEAR: 17
COMPANY_DB: AAD17
So, every company will multiple rows - one for each financial year.
The COMPANY_DB column will store the DB name to open for that session.
Once the user is authenticated, I want to change the connection string to point to the database in the COMPANY_DB column of the selected row and then let the logged in user perform transactions.
I am unable to figure out how to change the connection string that is embedded in startup.cs.
Any tips on how to achieve this will be most appreciated.
I figured out that you are using one DbContext class for each database. See here for more information: docs.
Remove AddDbContext from Startup, remove OnConfiguring from DbContext and pass options to the constructor.
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{ }
public DbSet<Blog> Blogs { get; set; }
}
Then, write service providing DbContext:
public interface IBlogContextProvider
{
BlogContext GetBlogContext(string connectionString);
}
public class BlogContextProvider : IBlogContextProvider
{
BlogContext GetBlogContext(string connectionString)
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connectionString);
return new BlogContext(optionsBuilder);
}
}
Add service in your Startup.cs:
services.AddScoped<IBlogContextProvider, BlogContextProvider>();
Now you can use DI:
public class HomeController : Controller
{
private IBlogContextProvider _provider;
public HomeController(IBlogContextProvider provider)
{
_provider = provider;
}
public ActionResult Index()
{
using (var context = _provider.GetBlogContext(<your connection string>))
{
//your code here
}
return View();
}
}
EDIT: Of course, you can write ContextProvider as generic.

Using SQL Server 2008 Schema with JPA

I got a MS SQL Server (2008) database with two schemas, dbo as default for general purpose e.g. authentication and myapp for domain objects. I want to have JPA-Entitys in both Schemas. I use SpringBoot for configuration.
Entitys tables are created in the right schema as they should, e.g. myschema.job, but relationship tables, e.g. Job_Employee are created within the default schema dbo. How can I set in whicht schema automatically created tables are stored (without changing the default schema as this just shifts the problem)?
#Entity
#Table(schema="myschema")
public class Job {[...]
My application.yml looks like:
spring:
profiles: dev
datasource:
datasource1:
url: jdbc:sqlserver://localhost;databaseName=mydb;schema=myschema
username: SA
password: ###
datasource2:
url: jdbc:sqlserver://localhost;databaseName=mydb;schema=dbo
username: SA
password: ###
jpa:
show-sql: true
hibernate.ddl-auto : create-drop
properties:
hibernate.dialect: org.hibernate.dialect.SQLServer2012Dialect
hibernate.default_schema: dbo
And the datasources are Configured in
#Configuration
#EnableJpaRepositories
public class JPAConfiguration {
#Bean
#Primary
#ConfigurationProperties("spring.datasource.datasource1")
public DataSourceProperties firstDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("spring.datasource.datasource1")
public DataSource firstDataSource() {
return firstDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties("spring.datasource.datasource2")
public DataSourceProperties secondDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("spring.datasource.datasource2")
public DataSource secondDataSource() {
return secondDataSourceProperties().initializeDataSourceBuilder().build();
}
}
Thanks!
The answer is: every Collection must be mapped to the right schema as well with #JoinTable annotation.
E.g. in our Case:
#JoinTable(schema="myschema")
#OneToMany(cascade=CascadeType.ALL)
#Column(nullable = false)
private List<Employee> employee;
This results in a table calles myschema.job_employee.

net core 1 (dnx 4.5.1) with enterpriselibrary 6 - setting up the connection string

i ve big problems running enterprise library data access block with net core 1 (dnx 4.5.1)
How can i setup the default connection string for entlib
my appsettings.json
"ConnectionString": "Server=localhost\sqlexpress;Initial Catalog=blind;User Id=blind;Password=blind"
Here is my problem (no default connectionstring)
Database db = DatabaseFactory.CreateDatabase();
how can i pass the appsettings ConnectionString to the entlib databasefactory
any help would be greatly appreciated
I know it's an old question, but I have a similar setup (but using .NET Core 2.0) and it took me awhile to figure out how to set the default database connection without using the web.config to manage it.
What I did was include the default database and all of the connection strings in the appsettings.json and then in my Startup class I read the appsettings.json into an object that I defined to store the default db name and the connection strings and configure the default + named database using DatabaseFactory.SetDatabase.
DatabaseFactory.SetDatabases() Definition
public class DataConfiguration
{
public string DefaultDatabase { get; set; }
public List<ConnectionStringSettings> ConnectionStrings { get; set; }
}
public class Startup
{
public Startup(IConfiguration configuration)
{
//Get the Database Connections from appsettings.json
DataConfig = configuration.Get<DataConfiguration>();
var defaultDb = DataConfig.ConnectionStrings?.Find(c => c.Name == DataConfig.DefaultDatabase);
DatabaseFactory.SetDatabases(() => new SqlDatabase(defaultDb.ConnectionString), GetDatabase);
Configuration = configuration;
}
public Database GetDatabase(string name)
{
var dbInfo = DataConfig.ConnectionStrings.Find(c => c.Name == name);
if (dbInfo.ProviderName == "System.Data.SqlClient")
{
return new SqlDatabase(dbInfo.ConnectionString);
}
return new MySqlDatabase(dbInfo.ConnectionString);
}
}
Whenever there is documentation, I always suggest reading it as it is usually good. This is one of those examples, check out the "Getting Started with ASP.NET 5 and Entity Framework 6". There are several things that you need to do to ensure that you are correctly configured.
Setup your connection string and DI.
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
}
Also, notice the path in the configuration, it seems to differ from yours.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped((_) =>
new ApplicationDbContext(
Configuration["Data:DefaultConnection:ConnectionString"]));
// Configure remaining services
}

Is it possible to configure multiple database connections in Dropwizard?

I am working on some code that leverages Dropwizard that will require that I need to connect to at least two different databases (I plan to use Hibernate as well). I was unable to find any examples/documentation that will allow me to configure two different database connections in the Database block of the .yml configuration file. Is this possible in Dropwizard? If not, what are the workarounds that people have used in the past. Thank you in advanced for your help!
You can configure multiple databases in dropwizard. In the config.yml you can have multiple database configuration like this.
database1:
driverClass: org.postgresql.Driver
user: user
password: pwd
url: jdbc:postgresql://localhost:5432/db1
validationQuery: select 1
minSize: 2
maxSize: 8
database2:
driverClass: org.postgresql.Driver
user: user
password: pwd
url: jdbc:postgresql://localhost:5432/db2
validationQuery: select 1
minSize: 2
maxSize: 8
And in the config class get both config details.
public class DBConfig extends Configuration {
private DatabaseConfiguration database1;
private DatabaseConfiguration database2;
public DatabaseConfiguration getDatabase1() {
return database1;
}
public DatabaseConfiguration getDatabase2() {
return database2;
}
}
And in your service configure which Dao to use which database.
#Override
public void run(MyConfiguration configuration,
Environment environment) throws ClassNotFoundException {
...
final DBIFactory factory = new DBIFactory();
// Note that the name parameter when creating the DBIs must be different
// Otherwise you get an IllegalArgumentException
final DBI jdbi1 = factory.build(
environment, configuration.getUserDatabase(), "db1");
final DBI jdbi2 = factory.build(
environment, configuration.getItemDatabase(), "db2");
final MyFirstDAO firstDAO = jdbi1.onDemand(MyFirstDAO.class);
final MySecondDAO secondDAO = jdbi2.onDemand(MySecondDAO.class);
...
}

Resources