Skip to content

Commit 52b4b17

Browse files
committed
Flyway two PostgreSQL databases
1 parent 81456a5 commit 52b4b17

19 files changed

+696
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
**[How To Auto-Create And Migrate Two Schemas In PostgreSQL Using Flyway](https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootFlywayPostgreSqTwoSchemas)**
2+
3+
**Note:** or production, don't rely on `hibernate.ddl-auto` (or counterparts) to export schema DDL to the database. Simply remove (disable) `hibernate.ddl-auto` or set it to `validate` and rely on Flyway or Liquibase.
4+
5+
**Description:** This application is an example of auto-creating and migrating two schemas in PostgreSQL using Flyway. In addition, each data source uses its own HikariCP connection pool. In case of PostgreSQL, where a database can have multiple schemas, we use the default `postgres` database and auto-create two schemas, `authors` and `books`. For this we rely on Flyway, which is capable to create the missing schemas.
6+
7+
**Key points:**\
8+
- for Maven, in `pom.xml`, add the Flyway dependency\
9+
- remove (disable) `spring.jpa.hibernate.ddl-auto`\
10+
- in `application.properties`, configure the JDBC URL for `books` as `jdbc:postgresql://localhost:5432/postgres?currentSchema=books` and for `authors` as `jdbc:postgresql://localhost:5432/postgres?currentSchema=authors`\
11+
- in `application.properties`, set `spring.flyway.enabled=false` to disable default behavior\
12+
- programmatically create two `DataSource`, one for `books` and one for `authors`\
13+
- programmatically create two `FlywayDataSource`, one for `books` and one for `authors`\
14+
- programmatically create two `EntityManagerFactory`, one for `books` and one for `authors`\
15+
- for `books`, place the migration SQLs files in `db\migration\books`\
16+
- for `authors`, place the migration SQLs files in `db\migration\authors`
17+
18+
-------------------------------
19+
20+
**You may like to try as well:**
21+
<a href="https://leanpub.com/java-persistence-performance-illustrated-guide"><p align="center"><img src="https://github.com/AnghelLeonard/Hibernate-SpringBoot/blob/master/Java%20Persistence%20Performance%20Illustrated%20Guide.jpg" height="410" width="350"/></p></a>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.jpa</groupId>
7+
<artifactId>HibernateSpringBootFlywayMySQLTwoDatabases</artifactId>
8+
<version>1.0</version>
9+
<packaging>jar</packaging>
10+
11+
<name>HibernateSpringBootFlywayMySQLTwoDatabases</name>
12+
<description>JPA project for Spring Boot</description>
13+
14+
<parent>
15+
<groupId>org.springframework.boot</groupId>
16+
<artifactId>spring-boot-starter-parent</artifactId>
17+
<version>2.1.4.RELEASE</version>
18+
<relativePath/> <!-- lookup parent from repository -->
19+
</parent>
20+
21+
<properties>
22+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
24+
<java.version>1.8</java.version>
25+
</properties>
26+
27+
<dependencies>
28+
<dependency>
29+
<groupId>org.springframework.boot</groupId>
30+
<artifactId>spring-boot-starter-data-jpa</artifactId>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-starter-jdbc</artifactId>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.springframework.boot</groupId>
38+
<artifactId>spring-boot-starter-web</artifactId>
39+
</dependency>
40+
<dependency>
41+
<groupId>mysql</groupId>
42+
<artifactId>mysql-connector-java</artifactId>
43+
<scope>runtime</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.springframework.boot</groupId>
47+
<artifactId>spring-boot-starter-test</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.springframework.boot</groupId>
52+
<artifactId>spring-boot-configuration-processor</artifactId>
53+
<optional>true</optional>
54+
</dependency>
55+
<dependency>
56+
<groupId>org.flywaydb</groupId>
57+
<artifactId>flyway-core</artifactId>
58+
</dependency>
59+
</dependencies>
60+
61+
<build>
62+
<plugins>
63+
<plugin>
64+
<groupId>org.springframework.boot</groupId>
65+
<artifactId>spring-boot-maven-plugin</artifactId>
66+
</plugin>
67+
</plugins>
68+
</build>
69+
</project>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.bookstore;
2+
3+
import com.bookstore.ds1.BookService;
4+
import com.bookstore.ds2.AuthorService;
5+
import org.springframework.boot.ApplicationRunner;
6+
import org.springframework.boot.SpringApplication;
7+
import org.springframework.boot.autoconfigure.SpringBootApplication;
8+
import org.springframework.context.annotation.Bean;
9+
10+
@SpringBootApplication
11+
public class MainApplication {
12+
13+
private final BookService bookService;
14+
private final AuthorService authorService;
15+
16+
public MainApplication(BookService bookService, AuthorService authorService) {
17+
this.bookService = bookService;
18+
this.authorService = authorService;
19+
}
20+
21+
public static void main(String[] args) {
22+
SpringApplication.run(MainApplication.class, args);
23+
}
24+
25+
@Bean
26+
public ApplicationRunner init() {
27+
return args -> {
28+
System.out.println("\n Saving an author (check the MySQL database) ...");
29+
authorService.persistAuthor();
30+
31+
System.out.println("\n Saving a book (check the PostgreSQL database) ...");
32+
bookService.persistBook();
33+
};
34+
}
35+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.bookstore.config;
2+
3+
import com.bookstore.flyway.propeties.FlywayAuthorProperties;
4+
import com.bookstore.flyway.propeties.FlywayBookProperties;
5+
import com.zaxxer.hikari.HikariDataSource;
6+
import org.flywaydb.core.Flyway;
7+
import org.springframework.beans.factory.annotation.Qualifier;
8+
import org.springframework.boot.autoconfigure.flyway.FlywayDataSource;
9+
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
10+
import org.springframework.boot.context.properties.ConfigurationProperties;
11+
import org.springframework.context.annotation.Bean;
12+
import org.springframework.context.annotation.Configuration;
13+
import org.springframework.context.annotation.DependsOn;
14+
import org.springframework.context.annotation.Primary;
15+
16+
@Configuration
17+
public class ConfigureDataSources {
18+
19+
// first database, booksdb
20+
@Primary
21+
@Bean(name = "configBooksDb")
22+
@ConfigurationProperties("app.datasource.ds1")
23+
public DataSourceProperties firstDataSourceProperties() {
24+
return new DataSourceProperties();
25+
}
26+
27+
@Primary
28+
@Bean(name = "configFlywayBooksDb")
29+
public FlywayBookProperties firstFlywayProperties() {
30+
return new FlywayBookProperties();
31+
}
32+
33+
@Primary
34+
@Bean(name = "dataSourceBooksDb")
35+
@ConfigurationProperties("app.datasource.ds1")
36+
public HikariDataSource firstDataSource(@Qualifier("configBooksDb") DataSourceProperties properties) {
37+
return properties.initializeDataSourceBuilder().type(HikariDataSource.class)
38+
.build();
39+
}
40+
41+
@Primary
42+
@FlywayDataSource
43+
@Bean(name = "flywayBooksDb", initMethod = "migrate")
44+
@DependsOn("dataSourceBooksDb")
45+
public Flyway firstFlyway(@Qualifier("configFlywayBooksDb") FlywayBookProperties properties) {
46+
return Flyway.configure()
47+
.dataSource(properties.getUrl(), properties.getUser(), properties.getPassword())
48+
.schemas(properties.getSchema())
49+
.locations(properties.getLocation())
50+
.load();
51+
}
52+
53+
// second database, authorsdb
54+
@Bean(name = "configAuthorsDb")
55+
@ConfigurationProperties("app.datasource.ds2")
56+
public DataSourceProperties secondDataSourceProperties() {
57+
return new DataSourceProperties();
58+
}
59+
60+
@Bean(name = "configFlywayAuthorsDb")
61+
public FlywayAuthorProperties secondFlywayProperties() {
62+
return new FlywayAuthorProperties();
63+
}
64+
65+
@Bean(name = "dataSourceAuthorsDb")
66+
@ConfigurationProperties("app.datasource.ds2")
67+
public HikariDataSource secondDataSource(@Qualifier("configAuthorsDb") DataSourceProperties properties) {
68+
return properties.initializeDataSourceBuilder().type(HikariDataSource.class)
69+
.build();
70+
}
71+
72+
@FlywayDataSource
73+
@Bean(name = "flywayAuthorsDb", initMethod = "migrate")
74+
@DependsOn("dataSourceAuthorsDb")
75+
public Flyway secondFlyway(@Qualifier("configFlywayAuthorsDb") FlywayAuthorProperties properties) {
76+
return Flyway.configure()
77+
.dataSource(properties.getUrl(), properties.getUser(), properties.getPassword())
78+
.schemas(properties.getSchema())
79+
.locations(properties.getLocation())
80+
.load();
81+
}
82+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.bookstore.config;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import javax.persistence.EntityManagerFactory;
6+
import javax.sql.DataSource;
7+
import org.springframework.beans.factory.annotation.Qualifier;
8+
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.context.annotation.Primary;
12+
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
13+
import org.springframework.orm.jpa.JpaTransactionManager;
14+
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
15+
import org.springframework.transaction.PlatformTransactionManager;
16+
import org.springframework.transaction.annotation.EnableTransactionManagement;
17+
18+
@Configuration
19+
@EnableJpaRepositories(
20+
entityManagerFactoryRef = "ds1EntityManagerFactory",
21+
transactionManagerRef = "ds1TransactionManager",
22+
basePackages = "com.bookstore.ds1"
23+
)
24+
@EnableTransactionManagement
25+
public class FirstEntityManagerFactory {
26+
27+
@Bean
28+
@Primary
29+
public LocalContainerEntityManagerFactoryBean ds1EntityManagerFactory(
30+
EntityManagerFactoryBuilder builder, @Qualifier("dataSourceBooksDb") DataSource dataSource) {
31+
32+
return builder
33+
.dataSource(dataSource)
34+
.packages(packagesToScan())
35+
.persistenceUnit("ds1-pu")
36+
.properties(hibernateProperties())
37+
.build();
38+
}
39+
40+
@Bean
41+
@Primary
42+
public PlatformTransactionManager ds1TransactionManager(
43+
@Qualifier("ds1EntityManagerFactory") EntityManagerFactory ds1EntityManagerFactory) {
44+
return new JpaTransactionManager(ds1EntityManagerFactory);
45+
}
46+
47+
protected String[] packagesToScan() {
48+
return new String[]{
49+
"com.bookstore.ds1"
50+
};
51+
}
52+
53+
protected Map<String, String> hibernateProperties() {
54+
return new HashMap<String, String>() {
55+
{
56+
put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
57+
}
58+
};
59+
}
60+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.bookstore.config;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import javax.persistence.EntityManagerFactory;
6+
import javax.sql.DataSource;
7+
import org.springframework.beans.factory.annotation.Qualifier;
8+
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
12+
import org.springframework.orm.jpa.JpaTransactionManager;
13+
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
14+
import org.springframework.transaction.PlatformTransactionManager;
15+
import org.springframework.transaction.annotation.EnableTransactionManagement;
16+
17+
@Configuration
18+
@EnableJpaRepositories(
19+
entityManagerFactoryRef = "ds2EntityManagerFactory",
20+
transactionManagerRef = "ds2TransactionManager",
21+
basePackages = "com.bookstore.ds2"
22+
)
23+
@EnableTransactionManagement
24+
public class SecondEntityManagerFactory {
25+
26+
@Bean
27+
public LocalContainerEntityManagerFactoryBean ds2EntityManagerFactory(
28+
EntityManagerFactoryBuilder builder, @Qualifier("dataSourceAuthorsDb") DataSource dataSource) {
29+
30+
return builder
31+
.dataSource(dataSource)
32+
.packages(packagesToScan())
33+
.persistenceUnit("ds2-pu")
34+
.properties(hibernateProperties())
35+
.build();
36+
}
37+
38+
@Bean
39+
public PlatformTransactionManager ds2TransactionManager(
40+
@Qualifier("ds2EntityManagerFactory") EntityManagerFactory secondEntityManagerFactory) {
41+
return new JpaTransactionManager(secondEntityManagerFactory);
42+
}
43+
44+
protected String[] packagesToScan() {
45+
return new String[]{
46+
"com.bookstore.ds2"
47+
};
48+
}
49+
50+
protected Map<String, String> hibernateProperties() {
51+
return new HashMap<String, String>() {
52+
{
53+
put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
54+
}
55+
};
56+
}
57+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.bookstore.ds1;
2+
3+
import java.io.Serializable;
4+
import javax.persistence.Entity;
5+
import javax.persistence.GeneratedValue;
6+
import javax.persistence.GenerationType;
7+
import javax.persistence.Id;
8+
9+
@Entity
10+
public class Book implements Serializable {
11+
12+
private static final long serialVersionUID = 1L;
13+
14+
@Id
15+
@GeneratedValue(strategy = GenerationType.IDENTITY)
16+
private Long id;
17+
18+
private String title;
19+
private String isbn;
20+
private String authors;
21+
22+
public Long getId() {
23+
return id;
24+
}
25+
26+
public void setId(Long id) {
27+
this.id = id;
28+
}
29+
30+
public String getTitle() {
31+
return title;
32+
}
33+
34+
public void setTitle(String title) {
35+
this.title = title;
36+
}
37+
38+
public String getIsbn() {
39+
return isbn;
40+
}
41+
42+
public void setIsbn(String isbn) {
43+
this.isbn = isbn;
44+
}
45+
46+
public String getAuthors() {
47+
return authors;
48+
}
49+
50+
public void setAuthors(String authors) {
51+
this.authors = authors;
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return "Book{" + "id=" + id + ", title=" + title + ", isbn=" + isbn + ", authors=" + authors + '}';
57+
}
58+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.bookstore.ds1;
2+
3+
import org.springframework.data.jpa.repository.JpaRepository;
4+
import org.springframework.stereotype.Repository;
5+
6+
@Repository
7+
public interface BookRepository extends JpaRepository<Book, Long> {
8+
}

0 commit comments

Comments
 (0)