MyBatisとSpringBootで素敵にORマッピング
概要
前回SpringBootで簡単なアプリを作成する記事を書いたのでDB(MySQL)との接続も書いとこうかなと思います。
本記事ではERMasterでER図やSQLなどを生成して、flywayでテーブル生成してますが面倒くさかったら直接テーブル作ってもらっても問題ないっす!
※EclipseのVersion: Oxygen.1a Release (4.7.1a)
テーブルの作成
DDLの定義
とりあえずAuto_inrementのidとvarcharのnameが存在するmemberテーブルを定義
/* Drop Tables */ DROP TABLE IF EXISTS members; /* Create Tables */ -- 会員情報一覧テーブル CREATE TABLE members ( -- id id int NOT NULL AUTO_INCREMENT COMMENT 'id', -- 名前 name varchar(255) NOT NULL COMMENT '名前', -- レコード生成日時 created_at datetime DEFAULT NOW() NOT NULL COMMENT 'レコード生成日時', -- レコード更新日時 updated_at datetime COMMENT 'レコード更新日時', PRIMARY KEY (id) ) COMMENT = '会員情報一覧テーブル';
(参考までに)自分はERMasterを使っているので以下手順で上記DDL生成
- ERMasterインストール手順に従いEclipseにERMasterをインストール!
- 新規ER図作成
- ファイル→新規→その他→ERMaster→次へ→自分のプロジェクト選択→ファイル名適当に入力→次へ→データベースはMySQL→完了
- ER図の画面でポチポチクリックして必要項目入力
- DDLファイルを生成
- プロパティー的なの出してエクスポート→DDL→OK
flywayの導入
pluginsとdependenciesに1行を追加
... plugins { id "org.flywaydb.flyway" version "4.2.0" } ... dependencies { // Flyway動かすためのconnector compile('mysql:mysql-connector-java') }
以下ファイルを生成し上で記載したmembersのDDLを記載
future/src/main/resources/db/migration/V1.0.0__members.sql
V1.0.0__xxx.sqlはFlywayの書き方でVの数字の順番にファイルを読み込んで実施してくれる
flywayでテーブル作成
データベース作成された状態で以下コマンド実行。
-Dflyway.urlのfutureは各自用意したデータベース名に変えといてください
cd future ./gradlew flywayClean flywayMigrate -Dflyway.url=jdbc:mysql://localhost:3306/future -Dflyway.user=root -Dflyway.password=root
無事テーブル出来てた
docker exec -it mysql01 bash mysql -u root -p future mysql> show tables; +------------------+ | Tables_in_future | +------------------+ | members | | schema_version | +------------------+ 2 rows in set (0.00 sec)
MybatisとSpringBootの連携
MybatisGeneratorのインストール
Qiitaに書いてた頃の記事にも書いたんですが、Java8から使えるLocalDateTime型に紐付いたMapper, Dtoを生成するには2018/7/4リリースのVersion:1.3.7以上じゃないといけないらしい。
そしてMavenならあるみたいだけどGradle版はまだなさそう。。。。
ということで、公式サイト6番目にあるEclipseを使う方式にする
EclipseMarketPlaceで「mybatis」と検索して「MyBatis Generator 1.3.7」をインストール!!
MybatisGeneratorの設定
以下設定ファイルを作成。
future
とかxyz.ucwork
って書いてある部分は自分の環境に合わせて適宜修正ください
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <context id="MySQLTables" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressDate" value="true" /> </commentGenerator> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/future" userId="root" password="root"> <property name="nullCatalogMeansCurrent" value="true" /> </jdbcConnection> <javaTypeResolver> <property name="useJSR310Types" value="true"/> </javaTypeResolver> <javaModelGenerator targetPackage="xyz.ucwork.future.domain.model" targetProject="future/src/main/java"> <property name="enableSubPackages" value="true" /> </javaModelGenerator> <sqlMapGenerator targetPackage="xyz.ucwork.future.domain.mapper" targetProject="future/src/main/resources"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <javaClientGenerator targetPackage="xyz.ucwork.future.domain.mapper" targetProject="future/src/main/java" type="MIXEDMAPPER"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <table schema="future" tableName="%" enableInsert="true" enableSelectByPrimaryKey="true" enableSelectByExample="false" enableUpdateByPrimaryKey="true" enableUpdateByExample="false" enableDeleteByPrimaryKey="true" enableDeleteByExample="false" enableCountByExample="false" selectByExampleQueryId="false" modelType="flat"> <property name="useActualColumnNames" value="false"/> </table> </context> </generatorConfiguration>
補足で説明
LocalDateTime型で出力するための設定
<javaTypeResolver> <property name="useJSR310Types" value="true"/> </javaTypeResolver>
information_schema
データベースとかのテーブルのmapper, dtoを出力しないための設定
<property name="nullCatalogMeansCurrent" value="true" />
テーブル全てのmapper, dto生成するための設定
<table schema="future" tableName="%"
Eclipseでのエラー対応
org.apache.ibatis が見つかりません
的なエラーが出てたんでbuild.gradleに1行追加
dependencies { ... // Mybatis動かす用 compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.2') }
Eclipse上でbuild.gradleの内容を反映するには、プロジェクト上で右クリックしてGradle→Gradleプロジェクトのリフレッシュを押すんだけど..Gradleがない!!!
右クリックして構成→Gradleネーチャーの追加を押したらぽんって出てきた!!らっきー!
SpringからMyBatisでDB操作してみる
Spring側の設定
このままだとTomcatへのデプロイ時にエラーが発生するから各種設定が必要
※xyz.ucwork
とfuture
は例のごとく自分の環境で置き換えてくださいね
Mybatis用の設定ファイルを準備
<?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> <setting name="logImpl" value="LOG4J" /> <setting name="mapUnderscoreToCamelCase" value="true" /> </settings> <typeAliases> <package name="xyz.ucwork.future.domain.model" /> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="" value="" /> </transactionManager> <dataSource type="UNPOOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://mysql01:3306/future" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <mappers> <package name="xyz.ucwork.future.domain.mapper" /> </mappers> </configuration>
Springのapplication.ymlにdatasourceの接続情報記載
※ymlのが好きなのでapplication.properties
はapplication.yml
に書き換えてます
spring: datasource: url: jdbc:mysql://mysql01:3306/future username: root password: root driver-class-name: com.mysql.jdbc.Driver
ここまで来たらtomcatにデプロイしてもエラー無くいけるはず!!
独自のMapperでINSERTしてSELECT
せっかく自動出力しときながら独自のを作成してinsertとselect
insertとselect用のMybatis独自設定
もう疲れてきた。自分で好きにMapper作る
package xyz.ucwork.future.domain.mapper.ext; import java.util.List; import xyz.ucwork.future.domain.model.Members; /** * membersテーブル操作用のmapper. * @author uchiyama-shintaro * */ public interface ExtMembersMapper { /** * 名前でmembersテーブルに追加. * @param members * @return */ int insertWithName(Members members); /** * 名前でレコード検索. * @param name * @return */ List<Members> selectByName(String name); }
自分で好きなMapper用xml作る。例のごとくxyz.ucworkとfutureは書き換えてね
<?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="xyz.ucwork.future.domain.mapper.ext.ExtMembersMapper"> <insert id="insertWithName" parameterType="xyz.ucwork.future.domain.model.Members"> INSERT INTO members( name) VALUES( #{name} ); <selectKey resultType="int" keyProperty="id" order="AFTER"> select @@IDENTITY </selectKey> </insert> <resultMap id="BaseResultMap" type="xyz.ucwork.future.domain.model.Members"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="created_at" jdbcType="TIMESTAMP" property="createdAt" /> <result column="updated_at" jdbcType="TIMESTAMP" property="updatedAt" /> </resultMap> <select id="selectByName" resultMap="BaseResultMap"> SELECT * FROM members WHERE name = #{name} </select> </mapper>
工夫ポイントとしてはinsert後のautoincrementの値をdtoに詰め込まれるようにselect @@IDENTITY
を入れたくらいかな
実際に取得してみる
特質ポイント的にはmapper読み込まれるように@MapperScan({"xyz.ucwork.future.domain.mapper"})
追加してるとこかな
package xyz.ucwork.future; import java.util.List; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import xyz.ucwork.future.domain.mapper.MembersMapper; import xyz.ucwork.future.domain.mapper.ext.ExtMembersMapper; import xyz.ucwork.future.domain.model.Members; @SpringBootApplication @RestController @MapperScan({"xyz.ucwork.future.domain.mapper"}) public class FutureApplication { @Autowired private ExtMembersMapper extMembersMapper; @Autowired private MembersMapper membersMapper; public static void main(String[] args) { SpringApplication.run(FutureApplication.class, args); } @RequestMapping("/") public String function() { // membersテーブルにインサート String name = "testName"; Members insertMembers = new Members(); insertMembers.setName(name); extMembersMapper.insertWithName(insertMembers); // idからレコード取得 Members members1 = membersMapper.selectByPrimaryKey(insertMembers.getId()); // nameからレコード取得 List<Members> members2 = extMembersMapper.selectByName(name); // 画面に表示 return "name from id: "+members1.getName()+" name from name: "+members2.get(0).getName(); } }
まとめ
なんだか息切れしてしまった
ちょっとDB操作したいだけなのに大変。絶対忘れちゃうからここにメモを残す