以前、Oracleと連携するSpring BootのWEBアプリケーションを作成していたが、同じ機能をMongoDBと連携するように変更してみたので、そのサンプルプログラムを共有する。
なお、今回はMongoDB上のデータ参照/作成/更新/削除を簡単に行うことができる「MongoRepository」を利用している。
前提条件
MongoDBのインストールが完了し、下記サイト内のデータベース「test」の下にコレクション「user_data」が作成されていること
完成した画面イメージ
下記記事の「完成した画面イメージの共有」を参照のこと。
サンプルプログラムの作成
作成したサンプルプログラムの構成は以下の通り。
なお、上記の赤枠は、今回説明するプログラムを示している。
build.gradleの内容は以下の通りで、MongoDBを利用するための「spring-boot-starter-data-mongodb」を追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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の接続先となるホスト名・ポート番号・データベース名を指定している。
1 2 3 4 5 6 7 8 9 | server: port: 8084 # MongoDB接続情報 spring: data: mongodb: host: localhost port: 27017 database: test |
さらに、コレクション「user_data」のエンティティクラスの内容は以下の通りで、コレクション名を@Documentアノテーションで指定し、主キーを@Idアノテーションで指定している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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が最大のユーザーデータを取得するためのメソッドを追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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へのアクセスを行うサービスクラスのインタフェースの内容は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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メソッドを、それぞれ利用している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 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; } } |
また、フォームクラス、コントローラクラスの内容は以下の通り。コントローラクラスは、先ほどのサービスクラスを呼び出している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 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); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | 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の主キー)を渡すようにしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <!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クラスの内容は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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上のデータ参照/作成/更新/削除を簡単に行うことができる。