この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
前回はDAOでクエリを実行しました。 今回は、クエリの実行手段を複数紹介します。 JPA自体はJavaとデータベースを繋ぐものでSpringの機能ではありませんが、基本として必要なので紹介します。
環境
Mac OSX 10.10.5 Yosemite Eclipse Mars2 Java 8 Spring Boot 1.3.6 PostgreSQL 9.5.1
JPQLの手順
1.EntityManagerの用意
@PersistenceContext
EntityManager entityManager;
これを元にクエリを作成します。 アプリ実行時に1つだけBeanに登録されるので、基本は@PresistenceContextで接続します。 複数箇所で接続するとエラーになるので記述場所に注意が必要です。 @Autowiredでも可能ですが制限があるようです。
2.クエリ作成
以下3種類の方法を紹介します。
方法① JPQLで作成
Query query = entityManager
.createQuery("from Fruit where id = :id")
.setParameter("id", 変数);
JPQLでクエリを作成する場合。setParameterで変数をセットできます。 2行目の「:id」には、3行目で設定したパラメータが適用されます。
方法② SQLで作成
Query query = entityManager
.createNativeQuery("select * from Fruit where id = ?1 or id = ?2")
.setParameter(1, 変数)
.setParameter(2, 変数);
2行目、SQLでクエリを作成する場合。 2行目の「?1」には、3行目で設定したパラメータが適用されます。 上記ではパラメータの設定方法を変えましたが、JPQLでもSQLでも共通で使えます。
方法③ クエリアノテーションで作成
@NamedQuery(
name="クエリ名",
query="JPQLもしくはSQL"
)
エンティティクラスにアノテートします。 queryに書いたものが、nameで呼ばれる訳ですね。 使用例については最後に書いておきます。
3.実行と取得
List<'クラス名'> list = query.getResultList();
Object result = query.getSingleResult();
1行目、クエリの実行結果をリストで取得しています。 2行目、実行結果を1件だけ取得します。 こちらは取得できなかったり、複数取得するとExceptionが発生します。
また、クエリ作成から取得までを下記の様に短縮可能です。
List<'クラス名'> list = entityManager
.createQuery("from テーブル名 where id = :id")
.setParameter("id",変数)
.getResultList();
DAOでクエリアノテーションを使ってみる
テーブルを用意
CREATE TABLE fruit (
id VARCHAR(2) NOT NULL,
name VARCHAR(10),
price integer,
PRIMARY KEY(id)
);
INSERT INTO fruit VALUES
('1','apple',300),
('2','orange',200),
('3','banana',100);
postgres=# select * from fruit;
id | name | price
----+--------+-------
1 | apple | 300
2 | orange | 200
3 | banana | 100
(3 rows)
エンティティ
package com.jpa.mydao;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import lombok.Data;
@Entity
@Data
@Table(name="fruit")
@NamedQueries({
@NamedQuery(
name="findAllFruitWithName",
query="SELECT f FROM Fruit f WHERE f.name LIKE :fruitName"
)
,
@NamedQuery(
name="findLikeName",
query="from Fruit where name LIKE :fruitName"
)
})
public class Fruit {
@Id
private String id;
private String name;
private Integer price;
}
基本は、15~18行目や20~23行目の様に@NamedQueryでJPQLかSQLをqueryに設定し、それらを呼び出す名称をnameに設定します。 14行目の様に、@NamedQueriesで囲めば複数のNamedQueryを設定可能です。 15~18行目、SQL。 20~23行目、PSQL。
DAOインターフェース
package com.jpa.mydao.dao;
import java.io.Serializable;
import java.util.List;
public interface FruitDao <T> extends Serializable {
public List<T> findAllFruitWithName(String name);
public List<T> findLikeName(String name);
}
分かりやすく、クエリ名と同じメソッド名で作成しました。
DAOオブジェクト
package com.jpa.mydao.dao;
import java.util.List;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.jpa.mydao.Fruit;
@Repository
public class FruitDaoJpql implements FruitDao<Fruit>{
private EntityManager entityManager;
public FruitDaoJpql(EntityManager entityManager) {
super();
this.entityManager = entityManager;
}
@Override
public List<Fruit> findAllFruitWithName(String name) {
List<Fruit> list = entityManager.createNamedQuery("findAllFruitWithName")
.setParameter("fruitName", "%" + name + "%")
.getResultList();
return list;
}
@Override
public List<Fruit> findLikeName(String name) {
List<Fruit> list = entityManager.createNamedQuery("findLikeName")
.setParameter("fruitName", "%" + name + "%")
.getResultList();
return list;
}
}
DAOインターフェースのメソッド:findAllFruitWithName。 23行目、エンティティで設定したクエリ名を呼んでいます。 24行目、クエリはLikeを使ったあいまい検索なので、パラメータのnameを%で囲んでいます。
DAOインターフェースのメソッド:findLikeName。 こちらも上記と同様ですね。
コントローラー
package com.jpa.mydao;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.jpa.mydao.dao.FruitDaoJpql;
@RestController
public class EntityManagerController {
@PersistenceContext
EntityManager entityManager;
FruitDaoJpql dao;
@PostConstruct
public void init() {
dao = new FruitDaoJpql(entityManager);
}
/* SQL */
@RequestMapping(value="/findallwith", method=RequestMethod.GET)
public List<Fruit> findAllWith(@RequestParam("name") String name) {
List<Fruit> list = dao.findAllFruitWithName(name);
return list;
}
/* JPQL */
@RequestMapping(value="/findlike", method=RequestMethod.GET)
public List<Fruit> findLike(@RequestParam("name") String name) {
List<Fruit> list = dao.findLikeName(name);
return list;
}
}
24~26行目、EntityManagerをDAOに渡して準備完了。 残り2つのメソッドは同じ内容ですね。 無駄にも感じますが、分かりやすさ重視という事で...
ターミナルで実行
実行コマンドと結果①
テーブルのカラムnameから「pp」をあいまい検索した結果。
$ curl http://localhost:8080/findlike?name=pp
[{"id":"1","name":"apple","price":300}]
実行コマンドと結果②
テーブルのカラムnameから「e」をあいまい検索した結果。
$ curl http://localhost:8080/findallwith?name=e
[{"id":"1","name":"apple","price":300},{"id":"2","name":"orange","price":200}]
さいごに
クエリアノテーションは中々見ない書き方で、アノテーション地獄の未来が見えてきますが、流れを覚えてしまえば慣れかなと思います。