[
{
"id" : 1,
"name" : "서울대학교",
"address" : "서울시 관악구 관악로 1"
}
]
[
{
"id" : 1,
"name" : "홍길동",
"age" : 24
},
{
"id" : 2,
"name" : "성춘향",
"age" : 26
}
]
datasource:
school:
url: jdbc:mysql://localhost:3306/schooldb?createDatabaseIfNotExist=true
username: root
password: root
student:
url: jdbc:mysql://localhost:3306/studentdb?createDatabaseIfNotExist=true
username: root
password: root
spring:
jpa:
hibernate:
ddl-auto: update
generate-ddl: true
show-sql: true
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "school")
public class School {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String address;
}
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
}
우리가 생성한 각각의 엔티티가 어느 datasource에 속하는지 명시해야하는데, 그 방법은 크게 두 가지가 있다.
@Table
어노테이션에 schema 속성에 값을 설정한다.
@Entity
@Table(name = "student", schema="student")
public class Student {
...
}
EntityManagerFactoryBuilder
을 생성할 때 packages 속성에 값을 설정한다. (뒤에서 설명)
@Primary
어노테이션을 붙여서 primary 데이터 베이스를 지정해야 한다.@Bean
@Primary
@ConfigurationProperties("datasource.student")
public DataSourceProperties studentDataSourceProperties(){
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource studentDataSource(){
return studentDataSourceProperties()
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
application.yml
파일에 있는 데이터 베이스 설정 값을 읽어서 DataSourceProperties
빈을 생성하고 그 설정 값을 토대로 DataSource
빈을 생성한다. 예제에서는 Student
데이터 베이스를 primary로 설정하기 위해 @Primary
어노테이션을 명시했다.적어도 하나의 DataSource 빈에 @Primary 어노테이션을 붙여야 한다. 그렇지 않을 경우, 어플리케이션이 실행되지 않는다.
@Bean
@ConfigurationProperties(prefix = "datasource.school")
public DataSourceProperties schoolDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource schoolDataSource() {
return schoolDataSourceProperties()
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
각각의 DataSource에 해당하는 EntityManager
객체를 얻기 위해 EntityManagerFactoryBean
을 사용한다.
@Bean(name = "studentEntityManager")
@Primary
public LocalContainerEntityManagerFactoryBean studentEntityManager(
EntityManagerFactoryBuilder builder) {
return builder.dataSource(studentDataSource()).packages(Student.class)
.build();
}
@Bean(name = "schoolEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean schoolEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder.dataSource(schoolDataSource()).packages(School.class)
.build();
}
엔티티가 어떤 Datasource에 바인딩될지 정하는 방법은
@Table
어노테이션에 schema 값을 설정하는 방법과 지금 작성한 packages 메소드에 바인딩될 엔티티 타입을 전달하는 방법이 있다.
각각의 DataSource에 해당하는 TransactionManager
를 설정한다. 위에서 설정한 EntityManagerFactory 를 참조할 수 있도록 @Qualifier
어노테이션을 사용한다.
@Bean(name = "studentTransactionManager")
@Primary
public PlatformTransactionManager studentTransactionManager(
@Qualifier("studentEntityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}
@Bean(name = "schoolTransactionManager")
public PlatformTransactionManager schoolTransactionManager(
@Qualifier("schoolEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}
@EnableJpaRepositories
어노테이션을 사용해서 각각의 DataSource에 대한 정보를 제공해야 한다.
@EnableJpaRepositories(
basePackages = "com.example.multipledatasources.repository.student",
entityManagerFactoryRef = "studentEntityManager",
transactionManagerRef = "studentTransactionManager"
)
basePackages
: Repository의 기본 패키지를 설정한다. entityManagerFactoryRef
: 환경 설정 파일에 정의된 EntityManagerFactory
빈을 참조하도록 한다.transactionManagerRef
: 환경 설정 파일에 정의된 TransactionManager
빈을 참조하도록 한다.환경 설정을 끝마친 각각의 DataSource
파일은 아래와 같다.
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.example.multipledatasources.repository.student",
entityManagerFactoryRef = "studentEntityManager",
transactionManagerRef = "studentTransactionManager"
)
public class StudentDataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("datasource.student")
public DataSourceProperties studentDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource studentDataSource() {
return studentDataSourceProperties()
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
@Bean(name = "studentEntityManager")
@Primary
public LocalContainerEntityManagerFactoryBean studentEntityManager(
EntityManagerFactoryBuilder builder) {
return builder.dataSource(studentDataSource()).packages(Student.class)
.build();
}
@Bean(name = "studentTransactionManager")
@Primary
public PlatformTransactionManager studentTransactionManager(
@Qualifier("studentEntityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}
}
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.example.multipledatasources.repository.school",
entityManagerFactoryRef = "schoolEntityManagerFactory",
transactionManagerRef = "schoolTransactionManager"
)
public class SchoolDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "datasource.school")
public DataSourceProperties schoolDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource schoolDataSource() {
return schoolDataSourceProperties()
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
@Bean(name = "schoolEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean schoolEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder.dataSource(schoolDataSource()).packages(School.class)
.build();
}
@Bean(name = "schoolTransactionManager")
public PlatformTransactionManager schoolTransactionManager(
@Qualifier("schoolEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}
}
@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
@Repository
public interface SchoolRepository extends JpaRepository<School, Integer> {
}
Student
와 관련된 API이고, 다른 하나는 School
과 관련된 API이다.@RestController
public class MainController {
@Autowired
private SchoolRepository schoolRepository;
@Autowired
private StudentRepository studentRepository;
@GetMapping(value = "school")
public ResponseEntity<List<School>> getSchool() {
return ResponseEntity
.status(HttpStatus.ACCEPTED)
.body(schoolRepository.findAll());
}
@GetMapping(value = "student")
public ResponseEntity<List<Student>> getStudent() {
return ResponseEntity
.status(HttpStatus.ACCEPTED)
.body(studentRepository.findAll());
}
}