MyBatis + Spring boot

# Mybatis

元々O/RマッパーはJDBC使っていたが
研修が終えてMybatisを使い始めたため、備忘録として記載してます。
初投稿ですので見辛いところも有りますがそこはお許しください。

#Gradleの設定
```build.graidle
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'mysql:mysql-connector-java'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
```

4行目にMyBatisを宣言


#aplication.properties
```aplication.properties
spring.datasource.url=jdbc:mysql://localhost:3306/student
spring.datasource.username=root
spring.datasource.password=root8080
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.configuration.map-underscore-to-camel-case=true
```

１〜４行目はMySQLの設定

mybatis.configuration.map-underscore-to-camel-case=true　はSQLのカラムをキャメルケースに直してくれる便利なライブラリです。

#Mapper
```UserMapper.java
package com.example.repository;

import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.example.domain.User;


@Mapper
public interface UserMapper {
	
    public void insert(User user);

    public List<User> findByUsers();

    public User findByOne(Integer id);
	
	public void updateUser(User user);
	
	public void deleteUser(Integer id);

}
```

RepositoryではなくMapperで作成
またインターフェイスで作成します。

:::note warn
必須
@Mapperは必ず記載
:::
これがないとMyBatisが動いてくれません。

###INSERT
```UserMapper.java
public void insert(User user);
```

###SELECT全件
```UserMapper.java
public List<User> findByUsers();
```

###SELECT１件
```UserMapper.java
public User findByOne(Integer id);
```

###UPDATE
```UserMapper.java
public void updateUser(User user);
```

###DELETE
```UserMapper.java
public void deleteUser(Integer id);
```

#domain
```User.java
package com.example.domain;

import lombok.Data;

@Data
public class User {

	private Integer id;
	private String name;
	private Integer depId;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getDepId() {
		return depId;
	}
	public void setDepId(Integer depId) {
		this.depId = depId;
	}
	
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", depId=" + depId  "]";
	}
}
```
lombokの@Dataがあればgetterとsetterを書く必要はなし
でも今回は練習用として記載してます。

ControllerとServiceは省きます。
#.xml
```UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.repository.UserMapper">

	<resultMap type="com.example.domain.User" id="userMap">
		<id column="u_id" property="id"/>
		<result column="u_name" property="name"/>
		<result column="u_dep_id" property="depId"/>
		<association property="department" resultMap="departmentMap"></association>
	</resultMap>
	
	<resultMap type="com.example.domain.Department" id="departmentMap">
		<result column="dep_id" property="id"></result>
		<result column="dep_name" property="name"></result>
	</resultMap>
	
	<insert id="insert">
		insert into users(name, dep_id) values(#{name},#{depId})
	</insert>
	
	<select id="findByUsers" resultType="com.example.domain.User">
		select * from users;
	</select>
	
	<update id="updateUser">
		update users set name = #{name}, dep_id = #{depId} where id = #{id}
	</update>
	
	<delete id="deleteUser">
		delete from users where id = #{id};
	</delete>

</mapper>
```
MyBatisの場合SQL文はxmlファイルに書くことができます。
この場合xmlファイルを格納する先はsrc/main/resources/内にUserMapper.javaと同じパス先を用意する必要が有ります。

::: note info
####src/main/java/com/example/repository/UserMapper.java
####src/main/resources/com/example/repository/UserMapper.xml
↑のようにパス先を揃えるとMyBatis側が自動でresources内で探してくれます
:::

また別のパス先を指定したい場合はproperties（又はyml）内に
####mybatis.mapper-locations=classpath*:/パス指定先/*.xml
を指定してあげれば動きます。

##INSERT
```UserMapper.xml
<insert id="insert">
		insert into users(name, dep_id) values(#{name},#{depId})
</insert>

#UserMapper.java
        public void insert(User user);
```
<insert>タグ内のidはUserMapper.java内のinsertメゾットと同じにする必要があります
また渡ってきたパラメーターは#{プロパティ名}と書く必要があります

#SELECT, UPDATE, DELETE
```UserMapper.xml
<select id="findByUsers" resultType="com.example.domain.User">
		select * from users;
</select>
<update id="updateUser">
		update users set name = #{name}, dep_id = #{depId} where id = #{id}
</update>
	
<delete id="deleteUser">
		delete from users where id = #{id};
</delete>
```
UPDATE, DELETEも同様です。
#####SELECTのみresultTypeを書き加え、中身には参照するdomain先のパスを指定します。


#１対１のテーブル結合
次は１対１のテーブル結合についてです
今回はusersとdepartmentsを結合させます

#domain
``` User.java
package com.example.domain;

import lombok.Data;

@Data
public class User {

	private Integer id;
	private String name;
	private Integer depId;
	private Department department; //departmentオブジェクトを保つために必要
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getDepId() {
		return depId;
	}
	public void setDepId(Integer depId) {
		this.depId = depId;
	}
	public Department getDepartment() {
		return department;
	}
	public void setDepartments(Department department) {
		this.department = department;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", depId=" + depId + ", department=" + department + "]";
	}
}
```
```Department.java
package com.example.domain;

import lombok.Data;

@Data
public class Department {

	private Integer id;
	private String name;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Department [id=" + id + ", name=" + name + "]";
	}	
}
```
#.xml
```UserMapper.xml
    <resultMap type="com.example.domain.User" id="userMap">
		<id column="u_id" property="id"/>
		<result column="u_name" property="name"/>
		<result column="u_dep_id" property="depId"/>
		<association property="department" resultMap="departmentMap"></association>
	</resultMap>
	
	<resultMap type="com.example.domain.Department" id="departmentMap">
		<result column="dep_id" property="id"></result>
		<result column="dep_name" property="name"></result>
	</resultMap>

    <select id="findByOne" resultMap="userMap">
		select 
            users.id as u_id, 
            users.name as u_name, 
            users.dep_id as u_dep_id, 
            departments.id as dep_id, 
            departments.name as dep_name 
        from users 
		left join departments ON users.dep_id = departments.id 
        WHERE users.id = #{id}
	</select>
```

###resultMap
resultMapをUserとDepartmentそれぞれ作成し結合を行います。
typeには参照するdomainのパスを指定、idは<select>タグが認識するために記載します。

columnにはselect結果のカラム名を入れ、propertyにはフィールド名を入れる
###idとresultの違い。
idタグは設定された値の数だけListに返してくれます。必須とのことなので主にPKをセットするのかと思われます。。。（知識が浅くてすみません）
他のフィールドはresultでセット。

###association
associationはそれぞれ作ったresultMapを繋げるために必要です。
propertyにはUser.java内のフィールドのdepartment（変数名）を書き、resultMapにdepartment用のresultMapのid名を書いてあげる。
ここだとdepartmentMapになります。

#１対多のテーブル結合
最後に１対多について記載します。
今回新たにArticleテーブルとCommentテーブルを作成しました。

#domain
```Article.java
package com.example.domain;

import java.util.List;

import lombok.Data;
@Data
public class Article {

	private Integer id;
	private String title;
	private String introduction;
	private List<Comment> commentList;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getIntroduction() {
		return introduction;
	}
	public void setIntroduction(String introduction) {
		this.introduction = introduction;
	}
	public List<Comment> getCommentList() {
		return commentList;
	}
	public void setCommentList(List<Comment> commentList) {
		this.commentList = commentList;
	}
	@Override
	public String toString() {
		return "Article [id=" + id + ", title=" + title + ", introduction=" + introduction + ", commentList=" + commentList + "]";
	}	
}
```

複数のCommentを持つため、フィールド内にはListでセット

```Comment.java
package com.example.domain;

import lombok.Data;

@Data
public class Comment {

	private Integer id;
	private Integer articleId;
	private String comment;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public Integer getArticleId() {
		return articleId;
	}
	public void setArticleId(Integer articleId) {
		this.articleId = articleId;
	}
	public String getComment() {
		return comment;
	}
	public void setComment(String comment) {
		this.comment = comment;
	}
	@Override
	public String toString() {
		return "Comment [id=" + id + ", articleId=" + articleId + ", comment=" + comment + "]";
	}
}
```
#.xml
```ArticleMapper.java
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.repository.ArticleMapper">

	<resultMap type="com.example.domain.Article" id="articleMap">
		<id property="id" column="a_id"></id>
		<result property="title" column="a_title"></result>
		<result property="introduction" column="a_introduction"></result>
		<collection property="commentList" ofType="com.example.domain.Comment">
			<result property="id" column="c_id"></result>
			<result property="article_id" column="c_articleId"></result>
			<result property="comment" column="c_comment"></result>
		</collection>
	</resultMap>
	
	<select id="findByArticle" resultMap="articleMap">
		select
            article.id as a_id, 
            article.title as a_title, 
            article.introduction as a_introduction,
		    comments.id as c_id, 
            comments.article_id as c_article_id, 
            comments.comment as c_comment 
		from article left join comments 
		on article.id = comments.article_id
	</select>
</mapper>
```
collectionを使うことで結合が可能となります。
propertyにはプロパティ名、ofTypeには参照するdomainのパスを指定してあげます。
これで結合が可能となります。

以上がMyBatisの基本的な書き方です。
まだまだ学ぶことが多そうです。

###参考記事
[Spring解体新書(Amazon)](https://www.amazon.co.jp/%E5%BE%8C%E6%82%94%E3%81%97%E3%81%AA%E3%81%84%E3%81%9F%E3%82%81%E3%81%AESpring-Boot-%E5%85%A5%E9%96%80%E6%9B%B8%EF%BC%9ASpring-%E8%A7%A3%E4%BD%93%E6%96%B0%E6%9B%B8%EF%BC%88%E7%AC%AC2%E7%89%88%EF%BC%89-Spring%E8%A7%A3%E4%BD%93%E6%96%B0%E6%9B%B8-ebook/dp/B08XPBPH9C)

https://qiita.com/NagaokaKenichi/items/380c1316383b2020a1e7

https://qiita.com/wataru908/items/bb743563f0682455b49d