テーブル形式で受け取ったJavaオブジェクトを、関係性のあるモデルに格納する

#この記事について

Javaでデータベースからテーブル形式のオブジェクトを受け取り、
そのオブジェクトを構造化されたオブジェクトに格納するときにドハマリしました...

備忘録も兼ねて解決方法のメモ。

##はじめに

| 出版社名(Publisher)       |ジャンル(Genre) |    書籍(Book)    |
|:-----------------:|:------------------|:------------------:|
| A社          |プログラミング|基礎まとめ|
| A社          |プログラミング|サルでもわかる|
| A社          |将棋|基礎まとめ|
| A社          |将棋|サルでもわかる|
| B社          |プログラミング|基礎まとめ|
| B社          |将棋|サルでもわかる|
| A社          |プログラミング|順番違い|

こーいうテーブルデータをBDから取得してきて

```
|
|-- A社 --------- プログラミング --------- 基礎まとめ
|   　       |                    |
|   　       |                    |---- サルでもわかる
|    　      |                    |
|   　       |                    |---- 順番違い
|   　       |    
|   　       |        
|   　       |----　   将棋   ------------ 基礎まとめ   
|                                |
|   　        　                    |---- サルでもわかる
|   　
|   　
|-- B社 --------- プログラミング --------- 基礎まとめ          
|    　      |                    
|   　       |----　   将棋   　　　　　--------- 基礎まとめ   

```

こんな感じにJavaオブジェクトにマッピングしたい。

出版社を選択すると、対応するジャンルがでてきて、
ジャンルを選択すると、対応する本情報が出てくる...といった感じ

最終的に出版社のリストがほしい。


##Javaオブジェクト

###テーブル情報を取得するJavaオブジェクト

```java:BookResultMap
package dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class BookResultMap {
	private String publisherName;
	private String genreName;
	private String bookTitle;

	public BookResultMap() {}

	public BookResultMap(String publisherName, String genreName, String bookTitle) {
		this.publisherName = publisherName;
		this.genreName = genreName;
		this.bookTitle = bookTitle;
	}
}
```


###最終的にデータを格納するJavaオブジェクト

Book     : 本の情報を表すクラス。
Genre    : ジャンルを表すクラス。 List<Book> を要素に持つ。
Publisher: 出版社情報を表すクラス。　List<Genre> を要素に持つ。

```java:Book.java
package model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Book {
	private String bookTitle;
}

```


```java:Genre.java
package model;

import java.util.List;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Genre {
	private String genreName;
	private List<Book> bookList;
}
```

```java:Publisher.java
package model;

import java.util.List;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class Publisher {
	private String publisherName;
	private List<Genre> genreList;
}
```

#実装

```java:DataConvert.java
package logic;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import dto.BookResultMap;
import model.Book;
import model.Genre;
import model.Publisher;

public class DataConvert {
    public static List<Publisher> convertObject(List<BookResultMap> bookResultMaps) {

		/*それぞれの関係性を保存するマップ。
		 * publishNameをキーとしたマップのvalueを、
		 * genreNameをキーとしたマップに設定する。
		*/
		Map<String, Map<String, List<Book>>> convertMap = new HashMap<>();
		for(int i = 0; i < bookResultMaps.size(); i++) {
			String publisherName = bookResultMaps.get(i).getPublisherName();
			String genreName = bookResultMaps.get(i).getGenreName();
			Book book = new Book();
			book.setBookTitle(bookResultMaps.get(i).getBookTitle());

			/*1.もしpublisherNameがconvertMapにキーとして存在していなかったら、
			 * convertMapにpublishNameをキーとしたHashMapを作成する。
			*/
			if(!convertMap.containsKey(publisherName)) {
				convertMap.put(publisherName, new HashMap<>());
			}
			
			/*2.もしgenreNameがconvertMap.get(publisherName)にキーとして存在していなかったら、
			 * convertMap.get(publisherName)にpublishNameをキーとしたHashMapを作成する。
			*/
			if(!convertMap.get(publisherName).containsKey(genreName)) {
				convertMap.get(publisherName).put(genreName, new ArrayList<>());
			}
			/*1, 2の作業をしたことにより
			 * publisherNameに関連したGenre内のArrayListを取得することができるため
			 * そこにBookを格納する。
			 * */
			convertMap.get(publisherName).get(genreName).add(book);
		}

		//convertMapに保存された情報をJavaオブジェクトに置き換える。
		List<Publisher> publisherList = new ArrayList<>();
		for(String publisherName: convertMap.keySet()) {
			Publisher publisher = new Publisher();
			List<Genre> genreList = new ArrayList<>();
			publisher.setPublisherName(publisherName);
			publisher.setGenreList(genreList);
			publisherList.add(publisher);

			for(String genreName: convertMap.get(publisherName).keySet()) {
				Genre genre = new Genre();
				genre.setGenreName(genreName);
				genre.setBookList(convertMap.get(publisherName).get(genreName));
				genreList.add(genre);
			}
		}

		return publisherList;
	}
}


```

##確認

###入力
```java:DataConvert.java
public class DataConvert {

	public static void main(String[] args) {

		//データベースからテーブルの形そのまま受け取ったオブジェクト
		List<BookResultMap> bookResultMaps = new ArrayList<>();
		BookResultMap brm1_1_1 = new BookResultMap("A社", "プログラミング", "基礎まとめ");
		BookResultMap brm1_1_2 = new BookResultMap("A社", "プログラミング", "サルでもわかる");
		BookResultMap brm1_2_1 = new BookResultMap("A社", "将棋", "基礎まとめ");
		BookResultMap brm1_2_2 = new BookResultMap("A社", "将棋", "サルでもわかる");
		BookResultMap brm2_1_1 = new BookResultMap("B社", "プログラミング", "基礎まとめ");
		BookResultMap brm2_2_1 = new BookResultMap("B社", "将棋", "基礎まとめ");
		BookResultMap brm1_1_3 = new BookResultMap("A社", "プログラミング", "順番違い");
		bookResultMaps.add(brm1_1_1);
		bookResultMaps.add(brm1_1_2);
		bookResultMaps.add(brm1_2_1);
		bookResultMaps.add(brm1_2_2);
		bookResultMaps.add(brm2_1_1);
		bookResultMaps.add(brm2_2_1);
		bookResultMaps.add(brm1_1_3);

		//データ変換
		List<Publisher> publisherList = convertObject(bookResultMaps);

		//確認
		System.out.println("publisherListのサイズ:" + publisherList.size());
		for(Publisher publisher: publisherList) {
			System.out.println("-------------------"+ publisher.getPublisherName() +"-----------------------");
			System.out.println("genreListのサイズ:" + publisher.getGenreList().size());
			for(Genre genre: publisher.getGenreList()) {
				System.out.println("-------"+ genre.getGenreName() +"--------");
				System.out.println("bookListのサイズ:"+  + genre.getBookList().size());
				for(Book book: genre.getBookList()) {
					System.out.println("----");
					System.out.println(publisher.getPublisherName());
					System.out.println(genre.getGenreName());
					System.out.println(book.getBookTitle());
				}
			}
		}
	}
}
```

###出力結果
```
publisherListのサイズ:2
-------------------B社-----------------------
genreListのサイズ:2
-------プログラミング--------
bookListのサイズ:1
----
B社
プログラミング
基礎まとめ
-------将棋--------
bookListのサイズ:1
----
B社
将棋
基礎まとめ
-------------------A社-----------------------
genreListのサイズ:2
-------プログラミング--------
bookListのサイズ:3
----
A社
プログラミング
基礎まとめ
----
A社
プログラミング
サルでもわかる
----
A社
プログラミング
順番違い
-------将棋--------
bookListのサイズ:2
----
A社
将棋
基礎まとめ
----
A社
将棋
サルでもわかる
```

出版社とジャンルに本情報を関連付けることができた。

##終わりに
他にも方法があると思いますが、とりあえず自分の解決方法を書きました。
テーブルの順番を維持したい場合、HashMapをLinkedHashMapにすればOKです
よりよい方法や、改善点等がありましたらお教えいただけると嬉しいです。

てかSQLでソートするなり構造変えるなりで、
もっと処理しやすい形でテーブルデータを取得してほしいですね、、、、、