こんにちは、くまごろーです。
今、家計簿アプリの出入金管理の部分を作成しているんですが、これがめちゃくちゃややこしい・・・
特にDB関係がわけわからなさ過ぎて、Spring JPAのことを嫌いになりかけたんですが、JPQLの使い方が少しずつわかってきて楽しくなってきました。
現時点でわかったことをまとめます。
1 自動実装されるメソッドには限界がある・・・?
Spring JPAには、Repositoryインターフェースに、ルールに則った命名のメソッドを宣言すれば、クエリを自動的に生成してくれます。
(参考:【Spring Data JPA】自動実装されるメソッドの命名ルール - Qiita)
今日のお昼までは、「これを駆使すればSQL使わなくても行けるんじゃね?」と思ってました。
でも、少し複雑なことをやろうとするともう厳しい。
<MoneyRecoreRepository.java>
public interface MoneyRecordRepository extends JpaRepository<MoneyRecord, Long>{ //ユーザー名で検索したり public List<MoneyRecord> findByUsername(String username); //レコードを削除したり public void deleteByRecordId(Long recordId); //ユーザー名で検索した上で日付順に並び変えたり public List<MoneyRecord> findByUsernameOrderByRecordDate(String username); //特定の期間のレコードだけを検索したり public List<MoneyRecord> findByRecordDateBetweenOrderByRecordDateAsc(LocalDate start, LocalDate end); }
これくらいの条件なら自動実装のメソッドでもいけましたが、例えば「ユーザー名で検索した記録を月毎に分けて、それを更に収入と支出に分けて月毎の合計金額を算出する」とかになると、ちょっと無理そうでした。(無理ですよね?もし可能だったら教えてほしい)
で、今まで避け続けていたJPQLに挑戦することにしました。
2 JPQLを使ってみる
JPQLとは、Java Persistence Query Language の略で、SQLのクエリと似たクエリ文を実行することでDBを操作する、JPAで使用できるクエリ言語らしい。
ここでは、作成途中のポートフォリオにJPQLを導入するに当たって追加した部分を載せていきます。
(1)DAOインターフェースを用意する
既に出入金用のRepositoryインターフェースを用意していましたが、自動実装のメソッドはそのまま使用したいので、別インターフェース・別クラスを用意することにしました。
<MoneyRecordDao.java>
@Repository public interface MoneyRecordDao <T> extends Serializable { //出入金をユーザーIDで検索 public List<T> findByUsername(String username); //出入金をユーザーIDと期間で検索 public List<T> find(String fstr, String a); }
(2)DAOクラスを実装
(1)で作成したインターフェースを実装する。
<MoneyRecordDaoImpl.java>
@Repository public class MoneyRecordDaoImpl implements MoneyRecordDao<MoneyRecord> { private static final long serialVersionUID = 1L; private EntityManager em; public MoneyRecordDaoImpl() { super(); } public MoneyRecordDaoImpl(EntityManager manager) { this(); em = manager; } @SuppressWarnings("unchecked") @Override public List<MoneyRecord> findByUsername(String username){ String qstr = "from MoneyRecord where username = :fstr"; Query query = em.createQuery(qstr).setParameter("fstr", username); List<MoneyRecord> list = query.getResultList(); em.close(); return list; } @SuppressWarnings("unchecked") @Override public List<MoneyRecord> find(String fstr, String a){ List<MoneyRecord> list = null; String qstr = "from MoneyRecord where username = ?1 and record_date like ?2 and category_id not like ?3"; Query query = em.createQuery(qstr).setParameter(1, fstr) .setParameter(2, a + "%") .setParameter(3, 20 + "%"); list = query.getResultList(); return list; } }
ポイントとしてはこんな感じ。
〇 クエリを作成する
public List<MoneyRecord> findByUsername(String username){ //"from MoneyRecord"は、SQLの"select * from MoneyRecord"に相当する //":fstr"・・・":〇〇"という形式で書くと、パラメータ用の変数として使える String qstr = "from MoneyRecord where username = :fstr"; //Queryクラスに、EntityManagerのcreateQueryメソッドでQueryを代入 //その際に、setParameterメソッドで変数に値を設定 Query query = em.createQuery(qstr).setParameter("fstr", username);
〇 結果を取得
//Queryクラスのgetメソッドで取得(結果の型に応じていくつかgetメソッドがあります)
List<MoneyRecord> list = query.getResultList();
〇 複数のパラメータを設定する場合
パラメータは、複数設定することもできる。
//"?1"というように、"?"の後に数字を指定する String qstr = "from MoneyRecord where username = ?1 and record_date like ?2 and category_id not like ?3"; //setParameterメソッドでは、第一引数に番号を指定する Query query = em.createQuery(qstr).setParameter(1, fstr) . setParameter(2, a + "%") .setParameter(3, 20 + "%");
〇 @SuppressWarningsアノテーション
コンパイル時の警告を抑制する。"unchecked"を引数に指定することで、getResultListの戻り値のListが適正かチェックしないようにしている。
DAOクラスに実装したメソッドは、自動実装のものと同じように使えます。
今日はここまでしかできませんでした。ここからバリエーションをうまく膨らましていきたい。データの検索、集計はJavaでもできなくないけど、なるべく全部JPQLでできるようになりたいなあ。
3 参考
www.amazon.co.jp
hacknote.jp