Spring Framework
jarにビルドして実行
./gradlew build
./gradlew --no-daemon build
# testなどを省きたいとき
./gradlew assemble
build/libs
の中にjarファイルが生成される。
Ref: https://stackoverflow.com/questions/4597850/gradle-build-without-tests
java -jar build/libs/hoge.jar
# 実行時に読み込むapplication.ymlを指定できる
java -jar build/libs/hoge.jar --spring.profiles.active=dev1
Ref: https://qiita.com/NagaokaKenichi/items/fd9b5e698776fe9b9cc4
Tips
SQLのログを出力
build.gradle
dependencies {
compile "org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4:1.16"
compile "org.slf4j:slf4j-api"
compile group: 'com.googlecode.log4jdbc', name: 'log4jdbc', version: '1.2'
}
src/main/resources/log4jdbc.log4j2.properties
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
config/logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<!-- log4jdbc-log4j2 -->
<logger name="jdbc.sqlonly" level="DEBUG"/>
<logger name="jdbc.sqltiming" level="INFO"/>
<logger name="jdbc.audit" level="DEBUG"/>
<logger name="jdbc.resultset" level="ERROR"/>
<logger name="jdbc.resultsettable" level="ERROR"/>
<logger name="jdbc.connection" level="DEBUG"/>
</configuration>
application.yml
logging.config: config/logback-spring.xml
Ref: Spring Boot解説第10回(開発環境編:ログの設定について~logback)
実行時のapplication.ymlのパス解決
H2で複数データベースを使う
ディレクトリ構成は次の前提で。
src
test
example
MyTest.kt
resources
schema-db1.sql
schema-db2.sql
application.yml
の例。
jdbc:h2:mem:
の後ろがDB名になる。
spring:
datasource:
db1:
url: jdbc:h2:mem:db1
username: sa
password: sa
db2:
url: jdbc:h2:mem:db2
username: sa
password: sa
jpa:
show-sql: false
hibernate:
ddl-auto: none
テストコードでは@Sql
アノテーションを使ってデータソースごとにスキーマ定義ファイルを読み込む。
@Test
@SqlGroup(
Sql(
"classpath:schema-db1.sql",
config = SqlConfig(dataSource = "db1DataSource", transactionManager = "db1TransactionManager")
),
Sql(
"classpath:schema-db2.sql",
config = SqlConfig(dataSource = "db2DataSource", transactionManager = "db2TransactionManager")
)
)
fun test() {
}
複数データベースを使う
ディレクトリ構成は次の前提で。
データベースごとにパッケージをわけて、それぞれのデータベース設定をするために**Config.kt
ファイルを置いている。
ファイル名はなんでも良い。
src
main
example
db1
MyEntity1.kt
MyEntity1Repository.kt
Db1Config.kt
db2
MyEntity2.kt
MyEntity2Repository.kt
Db2Config.kt
application.yml
は次のようになる。
spring:
datasource:
db1:
url: "jdbc:mysql://localhost:3301/my_database"
username: root
password: password
hikari:
connection-test-query: "SELECT 1"
maximum-pool-size: 3
minimum-idle: 1
db12:
url: "jdbc:mysql://localhost:3302/my_database"
username: root
password: password
hikari:
connection-test-query: "SELECT 1"
maximum-pool-size: 3
minimum-idle: 1
***Config.kt
の中身。
DATASOURCE_NAME
だけ変えたファイルをdb1、db2に用意する。
const val DATASOURCE_NAME = "db1"
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "${DATASOURCE_NAME}EntityManagerFactory",
transactionManagerRef = "${DATASOURCE_NAME}TransactionManager"
)
@EnableConfigurationProperties(HibernateProperties::class)
class IntermediateConfig {
@Autowired
lateinit var jpaProperties: JpaProperties
@Autowired
lateinit var hibernateProperties: HibernateProperties
@Bean(name = ["${DATASOURCE_NAME}DataSourceProperties"])
@Primary
@ConfigurationProperties(prefix = "spring.datasource.$DATASOURCE_NAME")
fun dataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Bean(name = ["${DATASOURCE_NAME}DataSource"])
@Primary
@ConfigurationProperties(prefix = "spring.datasource.$DATASOURCE_NAME.hikari")
fun dataSource(
@Qualifier("${DATASOURCE_NAME}DataSourceProperties") dataSourceProperties: DataSourceProperties
): DataSource {
return dataSourceProperties.initializeDataSourceBuilder().build()
}
@Bean(name = ["${DATASOURCE_NAME}EntityManagerFactory"])
@Primary
fun entityManagerFactory(
@Qualifier("${DATASOURCE_NAME}DataSource") dataSource: DataSource,
builder: EntityManagerFactoryBuilder
): LocalContainerEntityManagerFactoryBean {
return builder
.dataSource(dataSource)
.packages(this.javaClass.getPackage().name)
.properties(hibernateProperties.determineHibernateProperties(jpaProperties.properties, HibernateSettings()))
.persistenceUnit("${DATASOURCE_NAME}DataSource")
.build()
}
@Bean(name = ["${DATASOURCE_NAME}TransactionManager"])
@Primary
fun transactionManager(
@Qualifier("${DATASOURCE_NAME}EntityManagerFactory") entityManagerFactory: EntityManagerFactory
): PlatformTransactionManager {
return JpaTransactionManager(entityManagerFactory)
}
}
アノテーションにapplication.ymlの設定値を使う
const val
で定義すればいい
const val HOGE = "\${my.app.hoge}"
// OK
@Hoge(name = HOGE)
fun hoge(){}
@ConfigurationProperties
を使う方法だと、アノテーションのパラメータには使えない。
@ConfigurationProperties(prefix = "my.app")
@Component
data class MyConfig(
var hoge: Long = 0
)
// NG
class MyClass(val myConfig: MyConfig){
@Hoge(name = myConfig.hoge)
fun hoge(){}
}
バージョンに書いているGA, RC, M, SRの違い
同じクラスで複数Beanを定義する
@Bean
アノテーションにそれぞれ別々のname
を指定する。
@Configuration
class HogeApiBeanConfig() {
@Bean(name = ["hoge"])
fun webClient(): WebClient = WebClient.create("localhost:8000")
}
@Configuration
class FugaApiBeanConfig() {
@Bean(name = ["fuga"])
fun webClient(): WebClient = WebClient.create("localhost:8001")
}
利用側はメンバ名がname
に指定した名前と一致するようにDIすればよい。
@Service
class Client(
private val hoge: WebClient,
private val fuga: WebClient)
Spring Dependency Managementで一部の依存関係のみバージョンを上書きする
junit-jupiter
を5.4.0
に指定する例。
(デフォルトだと5.3.2
)
dependencyManagement {
def springCloudVersion = 'Greenwich.RELEASE'
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion"
}
}
ext['junit-jupiter.version'] = '5.4.0'
dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine"
}
HTTPレスポンスのオブジェクトを独自のシリアライザで変換する
@JsonDeserialize
、@JsonSerialize
を使う。
class Hoge(val stringVal: String)
data class HogeResponse(
@get:JsonDeserialize(using = HogeDeserializer::class)
@get:JsonSerialize(using = HogeSerializer::class)
val hoge: Hoge? = null
)
デシリアライザの実装。
class HogeDeserializer: JsonDeserializer<Hoge> {
fun deserialize(parser: JsonParser, context: DeserializationContext) {
val stringVal = parser.readValueAs(String::class.java)
return new Hoge(stringVal)
}
}
シリアライザの実装。
class HogeSerializer: JsonSerializer<Hoge> {
fun serialize(hoge: Hoge, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeString(hoge.stringVal)
}
}
この例だと
val hoge = Hoge("fuga")
を
{
"stringVal": "fuga"
}
に対応して変換できる。
JacksonでLocalDatetimeを使う
Ref: Spring Boot+JacksonのDate and Time APIでフォーマットを変更する
Spring Boot環境でJava8日付型(JSR-310)を使うための設定
Bean validation
Ref: アノテーションターゲット一覧 https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets
Jackson
LocalDateTimeのシリアライズ
基本的にはSpringが依存関係を見て自動的にモジュールを追加するが、自分でBeanを定義している場合はJavaTimeModule
を追加する。
@Configuration
class BeansConfig {
@Bean
fun objectMapper(): ObjectMapper =
ObjectMapper()
.apply {
registerKotlinModule()
// LocalDateTimeに必要
registerModule(JavaTimeModule())
}
}
フォーマットを指定したい場合は@JsonFormat
を利用する。
class Hoge(
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
val time: LocalDateTime,
)
Ref: LocalDateTime converter to JSON with Kotlin
application.yml
logging:
file: "log/main.log"
level.com.example: "debug"
spring:
jpa:
database: MYSQL
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: false
hibernate:
ddl-auto: update
datasource:
url: "jdbc:mysql://localhost:3306/my_database?useUnicode=yes&characterEncoding=UTF-8"
username: "root"
password: "password"
hikari:
connection-test-query: "SELECT 1"
maximum-pool-size: 3
minimum-idle: 1
Couchbase
https://www.baeldung.com/spring-data-couchbase
build.gradle
単体テスト: Unit test
WebTestClient, WireMock
依存関係にWireMockを追加する。
dependencies {
testCompile "org.springframework.cloud:spring-cloud-contract-wiremock"
}
@SpringBootTest(classes = [Application::class])
@AutoConfigureWebTestClient
@AutoConfigureWireMock(port = 8090)
@ActiveProfiles("unit")
class HogeTest {
var testClient = WebTestClient
.bindToServer()
.baseUrl("http://127.0.0.1:8090")
.build()
@Test
fun test() {
// Stubbing WireMock
stubFor(
get(urlEqualTo("/resource"))
.willReturn(
aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello World!")
)
)
testClient
.get()
.uri("/resource")
.exchange()
.expectBody(String::class.java)
.isEqualTo<Nothing>("Hello World!")
}
}
Ref: https://www.baeldung.com/spring-5-webclient
https://docs.spring.io/spring/docs/current/spring-framework-reference/pdf/testing-webtestclient.pdf
FAQ
could not autowire no beans of type found
./gradlew
したら直った。
./gradlew
VSCodeで開発
Java1.8で開発する。
# 念のため他のJDKを削除しておく
sudo apt remove openjdk*
sudo apt install openjdk-8-jdk
VSCodeに次の拡張機能を入れる
- Java Extension Pack(vscjava.vscode-java-pack)
- Spring Boot Extension Pack(pivotal.vscode-boot-dev-pack)
^⇑P
でコマンドパレットを開き、generateと入力してSpringInitializrを使って新規プロジェクトを作る。