MyBatisを使用できるようにする
使用した環境
spring | 5.3.0 |
DB | postgreSql |
ビルドツール | Maven |
1.(Mavenの場合)ライブラリをクラスパスに追加
pom.xml に下記のdependency を追加
(自動で使えるようにしてくれる)
<dependencies> <!-- JDBC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${org.springframework-version}</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.5.0</version> </dependency> <!-- MyBatis-Spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.1.0</version> </dependency> <!-- postgreSql --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.3.1</version> </dependency> <!-- ※今回は使用しないのでメモ書き--> <!-- mySQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> </dependencies>
!バージョンの組み合わせが間違っているとエラーになる
https://mybatis.org/spring/ja/index.html
2.設定ファイルに情報を追加
root-context.xml(applicationContext.xml)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- SQLセッションファクトリー --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="/WEB-INF/spring/mybatis-config.xml" /> </bean> <!-- postgreSql接続設定 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.postgresql.Driver" /> <property name="url" value="jdbc:postgresql://localhost:5432/(DB名)" /> <property name="username" value="(ユーザー名)" /> <property name="password" value="(パスワード)" /> </bean> <!-- ※今回使用しないためメモ書き --> <!-- MySql接続設定 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:5432/(DB名)" /> <property name="username" value="(ユーザー名)" /> <property name="password" value="(パスワード)" /> </bean> </beans>
3.MyBatisの設定ファイルを作成する
紐づけは上記のSQLセッションファクトリー内で行っている
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- テーブルの項目名のアンダースコアをキャメルケースに置き換える設定(例.user_id→userId) --> <setting name="mapUnderscoreToCamelCase" value="true" /> </settings> </configuration>
4.Beanファイルを作成する(適宜)
テーブルと同じ内容のBeanを作成しておく
public class MemberBean { private Integer userId; private String name; private String age; public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
5.Mapperファイル(javaとxml)を作成する
public interface OfferRepository { List<MemberBean> selectMember(); Integer deleteMember(); }
xmlファイル(main>repository内に作成)
※必ずjavaファイル側と階層とファイル名が同じになるようにしておく
例.jp>co>demo>offer>repository>OfferRepository
<?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="jp.co.demo.offer.repository.OfferRepository"> <!-- ↓java側と同じ名前にする --> <select id="selectMember" resultType="jp.co.demo.offer.model.MemberBean"> select * from member </select> <delete id="deleteMember"> delete from member </delete> </mapper>
6.mapperを登録する
【方法1】mybatis:scanを追加
servlet-context.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd"> <!-- 指定した配下のフォルダがmapperの検索範囲となる --> <mybatis:scan base-package="jp.co.demo" /> </beans>
【方法2】MapperScannerConfigurerを登録
root-context.xml
<beans> <!-- 指定した配下のフォルダがmapperの検索範囲となる --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="jp.co.demo" /> </bean> </beans>
使用方法
基本的にcontrollerでなくserviceで使う
@Service public class OfferService { @Autowired OfferRepository offerRepository; // mapper public List<MemberBean> getMember() { List<MemberBean> member = offerRepository.selectMember(); return member; } }
サンプル
Select
Mapper(java)
public interface OfferRepository { // パターン1.パラメータなし+Beanに格納 List<MemberBean> selectMember(); // パターン2.パラメータあり+resultMapを使用 MemberBean selectMemberByKey(Integer userId); // パターン3.パラメータがBean+単一項目を取得(※複数取れたらエラーになる) Integer getUserId(MemberBean memberBean); // パターン4.パラメータがMAP+MAPで取得 List<Map<String, Object>> getMember(Map sqlParam); }
Mapper(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="jp.co.demo.offer.repository.OfferRepository"> <!-- パターン1.パラメータなし+Beanに格納 --> <select id="selectMember" resultType="jp.co.demo.offer.model.MemberBean"> select * from member </select> <!-- パターン2.パラメータあり+resultMapを使用 --> <!-- テーブルの項目とBeanの紐づけをする --> <resultMap id="memberMap" type="jp.co.demo.offer.model.MemberBean"> <result column="USER_ID" property="userId" /> <result column="NAME" property="name" /> </resultMap> <select id="selectMemberByKey" resultMap="memberMap"> select user_id ,name from member where user_id= #{userId} </select> <!-- パターン3.パラメータがBean+単一項目を取得(※複数取れたらエラーになる) --> <select id="getUserId" resultType="Integer"> select user_id from member where name = #{name} and age = #{age} </select> <!-- パターン4.パラメータがMAP+MAPで取得 --> <select id="getMember" resultType="map"> select * from member where name = #{name} and age = #{age} </select> </mapper>
Service(呼び出し元)
@Service public class OfferService { @Autowired OfferRepository offerRepository; public void selectTest() { // パターン1.パラメータなし+Beanに格納 List<MemberBean> member = offerRepository.selectMember(); // パターン2.パラメータあり+resultMapを使用 MemberBean member2 = offerRepository.selectMemberByKey(1); // パターン3.パラメータがBean+単一項目を取得(※複数取れたらエラーになる) MemberBean bean = new MemberBean(); bean.setName("高橋"); bean.setAge("22"); Integer userId = offerRepository.getUserId(bean); // パターン4.パラメータがMap+Mapで取得 Map<String, Object> sqlParam = new HashMap<>(); sqlParam.put("name","高橋"); sqlParam.put("age","22"); List<Map<String, Object>> resultMap = offerRepository.getMember(sqlParam); return; } }
Insert
Mapper(java)
public interface OfferRepository { // パターン1.パラメータがBean // 戻り値がint、Integer、long等であれば更新件数を返す Integer insertMember1(MemberBean memberBean); // パターン2.パラメータがBean+登録した値を取得(自動採番される値を取得するのに有効) // 戻り値がboolean、Booleanであれば更新結果を返す(true:更新件数1以上、false:更新件数0) Boolean insertMember2(MemberBean memberBean); // パターン3.パラメータがMap+登録した値を取得(自動採番される値を取得するのに有効) // 戻り値がvoidであれば反映結果を返さない void insertMember3(Map<String, Object> sqlParam); // パターン4.一括登録 Integer insertMember4(List<MemberBean> paramList); }
Mapper(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="jp.co.demo.offer.repository.OfferRepository"> <!-- パターン1.パラメータがBean --> <insert id="insertMember1"> insert into member ( user_Id , name , age ) values ( #{userId} , #{name} , #{age} ) </insert> <!-- パターン2.パラメータがBean+登録した値を取得(自動採番される値を取得するのに有効) --> <!-- ※取得する項目がテーブルの先頭以外以外の場合、keyColumnはで項目の指定が必要 --> <!-- ※keyColumnとkeyPropertyはカンマ区切りで複数指定可能 --> <insert id="insertMember2" useGeneratedKeys="true" keyColumn="member_no" keyProperty="memberNo"> insert into member ( user_Id , name ) values ( #{userId} , #{name} ) </insert> <!-- パターン3.パラメータがMap+登録した値を取得(自動採番される値を取得するのに有効) --> <insert id="insertMember3" useGeneratedKeys="true" keyColumn="member_no" keyProperty="memberNo"> insert into member ( user_Id ) values ( #{userId} ) </insert> <!-- パターン4.一括登録 --> <insert id="insertMember4"> insert into member ( user_Id , name , age ) values <foreach collection="list" item="item" separator=","> ( #{item.userId} , #{item.name} , #{item.age} ) </foreach> </insert> </mapper>
Service(呼び出し元)
@Service public class OfferService { @Autowired OfferRepository offerRepository; public void insert() { // パターン1.パラメータがBean MemberBean bean = new MemberBean(); bean.setUserId(5); bean.setName("田中"); bean.setAge("41"); // 戻り値がIntegerの場合、登録件数を返す Integer count = offerRepository.insertMember1(bean); // パターン2.パラメータがBean+登録した値を取得(自動採番される値を取得するのに有効) MemberBean bean2 = new MemberBean(); bean.setUserId(6); bean.setName("中村"); // 戻り値がBooleanの場合、登録結果を返す Boolean result = offerRepository.insertMember2(bean); // keyPropertyを指定している場合、beanに登録した値(自動採番)が格納される Integer memberNo = bean.getMemberNo(); // パターン3.パラメータがMap+登録した値を取得(自動採番される値を取得するのに有効) Map<String, Object> sqlParam = new HashMap<>(); sqlParam.put("userId",7); // 戻り値がvoidの場合、反映結果を返さない offerRepository.insertMember3(sqlParam); // keyPropertyを指定している場合、Mapに登録した値(自動採番)が格納される Integer memberNo2 = (Integer)sqlParam.get("memberNo"); // パターン4.一括登録 List<MemberBean> paramlist = new ArrayList<>(); MemberBean bean3 = new MemberBean(); bean3.setUserId(8); bean3.setName("島田"); bean3.setAge("34"); paramlist.add(bean3); bean3 = new MemberBean(); bean3.setUserId(9); bean3.setName("高橋"); bean3.setAge("13"); paramlist.add(bean3); Integer count4 = offerRepository.insertMember4(paramlist); return; } }
Update
Mapper(java)
public interface OfferRepository { // パターン1.パラメータがBean // 戻り値がint、Integer、long等であれば更新件数を返す Integer updateMember1(MemberBean memberBean); // パターン2.パラメータがMap // 戻り値がboolean、Booleanであれば更新結果を返す(true:更新件数1以上、false:更新件数0) Boolean updateMember2(Map<String, Object> sqlParam); }
Mapper(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="jp.co.demo.offer.repository.OfferRepository"> <!-- パターン1.パラメータがBean--> <update id="updateMember1"> update member set name = #{name} , age = #{age} where user_id = #{userId} </update> <!-- パターン2.パラメータがMap --> <update id="updateMember2"> update member set name = #{name} , update_datetime = current_timestamp where user_id = #{userId} </update> </mapper>
Service(呼び出し元)
@Service public class OfferService { @Autowired OfferRepository offerRepository; public void update() { // パターン1.パラメータがBean MemberBean bean = new MemberBean(); bean.setUserId(5); bean.setName("たなか"); bean.setAge("11"); // 戻り値がIntegerの場合、登録件数を返す Integer count = offerRepository.updateMember1(bean); // パターン2.パラメータがMap Map<String, Object> sqlParam = new HashMap<>(); sqlParam.put("userId",6); sqlParam.put("name","にしむら"); // 戻り値がBooleanの場合、登録結果を返す Boolean result = offerRepository.updateMember2(sqlParam); return; } }
Delete
Mapper(java)
public interface OfferRepository { // パターン1.パラメータがBean // 戻り値がint、Integer、long等であれば更新件数を返す Integer deleteMember1(MemberBean memberBean); // パターン2.パラメータがMap // 戻り値がboolean、Booleanであれば更新結果を返す(true:更新件数1以上、false:更新件数0) Boolean deleteMember2(Map<String, Object> sqlParam); }
Mapper(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="jp.co.demo.offer.repository.OfferRepository"> <!-- パターン1.パラメータがBean--> <delete id="deleteMember1"> delete from member where user_id = #{userId} and name = #{name} </delete> <!-- パターン2.パラメータがMap --> <delete id="deleteMember2"> delete from member where user_id = #{userId} </delete> </mapper>
Service(呼び出し元)
@Service public class OfferService { @Autowired OfferRepository offerRepository; public void delete() { // パターン1.パラメータがBean MemberBean bean = new MemberBean(); bean.setUserId(5); bean.setName("たなか"); // 戻り値がIntegerの場合、登録件数を返す Integer count = offerRepository.deleteMember1(bean); // パターン2.パラメータがMap Map<String, Object> sqlParam = new HashMap<>(); sqlParam.put("userId",6); // 戻り値がBooleanの場合、登録結果を返す Boolean result = offerRepository.deleteMember2(sqlParam); return; } }
動的SQL
if文
<select id="selectMember1" resultType="jp.co.demo.offer.model.MemberBean"> select * from member where user_id = #{userId} <!-- ifの条件に合致する場合のみif内の処理を挿入する--> <if test="name != null"> and name like concat('%', #{name}, '%') </if> </select> <select id="selectMember2" resultType="jp.co.demo.offer.model.MemberBean"> select * from member <!-- whereをつけることでいずれかの結果を返すときだけwhereを挿入--> <!-- また、andまたはorで始まっていた場合これを削除する--> <where> <if test="userId != null"> user_id = #{userId} </if> <if test="name != null"> and name = #{name} </if> </where> </select>
choose
<select id="selectMember1" resultType="jp.co.demo.offer.model.MemberBean"> select * from member <where> <!-- chooseをつけるとwhenの中から合致するものを1つ実行--> <!-- すべて合致しない場合があるためwhereをつけておく--> <choose> <when test="userId != null"> user_id = #{userId} </when> <when test="name != null"> and name = #{name} </when> </choose> </where> </select>
foreach
<select id="selectMember1" resultType="jp.co.demo.offer.model.MemberBean"> select * from member where user_id in <!-- listの中身がなくなるまで繰り返す--> <foreach item="item" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
呼出元
public interface OfferRepository { List<MemberBean> selectMember1(List<Integer> listId); }
比較(<、>)を使用する場合
<!-- 「<」は「<」--> <!-- 「>」は「>」--> <select id="selectMember1" resultType="jp.co.demo.offer.model.MemberBean"> select * from member3 where user_id > 7 --user_id > 7と同じ </select>
SQL内のコメントアウト
<select id="selectMember1" resultType="jp.co.demo.offer.model.MemberBean"> select name --sql内のコメント(postgreSqlの場合--を使用) from member3 where user_id = 5 </select>
SQLの共通化(include)
<select id="selectMember1" resultType="jp.co.demo.offer.model.MemberBean"> select * from member where <!-- includeタグで共通化したsqlを呼び出す(select、fromでも使用可能)--> <include refid="whereMember" /> </select> <!-- sqlタグ内に共通化するsqlを記載する--> <sql id="whereMember"> user_id = 7 </sql>
トランザクション
【共通の準備】
root-context.xml(applicationContext.xml)に下記を追記する。
… <!-- トランザクションマネージャー --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> <property name="rollbackOnCommitFailure" value="true" /> </bean> …
方法1.アノテーション(@Transactional)を使用する
【準備】コンテキストファイルに下記を追記する
servlet-context.xml(spring-mvc.xml)
※<annotation-driven />が記載されているファイルに<tx:annotation-driven />を追加。<annotation-driven />は消さないこと。
<beans:beans xmlns=" … xmlns:tx="http://www.springframework.org/schema/tx" … xsi:schemaLocation=" … http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd … "> … <tx:annotation-driven /> …
【使用方法】
@Service public class OfferService { @Autowired OfferRepository offerRepository; @Transactional(rollbackFor = Exception.class) public void sample() { try { offerRepository.insertMember(); offerRepository.updateMember(); offerRepository.insertMember(); } catch (Exception e) { } return; } }
方法2.Bean定義ファイルを使用する
【準備】AOPを使用できるようにする
(Mavenの場合)pom.xmlに下記を記載してライブラリを取り込む
… <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${org.springframework-version}</version> </dependency> …
【準備】コンテキストファイルに下記を追記する
servlet-context.xml(spring-mvc.xml)
・メソッド名がtxExecuteやtxInsertから始まるものを対象にトランザクションを開始する
・対象のクラスの範囲はjp.co.demo.○でクラス名が「~Service」となっているものが対象
<beans:beans xmlns=" … xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" … xsi:schemaLocation=" … http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd … "> <!-- 宣言的トランザクションの織り込み --> <tx:advice id="txAdvice" transaction-manager="transactionManager" > <tx:attributes> <tx:method name="txExecute*" propagation="REQUIRED" rollback-for="java.lang.Exceptoin" /> <tx:method name="txInsert*" propagation="REQUIRED" rollback-for="java.lang.Exceptoin" /> <tx:method name="txUpdate*" propagation="REQUIRED" rollback-for="java.lang.Exceptoin" /> <tx:method name="txDelete*" propagation="REQUIRED" rollback-for="java.lang.Exceptoin" /> <tx:method name="txSelect*" read-only="true" /> </tx:attributes> </tx:advice> <aop:config> <aop:advisor pointcut="execution(* jp.co.demo..*Service.*(..))" advice-ref="txAdvice" /> </aop:config>
【使用方法】
・@Transactionalは不要
・クラス名、メソッド名をコンテキストファイルで定義した名前のルールに合わせる
@Service public class OfferService { @Autowired OfferRepository offerRepository; public void txExecuteSample() { try { offerRepository.insertMember(); offerRepository.updateMember(); offerRepository.insertMember(); } catch (Exception e) { } return; } }