以前、Oracleと連携するSpring BootのWEBアプリケーションを作成していたが、同じ機能をMongoDBと連携するように変更してみたので、そのサンプルプログラムを共有する。
なお、今回はMongoDB上のデータ参照/作成/更新/削除を簡単に行うことができる「MongoRepository」を利用している。
前提条件
MongoDBのインストールが完了し、下記サイト内のデータベース「test」の下にコレクション「user_data」が作成されていること
完成した画面イメージ
下記記事の「完成した画面イメージの共有」を参照のこと。
サンプルプログラムの作成
作成したサンプルプログラムの構成は以下の通り。

なお、上記の赤枠は、今回説明するプログラムを示している。
build.gradleの内容は以下の通りで、MongoDBを利用するための「spring-boot-starter-data-mongodb」を追加している。
plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//lombokを利用するための設定
compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
//MongoDBを利用するための設定
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-mongodb', version: '2.2.6.RELEASE'
}
また、application.ymlの内容は以下の通りで、MongoDBの接続先となるホスト名・ポート番号・データベース名を指定している。
server:
port: 8084
# MongoDB接続情報
spring:
data:
mongodb:
host: localhost
port: 27017
database: testさらに、コレクション「user_data」のエンティティクラスの内容は以下の通りで、コレクション名を@Documentアノテーションで指定し、主キーを@Idアノテーションで指定している。
package com.example.demo;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
//コレクション名を@Documentアノテーションで指定
@Data
@Document(collection = "user_data")
public class UserData {
/** MongoDBの主キー(変数名は任意) */
@Id
private String pKeyId;
/** ID */
private long id;
/** 名前 */
private String name;
/** 生年月日_年 */
private int birth_year;
/** 生年月日_月 */
private int birth_month;
/** 生年月日_日 */
private int birth_day;
/** 性別 */
private int sex;
/** メモ */
private String memo;
}
また、MongoRepositoryを継承したインタフェースの内容は以下の通りで、これを利用してMongoDBへのアクセスを行えるようにしている。また、IDが最大のユーザーデータを取得するためのメソッドを追加している。
package com.example.demo;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserDataRepository extends MongoRepository<UserData, String> {
/**
* ユーザーデータから、IDが最大のデータを取得する
* @return IDが最大のユーザーデータ
*/
UserData findTopByOrderByIdDesc();
}
さらに、MongoDBへのアクセスを行うサービスクラスのインタフェースの内容は以下の通り。
package com.example.demo;
import java.util.List;
public interface DemoService {
/**
* ユーザーデータリストを全件取得
* @return ユーザーデータリスト
*/
List<DemoForm> demoFormList();
/**
* 引数のIDに対応するユーザーデータを取得
* @param pKeyID MongoDBの主キー
* @return ユーザーデータ
*/
DemoForm findByPKeyId(String pKeyID);
/**
* 引数のユーザーデータを削除
* @param demoForm 追加・更新用Formオブジェクト
*/
void delete(DemoForm demoForm);
/**
* 引数のユーザーデータがあれば更新し、無ければ追加
* @param demoForm 追加・更新用Formオブジェクト
*/
void createOrUpdate(DemoForm demoForm);
}
次に、サービスクラスのインタフェースの実装内容は以下の通り。UserDataRepositoryクラスを用いてMongoDBにアクセスしていて、全データ取得はfindAllメソッド、特定データの取得はfindByIdメソッド、データ作成/更新はsaveメソッド、データ削除はdeleteメソッドを、それぞれ利用している。
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.springframework.data.domain.Sort.Direction.ASC;
@Service
public class DemoServiceImpl implements DemoService{
/**
* ユーザーデータ(user_data)へアクセスするリポジトリ
*/
@Autowired
private UserDataRepository repository;
/**
* {@inheritDoc}
*/
@Override
public List<DemoForm> demoFormList(){
List<DemoForm> demoFormList = new ArrayList<>();
//ユーザーデータ(user_data)から全データを取得する
List<UserData> userDataList = repository.findAll(new Sort(ASC, "id"));
for (UserData userData : userDataList) {
demoFormList.add(getDemoForm(userData));
}
return demoFormList;
}
/**
* {@inheritDoc}
*/
@Override
public DemoForm findByPKeyId(String pKeyID) {
Optional<UserData> userData = repository.findById(pKeyID);
return getDemoForm(userData.get());
}
/**
* {@inheritDoc}
*/
@Override
@Transactional(readOnly = false)
public void delete(DemoForm demoForm){
UserData userData = getUserData(demoForm);
repository.delete(userData);
}
/**
* {@inheritDoc}
*/
@Override
@Transactional(readOnly = false)
public void createOrUpdate(DemoForm demoForm){
//更新・追加処理を行うエンティティを生成
UserData userData = getUserData(demoForm);
//追加・更新処理
if(demoForm.getId() == null){
UserData tmpUserData = repository.findTopByOrderByIdDesc();
userData.setId(tmpUserData == null ? 1 : tmpUserData.getId() + 1);
repository.save(userData);
}else{
repository.save(userData);
}
}
/**
* DemoFormオブジェクトに引数のユーザーデータの各値を設定する
* @param userData ユーザーデータ
* @return DemoFormオブジェクト
*/
private DemoForm getDemoForm(UserData userData){
if(userData == null){
return null;
}
DemoForm demoForm = new DemoForm();
demoForm.setPKeyId(userData.getPKeyId());
demoForm.setId(String.valueOf(userData.getId()));
demoForm.setName(userData.getName());
demoForm.setBirthYear(String.valueOf(userData.getBirth_year()));
demoForm.setBirthMonth(String.valueOf(userData.getBirth_month()));
demoForm.setBirthDay(String.valueOf(userData.getBirth_day()));
demoForm.setSex(String.valueOf(userData.getSex()));
demoForm.setMemo(userData.getMemo());
demoForm.setSex_value(userData.getSex() == 1 ? "男" : "女");
return demoForm;
}
/**
* UserDataオブジェクトに引数のフォームの各値を設定する
* @param demoForm DemoFormオブジェクト
* @return ユーザーデータ
*/
private UserData getUserData(DemoForm demoForm){
UserData userData = new UserData();
userData.setPKeyId(demoForm.getPKeyId());
if(!StringUtils.isEmpty(demoForm.getId())){
userData.setId(Long.valueOf(demoForm.getId()));
}
userData.setName(demoForm.getName());
userData.setBirth_year(Integer.valueOf(demoForm.getBirthYear()));
userData.setBirth_month(Integer.valueOf(demoForm.getBirthMonth()));
userData.setBirth_day(Integer.valueOf(demoForm.getBirthDay()));
userData.setSex(Integer.valueOf(demoForm.getSex()));
userData.setMemo(demoForm.getMemo());
return userData;
}
}
また、フォームクラス、コントローラクラスの内容は以下の通り。コントローラクラスは、先ほどのサービスクラスを呼び出している。
package com.example.demo;
import com.example.demo.check.CheckDate;
import com.example.demo.check.CheckZenkaku;
import com.example.demo.check.FutureDate;
import lombok.Data;
import org.thymeleaf.util.StringUtils;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
//日付チェック・未来日チェックを独自アノテーションで実施
@Data
@CheckDate(dtYear = "birthYear", dtMonth = "birthMonth", dtDay = "birthDay"
, message = "{validation.date-invalidate}")
@FutureDate(dtYear = "birthYear", dtMonth = "birthMonth", dtDay = "birthDay"
, message = "{validation.date-future}")
public class DemoForm implements Serializable {
/** MongoDBの主キー */
private String pKeyId;
/** ID */
private String id;
/** 名前 */
@NotEmpty
@CheckZenkaku
private String name;
/** 生年月日_年 */
private String birthYear;
/** 生年月日_月 */
private String birthMonth;
/** 生年月日_日 */
private String birthDay;
/** 性別 */
@NotEmpty
private String sex;
/** メモ */
private String memo;
/** 確認チェック */
@NotEmpty
private String checked;
/** 性別(文字列) */
private String sex_value;
/** 生年月日_月のMapオブジェクト */
public Map<String,String> getMonthItems(){
Map<String, String> monthMap = new LinkedHashMap<String, String>();
for(int i = 1; i <= 12; i++){
monthMap.put(String.valueOf(i), String.valueOf(i));
}
return monthMap;
}
/** 生年月日_日のMapオブジェクト */
public Map<String,String> getDayItems(){
Map<String, String> dayMap = new LinkedHashMap<String, String>();
for(int i = 1; i <= 31; i++){
dayMap.put(String.valueOf(i), String.valueOf(i));
}
return dayMap;
}
/** 性別のMapオブジェクト */
public Map<String,String> getSexItems(){
Map<String, String> sexMap = new LinkedHashMap<String, String>();
sexMap.put("1", "男");
sexMap.put("2", "女");
return sexMap;
}
/**
* 生年月日の年・月・日が入力されているかをチェックする
* @return チェック結果
*/
@AssertTrue(message = "{validation.date-empty}")
public boolean isBirthDayRequired(){
if(StringUtils.isEmpty(birthYear)
&& StringUtils.isEmpty(birthMonth)
&& StringUtils.isEmpty(birthDay)){
return false;
}
return true;
}
/**
* 性別が不正な値でないかチェックする
* @return チェック結果
*/
@AssertTrue(message = "{validation.sex-invalidate}")
public boolean isSexInvalid(){
return StringUtils.isEmpty(sex) || getSexItems().keySet().contains(sex);
}
}
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.support.SessionStatus;
import java.util.ArrayList;
import java.util.List;
@Controller
@SessionAttributes(types = {DemoForm.class})
public class DemoController {
/**
* Demoサービスクラスへのアクセス
*/
@Autowired
private DemoService demoService;
/**
* ユーザーデータテーブル(user_data)のデータを取得して返却する
* @return ユーザーデータリスト
*/
@ModelAttribute("demoFormList")
public List<DemoForm> userDataList(){
List<DemoForm> demoFormList = new ArrayList<>();
return demoFormList;
}
/**
* 追加・更新用Formオブジェクトを初期化して返却する
* @return 追加・更新用Formオブジェクト
*/
@ModelAttribute("demoForm")
public DemoForm createDemoForm(){
DemoForm demoForm = new DemoForm();
return demoForm;
}
/**
* 初期表示(一覧)画面に遷移する
* @return 一覧画面へのパス
*/
@RequestMapping("/")
public String index(Model model){
List<DemoForm> demoFormList = demoService.demoFormList();
//ユーザーデータリストを更新
model.addAttribute("demoFormList", demoFormList);
return "list";
}
/**
* 更新処理を行う画面に遷移する
* @param id 更新対象のID
* @param model Modelオブジェクト
* @return 入力・更新画面へのパス
*/
@GetMapping("/update")
public String update(@RequestParam("id") String id, Model model){
//更新対象のユーザーデータを取得
DemoForm demoForm = demoService.findByPKeyId(id);
//ユーザーデータを更新
model.addAttribute("demoForm", demoForm);
return "input";
}
/**
* 削除確認画面に遷移する
* @param id 更新対象のID
* @param model Modelオブジェクト
* @return 削除確認画面へのパス
*/
@GetMapping("/delete_confirm")
public String delete_confirm(@RequestParam("id") String id, Model model){
//削除対象のユーザーデータを取得
DemoForm demoForm = demoService.findByPKeyId(id);
//ユーザーデータを更新
model.addAttribute("demoForm", demoForm);
return "confirm_delete";
}
/**
* 削除処理を行う
* @param demoForm 追加・更新用Formオブジェクト
* @return 一覧画面の表示処理
*/
@PostMapping(value = "/delete", params = "next")
public String delete(DemoForm demoForm){
//指定したユーザーデータを削除
demoService.delete(demoForm);
//一覧画面に戻る
return "redirect:/to_index";
}
/**
* 削除完了後に一覧画面に戻る
* @param model Modelオブジェクト
* @return 一覧画面
*/
@GetMapping("/to_index")
public String toIndex(Model model){
//一覧画面に戻る
return index(model);
}
/**
* 削除確認画面から一覧画面に戻る
* @param model Modelオブジェクト
* @return 一覧画面
*/
@PostMapping(value = "/delete", params = "back")
public String confirmDeleteBack(Model model){
//一覧画面に戻る
return index(model);
}
/**
* 追加処理を行う画面に遷移する
* @param model Modelオブジェクト
* @return 入力・更新画面へのパス
*/
@PostMapping(value = "/add", params = "next")
public String add(Model model){
model.addAttribute("demoForm", new DemoForm());
return "input";
}
/**
* エラーチェックを行い、エラーが無ければ確認画面に遷移し、
* エラーがあれば入力画面のままとする
* @param demoForm 追加・更新用Formオブジェクト
* @param result バインド結果
* @return 確認画面または入力画面へのパス
*/
@PostMapping(value = "/confirm", params = "next")
public String confirm(@Validated DemoForm demoForm, BindingResult result){
//追加・更新用Formオブジェクトのチェック処理でエラーがある場合は、
//入力画面のままとする
if(result.hasErrors()){
return "input";
}
//エラーが無ければ確認画面に遷移する
return "confirm";
}
/**
* 一覧画面に戻る
* @param model Modelオブジェクト
* @return 一覧画面の表示処理
*/
@PostMapping(value = "/confirm", params = "back")
public String confirmBack(Model model){
//一覧画面に戻る
return index(model);
}
/**
* 完了画面に遷移する
* @param demoForm 追加・更新用Formオブジェクト
* @param result バインド結果
* @return 完了画面
*/
@PostMapping(value = "/send", params = "next")
public String send(@Validated DemoForm demoForm, BindingResult result){
//チェック処理を行い、エラーがなければ、更新・追加処理を行う
if(result.hasErrors()){
return "input";
}
demoService.createOrUpdate(demoForm);
return "redirect:/complete";
}
/**
* 完了画面に遷移する
* @param sessionStatus セッションステータス
* @return 完了画面
*/
@GetMapping("/complete")
public String complete(SessionStatus sessionStatus){
//セッションオブジェクトを破棄
sessionStatus.setComplete();
return "complete";
}
/**
* 入力画面に戻る
* @return 入力画面
*/
@PostMapping(value = "/send", params = "back")
public String sendBack(){
return "input";
}
}また、HTMLファイルの中で今回変更した「list.html」の内容は以下の通りで、updateメソッド・delete_confirmメソッドを呼び出す際の引数にpKeyId(MongoDBの主キー)を渡すようにしている。
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>index page</title>
</head>
<body>
ユーザーデータテーブル(user_data)の全データ<br/><br/>
<table border="1" cellpadding="5">
<tr>
<th>ID</th>
<th>名前</th>
<th>生年月日</th>
<th>性別</th>
<th></th>
<th></th>
</tr>
<tr th:each="obj : ${demoFormList}">
<td th:text="${obj.id}"></td>
<td th:text="${obj.name}"></td>
<td th:text="|${obj.birthYear}年 ${obj.birthMonth}月 ${obj.birthDay}日|"></td>
<td th:text="${obj.sex_value}"></td>
<td><a href="/update" th:href="@{/update(id=${'__${obj.pKeyId}__'})}">更新</a></td>
<td><a href="/delete_confirm" th:href="@{/delete_confirm(id=${'__${obj.pKeyId}__'})}">削除</a></td>
</tr>
</table>
<br/><br/>
<form method="post" th:action="@{/add}">
<input type="submit" name="next" value="データ追加" /><br/><br/>
<input type="button" value="閉じる" onclick="window.close();" />
</form>
</body>
</html>さらに、DemoApplicationクラスの内容は以下の通り。
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class DemoApplication implements WebMvcConfigurer {
@Autowired
private MessageSource messageSource;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public LocalValidatorFactoryBean validator() {
//Spring Bootデフォルトのエラーメッセージのプロパティファイルを
//ValidationMessages.propertiesからmessages.propertiesに変更する
LocalValidatorFactoryBean localValidatorFactoryBean
= new LocalValidatorFactoryBean();
localValidatorFactoryBean.setValidationMessageSource(messageSource);
return localValidatorFactoryBean;
}
@Override
public org.springframework.validation.Validator getValidator() {
return validator();
}
}
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-mongo-repository/demo
要点まとめ
- Spring Boot上でMongoDBを操作するには、MongoRepositoryを利用すると、MongoDB上のデータ参照/作成/更新/削除を簡単に行うことができる。





