AndroidMybatis

Mybatis

2022-10-03  本文已影响0人  JunChow520

什么是MyBatis?

iBatis

持久层

数据持久化是将内存中的数据模型转换为存储模型,并将存储模型转换为内存中数据模型的统称。比如:文件的存储,数据的读取、数据表的增删改查等都是数据持久化操作。

框架

MyBatis是一个半自动映射的框架,需手动匹配POJO和SQL的映射关系。MyBatis需手动编写SQL,因此灵活多变,支持动态SQL、处理列表、动态生成表名、支持存储过程,工作量相对较大。

JDBC缺点

MyBatis

资料

MyBatis是一个开源、轻量级的数据持久化框架,是JDBC和Hibernate的替代方案。

MyBatis支持定制化SQL、存储过程、高级映射,可以在实体类和SQL语句之间建立映射关系,是一种自动化的ORM实现。

MyBatis的核心思想是将程序中大量的SQL语句剥离出来,使用XML文件或注解的方式实现SQL的灵活配置,将SQL语句与程序代码分离,在不修改程序代码的情况下,直接在配置文件中修改SQL语句。

安装

快速入门

创建Maven模块导入MyBatis坐标

$ vim pom.xml
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.9</version>
</dependency>

添加MySQL数据库驱动的依赖包

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.30</version>
</dependency>

添加Lombok依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>

添加日志相关依赖

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.11</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.11</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.36</version>
</dependency>

导入JUnit测试依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

编写MyBatis核心配置文件

$ vim resources/mybatis-config.xml

替换JDBC连接信息参数,解决硬编码问题。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/fw"/>
                <property name="username" value="root"/>
                <property name="password" value="q7tkI4QvegrjUTCm"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="SysUserMapper.xml"/>
    </mappers>
</configuration>

创建数据表

CREATE TABLE `sys_user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `code` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '唯一编号',
  `created_at` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  `created_by` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '创建人',
  `updated_at` datetime DEFAULT NULL COMMENT '更新时间',
  `updated_by` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '更新人',
  `username` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '账户',
  `password` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '密码',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户';

定义POJO类

$ vim pojo/SysUser.java
package com.jc.pojo;

import lombok.Data;
import java.util.Date;

@Data
public class SysUser {
    private Long id;
    private String code;
    private Date createdAt;
    private Date updatedAt;
    private String createdBy;
    private String updatedBy;

    private String username;
    private String password;
}

创建SQL映射文件,统一管理SQL语句,解决硬编码问题。

$ vim resources/SysUserMapper.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="sysUser">
    <select id="selectList" resultType="com.jc.pojo.SysUser">
        SELECT * FROM sys_user WHERE 1=1
    </select>
</mapper>
$ vim app.java
public static void mybatis(){
    InputStream is = null;
    try {
        is = Resources.getResourceAsStream("mybatis-config.xml");
    } catch (IOException e) {
        e.printStackTrace();
    }

    SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
    SqlSession ss = ssf.openSession();

    List<SysUser> ll = ss.selectList("sysUser.selectList");
    System.out.println(ll);

    ss.close();
}

MyBatis包含一个名为Resources的工具类,它包含一些实用方法可从类路径或其它位置中加载资源文件。

InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

每个MyBatis应用程序都使用了SqlSessionFactory实例,SqlSessionFactory实例可通过SqlSessionFactoryBuilder获得,SqlSessionFactoryBuilder可从XML配置文件或预定义配置类Configuration实例中获取。使用MyBatis后只需提供SQL语句,关注点可集中在SQL语句上。

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);

IDEA中解决SQL映射文件的警告提示信息,由于IDEA和数据库没有建立连接,因此在XML中无法是被表信息,解决方案只需在IDEA中配置并连接数据库即可。

配置数据库连接

Mapper代理开发

MyBatis开发中会编写大量SQL到XML文件中,除了特殊的业务逻辑外,存在大量结构相似的增删改查的SQL。

使用Mapper代理开发的目的是为了解决原生方式中的硬编码,同时为了简化后期执行SQL。

Mapper的目的是为了解决单表增删改查,基于MyBatis插件开发者无需编写SQL,无需再DAO中增加方法,只需要编写好实体类,就能够支持相应的操作。

定义与SQL映射文件同名的Mapper接口

$ vim java/com/jc/mapper/SysUserMapper.java
package com.jc.mapper;

import com.jc.pojo.SysUser;
import java.util.List;

public interface SysUserMapper {
    List<SysUser> list();
}

将Mapper接口与SQL映射文件放置在同一目录下

$ vim resources/jc/com/mapper/SysUserMapper.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.jc.mapper.SysUserMapper">
    <select id="list" resultType="com.jc.pojo.SysUser">
        SELECT * FROM sys_user WHERE 1=1
    </select>
</mapper>

设置SQL映射文件的namespace属性值为Mapper接口全限定名

namespace="com.jc.mapper.SysUserMapper"

在Mapper接口中定义方法,方法名为SQL映射文件中SQL语句的id属性,同时保持参数类型和返回值一致。

List<SysUser> list();

如果Mapper接口名与SQL映射文件名相同,并在同一目录下则可使用包扫描方式来简化SQL映射文件的加载。

$ vim resources/mybatis-config.xml
<mappers>
<!--        <mapper resource="com/jc/mapper/SysUserMapper.xml"/>-->
    <package name="com.jc.mapper" />
</mappers>

编码流程

  1. 通过SqlSession的getMapper()方法获取Mapper接口中的代理对象
InputStream is = null;
try {
    is = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
    e.printStackTrace();
}

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
SqlSession ss = ssf.openSession();

SysUserMapper m = ss.getMapper(SysUserMapper.class);
List<SysUser> l = m.list();
System.out.println(l);

ss.close();

openSession()默认接收名为autoCommit的布尔值用于设置自动提交事务,默认不填为false,表示关闭事务自动提交,因此在DML语句执行时需手工提交事务。

  1. 调用对应方法完成SQL的执行
SysUserMapper m = ss.getMapper(SysUserMapper.class);
List<SysUser> l = m.list();
System.out.println(l);

核心配置

$ vim resources/mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/fw"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
    <!--        <mapper resource="com/jc/mapper/SysUserMapper.xml"/>-->
        <package name="com.jc.mapper" />
    </mappers>
</configuration>

类型别名typeAliases

添加类型别名可为Java类型设置一个缩写名字,仅用于XML配置,目的在于降低冗余的全限定类名的书写。

$ vim resources/mybatis-config.xml
<configuration>
    <typeAliases>
        <package name="com.jc.pojo" />
    </typeAliases>
</configuration>

配置类型别名后,可直接简化SQL映射文件中resultType中的之前全限定类名。

$ vim resources\com\jc\mapper\SysUserMapper.xml
<mapper namespace="com.jc.mapper.SysUserMapper">
    <select id="list" resultType="SysUser">
        SELECT * FROM sys_user WHERE 1=1
    </select>
</mapper>

需要注意的是如果数据库表的名称和POJO实体类的属性名称不一致则不能自动封装数据,解决的方法有两种:

<mapper namespace="com.jc.mapper.SysUserMapper">
    <select id="list" resultType="SysUser">
        SELECT id, created_at AS createdAt, created_by AS createdBy FROM sys_user WHERE 1=1
    </select>
</mapper>

起别名的缺点在于每次查询都要定义一次别名,解决方案可采用SQL片段的方式,但SQL片段的方式也不够灵活。

<mapper namespace="com.jc.mapper.SysUserMapper">
    <sql id="column">
        id, created_at AS createdAt, created_by AS createdBy
    </sql>
    <select id="list" resultType="SysUser">
        SELECT
        <include refid="column" />
        FROM sys_user WHERE 1=1;
    </select>
</mapper>
<mapper namespace="com.jc.mapper.SysUserMapper">
    <resultMap id="resultMap" type="SysUser">
        <id column="id" property="id" />
        <result column="code" property="code" />
        <result column="created_at" property="createdAt" />
        <result column="created_by" property="createdBy" />
    </resultMap>
    <sql id="column">
        id, code, created_at, created_by
    </sql>
    <select id="list" resultMap="resultMap">
        SELECT
        <include refid="column" />
        FROM sys_user WHERE 1=1;
    </select>
</mapper>

MyBatisX

MyBatisX是一款基于IDEA的快速开发插件,为效率而生。

主要功能

MyBatisX

增删改查

主键查询

public interface SysUserMapper {
    SysUser getById(long id);
}
<mapper namespace="com.jc.mapper.SysUserMapper">
    <select id="getById" resultType="SysUser" parameterType="long">
        SELECT * FROM sys_user WHERE 1=1 AND id=#{id}
    </select>
</mapper>
参数占位符 说明
#{} 将所占据位置替换为?号,为防止SQL注入而设置的预处理语句。
${} 直接替换,有SQL注入风险。当表名或列名不固定的情况下可使用。

参数类型:可省略

parameterType="long"

特殊字符处理

<select id="getById" resultType="SysUser" parameterType="long">
    SELECT * FROM sys_user WHERE 1=1 AND id &lt; #{id}
</select>
<select id="getById" resultType="SysUser" parameterType="long">
    SELECT * FROM sys_user WHERE 1=1 AND id <![CDATA[<]]> #{id}
</select>

条件查询

接收参数三种方式

设置参数 说明
散装参数 使用@Param("SQL中的参数占位符名称")
实体类封装参数 只需保证SQL中参数名和实体类属性名对应即可
Map集合 只需保证SQL中参数名和Map集合键名对应即可

例如:多条件查询

List<SysUser> list(Map m);
List<SysUser> list(SysUser d);
List<SysUser> list(@Param("id") Long id, @Param("username") String username);

SQL语句需要随着用户输入或外部条件的变化而变化,即动态SQL。

<select id="list" resultMap="resultMap">
    SELECT * FROM sys_user WHERE 1=1
    <if test="id != null">AND id = #{id}</if>
    <if test="username != null">AND username LIKE #{username}</if>
</select>

注意LIKE模糊匹配时需预先对参数值username处理

username = “%“+username+”%”

MyBatis对动态SQL提供支持

标签 说明
if 条件判断
choose(when, otherwise) 从多个条件选择一个
trim(where, set) 截取
foreach 遍历循环

添加数据

int add(SysUser d);
<insert id="add" useGeneratedKeys="true" keyProperty="id">
INSERT INTO sys_user(
    created_at
    <if test="username != null">,username</if>
    <if test="password != null">,password</if>
) VALUES (
    NOW()
    <if test="username != null">,#{username}</if>
    <if test="password != null">,MD5(#{password})</if>
)
</insert>

测试获取添加后的主键

InputStream is = null;
try {
    is = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
    e.printStackTrace();
}

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
SqlSession ss = ssf.openSession();

SysUserMapper m = ss.getMapper(SysUserMapper.class);

SysUser d = new SysUser();
d.setUsername("root");
if(m.add(d) > 0){
    System.out.println(d.getId());
}

ss.commit();
ss.close();

修改数据

int edit(SysUser d);
<update id="edit">
    UPDATE sys_user
    <set>
        updated_at=NOW()
        <if test="username != null">,username = #{username}</if>
        <if test="password != null">,password = MD5(#{password})</if>
    </set>
    <where>
        <if test="id != null">AND id = #{id}</if>
        <if test="username != null">AND username = #{username}</if>
    </where>
</update>
InputStream is = null;
try {
    is = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
    e.printStackTrace();
}

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
SqlSession ss = ssf.openSession();

SysUserMapper m = ss.getMapper(SysUserMapper.class);

SysUser d = new SysUser();
d.setId(1L);
d.setPassword("root");
if(m.edit(d) > 0){
    System.out.println(d.getId());
}

ss.commit();
ss.close();

删除数据

int delById(long id);
<delete id="delById">
    DELETE FROM sys_user
    <where>
        <if test="id != null">id=#{id}</if>
    </where>
</delete>
int delByIds(@Param("ids") long[] ids);
<delete id="delByIds">
    DELETE FROM sys_user
    <where>
        id IN
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </where>
</delete>

参数传递

MyBatis接口方法可接收各种类型的参数,底层对这些参数进行不同的封装处理。

单个参数 说明
POJO 直接使用,属性名和参数占位符名称必须保持一致。
Map 直接使用,键名和参数占位符名称必须保持一致。
Collection 会封装成为Map,可使用@Param注解。

当使用多个参数传递时,MyBatis会将多个参数封装成为一个Map集合,MyBatis底层提供了ParamNameResolver类进行参数封装,使用@Param注解时会替换Map集合中默认的键名。

建议参数传递时使用@Param注解来修改Map集合中默认的键名,并使用修改后的名称来获取值,这样可读性更高。

分层架构

分层 描述
API接口层 提供给外部使用的接口API
数据处理层 负责具体的SQL查询、解析、执行、结果映射处理,目的是根据调用的请求完成依次数据库操作。
基础支撑层 负责最基础的功能支撑,包括连接管理、事务管理、配置加载、缓存处理。
MyBatis

框架架构

步骤 名称 描述
1 加载配置 将SQL配置信息加载成一个个的MappedStatement对象并存储到内存
2 SQL解析 API接口成接收到调用请求时,接收到传入SQL的ID和对象,根据ID找到对应的MapperStatement,在根据入参对象对MapperStatement解析,解析后获得最终要执行的SQL语句和参数。
3 SQL执行 将最终得到的SQL和参数拿到数据库进行执行,获得操作数据库的结果。
4 结果映射 将操作数据库的结果按映射配置进行转换

执行流程

处理操作请求过程

  1. 根据SQL的ID查找对应的MappedStatement对象
  2. 根据传入参数对象解析MappedStatement对象,最终得到要执行的SQL和执行传入参数。
  3. 获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
  4. 根据MappedStatement对象中的结果映射配置,对得到的执行结果进行转换处理,并得到最终的处理结果。
  5. 释放连接资源

操作流程

  1. 加载配置并初始化
项目 说明
触发条件 加载配置文件
处理过程 将SQL配置信息加载成为一个一个的MapperStatement对象(包括传入参数映射配置、执行的SQL语句、结果映射配置),存储到内存中。

配置来源两个地方:配置文件、Java代码注解

  1. 接收调用请求
项目 说明
触发条件 调用MyBatis提供的API
传入参数 为SQL的ID和传入参数对象
处理过程 将请求传递给下层(请求处理层)进行处理
  1. 处理操作请求
项目 说明
触发条件 API接口层传递请求过来
传入参数 为SQL的ID和传入参数对象
  1. 返回处理结果

注解开发

使用注解开发会比配置文件更加方便,注解适用于简单查询,对于复杂查询依然使用XML配置文件的方式。

注解 说明
@Select 查询
@Insert 添加
@Update 修改
@Delete 删除

@Mapper

@Mapper注解是由MyBatis框架定义的一个描述数据层接口的注解(描述性作用)。

@Mapper用于告知SpringBoot框架此接口的实现类是由MyBatis负责创建,并将其实现类对象存储到Spring容器中进行管理。

@Mapper
public interface SysDictTypeMapper

SSM项目中大量配置文件的读写,可通过注解的方式来简化操作。对于DAO接口层的配置可通过@mapper注解来实现。

若直接在Mapper接口类中添加@Mapper注解,需在每个Mapper接口类都添加@Mapper注解,相对较为繁琐。

@Mapper的作用是给Mapper接口自动生成一个实现类,让Spring对Mapper接口的Bean进行管理,省略较为复杂的XML文件。

@Mapper注解是MyBatis自动配置时,默认扫描的注解类。

@MapperScan

通过@MapperScan可指定需要扫描的Mapper接口类的包路径,在路径中可使用*作为通配符对包名进行匹配。也可使用@MapperScan注解对多个包进行扫描。

Spring集成

当MyBatis与依赖注入框架注入Spring、Guice等同时使用时,SqlSession将会被依赖注入框架所创建,因此无需使用SqlSessionFactoryBuilderSqlSessionFactory

MyBatis
上一篇下一篇

猜你喜欢

热点阅读