はじめに
実際のプロジェクトでは複数のデータベースに繋ぐことが多いと思うので、JPA使って複数のデータベースに繋ぐのをKotlinでやってみました。
環境
Kotlin
Spring-boot 2.0.1.RELEASE
Properties
プロパティはデフォルトの値ではなく独自の値をマッピングするので、それを繋ぎたいだけ用意します。
今回は面倒というのもあり、ローカルに立てたpostgresに2つDatabaseを立ててそれに繋ぎたいと思います。
構成は
testdb下にtestスキーマを作成してcorpテーブル
postgres下にtestスキーマを作成してuserテーブル
としました。
spring.datasource.testdb.driver-class-name=org.postgresql.Driver
spring.datasource.testdb.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.testdb.username=postgres
spring.datasource.testdb.password=admin
spring.datasource.postgres.driver-class-name=org.postgresql.Driver
spring.datasource.postgres.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.postgres.username=postgres
spring.datasource.postgres.password=admin
Contoroller
結果を見たいので、今回のDBを作ったテーブルからfindAllした結果を返すControllerを作成しておきます。
package com.tasogarei.databasetest.contorller;
import com.tasogarei.databasetest.entity.testdb.CorpEntity
import com.tasogarei.databasetest.entity.postgres.UserEntity;
import com.tasogarei.databasetest.repository.testdb.CorpRepository
import com.tasogarei.databasetest.repository.postgres.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody
@Controller
class TestController {
@Autowired
lateinit var userRepository : UserRepository
@Autowired
lateinit var corpRepository: CorpRepository
@ResponseBody
@GetMapping("/postgres")
fun getMapping() : ResponseEntity<List<UserEntity>> {
return ResponseEntity.ok().body(userRepository.findAll())
}
@ResponseBody
@GetMapping("/test")
fun postgresMapping() : ResponseEntity<List<CorpEntity>> {
return ResponseEntity.ok().body(corpRepository.findAll())
}
}
Entity Repository
JPA接続で使用するEntityとRepositoryを作成します。
設定時にパッケージを指定するので、データベース毎に分けておいた方が無難です。
package com.tasogarei.databasetest.entity.postgres
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.Table
@Table(name = "user", schema = "test")
@Entity
class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
var id: Int = 0
@Column
var name: String = ""
}
package com.tasogarei.databasetest.entity.testdb
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.Table
@Table(name = "corp", schema = "test")
@Entity
class CorpEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
var id: Int = 0
@Column
var name: String = ""
}
package com.tasogarei.databasetest.repository.postgres
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import com.tasogarei.databasetest.entity.postgres.UserEntity
@Repository
interface UserRepository : JpaRepository<UserEntity, Int>
package com.tasogarei.databasetest.repository.testdb
import com.tasogarei.databasetest.entity.testdb.CorpEntity
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface CorpRepository : JpaRepository<CorpEntity, Int>
データベースの接続
実際のデータベースの接続はBeanで行うので、Beanでやってあげます。
やっていることはコンフィグから値読み込んでDataSourceとEntityManagerFactory作成してあげているだけです。
両方とも1つで作成する際のBeanと同じような感じです。
ただし、Beanの名前を分けてあげる必要があるのと片方に@Primary
を付けてあげるのだけ忘れないであげてください。
あと、TransactionManager作っておかないといけない気がしたので、一緒に作っています。
データベースを読み込むだけなら特に必要はなかったですが、ないとおかしいと感じたので作りました。
動くのは確認済みです。
@EnableJpaRepositories
をきちんと設定しないと動かないので注意してください。
package com.tasogarei.databasetest.config
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Primary
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.orm.jpa.JpaTransactionManager
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
import org.springframework.stereotype.Component
import org.springframework.transaction.PlatformTransactionManager
import java.util.*
import javax.persistence.EntityManagerFactory
import javax.sql.DataSource
@Component
@ConfigurationProperties(prefix = "spring.datasource.postgres")
@EnableJpaRepositories(
basePackages=["com.tasogarei.databasetest.repository.postgres"],
entityManagerFactoryRef="postgresEntityManagerFactory",
transactionManagerRef="postgresTransactionManager"
)
class PostgresDataSourceConfig {
lateinit var driverClassName: String
lateinit var url: String
lateinit var username: String
lateinit var password: String
@Bean("postgresDataSource")
@Primary
fun dataSource() : DataSource {
return DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).build()
}
@Bean("postgresEntityManagerFactory")
@Primary
fun entityManagerFactory(@Qualifier("postgresDataSource") dataSource: DataSource): EntityManagerFactory {
val properties = Properties()
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL95Dialect")
val factory = LocalContainerEntityManagerFactoryBean()
factory.dataSource = dataSource
factory.setPackagesToScan("com.tasogarei.databasetest.entity.postgres")
factory.jpaVendorAdapter = HibernateJpaVendorAdapter()
factory.setJpaProperties(properties)
factory.afterPropertiesSet()
return factory.nativeEntityManagerFactory
}
@Bean("postgresTransactionManager")
@Primary
fun transactionManager(@Qualifier("postgresDataSource") dataSource: DataSource,
@Qualifier("postgresEntityManagerFactory") entityManagerFactory: EntityManagerFactory): PlatformTransactionManager {
val jpaTransactionManager = JpaTransactionManager()
jpaTransactionManager.dataSource = dataSource
jpaTransactionManager.entityManagerFactory = entityManagerFactory
return jpaTransactionManager
}
}
package com.tasogarei.databasetest.config
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
import org.springframework.stereotype.Component
import java.util.*
import javax.persistence.EntityManagerFactory
import javax.sql.DataSource
import org.springframework.orm.jpa.JpaTransactionManager
import org.springframework.transaction.PlatformTransactionManager
@Component
@ConfigurationProperties(prefix = "spring.datasource.testdb")
@EnableJpaRepositories(
basePackages=["com.tasogarei.databasetest.repository.testdb"],
entityManagerFactoryRef="testdbEntityManagerFactory",
transactionManagerRef="testdbTransactionManager"
)
class TestDbDataSourceConfig {
lateinit var driverClassName: String
lateinit var url: String
lateinit var username: String
lateinit var password: String
@Bean("testdbDataSource")
fun dataSource() : DataSource {
return DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).build()
}
@Bean("testdbEntityManagerFactory")
fun entityManagerFactory(@Qualifier("testdbDataSource") dataSource: DataSource): EntityManagerFactory {
val properties = Properties()
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL95Dialect")
val factory = LocalContainerEntityManagerFactoryBean()
factory.dataSource = dataSource
factory.setPackagesToScan("com.tasogarei.databasetest.entity.testdb")
factory.jpaVendorAdapter = HibernateJpaVendorAdapter()
factory.setJpaProperties(properties)
factory.afterPropertiesSet()
return factory.nativeEntityManagerFactory
}
@Bean("testdbTransactionManager")
fun transactionManager(@Qualifier("testdbDataSource") dataSource: DataSource,
@Qualifier("testdbEntityManagerFactory") entityManagerFactory: EntityManagerFactory): PlatformTransactionManager {
val jpaTransactionManager = JpaTransactionManager()
jpaTransactionManager.dataSource = dataSource
jpaTransactionManager.entityManagerFactory = entityManagerFactory
return jpaTransactionManager
}
}