リポジトリーの作成 - @Repository

リポジトリーには、@Repository を使用します。JDBC により、データベースへ接続し、POST されたデータを保存するため、コンフィグレーション、およびリポジトリーを、それぞれ以下を参考に作成します。

// src/main/java/io/github/yo1000/sss/config/JdbcConfiguration.java
package io.github.yo1000.sss.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

@Configuration
public class JdbcConfiguration {
    @Bean
    @Autowired
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}
// src/main/java/io/github/yo1000/sss/repository/MemoRepository.java
package io.github.yo1000.sss.repository;

import io.github.yo1000.sss.model.Memo;

import java.util.List;

public interface MemoRepository {
    List<Memo> find();
    List<Memo> findByAuthor(String author);
    void save(Memo item);
}
// src/main/java/io/github/yo1000/sss/repository/jdbc/JdbcMemoRepository.java
package io.github.yo1000.sss.repository.jdbc;

import io.github.yo1000.sss.model.Memo;
import io.github.yo1000.sss.repository.MemoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class JdbcMemoRepository implements MemoRepository {
    private JdbcTemplate jdbcTemplate;

    @Autowired
    public JdbcMemoRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void save(Memo item) {
        getJdbcTemplate().update(
                "INSERT INTO MEMO (MEMO, AUTHOR, CREATED) VALUES (?, ?, CURRENT_TIMESTAMP)",
                item.getMemo(), item.getAuthor());
    }

    @Override
    public List<Memo> find() {
        return getJdbcTemplate().query(
                "SELECT MEMO, AUTHOR FROM MEMO ORDER BY CREATED ASC", (resultSet, i) -> {
                    Memo item = new Memo();
                    item.setMemo(resultSet.getString("MEMO"));
                    item.setAuthor(resultSet.getString("AUTHOR"));
                    return item;
                });
    }

    @Override
    public List<Memo> findByAuthor(String author) {
        return getJdbcTemplate().query(
                "SELECT MEMO, AUTHOR FROM MEMO WHERE AUTHOR = ? ORDER BY CREATED ASC", (resultSet, i) -> {
                    Memo item = new Memo();
                    item.setMemo(resultSet.getString("MEMO"));
                    item.setAuthor(resultSet.getString("AUTHOR"));
                    return item;
                }, author);
    }

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }
}

リポジトリーを作成する場合には、必ずインターフェースを定義したうえで実装します。インターフェースを定義し、これに対してサービス側で依存性を注入することで、特定の実装への依存を回避することができるようになります。

実装への依存を回避できるようになると、実装方法に変更が生じた場合の対応に柔軟さが出てくるようになります。例えば、今回のように JDBC を直接使用した永続化による実装から、JPA を使用した永続化に実装を変更したい場合、注入する具象クラスを切り替えるだけで、アプリケーションの変更は完了します。リポジトリーを参照しているサービスには、変更による影響が生じないことになります。

これを踏まえ、インターフェースに対して、依存性を注入するように、サービス、コントローラーも変更します。

// src/main/java/io/github/yo1000/sss/service/MemoService.java
package io.github.yo1000.sss.service;

import io.github.yo1000.sss.model.Memo;
import io.github.yo1000.sss.repository.MemoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
public class MemoService {
    private MemoRepository memoRepository;

    @Autowired
    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }

    public Memo join(String memo, String author) {
        Memo item = new Memo();
        item.setMemo(memo);
        item.setAuthor(author);
        item.setCreated(new Date());

        return item;
    }

    public List<Memo> readAll() {
        return getMemoRepository().find();
    }

    public List<Memo> readByAuthor(String author) {
        return getMemoRepository().findByAuthor(author);
    }

    public void write(String memo, String author) {
        Memo item = new Memo();
        item.setMemo(memo);
        item.setAuthor(author);
        getMemoRepository().save(item);
    }

    public MemoRepository getMemoRepository() {
        return memoRepository;
    }
}
// src/main/java/io/github/yo1000/sss/controller/page/MemoController.java
package io.github.yo1000.sss.controller.page;

import io.github.yo1000.sss.model.Memo;
import io.github.yo1000.sss.service.MemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("memo")
public class MemoController {
    private MemoService memoService;

    @Autowired
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    @RequestMapping("")
    public String get(Model model) {
        model.addAttribute("items", getMemoService().readAll());
        return "memo";
    }

    @RequestMapping("{author}")
    public String get(@PathVariable String author,
                      Model model) {
        model.addAttribute("items", getMemoService().readByAuthor(author));
        return "memo";
    }

    @RequestMapping("param/{memo:[a-zA-Z0-9]+}")
    public String getParams(@PathVariable String memo,
                            @RequestParam(required = false, defaultValue = "Default Author") String author,
                            Model model) {
        List<Memo> items = new ArrayList<>();
        items.add(getMemoService().join(memo, author));

        model.addAttribute("items", items);
        return "memo";
    }

    @RequestMapping(value = "", method = RequestMethod.POST)
    public String post(@ModelAttribute Memo item,
                       Model model) {
        getMemoService().write(item.getMemo(), item.getAuthor());
        return "redirect:/memo";
    }

    public MemoService getMemoService() {
        return memoService;
    }
}

アプリケーションを再起動して、改めて、http://localhost:8080/memo/金次郎 へアクセスします。

Flyway によるセットアップで挿入されたレコードが、表示されることを確認できました。今度は入力欄にそれぞれ値を入力して、Submit ボタンをクリックしてみます。

入力値がレコードとして保存されたことが確認できました。

ここまでで、画面の表示から、値の受け渡しまでの、基本的な機能については一通り触れることができました。簡単なアプリケーションであれば、ここまでの内容で作成できるでしょう。

アノテーション

今回、新たに登場したアノテーションを簡単に説明しておきます。

@Configuration

コンフィグレーションクラスに設定します。このアノテーションが設定されると、クラスが DI コンテナへの登録対象としてマークされます。

@Bean

ステレオタイプアノテーションのついていないクラスを、明示的に DI コンテナに登録するためのアノテーションです。これは XML-based configuration での、<bean></bean> 要素に相当するアノテーションです。

このアノテーションが設定されたメソッドに返却されたインスタンスは、そのメソッド名をキーに、DI コンテナへ登録されます。これを設定可能なメソッドは、引数を受け取らないか、DI コンテナにより引数を注入可能である必要があります。

@Repository

リポジトリークラスに設定します。このアノテーションが設定されると、クラスが DI コンテナへの登録対象としてマークされます。

サンプル

ここまでのコードサンプルは、以下より入手できます。

$ git clone -b chapter/6 https://github.com/yo1000/self-study-spring.git

results matching ""

    No results matching ""