B-Teck!

お仕事からゲームまで幅広く

【Kotlin/MySQL】MyBatisでレコードを取得するメモ

Kotlin + MyBatisを利用してMySQLに接続し、レコードの情報を取得するまでのメモです。
CRUD一通りとか動的クエリとかは別で書きます。

環境

Kotlin : 1.4.0
MyBatis : 3.5.5
MySQL Connector/J : 8.0.21

ビルドツールはMavenを前提としています。

DBの中のデータはこんな感じ。 f:id:beatdjam:20200824023500p:plain

DB自体の環境については下記の記事を参照
【MySQL/Docker】docker-composeでMySQL5.7のイメージを作成して接続する備忘録 - B-Teck!

ディレクトリ構成

├src/main/
│  ├kotlin/
│  │  └mybatis/
│  │     └Main.kt
│  └resources/
│       ├mapper.xml : 実行するクエリの定義ファイル
│       └mybatis-config.xml : MyBatisの設定ファイル
└pom.xml : Mavenの依存性管理ファイル

ライブラリ読み込み

pom.xmlに下記の記述を追加して、MyBatis本体とMySQLに接続するためのコネクタを利用できるようにします。

<!-- DB -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>

MyBatisの設定ファイルを作成します

mybatis-config.xmlにDBへの接続情報などの設定値を定義します。 後述するSQLを書くためのファイルはブロックに記述が必要です。

<?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="defaultStatementTimeout" value="20"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <environments default="sample">
        <environment id="sample">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3314/sampledb"/>
                <property name="username" value="user"/>
                <property name="password" value="password"/>
                <property name="poolMaximumActiveConnections" value="5"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper.xml"/>
    </mappers>
</configuration>

SQLを定義する

mappersブロックに記述されていたxmlの中身です。
SQLと利用するMapper(namespace)、レコードを取り出すための型情報(resultType)が定義されています。 idはそのままMapperのメソッド名になります。

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="main.Mapper">
    <select id="selectAll" resultType="main.User">
        SELECT name, email
        FROM users;
    </select>
</mapper>

実装

取得するエンティティを表現するためのdata classを用意します。

data class User(val name: String, val email: String)

mapper.xmlと対になる形のinterfaceを用意します

設定ファイルのところでも書いたように、mapperで定義したid= selectAll がメソッド名になっています。

interface Mapper {
    fun selectAll(): List<User>?
}

接続情報を読み込んでDBに接続、レコードを取得

今回はサンプル用の実装なのでSessionFactoryを使い捨てていますが、
本来はアプリケーションが生存している間は同じものを使い回すことが推奨されています。
MyBatis – MyBatis 3 | スタートガイド のスコープとライフサイクル参照

package main

import org.apache.ibatis.session.SqlSessionFactoryBuilder

fun main() {
    // configをInputStreamで取り出す
    val config = Thread.currentThread()
        .contextClassLoader
        .getResourceAsStream("mybatis-config.xml")
    // 接続用のセッションを作成
    val sessionFactory = SqlSessionFactoryBuilder().build(config)
    val session = sessionFactory.openSession()

    // Mapper経由で情報を取り出し
    val users = session.getMapper(Mapper::class.java).selectAll()
    println(users)
    // [User(name=exam taro, email=taro@example.com), User(name=exam jiro, email=jiro@example.com)]
}

【Android/Kotlin】端末内に保存されたQRコードから情報を読み取る

下記の記事の続きです blog.beatdjam.com

端末組み込みの画像一覧からQRコードの画像を取得して、テキストを読み取るまで。

端末内の画像一覧から任意の画像を取得するためのIntentを作成

画像一覧を表示し、選択した画像の情報を得るためのIntentを作成します。

/**
 * 端末内画像取得用のActivityを起動するIntentを作成
 */
fun createGetDeviceImageIntent() = Intent(Intent.ACTION_OPEN_DOCUMENT).also {
 it.addCategory(Intent.CATEGORY_OPENABLE)
 it.type = "image/*"
}

startActivityForResult() で起動することで、onActivityResult() 内で Intent::data から対象画像のUriオブジェクトを取り出すことができます。

取得したUrlからBitmap形式でファイルを取得

/**
 * 画像選択Intentから渡されたURIを用いて、Bitmap取得を行う
 */
fun getBitmapFromUri(context: Context, uri: Uri?) = when {
    uri != null -> context.contentResolver
        .openFileDescriptor(uri, "r")
        ?.use { BitmapFactory.decodeFileDescriptor(it.fileDescriptor) }
    else -> null
}

Bitmapファイルを入力としてQRコードの情報を読み取る

/**
 * bitmapからQRCode読み取りを実行
 */
fun readQRCodeFromImage(bitmap: Bitmap) = with(bitmap) {
    val pixels = IntArray(width * height)
    getPixels(pixels, 0, width, 0, 0, width, height)
    val source = RGBLuminanceSource(width, height, pixels)
    val binaryBitmap = BinaryBitmap(HybridBinarizer(source))
    MultiFormatReader().decode(binaryBitmap)?.text
}

読み取りで触ったpackageがこのあたりだったので、ZXing Android Embeddedじゃなくて生のZXing触るしかbitmapから読み込む方法がなさそうだった。

* android.graphics.Bitmap
* com.google.zxing.BinaryBitmap
* com.google.zxing.MultiFormatReader
* com.google.zxing.RGBLuminanceSource
* com.google.zxing.common.HybridBinarizer

【Java/Kotlin】resourcesディレクトリにあるファイルを読む

ルートからの絶対パスで指定したリソースファイルを取得するスニペット。
IOExceptionはそのまま外に投げているので必要があればcatchしてリカバリーまで書くこと。

Java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

public class JavaResource {
    public String javaResourceAccess(String fileName) throws IOException {
        final InputStream someStream = this.getClass().getClassLoader().getResourceAsStream(fileName);
        if (someStream == null) return null;

        try (BufferedReader br = new BufferedReader(new InputStreamReader(someStream, StandardCharsets.UTF_8))) {
            // Kotlin版のBufferedReader.readText()がsuffixに"\n"が付与されているため、合わせてつけている
            // 通常利用の場合は `Collectors.joining("\n")` でもよい
            return br.lines().collect(Collectors.joining("\n", "", "\n"));
        }
    }
}

Kotlin

class KotlinResource {
    @Throws(IOException::class)
    fun kotlinResourceAccess(fileName : String) = this.javaClass
        .classLoader
        .getResourceAsStream(fileName)
        ?.bufferedReader()
        ?.use { it.readText() }
}