Spring Data JPAを使ってみた感想【使用例あります】

Spring Data JPAを使ってみた感想【使用例あります】

Spring Data JPAを実際に使いたい人「概要は理解しました。Spring Data JPAの実際の使い方を知りたいです。」


そんな方向けになります。

本記事ではSpring Data JPAを実際に使えるようになることを目的としています。


Spring Data JPAの概要について知らない方は以下の記事によくまとまっているのでまずは理解した上で本記事を読み進めてください。
» Spring Data JPAとは?【メリット・使い方わかります】

構成は以下です。

それでは、順に見ていきましょう。

著者情報

ちなみにですが、私は5年以上IT系エンジニアとして働いており、主にJavaを主戦場にしています。Webアプリケーションと業務系のアプリケーションの経験を持つごく普通のエンジニアです。

Spring Data JPAを使ってみた感想

実際にSpring Data JPAを使ってみた感想をまとめると以下になります。

  • アプリケーション作成開始からデータアクセス処理作成までが圧倒的に早くなったので驚きました。
  • 直感的に記述できるので、迷うことなくスムーズに処理を書けていた気がします。
  • データベースを扱う上で壁となるトランザクション処理や排他制御をほとんど意識しなくていいのでハードルが一気に下がった印象です。


正直こんなに変わるかと思うぐらいデータベースアクセスまでの道のりが楽になりました。

色々要因は考えられますが、

個人的にはクエリを作成しなくなったのが大きいかなと思います。

開発経験者ならわかるかと思いますが、意外と簡単なSQL(クエリ)でも頭を使います。

エンジニアは頭のリソースを効率的に使いたいと思っている人が多いと思いますが、簡単なクエリを書く必要がなくなるのは、頭を使わず、手を止めづに処理を書けるようになるので非常に大きなメリットかと思います。

結果、直感的書ける。うん、いいですね。


Spring Data JPAを使ってみた感想は以上になります。
それでは実際にSpring Data JPAを使い方を見ていきましょう。

Spring Data JPAの使用例

Spring Data JPAを使用する手順は以下になります。

  1. Spring Data JPAをプロジェクトの依存関係に追加
  2. DataSourceの定義(Spring Bootでは不要)
  3. EntityManagerFactoryの定義(Spring Bootでは不要)
  4. JpaTransactionManagertの定義(Spring Bootでは不要)
  5. Entityクラスの作成
  6. Repositoryクラスの作成
  7. Serviceクラスの作成


Spring Bootでは設定系の手順が不要となります。
これは開発の工数を減らすためにも大きなメリットです。
Spring Bootについて知りたい方は以下の記事によくまとまっていますので参考にしてみてください。
» Spring Bootとは?【Java フレークワーク】


今回はSpring Bootを使用したSpring Data JPAの使用方法について記載します。

それでは順見ていきましょう。

【手順①】Spring Data JPAをプロジェクトの依存関係に追加

Spring Bootを使用する場合はSpring Data JPA用のStarter(spring-boot-starter-data-jpa)が用意されているので、これを依存関係に追加します。

それだけで、Spring Data JPAが依存するプロジェクトが自動で追加されます。
細かいことを考えなくていいので便利です。

使用例は以下になります。
※今回はMavenプロジェクトで作成します。

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

【手順②】DataSourceの定義(Spring Bootでは不要)

Spring Bootを使用しない場合はDataSouceのBean定義をする必要があります。

DataSouceとは、データベースにアクセスするためのコネクション(java.sql.Connection)をアプリケーションに提供する役割を担います。様々な種類のDataSouceが存在し、Bean定義で使用するDataSouceを指定します。


今回はSpring Bootを使用する場合なので使用例は割愛させていただきます。

【手順③】EntityManagerFactoryの定義(Spring Bootでは不要)

Spring Bootを使用しない場合はEntityManagerFactoryのBean定義をする必要があります。

EntityManagerFactoryとは、EntityManagerを作成する際に必要となるもの


ここの話はSpring Data JPAよりもJPAの設定の話になります。
つまり、Bean定義でJPAの設定をするイメージです。
JPAについては、以下の記事でよくまとまっています。参考にしてみてください。
» Java標準ORMであるJPAとは【初心者向け】


今回はSpring Bootを使用する場合なので使用例は割愛させていただきます。

【手順④】JpaTransactionManagerの定義(Spring Bootでは不要)

Spring Bootを使用しない場合はJpaTransactionManagerのBean定義をする必要があります。

JpaTransactionManagerとは、SpringでJPAのトランザクションを管理する場合に使用するもの

ポイント

JpaTransactionManagerのBean定義をすると、@Transactionalアノテーションをメソッドに付加することでJPAのトランザクションを管理できるようになります。


今回はSpring Bootを使用する場合なので使用例は割愛させていただきます。

【手順⑤】Entityクラスの作成

使用例は以下になります。

package com.example.spring_react.springreactapp.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

/**
 * @Entity:Entityクラスであることを宣言する
 * @Table:name属性で連携するテーブル名を指定する
 */
@Data
@Entity
@Table(name = "user")
public class User {
    /**
     *  @Id:主キーに指定する。※複合キーの場合は@EmbeddedIdを使用
     *  @GeneratedValue:主キーの指定をJPAに委ねる
     *  @Column:name属性でマッピングするカラム名を指定する
     */

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;

    @Column(name = "name")
    private String name;
    
    @Column(name = "email")
    private String email;
    
    @Column(name = "password")
    private String password;
    
    @Column(name = "age")
    private int age;
    
    @Column(name = "gender")
    private int gender;
    
    @Column(name = "enable_flag")
    private Boolean enable;
    
    @Column(name = "role")
    private int role;
}

作成したテーブル情報

ちなみに、Spring Data JPAでEntityと紐付けるテーブル情報は以下になります。

CREATE TABLE user(
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(120) NOT NULL,
    email VARCHAR(120) NOT NULL,
    password VARCHAR(120) NOT NULL,
    age INT NOT NULL,
    gender INT NOT NULL,
    enable_flag TINYINT(1) NOT NULL DEFAULT 1,
    role INT,
    UNIQUE KEY (email)
);

【手順⑥】Repositoryクラスの作成

使用例は以下になります。

package com.example.spring_react.springreactapp.repository;

import com.example.spring_react.springreactapp.model.User;

import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Entityと主キーの型をジェネリクスに指定したJpaRepositoryを継承する
 * Entity:User、主キーの型:Integer
 * 
 * Spring Data JPAで提供されているAPIではできない処理を記述する
 * 
 * ※カスタムメソッドを実装したクラスのインターフェースを指定することで、カスタムメソッドを追加可能
 */
public interface UserRepository extends JpaRepository<User, Integer> {
    /**
     * メソッド名からクエリが自動的に生成される
     * 
     * 今回はEmailカラムで検索するクエリ
     */
    User findByEmail(String email);

    /**
     * JPQLでも指定可能
     * 
     * @Queryアノテーションを指定して、valueにJPQLを記述することでクエリを発行できる
     * ※今回は割愛
     */
}

【手順⑦】Serviceクラスの作成

使用例は以下になります。

package com.example.spring_react.springreactapp.service;

import java.util.List;

import com.example.spring_react.springreactapp.model.LoginUser;
import com.example.spring_react.springreactapp.model.User;
import com.example.spring_react.springreactapp.repository.UserRepository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Service:ビジネスロジックを提供するコンポーネントであることを宣言する。
 * ※データ永続の処理は@Repositoryを付与したコンポーネントで行う。
 */
@Service
public class UserService implements org.springframework.security.core.userdetails.UserDetailsService {

    //private final UserDao userDao;

    @Autowired
    PasswordEncoder passwordEncoder;

    /**
     * コンテナに登録されているRepositoryコンポーネントをインジェクションしてます
     */
    @Autowired
    UserRepository userRepository;

    @Transactional(readOnly = true)
    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        //LoginUser user = new LoginUser(userDao.findByEmail(email));
        // 個別に用意したメソッドを実行しています。
        User user = userRepository.findByEmail(email);
        LoginUser loginUser = new LoginUser(user);
        return loginUser;
    }

    /**
     * 全ユーザ取得
     */
    @Transactional(readOnly = true)
    public List<User> userList() {
        //return userDao.selectUsers();
        // Spring Data JPAで用意されているメソッド(API)を実行しています。
        return userRepository.findAll();
    }

    /**
     * ユーザ作成
     */
    @Transactional
    public void insertUser(String name, String email, String password, int age, int gender, boolean enable_flag, int role) {
        //userDao.insertUser(name, email, passwordEncoder.encode(password), age, gender, enable_flag, role);
        User user = new User();
        user.setName(name);
        user.setEmail(email);
        user.setPassword(passwordEncoder.encode(password));
        user.setAge(age);
        user.setGender(gender);
        user.setEnable(enable_flag);
        user.setRole(role);
        // Spring Data JPAで用意されているメソッド(API)を実行しています。
        userRepository.save(user);
    }
}

余談:Spring Data JPAを使わない場合

Spring Data JPAを使わない場合は、上記のソースコード内でコメントアウトしている個別で作成したUserDaoクラスのメソッド内でクエリを直接記述して実行していました。

下記が元々使っていたUserDaoクラスですが、見てわかるようにSQLを記述してます。

package com.example.spring_react.springreactapp.repository;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;

import com.example.spring_react.springreactapp.model.User;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

@Component("userDao")
public class UserDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public User findByEmail(String email){
        String sql = "select id, name, email, password, age, gender, enable_flag, role from user where email = ?";
        RowMapper<User> rowMapper = new UserRowMapper();
        return jdbcTemplate.queryForObject(sql, rowMapper, email);
    }

    public List<User> selectUsers() {
        String sql = "select id, name, email, password, age, gender, enable_flag, role from user";
        RowMapper<User> rowMapper = new UserRowMapper();
        return jdbcTemplate.query(sql, rowMapper);
    }

    public int insertUser(String name, String email, String password, int age, int gender, boolean enable_flag, int role) {
        String sql = "insert into user(name, email, password, age, gender, enable_flag, role) values (?,?,?,?,?,?,?)";
        return jdbcTemplate.update(sql, name, email, password, age, gender, enable_flag, role);
    }
}


UserRepositoryと比べてもらうと一目瞭然ですが、クエリ文を書かなくていいだけで、ものすごく楽だと感じました。。

例ではテーブル一つに対してだけですが、複数テーブルがあるとすると恐ろしいですよね笑

フレームワークの凄さを実感できるかと思います。



以上、Spring Data JPAを実際に使ってみました。

今回の記事が参考になれば幸いです。


人気記事①:
【厳選4冊+α】Spring Framework初心者におすすめな本

人気記事②:現役エンジニアがおすすめするプログラミングスクール5社:無料あり