RepositoryクラスをDIで使う方法を初心者向けに徹底解説!Springでの活用ポイント
新人
「Springでデータベースにアクセスするクラスを作るとき、Repositoryクラスってよく聞きますけど何ですか?」
先輩
「Repositoryクラスは、データベースとやり取りするためのクラスだよ。Springでは、データの取得や保存を担当する重要な役割を持っているんだ。」
新人
「なるほど。でも、そのRepositoryクラスをDIっていう方法で使うってどういう意味ですか?」
先輩
「DIは依存性注入のこと。Springの便利な機能で、Repositoryクラスを簡単に使えるようにしてくれる仕組みだよ。今から詳しく説明するね。」
1. Repositoryクラスとは?
SpringにおけるRepositoryクラスとは、アプリケーションとデータベースの間でデータのやり取りを行う専用クラスです。主にデータの読み込み、保存、削除、更新といった操作を担当します。@Repositoryアノテーションを付けることで、Springがこのクラスをデータアクセス用のコンポーネントとして認識し、エラーハンドリングなども自動で行ってくれます。
例えば、ユーザー情報をデータベースから取得する場合、このRepositoryクラスにSQLやJPAの処理をまとめておくことで、ControllerやServiceクラスが簡潔になります。これにより、コードの可読性や保守性が向上します。
2. DI(依存性注入)とは?
DI(Dependency Injection)とは、クラスが必要とする依存オブジェクトを外部から注入する仕組みです。Springでは、このDIを使うことで、Repositoryクラスを自動的にControllerやServiceに組み込むことができます。
通常、Javaのコードで他のクラスを使う場合はnewキーワードでインスタンスを生成しますが、SpringのDIを使えばインスタンス生成を自分で行う必要はありません。Springがアプリケーション起動時にインスタンスを作り、必要な場所に渡してくれます。
これにより、クラス同士の結合度が下がり、テストのしやすさやコードの再利用性が向上します。特にRepositoryクラスはデータベースアクセスに依存するため、DIを使うことでテスト時にモック化もしやすくなります。
3. SpringにおけるRepositoryクラスの役割
Springでは、Repositoryクラスはデータ層(Persistence Layer)を担当します。主な役割は次の通りです。
- データベースからのデータ取得
- データの新規登録や更新
- 特定条件でのデータ検索
- データ削除
Spring Data JPAを使えば、インターフェースを定義するだけで基本的なCRUD処理を自動で実装できます。以下は簡単な例です。
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
このようにJpaRepositoryを継承し、@Repositoryを付与することで、Springが自動的に実装を生成します。そしてDIを使えばControllerやServiceで簡単に呼び出せるようになります。
DIを使ったControllerからのRepository呼び出し例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/user")
public String getUser() {
User user = userRepository.findByUsername("taro");
System.out.println(user.getName());
return "userView";
}
}
この例では、@Autowiredを使ってUserRepositoryをControllerに注入しています。これにより、newを使わずにRepositoryのメソッドを呼び出すことができます。
4. RepositoryクラスをDIで使うための設定
SpringでRepositoryクラスをDI(依存性注入)で使うためには、まずクラスやインターフェースに@Repositoryアノテーションを付けて、Springの管理対象にする必要があります。そして、ControllerやServiceでこのRepositoryを利用する際には、@Autowiredまたはコンストラクタ注入を使ってインスタンスを受け取ります。
フィールドに直接@Autowiredを付ける方法は簡単ですが、最近のSpringではテストのしやすさや不変性の観点からコンストラクタ注入が推奨されています。コンストラクタ注入では、依存するオブジェクトをコンストラクタの引数として受け取り、Springが自動的にインスタンスを渡してくれます。
@Controller
public class UserController {
private final UserRepository userRepository;
// コンストラクタ注入
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/user")
public String getUser() {
User user = userRepository.findByUsername("taro");
System.out.println(user.getName());
return "userView";
}
}
このように、コンストラクタにUserRepositoryを指定するだけで、Springが自動的に該当するBeanを注入してくれます。これがDIの便利さであり、特に大規模開発や単体テストを行う際に強力な助けとなります。
5. 実際にControllerからRepositoryを呼び出す方法
Repositoryクラスは単独では動作しません。実際のアプリケーションではControllerから呼び出して使うことが一般的です。Controllerはユーザーのリクエストを受け取り、必要に応じてRepositoryを経由してデータを取得・保存します。
例えば、ユーザー名で検索してプロフィール画面を表示する機能を作る場合、以下のように実装できます。
@Controller
public class ProfileController {
private final UserRepository userRepository;
public ProfileController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/profile")
public String showProfile(Model model) {
User user = userRepository.findByUsername("hanako");
model.addAttribute("user", user);
return "profileView";
}
}
このコードでは、userRepositoryをコンストラクタで受け取り、findByUsernameメソッドを使ってユーザー情報を取得しています。そして、その結果をModelに格納してビューに渡します。
こうすることで、Controllerのコードは非常にシンプルになり、データアクセスのロジックはRepositoryクラスに集約されます。これがMVCアーキテクチャにおける役割分担のメリットです。
6. Gradle環境での依存関係の追加(Pleiadesでの設定方法)
Pleiades+Gradle環境でSpringのRepositoryクラスやDIを使うには、まず必要な依存関係をbuild.gradleに追加する必要があります。特に、Spring BootとSpring Data JPAを使う場合は以下の依存関係が必要です。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'com.h2database:h2'
}
PleiadesでGradleプロジェクトを作成した場合、依存関係は「build.gradle」に直接記述できます。また、Pleiadesのプロジェクトエクスプローラから依存関係を追加して保存すると、自動的にGradleの同期が行われます。
初心者の場合は、まず組み込みデータベースのH2を使って練習するのがおすすめです。H2は設定が簡単で、アプリケーションを起動するだけでメモリ上にデータベースが作られます。これにより、RepositoryクラスとDIの仕組みをすぐに試せます。
また、PleiadesではGradleタブから「Refresh」ボタンを押すことで依存関係の再読み込みができます。依存関係を追加したのに反映されない場合は、この操作を行ってください。
DIとRepositoryクラスを組み合わせたサンプル全体像
@Controller
public class DemoController {
private final UserRepository userRepository;
public DemoController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/demo")
public String demo(Model model) {
User user = userRepository.findByUsername("satoshi");
model.addAttribute("user", user);
return "demoView";
}
}
このサンプルでは、Gradleで依存関係を追加した状態でUserRepositoryを作成し、Controllerからコンストラクタ注入で呼び出しています。これにより、SpringのDI機能をフル活用しつつ、Repositoryクラスの役割を最大限活かせる構造になっています。
7. RepositoryクラスのDIでよくあるエラーとその対処法
SpringでRepositoryクラスをDIする際、初心者が遭遇しやすいエラーはいくつかあります。特に、依存性が見つからない場合やBean定義の不備によるエラーが多く見られます。
よくあるエラーの一つは、No qualifying bean of typeというエラーメッセージです。これは、Springが対象のRepositoryクラスをBeanとして認識できていないことを意味します。原因としては@Repositoryアノテーションの付け忘れや、パッケージスキャンの範囲外にRepositoryクラスが配置されている場合が考えられます。
また、コンストラクタ注入を使っている場合、引数が一致しない、または型の不一致があるとアプリケーションが起動時に失敗します。これを防ぐには、Repositoryのインターフェース名と型を正しく一致させることが重要です。
例えば、次のようなケースではエラーになります。
@Controller
public class ErrorExampleController {
private final UserRepository repository; // 正しい型名にする必要あり
public ErrorExampleController(ProductRepository repository) { // 型が違う
this.repository = repository;
}
}
この場合、型の不一致が原因です。正しい型を指定すれば解決します。エラーが発生したら、アノテーションの有無、型の一致、パッケージ構成を見直しましょう。
8. 複数のRepositoryクラスをDIする場合のポイント
アプリケーションが成長すると、複数のテーブルやデータソースを扱うために複数のRepositoryクラスを作成することが一般的です。この場合、DIを適切に行うためにはいくつかのポイントがあります。
まず、複数のRepositoryを1つのControllerやServiceで使う場合は、それぞれをコンストラクタの引数として受け取るようにします。これにより、Springは必要なRepositoryインスタンスを自動的に注入します。
@Controller
public class MultiRepoController {
private final UserRepository userRepository;
private final ProductRepository productRepository;
public MultiRepoController(UserRepository userRepository, ProductRepository productRepository) {
this.userRepository = userRepository;
this.productRepository = productRepository;
}
@GetMapping("/multi")
public String handleRequest(Model model) {
model.addAttribute("users", userRepository.findAll());
model.addAttribute("products", productRepository.findAll());
return "multiView";
}
}
もし複数のRepositoryが同じインターフェース型を持っている場合、SpringはどのBeanを注入するか判断できず、NoUniqueBeanDefinitionExceptionが発生します。その場合は@Qualifierアノテーションを使って明示的に指定する必要があります。
@Autowired
@Qualifier("specialUserRepository")
private UserRepository userRepository;
このように指定すれば、同じ型のRepositoryが複数あっても適切なものを選んで注入できます。
9. RepositoryクラスのDIを使ったアプリケーションの具体例
最後に、RepositoryクラスとDIを組み合わせた具体的なアプリケーション例を紹介します。ここでは、ユーザーと商品データを同時に扱う画面を表示する機能を作ります。開発環境はPleiades+Gradleで、@Controllerを使用します。
@Controller
public class DashboardController {
private final UserRepository userRepository;
private final ProductRepository productRepository;
public DashboardController(UserRepository userRepository, ProductRepository productRepository) {
this.userRepository = userRepository;
this.productRepository = productRepository;
}
@GetMapping("/dashboard")
public String showDashboard(Model model) {
List<User> users = userRepository.findAll();
List<Product> products = productRepository.findAll();
model.addAttribute("users", users);
model.addAttribute("products", products);
return "dashboardView";
}
}
この例では、複数のRepositoryを同時に注入し、それぞれから取得したデータをビューに渡しています。これにより、ユーザーと商品の一覧を1つの画面にまとめて表示することが可能になります。
また、この構造によりControllerはリクエスト処理のみに集中でき、データアクセスの詳細はRepositoryクラスに任せることができます。結果として、コードの見通しが良くなり、保守性も大幅に向上します。
初心者のうちは、まず1つのRepositoryクラスから始めて慣れたら複数のRepositoryを使う設計に挑戦すると、理解が深まりやすいです。