ページ

2016/10/30

Springでauto_incrementなカラムに対して0を指定する

|

ユニーク制約の一部にNullableなカラムを含めたいが、それではユニークにならないので回避したい。

(a, b, c) という複数カラムのユニーク制約を定義したい場合に、この中にNULLを許可するカラムがありNULLが入ってきてしまうと、MySQLなどではユニーク性が担保されない。

これを回避するために NULL の代わりに 0 を使ってみたら良さそうだが、Springではどうやればいいのか?

SpringのAOPでSQLエラーをハンドリングする

|

Spring BootでJDBCを使っていてSQLExceptionが発生した場合に、HibernateのSqlExceptionHelperが以下のようにERRORログを出力してしまう。

2016-10-30 13:56:45.772  WARN 40153 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 23502, SQLState: 23502
2016-10-30 13:56:45.772 ERROR 40153 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper   : 列 "TEAM_ID" にはnull値が許されていません
NULL not allowed for column "TEAM_ID"; SQL statement:
insert into tag (id, created_at, updated_at, account_id, name, team_id) values (null, ?, ?, ?, ?, ?) [23502-190]

未知の例外に対してはこの挙動でも問題ないように思うが、例えばデッドロックが発生した時にはリトライしたいとか、自分でハンドリングしたいケースがあった場合には、本当に問題といえる状態だと判断ができるレイヤでERRORログを出力するように制御したい。

…ということで、このログ出力を回避して自分でハンドリングする方法について。

Spring Security 4.0ではHeaderWriterを使って静的リソースのキャッシュヘッダを適切につける

|

以前のエントリ
Spring BootでJavaScript/CSSライブラリにフィンガープリントをつける
Spring BootでJavaScript/CSSライブラリにフィンガープリントをつける (2)
の方法だけだと、フィンガープリントを付けることはできてもSpring Securityによりキャッシュ無効化のヘッダが付いてしまうためキャッシュされない。
そこで静的リソースについてはキャッシュヘッダを無効化してみて、一見 Cache-Control: no-cache のようなヘッダはなくなったかのように見えたが、Font Awesomeのアイコンがどうもちらつくな…と思って調べた。

すると、静的リソースについては全体的にキャッシュ系のヘッダを無効化していたものの、Expiresヘッダだけはついてしまっていた。
※Spring Boot 1.3.2 (Spring Security 4.0.3) を使用。

Thymeleaf/JPAのConversion

|

基本的な部分かもしれないが、Thymeleaf、JPAでの型変換を行う方法について。

Thymeleafでの型変換

Thymeleafでは、SpringのConverterが定義されていれば${{variable}}の形式で書くことでConverterで変換した結果を出力してくれる。
なので、HTML上で分岐を書いたり独自の変換用のBeanを定義するよりはこの仕組みを使ったほうがいい。

例えば以下のような列挙型を定義していたとする。

Springのバリデーションエラーメッセージを通常メッセージに統合する

|

Bean ValidationのエラーメッセージはValidationMessages.propertiesもしくはValidationMessages_ja.propertiesに書かなければならないと思っていたが、そうではなかった。

以下のようにMessageSourceで読み込んだ内容をLocalValidatorFactoryBeanに渡せば良い。

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Autowired
    private MessageSource messageSource;

    @Bean
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
        localValidatorFactoryBean.setValidationMessageSource(messageSource);
        return localValidatorFactoryBean;
    }

    @Override
    public Validator getValidator() {
        return validator();
    }
}

messages.propertiesはnative2asciiで変換せず直接マルチバイトのメッセージを書いていても動作するので、この方法の方がいいかもしれない。
(コードレビューでも直接日本語のメッセージが読めるし)

2016/10/16

SpringのControllerメソッド実行時にAOPでログ出力する

|

APIだとAOPでロギングするのは割と簡単だが、画面の場合にうまいやり方が見つからず試行錯誤したのでメモ。

@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")

などを使えばリクエストの前後に処理を挟むことは簡単。
APIの場合は、バリデーションエラーや例外に対して共通的な処理を入れることは容易いが、画面の場合、バリデーションエラーが発生したら共通エラー画面ではなく元の画面を表示したいし、例外に対しても同じ。
例外に関しては個別のControllerのメソッド内でtry-catchを入れてログ出力すればいいが、バリデーションエラーは画面に表示されるだけでログに出力する方法がわかっていなかった。

2016/10/09

Springのテストを速くする工夫

|

Spring BootでControllerを含めたテストをする場合に、
MockMvcBuilders.webAppContextSetup() でセットアップして @WebIntegrationTests にすると通常のアプリケーションとしてテストできるが、数が多くなってくると重い。

いくつか改善できそうなポイントを調べたので記録しておく。

なお、極力Springを使わないのがベストだが、今回はそれができないケースを想定しているので除外。

HerokuのGradleビルドパックでSpringアプリを動かす

|

以前、JavaとNode.jsでSpringアプリを動かしたが、自作の gradle-web-resource-plugin を使えば既にJava/Gradleだけで済むようになっていることもあり、Heroku の Gradle ビルドパックに切り替えてみた。

網羅的な手順ではなく、上記アプリのビルドパックを切り替える場合の記録なので注意。