Mybatis
元々O/RマッパーはJDBC使っていたが
研修が終えてMybatisを使い始めたため、備忘録として記載してます。
初投稿ですので見辛いところも有りますがそこはお許しください。
#Gradleの設定
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
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
1〜4行目はMySQLの設定
mybatis.configuration.map-underscore-to-camel-case=true はSQLのカラムをキャメルケースに直してくれる便利なライブラリです。
#Mapper
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で作成
またインターフェイスで作成します。
必須
@Mapperは必ず記載
これがないとMyBatisが動いてくれません。
###INSERT
public void insert(User user);
###SELECT全件
public List<User> findByUsers();
###SELECT1件
public User findByOne(Integer id);
###UPDATE
public void updateUser(User user);
###DELETE
public void deleteUser(Integer id);
#domain
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
<?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と同じパス先を用意する必要が有ります。
####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
<insert id="insert">
insert into users(name, dep_id) values(#{name},#{depId})
</insert>
#UserMapper.java
public void insert(User user);
タグ内のidはUserMapper.java内のinsertメゾットと同じにする必要があります
また渡ってきたパラメーターは#{プロパティ名}と書く必要があります
#SELECT, UPDATE, DELETE
<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先のパスを指定します。
#1対1のテーブル結合
次は1対1のテーブル結合についてです
今回はusersとdepartmentsを結合させます
#domain
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 + "]";
}
}
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
<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はタグが認識するために記載します。
columnにはselect結果のカラム名を入れ、propertyにはフィールド名を入れる
###idとresultの違い。
idタグは設定された値の数だけListに返してくれます。必須とのことなので主にPKをセットするのかと思われます。。。(知識が浅くてすみません)
他のフィールドはresultでセット。
###association
associationはそれぞれ作ったresultMapを繋げるために必要です。
propertyにはUser.java内のフィールドのdepartment(変数名)を書き、resultMapにdepartment用のresultMapのid名を書いてあげる。
ここだとdepartmentMapになります。
#1対多のテーブル結合
最後に1対多について記載します。
今回新たにArticleテーブルとCommentテーブルを作成しました。
#domain
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でセット
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
<?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)