I use JPA annotations (Hibernate implementation) to initialize my DB schema. And i follow the article DYNAMIC DATASOURCE ROUTING to implement the dynamic datasource routing class.
However, i have two databases (mapped 2 data sources). I set the first data source as defaultTargetDataSource. then start my application. When my application try to access 2nd data source, it tell me the table doesn't exist. It seems AbstractRoutingDataSource only create the table for the default data source but other data sources.
Is there any idea to create schema in all databases ?
PS.I'm using AbstractRoutingDataSource to implement my own DB shards.
I guess that you are using the hibenate configuration:
spring:
jpa:
hibernate:
ddl-auto: update
to reflect the entity changes to the database schema. This works fine as long as we use a single data source that is configured to be connected at startup.
However, if you have multiple data sources it is not possible to use this feature. The general approach with AbstractRoutingDataSource is to not have a data source at startup but select it at runtime.
If you select a primary data source, then it will be only applied to the primary one as hibernates applies this feature at startup, but the remaining databases will not be migrated.
To reflect the changes to all of your databases you can use a database migration tool such as Flyway or Liquibase.
Flyway is using SQL and pretty easy to configure and use to use.
Related
Is there a way to define with hibernate which entities should be created in which tenant? Because for different tenant, tables are not the same.
And second question, is it possible to configure (also with hibernate) that I want to get access to tenant2 tables from tenant1.
I use embedded h2 database. I would like to automatically create tables with hibernate.hbm2ddl.auto and fill these tables with flyway migration files.
Well, I'm using a Multitenant Architecture in Spring and I can help you with what I'm doing.
1: If you have different tables for different tenants you could either use a TenantFilter over your Entity or you can define your customized schema in schema.sql and use JdbcTemplate to execute the SQL.
I would prefer the second option over the first because it guarantees the correct schema creation.
2: You can make a switch to your tenant2 from tenant1 using a TenantContext and once you're done with your processing you can switch back to tenant1.
There are many demo projects on multitenancy with spring boot on GitHub you can have a look there.
I am working on a project in Java (using Spring Boot, Thymeleaf, Hibernate, JPA, MySql). Every time I create a new Model Class, I have to create a table in the database or if I make any change in the Model class I have to alter the table by myself. Is there any way to avoid this database related stuff. For example I will make Model classes and declare their relationships my Database tables will be generated automatically. In future if I make any changes to my classes they will be applied to the database automatically without loosing any data.
Previously I worked on PHP, Laravel. There all I needed to do is 1) run command php artisan make:migration create_posts_table, 2) declare columns like $table->string('title');, $table->foreign('user_id')->references('id')->on('users'); and then 3) run command php artisan migrate. That's it. No SQL scripts needed. I was wondering if Java, Spring has something like this.
Sure you can do it.
Use spring.jpa.hibernate.ddl-auto=update in your application.properties.
You can also use more advanced tools like https://www.liquibase.org/
Ideal way
spring.jpa.hibernate.ddl-auto=none
In my opinion, the ideal way is to create one SQL file which will create the schema at the startup for us.
To let Spring Boot to create it for you
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.ddl-auto= # DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Defaults to "create-drop" when using an embedded database and no schema manager was detected. Otherwise, defaults to "none".
Other Possible values: create, create-drop, validate
More Detailed Explanation
You can do migration using Flyway, it's similar to Laravel migration.
Add the dependency and put your migration SQL files to classpath:db/migration. Flyway will automatically check the sql files version and apply any pending migrations.
https://flywaydb.org/documentation/plugins/springboot
If you use DAO as an interface between the application and the database, you can change the database from RDB to Hadoop (which has totally different table schema) without making any modification to the application side. (all you need to do is to change DAO.) If you use JPA, is it also easy to change the database? Can you change the database without making any modification on the application?
If yes, could you tell me how to do it?
JPA is only for RDBMS (Relational Database Management System) so yes you change change between Oracle, MSSQL, MySQL, PostreSQL etc BUT you cannot change to a NoSQL database like Hadoop or MongoDB.
To change it you have to use the Database JDBC Driver and set the Hibernate dialect (or whatever JPA implementation you use) accordingly.
Bonjour,
I am working on changing me Java application from using postgres to an embedded database. I would like the application to deploy with an initial set of data in the database. In the past during installation I have executed an sql script to fully generate the schema and insert the data in to my tables.
Ideally (becasue I don't really want to work out how to connect to the embedded database to generate it) I want to let JPA create my schema for the first time, and when it does I then want to be able to run my SQL to insert the data.
My search has turned up the obvious hibernate and JPA properties that allow running of an SQL script.
Firstly I found when using "hibernate.hbm2ddl.auto" you can define an import.sql file this made me very happy for a day until I realised it only works with create and not with update. My application when using postgres had this set to update. And what i would really like is for it to know if it's had to create the schema and if it has then run the import.sql. No Joy though.
I then moved on to using "javax.persistence.schema-generation.database.action" set to "create" I figured using the JPA specification was probably wiser anyway and so I defined "javax.persistence.sql-load-script-source" the spec says for "create"
The provider will create the database artifacts on application
deployment. The artifacts will remain unchanged after application
redeployment.
This lead me to believe it would do exactly what I wanted, only create the tables "on application deployment" however when I ran my tests using this, each test (creating a new spring context) tried to just create all the tables again and obviously failed, which made me realise application deployment didn't mean what i thought it meant (wishful thinking) and now I realise that JPA doesn't seem to even have an equivalent of Hibernates "update" property, so it's always going to generate the tables?
What I want is to have my tables and data generated when you first spin up the app and for subsequent executions to know the data is there and use it, I am assuming it's too much to hope for that this exists, but i'm sure that this must be a common requirement? So my question is what is the general recommended way to achieve the goal of allowing JPA to create my schema but being able to insert some data in to a db that persists between executions?
The answer is flyway. It is a database migration library, and if you are using Spring boot it is seamlessly integrated, with regular Spring you have to create a bean, which get a reference to the connection pool, creates a connection and does the migration.
Flyway creates a table so it keeps track of which scripts has already been applied to the database, and the scripts are simply part of the resources.
We normally use JPA to generate the initial script. This script becomes V1__initial.sql, if we need to add some data we can add V2__addUsers.sql and V3__addCustomers.sql etc.
Later when we need to rename columns or add additional tables, we simply add new scripts as part of the War file, and when the application is loaded Flyway looks at it's internal table, to see the current version, and then applies any new scripts to bring it up to de desired version.
In Spring the code would look like this
private void performFlywayMigration(DataSource dataSource) {
Flyway flyway = new Flyway();
flyway.setLocations("db/migration");
flyway.setDataSource(dataSource);
log.debug("Starting database migration.");
flyway.migrate();
log.debug("Database migration completed.");
MigrationInfo current = flyway.info().current();
if (current.getState() == MigrationState.FUTURE_SUCCESS) {
log.warn("The Database schema is version " + current.getVersion() + ", this application expects version " + flyway.getBaselineVersion().getVersion());
}
}
In general you should not JPA to create tables directly. because you sometimes need to modify the scripts, for instance on Sybase Varchar(255) means 255 bytes, so if you are storing 2 or 3 byte Unicode chars, you need more space - JPA implementation does not account for that (last time I checked).
I am using Hibernate's multi-tenancy feature via JPA, with a database per tenant strategy. One of my requirements is to be able to run a query against a table that exists in each database but obviously with different data. Is this possible?
Thanks in advance for your time.
Nope. this is not possible because when hibernate runs queries it is already initialized with a connection. MT support in Hibernate is basically done a little "outside of Hibernate" itself. It's kind of feeding hibernate with a proper connection and when it's fed :) it's bound to that connection.
If you need cross-tenant queries you might want to reconsider multitenancy or change JPA provider to the one that support "shared schema approach" e.g. EclipseLink. With shared shema approach you have two choices:
run native query agains table containing mt-aware entities
create additional entity - dont mark it as multitenant - map it to the table containing mt-ware entities and run JPQL query in standard manner