have an application that runs Spring MVC.
I need it to access 2 different databases in my app (Two Sql servers).
How do I configure this?
You can access the first database with an EntityManager and use a JdbcTemplate to access the second database
1.application.properties
#SQL Server 1
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = [sql Server Driver class name]
#SQl Server 2
spring.secondaryDatasource.url = [url]
spring.secondaryDatasource.username = [username]
spring.secondaryDatasource.password = [password]
spring.secondaryDatasource.driverClassName = [sql Server Driver class name]
2.Create #Configuration class and declare two datasource beans.Create a Jbc template to use to access sql server 2
#Bean
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="spring.secondaryDatasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(secondaryDataSource());
}
Usage Example
#Repository
public class CustomerRepositoryImpl implements CustomerRepository {
private final JdbcTemplate jdbcTemplate;
public CustomerRepositoryImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
You can also look at the documentation:
https://docs.spring.io/spring-boot/docs/1.2.3.RELEASE/reference/htmlsingle/#howto-use-two-entity-managers
and this site
https://www.baeldung.com/spring-data-jpa-multiple-databases
Related
Room.databaseBuilder(getApplicationContext(), UserDatabase.class,"users_db")
.createFromAsset("users.db")
.build();
UserDatabase db = Room.databaseBuilder(getApplicationContext(),
UserDatabase.class,"users_db").allowMainThreadQueries()
.fallbackToDestructiveMigration()
.addMigrations()
.build();
Even I tried to add as a file to prepopulate as "createFromFile" with the following code
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromFile(new File("mypath"))
.build();
If the above is the only code then unless you try to access the database, the database will not be created. That is just getting an instance of the built database just creates an instance that is ready to open the database.
So if you have, for example :-
UserDatabase db = Room.databaseBuilder(getApplicationContext(),
UserDatabase.class,"users_db").allowMainThreadQueries()
.createFromAsset("users.db")
.build();
Then it is not until, for example, you did
SupportSQLiteDatabase accessedDatabase = db.getOpenHelper().getWritableDatabase();
That an attempt to open the database (in the case of no database existing, then an attempt to create the database, and in the case of createFromAsset the create database will then attempt the copy from the asset).
Typically you would not get a SupportSQliteDatabase but would simply try to access the database via the Dao's ( i.e. the methods in the interfaces or abstract classes that are annotated with #Dao).
Demonstration
Consider the following :-
An existing database from an external source (Navicat in the example) :-
just a single table and therefore #Entity annotated class will be required.
as can be seen it has 3 users.
IMPORTANT the schema MUST adhere to what Room expects, which has limitations
The database is copied to the assets folder as users.db :-
An #Entity annotated class User (so the table name will be User) :-
#Entity
class User {
#PrimaryKey
Long id = null;
String username;
}
2 columns
id which is the Primary Key and as the type is Long then the column type must be INTEGER
username, as it's a String then column type MUST be TEXT, as there is no #NonNUll annotation then NOT NULL should not be coded (if it were then the schema room expects would not be as above)
An #Dao annotated class UserDao :-
#Dao
interface UserDao {
#Insert
long insert(User user);
#Query("SELECT * FROM user")
List<User> getAllUsers();
}
An #Database annotated class UserDatabase :-
#Database(entities = {User.class}, version = 1,exportSchema = false)
abstract class UserDatabase extends RoomDatabase {
abstract UserDao getUserDao();
private static volatile UserDatabase instance = null;
static UserDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context,UserDatabase.class,"users_db")
.allowMainThreadQueries()
.createFromAsset("users.db")
.build();
}
return instance;
}
}
note the getInstance() method (see the activity code that follows)
Putting it all together in an activity
First a shortened version that does not access the database but just gets an instance of the UserDatabase :-
public class MainActivity extends AppCompatActivity {
UserDatabase db;
UserDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = Room.databaseBuilder(this,UserDatabase.class,"users_db")
.allowMainThreadQueries()
.createFromAsset("users.db")
.build();
dao = db.getUserDao(); //<<<<< WILL NOT OPEN THE DATABASE
}
}
When the above is run AppInspection shows nothing.
However, using :-
public class MainActivity extends AppCompatActivity {
UserDatabase db;
UserDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = Room.databaseBuilder(this,UserDatabase.class,"users_db")
.allowMainThreadQueries()
.createFromAsset("users.db")
.build();
dao = db.getUserDao(); //<<<<< WILL NOT OPEN THE DATABASE
//db.getOpenHelper().getWritableDatabase(); //<<<<< Force an open of the database
//<<<<< WILl OPEN THE DATABASE
for(User u: dao.getAllUsers()) {
Log.d("USERINFO","UserName is" + u.username + " ID is " + u.id);
}
}
/*
Example of using the singleton (not called)
*/
void unusedFromExample() {
UserDatabase db = UserDatabase.getInstance(this);
UserDao dao = db.getUserDao();
/// etc
}
}
and the log includes (as expected) :-
D/USERINFO: UserName isuser1 ID is 1
D/USERINFO: UserName isuser2 ID is 2
D/USERINFO: UserName isuser3 ID is 2000
And AppInspection shows the database and that it has ben copied.
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.
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.
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.
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
}