参考:http://qiita.com/opengl-8080/items/05d9490d6f0544e2351a
Spring Boot に必要なライブラリやコントローラ雛形が登録されたMavenアーキタイプを使ってプロジェクトを作成する(いい加減Gradleおぼえないと。。)
https://github.com/making/spring-boot-blank
# mvn archetype:generate -DarchetypeGroupId=am.ik.archetype -DarchetypeArtifactId=spring-boot-blank-archetype -DarchetypeVersion=1.0.6
mvn> Define value for property 'groupId': : com.github.asufana
mvn> Define value for property 'artifactId': : MyFirstSpringBoot
mvn> Define value for property 'version': 1.0-SNAPSHOT: :
mvn> Define value for property 'package': com.github.asufana: :
#
# tree
.
└── MyFirstSpringBoot
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── github
│ │ └── asufana
│ │ ├── App.java
│ │ ├── AppConfig.java
│ │ └── HelloController.java
│ └── resources
│ ├── application.properties
│ ├── log4jdbc.log4j2.properties
│ └── templates
│ └── hello.html
└── test
├── java
│ └── com
│ └── github
│ └── asufana
│ └── HelloControllerTest.java
└── resources
# mvn clean test
......
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.367s
[INFO] Finished at: Sun Mar 20 10:49:49 JST 2016
[INFO] Final Memory: 22M/242M
[INFO] ------------------------------------------------------------------------
# mvn clean spring-boot:run
http://localhost:8080 にアクセスできることを確認
まずビルドする
# mvn clean package
# ls -al target/*.jar
-rw-r--r--@ 1 hana staff 16356038 3 20 11:09 target/MyFirstSpringBoot-1.0-SNAPSHOT.jar
生成されたjarファイルをデプロイ先に配置したとして実行してみる
# java -jar target/MyFirstSpringBoot-1.0-SNAPSHOT.jar
http://localhost:8080 にアクセスできることを確認
# cd MyFirstSpringBoot
# mvn idea:idea
IdeaからプロジェクトOpenして、HelloControllerTest がテストできることを確認する
Annotation Processing 有効化
- Idae preferenses から Build > Compiler > Annotation Processors から
Enable annotation processing
を有効化
Lombok IntelliJ Idea プラグイン
- Idea preferenses から Plugins > Browse repositories から
Lombok Plugin
をインストールして、Idea再起動
- pom.xml にて Lombok 依存性を登録する
- 先に利用した spring-boot-blank-archetype には含まれている
*なお Idea の Lombok plugin も lombok.config に対応していない模様。。
JPAライブラリを pom.xml に追加
<dependencies>
...
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependencies>
主にJpaRepositoryインターフェースを利用する方法と、EntityManagerを直接使う方法がある。
サンプルエンティティクラス
/** エンティティ */
@Entity
@Getter
@Accessors(fluent = true)
public class Model {
@Id
@GeneratedValue
protected Long id;
}
JpaRepository を extends し、@Repository
アノテートする。@Autowired
から DI により ModelRepo クラスのインスタンスが取得でき、saveAndFlush()
findAll()
getOne()
メソッドなどが提供される。
/** リポジトリ */
@Repository
public interface ModelRepo extends JpaRepository<Model, Long> { }
/** テスト */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
public class ModelRepoTest {
@Autowired
ModelRepo repo;
@Test
//JpaRepositoryインターフェースに用意されたデフォルトクエリから取得
//saveAndFlush(), findAll(), getOne()
public void testDefaultMethod() {
Model model = repo.saveAndFlush(new Model());
//生成されること
assertThat(model, is(notNullValue()));
//取得できること
assertThat(repo.findAll().size(), is(1));
//取得できること
assertThat(repo.getOne(model.id()), is(notNullValue()));
}
}
DIについてはこちらを参照:http://d.hatena.ne.jp/nowokay/20160406
JPQL により findById()
という文字列からクエリが自動生成される。
- findBy◯◯And◯◯
- findBy◯◯Or◯◯
- findBy◯◯IsNull
- findBy◯◯In(List)
など
/** リポジトリ */
@Repository
public interface ModelRepo extends JpaRepository<Model, Long> {
Model findById(Long id);
}
/** テスト */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
public class ModelRepoTest {
@Autowired
ModelRepo repo;
@Test
//JpaRepositoryインターフェースで自動生成するクエリから取得
public void testGeneratedMethod() {
Model model = repo.saveAndFlush(new Model());
//取得できること
assertThat(repo.findById(model.id()), is(notNullValue()));
}
}
インターフェースを定義し、@Query
でクエリ文字列を記述する
/** リポジトリ */
@Repository
public interface ModelRepo extends JpaRepository<Model, Long> {
@Query("select m from Model m where id=:id")
List<Model> findByIdWithQuery(@Param("id") Long id);
}
/** テスト */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
public class ModelRepoTest {
@Autowired
ModelRepo repo;
@Test
//Queryアノテーションから取得
public void testQueryAnnotation() {
Model model = repo.saveAndFlush(new Model());
//取得できること
assertThat(repo.findByIdWithQuery(model.id()), is(notNullValue()));
}
}
DAO などを用意し、そこで EntityManager
インスタンスに JPQL を渡す
/** DAO */
public class ModelDao {
private EntityManager em;
public ModelDao(EntityManager em) {
this.em = em;
}
public Model findById(Long id) {
Query query = em.createQuery("from Model where id=:id")
.setParameter("id", id);
return (Model) query.getSingleResult();
}
}
/** テスト */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
public class ModelDaoTest {
@Autowired
ModelRepo repo;
@PersistenceContext
protected EntityManager em;
@Test
public void testFIndById() {
Model model = repo.saveAndFlush(new Model());
//取得できること
ModelDao dao = new ModelDao(em);
assertThat(dao.findById(model.id()), is(notNullValue()));
}
}
エンティティに @NamedQuery
でクエリ名と JPQL を記述。DAO に実装を記述する。
/** エンティティ */
@Entity
@Getter
@Accessors(fluent = true)
@NamedQuery(name="findByIdWithNamedQuery", query="from Model where id=:id")
public class Model {
@Id
@GeneratedValue
protected Long id;
}
/** DAO */
public class ModelDao {
private EntityManager em;
public ModelDao(EntityManager em) {
this.em = em;
}
public Model findByIdWithNamedQuery(Long id) {
Query query = em.createNamedQuery("findByIdWithNamedQuery")
.setParameter("id", id);
return (Model) query.getSingleResult();
}
}
/** テスト */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
public class ModelDaoTest {
@Autowired
ModelRepo repo;
@PersistenceContext
protected EntityManager em;
@Test
//NamedQueryを利用したクエリから取得
public void testFindByIdWithNamedQuery() {
Model model = repo.saveAndFlush(new Model());
//取得できること
ModelDao dao = ModelDao.of(em);
assertThat(dao.findByIdWithNamedQuery(model.id()), is(notNullValue()));
}
}
-
Spring は DI が基本。原則
@Autowired
で、DI が生成したインスタンスを取得する -
DI によりインスタンス生成(Repo repo = new Repo()など)をソースコードから排除できる(依存性がなくなる)
-
ただし
@Autowired
が利用できるところとできないところがある(このあたり調査していないのでよくわかっていない) -
エンティティからはリポジトリインスタンスを取得できないため、下記ユーティリティから取得する
-
参考:http://stackoverflow.com/questions/28365154/autowired-not-working-in-a-class-entity
-
このあたりの Spring のベストプラクティスを知りたい
package com.github.asufana.ddd.utils;
import org.springframework.beans.*;
import org.springframework.context.*;
import org.springframework.stereotype.*;
@Component
public class WireUtils implements ApplicationContextAware {
private static final String ERR_MSG = "Spring utility class not initialized";
private static ApplicationContext context;
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T wired(final Class<T> clazz) {
if (context == null) {
throw new IllegalStateException(ERR_MSG);
}
return context.getBean(clazz);
}
@SuppressWarnings("unchecked")
public static <T> T wired(final String name) {
if (context == null) {
throw new IllegalStateException(ERR_MSG);
}
return (T) context.getBean(name);
}
}
エンティティからユーティリティでリポジトリインスタンスを取得する
@Entity
@Table(name = "threads", uniqueConstraints = {@UniqueConstraint(columnNames = {"thread_title"})})
@Getter
@Accessors(fluent = true)
public class Thread extends AbstractEntity {
private final ThreadTitle title;
public Thread(@NonNull ThreadTitle title) {
this.title = title;
isSatisfied();
}
//エンティティ生成条件を確認
public void isSatisfied() {
//重複確認
Thread other = repo().findByTitle(title);
if (other != null) {
throw new RuntimeException("Entity is duplicate.");
}
}
//OneToMany先を取得
public PostCollection posts() {
return new PostCollection(wired(PostRepo.class).findBy(this));
}
//エンティティ保存
public Thread save() {
repo().saveAndFlush(this);
return this;
}
//エンティティからリポジトリインスタンスを取得
public ThreadRepo repo() {
return wired(ThreadRepo.class);
}
}
- エンティティ基底クラス
- 値オブジェクト基底クラス
- リポジトリ基底クラス
- DIインスタンス取得ユーティリティ
- サンプルアプリ
- https://github.com/asufana/spring-boot-ddd/tree/master/sample/spring-boot-ddd-sample