Spring Boot + JPA + MySQLで簡単なデータアクセスのサンプルを作ってみます。
なお、STS3(3.9.6)+SpringBoot2.0+tymeleaf3.0迄動作確認しています。
今回の仕様
プライマリキーが一つの単純なテーブルばかりではないので、後のことも考えて、簡単と言いながら、ちょっとだけ実用を意識します。
今回の目標は、以下にします。
複数キーを持つテーブルを作る。
名前は、person_entityにしています。
create table person_entity (
pid VARCHAR(150) not null comment 'パーソンID'
, item_id VARCHAR(150) not null comment '項目ID'
, value VARCHAR(300) comment '値'
, update_date TIMESTAMP comment '更新日'
, constraint person_entity_PKC primary key (pid,item_id)
) comment 'パーソン' ;
対応するエンティティクラスを作る
名前付けルールで、クラス名は「PersonEntity」の必要があります。
@Entity
@IdClass(value=PersonEntityKey.class)
public class PersonEntity {
public PersonEntity(String id,String itemId,String value){
super();
setPid(id);
setItemId(itemId);
setValue(value);
}
@Idprivate String pid;
@Id
private String itemId;
private String value;
@Temporal(TemporalType.TIMESTAMP)
private Date updateDate;
@PrePersist
@PreUpdate
protected void now(){
setUpdateDate(new Date());
}}
※ごちゃごちゃするので、getter、setterは省いて引用してます。
@Idというアノテーションがついているとプライマリキーです。
キーが一つの場合は、@Idを指定するだけで良いのですが、キーが2つ以上だと、@Idを2つ書くだけだとエラーになります。
それを回避するための定義が、「@IdClass(value=PersonEntityKey.class)」の部分です。
プライマリキーにする項目だけを定義したクラスをつくります。
valueの後ろに指定します。
今回の場合の中身はこんな感じです。
public class PersonEntityKey implements Serializable {
private static final long serialVersionUID = 1L;
private String pid;
private String itemId;
}
※これもgettter、setterは省略してます。
このクラスを「@IdClass(value=PersonEntityKey.class)」の様に指定すれば、@Idを2つ指定してもエラーにならなくなり、後の操作は@Id一つの場合と同じように扱えます。
そのテーブルに対するCRUDを、Jprepositoryで用意する。
このエンティティクラスPersonEntityに対応する、リポジトリクラスを作ります。
@Repository
public interface PersonRepository extends JpaRepository<PersonEntity, PersonEntityKey> {
// Select
public List<PersonEntity> findByPid(String pid);
}
こんな感じで、 JpaRepositoryを継承したインタフェースを作るだけで、基本的なCRUDが提供されるので、超楽です。
<PersonEntity, PersonEntityKey>の部分は、対象とするエンティティクラスと、プライマリキーを収めたクラスを指定します。
ちなみに、Entityクラスの@Idアノテーションがひとつだけ(キーが1つ)の場合は、上記のPersonEntityの部分に、Stringなどのキーの型を表すクラスを指定すれば良いです。
しかも、エンティティクラスに、こう書いておくだけで、INSERT/UPDATE時に自動的に更新日付を記録してくれます。
@Temporal(TemporalType.TIMESTAMP)
private Date updateDate;
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}@PrePersist
@PreUpdate
protected void now(){
setUpdateDate(new Date());
}
これは結構、手間が省けて助かりますね。
とりあえず、pidを指定してselectしたいので、findByPidというメソッドを定義してます。
これも、名前付けルールだけ気をつけて、定義だけしとけば中身は自動的に生成されます。(findBy・・の後ろの名前とDB物理名が一致しないと実行時エラーになります)
一連のテーブルアクセスを、JUNITテストでやってみる
じゃあ、これで本当にCRUDできるのか、JUNITのテストケースを書いてみます。
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestPersonRepository {@Autowired
public PersonRepository personRepository;
@Test
public void test() {
PersonEntity pe = new PersonEntity("001","001","value W 2 格納テスト");// Create(Insert)
personRepository.saveAndFlush(pe);
// Read(Select)
List<PersonEntity> peList = personRepository.findByPid("001");
// Update
personRepository.saveAndFlush(pe);
assertTrue(peList.get(0).getItemId().equals("001"));
PersonEntity pe1 = peList.get(0);// Delete
personRepository.delete(pe1);
}
saveAndFlush() というメソッドを2回よんでいるのは、レコードがなければInsert、あればupdate を自動で切り替えてくれるみたいなので、それを確認しているわけです。
これでJUNITテストを実行して、グリーンならOKです。
まとめ
とりあえず、この程度の手間で、一通りのCRUDができて、かつ、単体テストまでできるのは有り難いですね。
もちろん、もっと複雑なSQLが必要な場合は、ダイレクトにSQLを書いたり、それなりの手間はかかります。
でも、今時、ガリガリと複雑なSQLばかりを書くのもスマートじゃないし、設計で考慮して、極力、このシンプルな手順で処理できるようにしたらいいのかなと思ってます。
STS+Spring Boot+thymeleaf 関連記事
入力画面に関連する記事
参照画面・画面遷移に関連する記事
参照画面:テーブルを使い、行毎に色分けした一覧表を表示する。
入力チェックに関連する記事
入力チェック:@Patternと正規表現で独自チェックする。
入力チェック用アノテーション定義を自分で作る。(独自実装版)
データアクセス・その他に関連する記事