springboot极简教程016-数据库之MyBatis的XML配置方式及自动生成器

2018/10/28 springboot 共 12549 字,约 36 分钟

MyBatis的XML配置方式没有纯手动配置过,XML完全写不好,特别是在刚入门的时候,因此不建议直接从这个角度入手,即使要考虑学习,可以先通过自动生成器的方式入手,然后参考自动生成器生成的XML文件来学习。

然而,MyBatis官方推荐XML方式,所以还是说一说。

MyBatis自动生成器介绍

MyBatis操作数据库的方式介于JDBC与ORM之间,如果完全纯写SQL语句进行查询显得啰嗦枯燥,如果纯用ORM的话完全隔离了SQL语句又显得不习惯,而且很多复杂的查询ORM也很难实现,MyBatis就介于这之间,使得仍然可以操作SQL语句又不至于那么麻烦。

一个可以拿出来说说的功能就是自动生成器:generator,可以根据已经存在的数据库表来反向生成代码,下面看看怎么使用。

项目代码可以参考:L10MyBatisGenerator

创建数据库表

使用MyBatis的自动生成器功能来反向生成代码,就首先需要有数据库表,所以第一步是先创建一个数据库表。

DROP DATABASE IF EXISTS student;
CREATE DATABASE student DEFAULT CHARACTER SET utf8;
 
use student;
 
CREATE TABLE student(
  id int(11) NOT NULL AUTO_INCREMENT,
  student_id int(11) NOT NULL UNIQUE,
  name varchar(255) NOT NULL,
  age int(11) NOT NULL,
  sex varchar(255) NOT NULL,
  birthday date DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
 
insert into student(student_id, name, age, sex, birthday) value(2018001, "Alice", 28, "女", "1990-10-18");
insert into student(student_id, name, age, sex, birthday) value(2018002, "Bob", 27, "男", "1991-10-18");
insert into student(student_id, name, age, sex, birthday) value(2018003, "David", 26, "男", "1992-10-18");
insert into student(student_id, name, age, sex, birthday) value(2018004, "Jim", 38, "男", "1980-10-18");
insert into student(student_id, name, age, sex, birthday) value(2018005, "Mark", 18, "男", "2000-10-18");

添加依赖

<!--MyBatis-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

<!--MyBatis generator-->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.6</version>
</dependency>

<!-- mysql -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.21</version>
</dependency>

添加插件

<!-- 自动生成代码插件 -->
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.6</version>
    <configuration>
        <verbose>true</verbose>
        <overwrite>true</overwrite>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
    </dependencies>
</plugin>
<!--使用MapperPlugin需要的依赖-->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper</artifactId>
    <version>3.4.0</version>
</dependency>

generatorConfig.xml

generatorConfig.xmlMyBatis-generator默认使用的配置文件,也可以在pom中修改,因为觉得没有必要,就用默认好了,需要在resources目录下创建generatorConfig.xml文件。

<?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>

    <!--导入属性配置 -->
    <properties resource="application.yml"/>

    <context id="DB2Tables" targetRuntime="MyBatis3">

        <!--是否在代码中显示注释-->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--jdbc的数据库连接 -->
        <!--<jdbcConnection-->
        <!--connectionURL="${spring.datasource.url}"-->
        <!--userId="${spring.datasource.username}"-->
        <!--password="${spring.datasource.password}"-->
        <!--driverClass="com.mysql.jdbc.Driver"-->
        <!--/>-->
        <jdbcConnection
                connectionURL="jdbc:mysql://127.0.0.1:3306/student?characterEncoding=UTF-8"
                userId="root"
                password="root"
                driverClass="com.mysql.jdbc.Driver"
        />

        <!--生成pojo类存放位置-->
        <javaModelGenerator targetPackage="com.example.usemybatis.dao.model" targetProject="src/main/java">
            <!-- 是否允许子包,即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="true"/>

            <!-- 是否对类CHAR类型的列的数据进行trim操作 -->
            <property name="trimStrings" value="true"/>

            <!-- 是否对model添加 构造函数 -->
            <property name="constructorBased" value="true"/>
        </javaModelGenerator>

        <!--Mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 -->
        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!--生成mapper类存放位置-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.example.usemybatis.dao.mapper"
                             targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!--生成对应表及类名-->
        <table tableName="student" domainObjectName="Student" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <!--使用自增长键-->
            <property name="my.isgen.usekeys" value="true"/>
            <!--使用数据库中实际的字段名作为生成的实体类的属性-->
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>

    </context>
</generatorConfiguration>

配置文件指定:

  • javaModelGenerator中指定自动生成model到包com.example.usemybatis.dao.model下
  • javaClientGenerator指定自动生成的实体操作类到包com.example.usemybatis.dao.mapper下
  • sqlMapGenerator指定mapper(实体操作类)的xml映射文件生成到src/main/resources目录下的mapper下

运行generator

两种方式运行generator

  • MavenProject中的插件:选择mybatis-generator:generate右键运行
  • 创建运行配置:EditConfiguration–新建Maven配置–Name:generator–CommandLine:mybatis-generator:generate -e

运行后会,会按照generatorConfig.xml配置自动生成model和mapper及XML映射文件。

我们看看在com.example.usemybatis.dao.model下生成的实体类Student

package com.example.usemybatis.dao.model;

import java.util.Date;

public class Student {
    private Integer id;

    private Integer student_id;

    private String name;

    private Integer age;

    private String sex;

    private Date birthday;

    public Student(Integer id, Integer student_id, String name, Integer age, String sex, Date birthday) {
        this.id = id;
        this.student_id = student_id;
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.birthday = birthday;
    }

    public Student() {
        super();
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getStudent_id() {
        return student_id;
    }

    public void setStudent_id(Integer student_id) {
        this.student_id = student_id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex == null ? null : sex.trim();
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

再看看在com.example.usemybatis.dao.mapper下生成的实体操作类StudentMapper

package com.example.usemybatis.dao.mapper;

import com.example.usemybatis.dao.model.Student;
import com.example.usemybatis.dao.model.StudentExample;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface StudentMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(Student record);

    int insertSelective(Student record);

    List<Student> selectByExample(StudentExample example);

    Student selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(Student record);

    int updateByPrimaryKey(Student record);

    @Select("SELECT * FROM student")
    List<Student> findAll();
}

可以看到实体操作类会有一个@Mapper注解,实体操作类的接口函数对数据库的操作体现在XML映射文件里,如果IDEA安装了Free MyBatis-plugin插件,会在每个接口上有一个绿色箭头,点一下这些绿色箭头便能自动跳转到对应的XML文件中的配置了。

最后看看在src/main/resources/mapper下生成的XML映射文件StudentMapper.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.usemybatis.dao.mapper.StudentMapper">
    <resultMap id="BaseResultMap" type="com.example.usemybatis.dao.model.Student">
        <constructor>
            <idArg column="id" javaType="java.lang.Integer" jdbcType="INTEGER"/>
            <arg column="student_id" javaType="java.lang.Integer" jdbcType="INTEGER"/>
            <arg column="name" javaType="java.lang.String" jdbcType="VARCHAR"/>
            <arg column="age" javaType="java.lang.Integer" jdbcType="INTEGER"/>
            <arg column="sex" javaType="java.lang.String" jdbcType="VARCHAR"/>
            <arg column="birthday" javaType="java.util.Date" jdbcType="DATE"/>
        </constructor>
    </resultMap>
    <sql id="Example_Where_Clause">
        <where>
            <foreach collection="oredCriteria" item="criteria" separator="or">
                <if test="criteria.valid">
                    <trim prefix="(" prefixOverrides="and" suffix=")">
                        <foreach collection="criteria.criteria" item="criterion">
                            <choose>
                                <when test="criterion.noValue">
                                    and ${criterion.condition}
                                </when>
                                <when test="criterion.singleValue">
                                    and ${criterion.condition} #{criterion.value}
                                </when>
                                <when test="criterion.betweenValue">
                                    and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                                </when>
                                <when test="criterion.listValue">
                                    and ${criterion.condition}
                                    <foreach close=")" collection="criterion.value" item="listItem" open="("
                                             separator=",">
                                        #{listItem}
                                    </foreach>
                                </when>
                            </choose>
                        </foreach>
                    </trim>
                </if>
            </foreach>
        </where>
    </sql>
    <sql id="Base_Column_List">
    id, student_id, name, age, sex, birthday
  </sql>
    <select id="selectByExample" parameterType="com.example.usemybatis.dao.model.StudentExample"
            resultMap="BaseResultMap">
        select
        <if test="distinct">
            distinct
        </if>
        'false' as QUERYID,
        <include refid="Base_Column_List"/>
        from student
        <if test="_parameter != null">
            <include refid="Example_Where_Clause"/>
        </if>
        <if test="orderByClause != null">
            order by ${orderByClause}
        </if>
    </select>
    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from student
        where id = #{id,jdbcType=INTEGER}
    </select>
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from student
    where id = #{id,jdbcType=INTEGER}
  </delete>
    <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.example.usemybatis.dao.model.Student"
            useGeneratedKeys="true">
    insert into student (student_id, name, age, 
      sex, birthday)
    values (#{student_id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, 
      #{sex,jdbcType=VARCHAR}, #{birthday,jdbcType=DATE})
  </insert>
    <insert id="insertSelective" keyColumn="id" keyProperty="id"
            parameterType="com.example.usemybatis.dao.model.Student" useGeneratedKeys="true">
        insert into student
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="student_id != null">
                student_id,
            </if>
            <if test="name != null">
                name,
            </if>
            <if test="age != null">
                age,
            </if>
            <if test="sex != null">
                sex,
            </if>
            <if test="birthday != null">
                birthday,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="student_id != null">
                #{student_id,jdbcType=INTEGER},
            </if>
            <if test="name != null">
                #{name,jdbcType=VARCHAR},
            </if>
            <if test="age != null">
                #{age,jdbcType=INTEGER},
            </if>
            <if test="sex != null">
                #{sex,jdbcType=VARCHAR},
            </if>
            <if test="birthday != null">
                #{birthday,jdbcType=DATE},
            </if>
        </trim>
    </insert>
    <update id="updateByPrimaryKeySelective" parameterType="com.example.usemybatis.dao.model.Student">
        update student
        <set>
            <if test="student_id != null">
                student_id = #{student_id,jdbcType=INTEGER},
            </if>
            <if test="name != null">
                name = #{name,jdbcType=VARCHAR},
            </if>
            <if test="age != null">
                age = #{age,jdbcType=INTEGER},
            </if>
            <if test="sex != null">
                sex = #{sex,jdbcType=VARCHAR},
            </if>
            <if test="birthday != null">
                birthday = #{birthday,jdbcType=DATE},
            </if>
        </set>
        where id = #{id,jdbcType=INTEGER}
    </update>
    <update id="updateByPrimaryKey" parameterType="com.example.usemybatis.dao.model.Student">
    update student
    set student_id = #{student_id,jdbcType=INTEGER},
      name = #{name,jdbcType=VARCHAR},
      age = #{age,jdbcType=INTEGER},
      sex = #{sex,jdbcType=VARCHAR},
      birthday = #{birthday,jdbcType=DATE}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

如果IDEA安装了Free MyBatis-plugin插件,也会有显示绿色箭头,点击绿色箭头可以跳转到对应的实体操作类的接口函数。

可以看到,XML配置方式其实是比较复杂的,如果自动生成的实体操作类和XML文件不足以满足要求,还可以手动添加代码或XML内容。

Free MyBatis-plugin插件

我前面说了,比较推荐注解方式而不是这种XML配置方式,一个很蹩脚之处就是需要在Java代码与XML配置文件之间来回跳转,感觉开发体验并不是很好。当然为了解决这个问题,IDEA有个插件:Free MyBatis-plugin,可以直接从Mapper文件与XML文件互相跳转。其实要我说问题并没有解决,只不过是更加方便地在Java代码与XML文件之间跳转罢了,这个设计思路有点类似于AndroidStudio在开发Android应用程序时,在Activity与layout.xml之间的关联跳转。

Free MyBatis-plugin插件安装后需要重启IDEA才能生效。

手动方式

说完自动生成器,估计你已经不想用手动方式配置了,但是并不是所有的查询都能通过自动方式来生成的,有些情况下仍然需要自行配置XML,这个时候你可以参考下自动生成器生成的XML来学习和调整,也可以结合MyBatis官方资料来学习。

值得注意的是:在手动配置了XML及对应的mapper之后,后面就尽量不要再用自动生成器了,可能会导致覆盖,当然应该可以设置,这个我就没有细究了。

文档信息

Search

    Table of Contents