banner
jzman

jzman

Coding、思考、自觉。
github

Spring BootシリーズMyBatis設定の詳細解説

PS:あなたの状態はあなたの心の持ち方に依存しています。焦りを感じなくなるためには、まず生活のリズムを整えましょう。

前の数記事では、インターフェース開発、Thymeleaf テンプレート、一般的な構文、テンプレートレイアウト、プロジェクトの国際化、JDBC などを試みました。この記事を読む前に、前の数記事を読むことをお勧めします:

MyBatis は優れた永続層フレームワークで、XML またはアノテーションを使用して設定とマッピングを行い、POJO をデータベースのレコードに簡単にマッピングできます。

  1. MyBatis の作業フロー
  2. 依存関係と設定
  3. @Mapper@MapperScan
  4. エンティティクラス
  5. Mapper 設定ファイル
  6. Mapper インターフェース
  7. Mapper マッピングファイル
  8. collection タグの使用
  9. 複数データソースの設定
  10. テスト結果
  11. MyBatis のアノテーション設定

MyBatis の作業フロー#

MyBatis の作業フローは以下の図のようになります:

image

  1. mybatis-config.xml 設定ファイルを読み込みます;
  2. Mapper マッピングファイルまたは対応するアノテーションの内容をロードし、そこに対応する SQL 文が定義されています;
  3. 設定情報に基づいてセッションファクトリSqlSessionFactoryを作成します;
  4. セッションファクトリに基づいてSqlSessionを作成し、そこには SQL を実行するために必要なすべてのメソッドが含まれています;
  5. SQL 文を実行するためのExecutor実行器を作成します。セッションファクトリSqlSessionFactoryを作成する際にExecutorが作成され、そのデフォルトの実行器タイプはExecutorType.SIMPLEです;
  6. MappedStatementオブジェクト。このオブジェクトはExecutor実行器メソッドのパラメータであり、主に Mapper XML ファイルのマッピング情報のラッピングです;
  7. 入力パラメータのマッピング;
  8. 出力パラメータのマッピング。

依存関係と設定#

Spring Boot プロジェクトを作成し、その build.gradle ファイルに MyBatis と MySQL ドライバの依存関係を以下のように追加します:

dependencies {
    // ...
    // myBaits
    // http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/index.html
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1'
    // mysqlドライバ
    runtime("mysql:mysql-connector-java")
    // ...
}

次に、プロジェクトの application.properties ファイルにデータベース接続パラメータと MyBatis 関連の設定を以下のように構成します:

# データベースユーザー名
spring.datasource.username=root
# データベースパスワード
spring.datasource.password=admin
# JDBCドライバ
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JDBC URL
spring.datasource.url=jdbc:mysql://localhost:3306/db_student?serverTimezone=Asia/Shanghai
#spring.datasource.url=jdbc:mysql://localhost:3306/db_student?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true

# MyBatis XML設定ファイルの状態チェックを実行するかどうか、状態をチェックするだけで、デフォルトはfalse
mybatis.check-config-location=true
# mybatis-config.xmlファイルの位置
mybatis.config-location=classpath:mybatis/mybatis-config.xml
# Mapperに対応するxmlパス
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
# エイリアスのパスを設定し、完全修飾クラス名を書くのを避ける
mybatis.type-aliases-package=com.manu.mybatisxml.model

MyBatis の主な設定は、設定ファイル mybatis-config.xml のパス、Mapper に対応する XML ファイルのパスです。

@Mapper と @MapperScan#

@Mapperアノテーションは Mapper インターフェースをマークするために使用され、そのアノテーションが付けられたインターフェースはすべて対応する動的プロキシクラスが生成されます。複数の Mapper インターフェースがある場合は、すべてに@Mapperアノテーションを付ける必要があります。使用方法は以下の通りです:

@Mapper
public interface ClassMapper{
    ///
}

@MapperScanはプロジェクトのエントリポイントクラスにマークを付け、スキャンするインターフェースがある 1 つまたは複数のパッケージを構成できます。また、ワイルドカード*を使用して構成することもできます。使用方法は以下の通りです:

@SpringBootApplication
// 指定されたパッケージ内のインターフェースをスキャン
@MapperScan("com.manu.mybatisxml.mapper")
// @MapperScan("com.manu.mybatisxml.*.mapper")
// @MapperScan({"pack1","pack2"})
public class SpringBootMybatisXmlApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootMybatisXmlApplication.class, args);
    }
}

エンティティクラス#

ケースはクラスと学生の関係、つまり一対多の関係です。クラスClassを以下のように定義します:

/**
 * クラス
 */
public class Class {
    private String classId;
    private String name;
    private List<Student> students;
    public Class() {
    }
    public Class(String classId, String name) {
        this.classId = classId;
        this.name = name;
    }
    // ...
    // setter、getter、toString
}

学生クラスStudentを以下のように定義します:

/**
 * 学生クラス
 */
public class Student {
    private String classId;
    private String sno;
    private String name;
    private String grade;
    public Student() {
    }
    public Student(String classId, String sno, String name, String grade) {
        this.classId = classId;
        this.sno = sno;
        this.name = name;
        this.grade = grade;
    }
    // ...
    // setter、getter、toString
}

MyBatis 設定ファイル#

MyBatis の設定ファイルは mybatis-config.xml ファイルです。Spring Boot で MyBatis を使用する際、この設定ファイルのほとんどの設定は application.properties ファイルで構成できるため、Spring Boot プロジェクトではこの設定ファイルを利用して完全修飾クラス名を簡略化できます。以下のようになります:

<?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>
    <typeAliases>
        <!--エイリアスを定義し、完全修飾クラス名を書くのを避ける-->
        <typeAlias alias="Integer" type="java.lang.Integer" />
        <typeAlias alias="Long" type="java.lang.Long" />
        <typeAlias alias="HashMap" type="java.util.HashMap" />
        <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
        <typeAlias alias="ArrayList" type="java.util.ArrayList" />
        <typeAlias alias="LinkedList" type="java.util.LinkedList" />
        <typeAlias alias="Student" type="com.manu.mybatisxml.model.Student" />
        <typeAlias alias="Class" type="com.manu.mybatisxml.model.Class" />
    </typeAliases>
</configuration>

Mapper インターフェース#

Mapper インターフェースのメソッド名は Mapper マッピングファイル内の対応する SQL 文のメソッドに対応し、メソッド名は対応する SQL 文のid属性と同じでなければなりません。ClassMapperは以下のようになります:

/**
 * ClassMapper.xmlに対応するMapperインターフェース
 */
public interface ClassMapper {
    /**
     * データを1件挿入
     * @param student student
     */
    void insertStudent(Student student);
    void insertClass(Class course);
    /**
     * snoに基づいて1件のレコードを削除
     * @param sno sno
     */
    void deleteStudent(String sno);
    /**
     * データを更新
     * @param student student
     */
    void updateStudent(Student student);
    /**
     * 名前に基づいてデータを検索
     * @param name name
     * @return
     */
    Student findStudentByName(String name);
    /**
     * すべてのデータを検索
     * @return
     */
    List<Student> findAllStudent();
    /**
     * 集合データを検索
     * @param name name
     * @return
     */
    Class findClassStudents(String name);
    /**
     * 集合データのネストされた検索
     * @param classId classId
     * @return
     */
    Class findClassStudents1(String classId);
}

Mapper マッピングファイル#

Mapper マッピングファイルは XML をベースにしており、SQL 文に対応する SQL タグを使用して柔軟に SQL 文を構築します。一部のタグとその属性は見名知意で、一般的なタグは以下の通りです:

  • mapper :Mapper マッピングファイルに対応する Mapper インターフェースクラスを設定;
  • resultMap:クエリ文の結果セット;
  • resultresultMapタグ内のフィールドを定義するために使用;
  • idresultMapタグ内の主キーのフィールドを定義するために使用;
  • collection:集合データ、例えばList<Student>のようなデータ;
  • sql:他の SQL 文で使用するための SQL 文ブロックを定義;
  • insert:挿入文;
  • delete:削除文;
  • update:更新文;
  • select:クエリ文。

一般的な属性は以下の例の関連コメントで確認できます。上記の Mapper インターフェースクラスClassMapperに対応する Mapper マッピングファイルは以下の通りです:

<?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.manu.mybatisxml.mapper.ClassMapper">
    <!--Student POJOのマッピング結果セット-->
    <!--id:一意の識別子-->
    <!--type:具体的なPOJOオブジェクトのタイプ-->
    <resultMap id="StudentResultMap" type="com.manu.mybatisxml.model.Student">
        <!--column:主キーのフィールドはクエリ文のエイリアスフィールドでも可能-->
        <!--property:POJOオブジェクト内のプロパティに対応-->
        <!--jdbcType:フィールドのタイプ-->
        <id column="classId" property="classId" jdbcType="VARCHAR" />
        <!--column:テーブルのフィールド-->
        <result column="userName" property="name" jdbcType="VARCHAR" />
        <result column="sno" property="sno" jdbcType="VARCHAR" />
        <result column="grade" property="grade" jdbcType="VARCHAR" />
    </resultMap>

    <!--Student POJOのマッピング結果セット、集合結果セットを含む-->
    <resultMap id="ClassWithCollectionResultMap" type="com.manu.mybatisxml.model.Class">
        <id column="classId" property="classId" jdbcType="VARCHAR" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <!--ofType:集合内のデータタイプ-->
        <collection property="students" ofType="Student">
            <id column="sno" property="sno" jdbcType="VARCHAR" />
            <result column="userName" property="name" jdbcType="VARCHAR" />
            <result column="classId" property="classId" jdbcType="VARCHAR" />
            <result column="grade" property="grade" jdbcType="VARCHAR" />
        </collection>
    </resultMap>

    <!--Student POJOのマッピング結果セット、集合結果セットを含む、ネストされた検索-->
    <resultMap id="ClassWithCollectionResultMap1" type="com.manu.mybatisxml.model.Class">
        <id column="classId" property="classId" jdbcType="VARCHAR" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <!--column:ネストされた検索の条件-->
        <!--select:ネストされた検索の文-->
        <collection column="{classId = classId}" property="students" ofType="Student"
            select="getStudent" />
    </resultMap>

    <select id="getStudent" parameterType="String" resultMap="StudentResultMap">
        SELECT *
        FROM mybatis_student
        WHERE classId = #{classId}
    </select>

    <!--基本フィールドを定義-->
    <sql id="BaseStudentColumn">
        sno,userName,classId,grade
    </sql>

    <!--データを挿入-->
    <!--id識別子はMapperインターフェース内のメソッド名に対応-->
    <insert id="insertClass" parameterType="Class">
        INSERT INTO mybatis_class(classId, name)
        VALUES (#{classId}, #{name})
    </insert>
    <insert id="insertStudent" parameterType="Student">
        INSERT INTO mybatis_student(classId, userName, sno, grade)
        VALUES (#{classId}, #{name}, #{sno}, #{grade})
    </insert>

    <!--データを削除-->
    <delete id="deleteStudent" parameterType="String">
        DELETE
        FROM mybatis_student
        WHERE sno = #{sno}
    </delete>

    <!--データを更新-->
    <update id="updateStudent" parameterType="Student">
        UPDATE mybatis_student
        SET userName = #{name},
            classId  = #{classId},
            grade    = #{grade},
            sno      = #{sno}
        WHERE sno = #{sno}
    </update>

    <!--条件を満たすデータ集合を検索-->
    <select id="findClassStudents" parameterType="String" resultMap="ClassWithCollectionResultMap">
        SELECT mybatis_class.classId,
               mybatis_class.name,
               mybatis_student.sno,
               mybatis_student.userName,
               mybatis_student.grade
        FROM mybatis_student,
             mybatis_class
        WHERE mybatis_class.classId = mybatis_student.classId
          and mybatis_class.name = #{name}
    </select>

    <!--条件を満たすデータ集合を検索-->
    <select id="findClassStudents1" parameterType="String"
        resultMap="ClassWithCollectionResultMap1">
        SELECT mybatis_class.classId,
               mybatis_class.name,
               mybatis_student.sno,
               mybatis_student.userName,
               mybatis_student.grade
        FROM mybatis_student,
             mybatis_class
        WHERE mybatis_class.classId = mybatis_student.classId
          and mybatis_class.classId = #{classId}
    </select>

    <!--単一データを検索-->
    <select id="findStudentByName" resultMap="StudentResultMap" parameterType="String">
        SELECT *
        FROM mybatis_student
        WHERE userName = #{name}
    </select>

    <!--すべてのデータを検索-->
    <select id="findAllStudent" resultMap="StudentResultMap">
        SELECT
        <include refid="BaseStudentColumn" />
        FROM mybatis_student
    </select>
</mapper>

collection タグの使用#

上記では Mapper ファイル内の一般的なタグのいくつかを紹介しました。他のタグの使用については特に言及することはありませんが、ここでは<collection/>タグの使用について特に説明します。このタグは主に結果セットを示すために使用され、クラスClass内の学生集合List<Student>を通じて、指定されたクラスの学生集合を検索できます。第一の方法は以下の通りです:

<!--Student POJOのマッピング結果セット、集合結果セットを含む-->
<resultMap id="ClassWithCollectionResultMap" type="Class">
    <id column="classId" property="classId" jdbcType="VARCHAR" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <!--ofType:集合内のデータタイプ-->
    <collection property="students" ofType="Student">
        <id column="sno" property="sno" jdbcType="VARCHAR" />
        <result column="userName" property="name" jdbcType="VARCHAR" />
        <result column="classId" property="classId" jdbcType="VARCHAR" />
        <result column="grade" property="grade" jdbcType="VARCHAR" />
    </collection>
</resultMap>

対応するクエリ SQL マッピングは以下の通りです:

<!--条件を満たすデータ集合を検索-->
<select id="findClassStudents" parameterType="String" resultMap="ClassWithCollectionResultMap">
    SELECT mybatis_class.classId,
           mybatis_class.name,
           mybatis_student.sno,
           mybatis_student.userName,
           mybatis_student.grade
    FROM mybatis_student,
         mybatis_class
    WHERE mybatis_class.classId = mybatis_student.classId
      and mybatis_class.name = #{name}
</select>

第二の方法は以下の通りです:

<!--Student POJOのマッピング結果セット、集合結果セットを含む、ネストされた検索-->
<resultMap id="ClassWithCollectionResultMap1" type="com.manu.mybatisxml.model.Class">
    <id column="classId" property="classId" jdbcType="VARCHAR" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <!--column:ネストされた検索の条件-->
    <!--select:ネストされた検索の文-->
    <collection column="{classId = classId}" property="students" ofType="Student"
        select="getStudent" />
</resultMap>

<select id="getStudent" parameterType="String" resultMap="StudentResultMap">
    SELECT *
    FROM mybatis_student
    WHERE classId = #{classId}
</select>

対応するクエリ SQL マッピングは以下の通りです:

<!--条件を満たすデータ集合を検索-->
<select id="findClassStudents1" parameterType="String"
    resultMap="ClassWithCollectionResultMap1">
    SELECT mybatis_class.classId,
           mybatis_class.name,
           mybatis_student.sno,
           mybatis_student.userName,
           mybatis_student.grade
    FROM mybatis_student,
         mybatis_class
    WHERE mybatis_class.classId = mybatis_student.classId
      and mybatis_class.classId = #{classId}
</select>

Mapper インターフェース内で定義されたfindClassStudentsを使用することで、Studentの対応する集合を検索できます。

複数データソースの設定#

複数のデータソース設定ファイルを作成し、異なるデータソースと異なるSqlSessionFactoryなどを生成します。主なデータソース設定は以下の通りです:

/**
 * @Primaryは主データソースを示します
 * basePackages:スキャンするMapperインターフェースを指定
 * sqlSessionTemplateRef:Mapperパス下で指定されたSqlSessionTemplateを指定
 */
@Configuration
@MapperScan(basePackages = "com.manu.multimybatisxml.mapper.primary",
        sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDataSourceConfig {

    @Primary
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/primary/*.xml"));
        return sessionFactoryBean.getObject();
    }

    @Primary
    @Bean
    public DataSourceTransactionManager primaryDataSourceTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Primary
    @Bean
    public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

第二のデータソースの設定は上記と同様ですが、@Primaryをマークせず、第二のデータソースの名前や対応する Mapper マッピングファイルなどを変更します。ここでは詳しく説明しません。

次に、上記の設定で指定されたプレフィックスに従って、application.properties ファイルに複数のデータベース接続を以下のように構成します:

# dataSourceOne
spring.datasource.primary.username=root
spring.datasource.primary.password=admin
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.datasource.jdbc-url 複数データソースでカスタム接続プールを上書きするために使用
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/data_source_one?serverTimezone=Asia/Shanghai

# dataSourceTwo
spring.datasource.secondary.username=root
spring.datasource.secondary.password=admin
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/data_source_two?serverTimezone=Asia/Shanghai

# MyBatis XML設定ファイルの状態チェックを実行するかどうか、状態をチェックするだけで、デフォルトはfalse
mybatis.check-config-location=true
# mybatis-config.xmlファイルの位置
mybatis.config-location=classpath:mybatis/mybatis-config.xml
# エイリアスのパスを設定し、完全修飾クラス名を書くのを避ける
mybatis.type-aliases-package=com.manu.multimybatisxml.model

具体的な内容は、キーワード【Spring Boot】を返信することでソースコードのリンクを取得できます。

テスト結果#

このケースは使用方法を説明するためだけのものであり、読者はその合理性を気にする必要はありません。テストクラスを以下のように作成します:

/**
 * MyBatisTest
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyBatisTest {
    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    private ClassMapper mClassMapper;

    @Test
    public void insert() {
        Class class1 = new Class("class1", "一班");
        Class class2 = new Class("class2", "二班");
        mClassMapper.insertClass(class1);
        mClassMapper.insertClass(class2);

        List<Student> students = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Student student;
            if (i % 2 == 0) {
                student = new Student("class1", "sno" + i, "Student"+i, "A");
            } else {
                student = new Student("class2", "sno" + i, "Student"+i, "B");
            }
            mClassMapper.insertStudent(student);
        }
    }

    @Test
    public void deleteStudentBySno() {
        mClassMapper.deleteStudent("sno0");
    }

    @Test
    public void updateStudent() {
        Student student = new Student("class1","sno1","student1","C");
        mClassMapper.updateStudent(student);
    }

    @Test
    public void findStudentByName() {
        Student student = mClassMapper.findStudentByName("student5");
        System.out.println(student);
    }

    @Test
    public void findAllStudent() {
        List<Student> students = mClassMapper.findAllStudent();
        for (Student student : students) {
            System.out.println(student.toString());
        }
    }

    @Test
    public void findClassStudents(){
        Class clazz = mClassMapper.findClassStudents("一班");
        System.out.println("classId:"+clazz.getClassId()+",name:"+clazz.getName());

        List<Student> students = clazz.getStudents();
        for (Student student : students) {
            System.out.println(student.toString());
        }
    }

    @Test
    public void findClassStudents1(){
        Class clazz = mClassMapper.findClassStudents1("class1");
        System.out.println("classId:"+clazz.getClassId()+",name:"+clazz.getName());

        List<Student> students = clazz.getStudents();
        for (Student student : students) {
            System.out.println(student.toString());
        }
    }
}

ここでfindClassStudentsメソッドの実行結果は以下のようになります:

classId:class1,name:一班
Student{classId='class1', sno='sno1', name='student1', grade='C'}
Student{classId='class1', sno='sno2', name='Student2', grade='A'}
Student{classId='class1', sno='sno4', name='Student4', grade='A'}
Student{classId='class1', sno='sno6', name='Student6', grade='A'}
Student{classId='class1', sno='sno8', name='Student8', grade='A'}

アノテーション設定#

MyBatis は XML による設定の他に、アノテーションによる設定も可能です。以下のようになります:

@Mapper
public interface StudentMapper {
    /**
     * アノテーション内のSQL文は自動的にオブジェクトstudentの関連プロパティを取得します
     */
    @Insert("INSERT INTO mybatis_student(userName,sno,grade) VALUES(#{name},#{sno},#{grade})")
    void insert(Student student);

    /**
     * StudentFactory内では自動的にオブジェクトstudentの関連プロパティをSQL文内で取得します
     * StudentFactory内のinsert2メソッドは#{属性名}の形式で変数値を取得します
     */
    @InsertProvider(type = StudentFactory.class, method = "insert1")
    void insert1(Student student);

    /**
     * 直接パラメータを渡す
     * StudentFactory内のinsert2メソッドは#{変数名}の形式で変数値を取得します
     * さらに、StringBufferを使用してSQLを結合することもできます。insert2メソッド内でSQL文字列を結合して返します
     */
    @InsertProvider(type = StudentFactory.class, method = "insert2")
    void insert2(String sno, String name, String grade);
}

上記のメソッドを実装すると、以下のようになります:

public class StudentFactory {
    public String insert1(Student student) {
        String sql = new SQL() {{
            INSERT_INTO("mybatis_student");
            VALUES("sno", "#{sno}");
            VALUES("userName", "#{name}");
            VALUES("grade", "#{grade}");
        }}.toString();
        System.out.println("SQL:" + sql);
        return sql;
    }

    public String insert2(String sno,String name,String grade) {
        String sql = new SQL() {{
            INSERT_INTO("mybatis_student");
            VALUES("sno", "#{sno}");
            VALUES("userName", "#{name}");
            VALUES("grade", "#{grade}");
        }}.toString();
        System.out.println("SQL:" + sql);
        return sql;
    }
}

最後に、テストを行います。以下のようになります:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyBatisAnnotationTests {

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    StudentMapper mStudentMapper;

    @Test
    public void insert() {
        Student student = new Student("sno0", "jzman0", "A");
        mStudentMapper.insert(student);
    }

    @Test
    public void insert1() {
        Student student = new Student("sno1", "jzman1", "A");
        mStudentMapper.insert1(student);
    }

    @Test
    public void insert2() {
        Student student = new Student("sno2", "jzman2", "A");
        mStudentMapper.insert2(student.getSno(), student.getName(), student.getGrade());
    }
}

MyBatis はアノテーション方式でコードが少なくなりますが、SQL の柔軟性には一定の制限があります。ここでは実践しないので、詳細には触れません。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。