查看原文
其他

MyBatis 入门看这一篇就够了~

汪伟俊 Java技术迷 2021-11-29

汪伟俊 | 作者

Java技术迷 | 出品


MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。


MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

本篇文章我们就来一起学习这一优秀的持久层框架——MyBatis。

MyBatis初体验

持久层框架当然是操作数据库了,为此,创建数据库和数据表:

CREATE DATABASE mybatis;

CREATE TABLE tbl_employee(
 id INT(11) PRIMARY KEY AUTO_INCREMENT,
 last_name VARCHAR(255),
 gender CHAR(1),
 email VARCHAR(255)
);

INSERT INTO tbl_employee VALUES('tom',0,'tom@qq.com');

接下来我们创建一个maven项目,引入MyBatis的依赖:

<!-- mybatis -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.4.1</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.37</version>
</dependency>

接着创建数据表对应的Bean类:

@Data
@ToString
public class Employee {

    private Integer id;
    private String lastName;
    private char gender;
    private String email;
}

需要知道的是,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。

SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

所以我们需要在类路径下编写一个XML文件(mybatis-config.xml)来完成SqlSessionFactory的构建:

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 注册Mapper映射文件 -->
    <mappers>
        <mapper resource="EmployeeMapper.xml"/>
    </mappers>
</configuration>

该文件即为MyBatis的全局配置文件,用于配置MyBatis的一些功能,这里需要注意两点:首先是数据源的配置,然后是Mapper映射文件的注册,MyBatis的增删改查完全依赖于Mapper映射文件,所以我们还需要在类路径下编写Mapper映射文件(EmployeeMapper.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.wwj.mybatis.mapper.EmployeeMapper">
    <select id="selectEmp" resultType="com.wwj.mybatis.bean.Employee">
        select * from tbl_employee where id = #{id}
    </select>
</mapper>

这里也需要注意几点, namespace 意为名称空间,主要起一个标识作用,可随意命名;其次是 <select> 标签,它表示的是一个查询操作,相应地,它还有 <insert><delete><update> 标签分别用于插入、删除和更新;在 <select> 标签中指定了id和resultType属性,其中id仍然为一个标识符,resultType为本次查询得到的结果类型,MyBatis会自动将数据表中查询到的数据封装成该类型,最后在标签内编写sql语句即可, #{id} 能够取出传递过来的id值。

一切准备就绪后,我们就可以编写业务代码了:

@Test
public void test() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream stream = Resources.getResourceAsStream(resource);
    // 构建sqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
    // 获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 执行sql
    Employee employee = sqlSession.selectOne("selectEmp"1);
    System.out.println(employee);
}

通过全局配置文件构建sqlSessionFactory对象,并通过sqlSessionFactory获取sqlSession,调用selectOne方法即可查询到一个结果,第一个参数值为Mapper映射文件中操作标签的id值,为了避免重复引发的问题,一般以名称空间 + 操作标签id值为参数值:com.wwj.mybatis.mapper.EmployeeMapper.selectEmp ;第二个参数值会被MyBatis传递给sql,也就是说,第二个参数值1会被 #{id} 成功获取。

执行结果:

Employee(id=1, lastName=null, gender=0, email=tom@qq.com)

很显然,我们查询到了结果,然而会发现lastName属性值为空,这是为什么呢?原来,JavaBean中的lastName与数据表中last_name字段不匹配,为了解决这一问题,我们可以重新编写一下sql语句:

select id,last_name lastName,gender,email from tbl_employee where id = #{id}

在查询的时候将字段重新设置属性名即可,不过MyBatis也考虑到了这一点,所以特地设置了一个全局属性,我们只需要开启它即可自动处理这一问题:

<settings>
  <!-- 开启驼峰命名 -->
  <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

在全局配置文件中设置开启驼峰命名即可,但需要注意,它只能处理遵守驼峰命名规范的属性,比如lastName和last_name,empSalary和emp_salary。

接口式编程

在刚才我们体验了一下MyBatis框架的简单查询,会发现这种方式存在着一些问题,比如操作标识过长、第二个参数因为是Object类型,也会出现很多错误,为此,MyBatis提供了更加优雅的解决方案——接口式编程。

我们改造一下刚才的程序,首先编写一个接口:

public interface EmployeeMapper {

    Employee getEmpById(Integer id);
}

在该接口中我们可以声明各种操作方法而无需具体实现,比如通过id查询Employee,此时我们只需要稍加修改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.wwj.mybatis.mapper.EmployeeMapper">
    <select id="getEmpById" resultType="com.wwj.mybatis.bean.Employee">
        select * from tbl_employee where id = #{id}
    </select>
</mapper>

首先将名称空间写为接口的全类名,此时该映射文件会与接口进行唯一绑定,此时 <select> 标签的id属性值就需要写为接口方法名,resultType为接口方法的返回值。

到这里就改造完成了,当然,我们的业务代码也发生了相应的变化:

@Test
public void test() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream stream = Resources.getResourceAsStream(resource);
    // 构建sqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
    // 获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 获取Mapper接口的实现
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    // 调用方法
    Employee employee = employeeMapper.getEmpById(1);
    System.out.println(employee);
}

仍然需要构建sqlSessionFactory对象,并通过其获得sqlSession,此时通过sqlSession调用getMapper方法就能够获取到MyBatis为我们创建的接口实现类,此时通过该实现类即可调用方法完成查询。

执行结果:

Employee(id=1, lastName=tom, gender=0, email=tom@qq.com)

全局配置文件

掌握了简单的查询操作之后,我们来看看MyBatis的全局配置文件究竟能做些什么?

引入外部属性文件

还记得我们在全局配置文件中如何配置数据源的吗?

<dataSource type="POOLED">
  <property name="driver" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql:///mybatis"/>
  <property name="username" value="root"/>
  <property name="password" value="123456"/>
</dataSource>

这种方式将属性值写死在了配置文件中,显然不是一个好的办法,为此,我们可以使用MyBatis提供的 properties 标签来引入外部的属性文件,所以在类路径下编写一个属性文件(dbconfig.properties):

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis
jdbc.username=root
jdbc.password=123456

此时将该文件引入即可:

<?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>
  <!-- 引入外部属性文件 -->
  <properties resource="dbconfig.properties"/>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <!-- 配置数据源 -->
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.Driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
      </dataSource>
    </environment>
  </environments>
  ......
</configuration>

properties 标签有两种方式来引入外部属性文件:

  • resource:引入本地属性文件
  • url:引入网络属性文件

将属性文件引入后,我们就可以通过 ${键名} 的方式取出属性文件中键名对应的键值,实现数据源的动态配置。

settings

全局配置文件中有一个非常重要的标签:settings ,通过该标签能够设置MyBatis的一些运行时行为,比如我们在入门案例中开启了MyBatis的驼峰命名。settings标签能够设置非常多的行为,具体列举如下:

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
aggressiveLazyLoading开启时,任一方法的调用都会加载该对象的所有延迟加载属性。否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。true | falsefalse (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled是否允许单个语句返回多结果集(需要数据库驱动支持)。true | falsetrue
useColumnLabel使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。true | falsetrue
useGeneratedKeys允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。true | falseFalse
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。FULL 会自动映射任何复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
autoMappingUnknownColumnBehavior指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException)NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement);BATCH 执行器不仅重用语句还会执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定数据库驱动等待数据库响应的秒数。任意正整数未设置 (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。任意正整数未设置 (null)
defaultResultSetType指定语句默认的滚动策略。(新增于 3.5.2)FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)未设置 (null)
safeRowBoundsEnabled是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。true | falseFalse
safeResultHandlerEnabled是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。true | falseTrue
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。true | falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。默认值为 SESSION,会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。SESSION | STATEMENTSESSION
jdbcTypeForNull当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。OTHER
lazyLoadTriggerMethods指定对象的哪些方法触发一次延迟加载。用逗号分隔的方法列表。equals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成使用的默认脚本语言。一个类型别名或全限定类名。org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)一个类型别名或全限定类名。org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。true | falsefalse
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回 null。当开启这个设置时,MyBatis会返回一个空实例。请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)true | falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。任何字符串未设置
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
proxyFactory指定 Mybatis 创建可延迟加载对象所用到的代理工具。CGLIB | JAVASSISTJAVASSIST (MyBatis 3.3 以上)
vfsImpl指定 VFS 的实现自定义 VFS 的实现的类全限定名,以逗号分隔。未设置
useActualParamName允许使用方法签名中的名称作为语句参数名称。为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)true | falsetrue
configurationFactory指定一个提供 Configuration 实例的类。这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)一个类型别名或完全限定类名。未设置
shrinkWhitespacesInSql从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。(新增于 3.5.5)true | falsefalse
defaultSqlProviderTypeSpecifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.A type alias or fully qualified class nameNot set

所有属性的默认值,及其各值对应的功能在表中都已详细介绍。

typeAliases

typeAliases意为类型别名,该标签能够为Java类型设置一个缩短的名字,以此方便程序的书写。

比如EmployeeMapper.xml文件中:

<select id="getEmpById" resultType="com.wwj.mybatis.bean.Employee">
  select * from tbl_employee where id = #{id}
</select>

resultType的属性值为Employee的全类名,我们可以在全局配置文件中设置Employee的别名:

<typeAliases>
  <typeAlias type="com.wwj.mybatis.bean.Employee" alias="emp"/>
</typeAliases>

type属性指定需要设置别名的Java类型,默认别名为类名首字母小写,也可以设置alias属性来指定别名,现在EmployeeMapper.xml文件中就可以简写为:

<select id="getEmpById" resultType="emp">
  select * from tbl_employee where id = #{id}
</select>

当我们需要起别名的Java类型过多时,一个一个地进行设置显然不太合理,为此,我们可以使用 package 标签来实现批量起别名,比如:

<typeAliases>
  <package name="com.wwj.mybatis.bean"/>
</typeAliases>

此时 com.wwj.mybatis.bean 包及其子包下的所有类都将被设置别名,默认为类名首字母小写;然而别名是不区分大小写的,所以大小写均可。

environments

该标签用于配置环境,通过其子标签 environment 可以配置多个环境并切换,比如:

<environments default="development">
  <environment id="test">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
    </dataSource>
  </environment>

  <environment id="development">
    <transactionManager type="JDBC"/>
    <!-- 配置数据源 -->
    <dataSource type="POOLED">
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
    </dataSource>
  </environment>
</environments>

通过default属性指定id即可轻松切换环境,在 environment 标签中需要配置两个属性, transactionManagerdataSource ,其中 transactionManager 有两种取值:

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。

dataSource 有三种取值:

  • UNPOOLED** **– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
  • POOLED – 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。这种处理方式很流行,能使并发 Web 应用快速响应请求。
  • JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

后期我们的数据源将交由Spring管理,所以这里简单地了解一下即可。

databaseIdProvider

该标签用于多数据库支持,可以很方便地进行数据库的移植,所以我们准备一下Oracle的配置信息,首先是数据源的配置:

orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=123456

然后在全局配置文件中配置一个 enviroment标签,并将环境指定为Oracle:

<environments default="oracle">
  <environment id="oracle">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
      <property name="driver" value="${orcl.driver}"/>
      <property name="url" value="${orcl.url}"/>
      <property name="username" value="${orcl.username}"/>
      <property name="password" value="${orcl.password}"/>
    </dataSource>
  </environment>

  <environment id="mysql">
    <transactionManager type="JDBC"/>
    <!-- 配置数据源 -->
    <dataSource type="POOLED">
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
    </dataSource>
  </environment>
</environments>

<databaseIdProvider type="DB_VENDOR">
  <!-- 为数据库厂商起一个别名 -->
  <property name="MySQL" value="mysql"/>
  <property name="Oracle" value="oracle"/>
</databaseIdProvider>

在这里我们使用 databaseIdProvider 标签为数据库产商起了一个别名,然后修改Mapper映射文件的内容:

<mapper namespace="com.wwj.mybatis.mapper.EmployeeMapper">
    <select id="getEmpById" resultType="employee" databaseId="mysql">
        select * from tbl_employee where id = #{id}
    </select>

    <select id="getEmpById" resultType="employee" databaseId="oracle">
        select * from tbl_employee where id = #{id}
    </select>
</mapper>

分别指定 databaseId 为刚才起的数据库厂商别名即可,最后不要忘记导入Oracle的驱动:

<dependency>
  <groupId>com.oracle</groupId>
  <artifactId>ojdbc6</artifactId>
  <version>11.2.0.3</version>
</dependency>

mappers

该标签用户注册Mapper的映射文件,比如:

<mappers>
  <mapper resource="EmployeeMapper.xml"/>
</mappers>

mapper 标签共有三种方式进行注册:

  • resource:注册类路径下的文件
  • url:注册网络或磁盘路径下的文件
  • class:注册接口,一般用在基于注解的接口上,比如:
public interface EmployeeMapper {

    @Select("select * from tbl_employee where id = #{id}")
    Employee getEmpById(Integer id);
}

mappers 标签还支持批量注册,用法如下:

<mappers>
  <package name="com.wwj.mybatis.mapper"/>
</mappers>

但此时需要注意,Mapper映射文件必须与其绑定的接口文件名一致,且必须位于同包下,否则MyBatis是找不到映射文件的。

Mapper映射文件

MyBatis的重中之重就是Mapper映射文件,所有的增删改查都依靠映射文件来完成。

实现增删改查

在前面我们已经使用了MyBatis的查询功能,接下来分别实现一下数据的新增、修改和删除。

首先在EmployeeMapper接口中声明三个方法:

public interface EmployeeMapper {

    void addEmp(Employee employee);
    void updateEmp(Employee employee);
    void deleteEmpById(Integer id);
}

声明了方法后,就可以在EmployeeMapper.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.wwj.mybatis.mapper.EmployeeMapper">
    <insert id="addEmp" parameterType="com.wwj.mybatis.bean.Employee">
        insert into tbl_employee(last_name, gender, email)
        values (#{lastName}, #{gender}, #{email})
    </insert>
    <update id="updateEmp">
        update tbl_employee
        set last_name=#{lastName},
            gender=#{gender},
            email=#{email}
        where id = #{id}
    </update>
    <delete id="deleteEmpById">
        delete
        from tbl_employee
        where id = #{id}
    </delete>
</mapper>

都是一些基础操作,没什么好说的,唯一需要注意的是 parameterType 用于指定方法的参数类型,也可以不指定。

最后编写业务代码:

@Test
public void test() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream stream = Resources.getResourceAsStream(resource);
    // 构建sqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
    // 获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 获取Mapper接口的实现
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    // 调用方法
    Employee employee = new Employee(null"jack"'1'"jack@qq.com");
    employeeMapper.addEmp(employee);
    // 提交
    sqlSession.commit();
}

需要注意,通过 openSession() 得到的SqlSession对象是不会自动提交数据的,所谓在进行非查询的操作时,我们一定要调用 commit() 方法进行手动提交,当然你也可以在调用 openSession(true) 时传入 true 来得到一个能够自动提交的SqlSession对象。

接下来是更新操作:

Employee employee = new Employee(1"jack"'1'"jack@qq.com");
employeeMapper.updateEmp(employee);

最后是删除操作:

employeeMapper.deleteEmpById(2);

MyBatis还支持非查询方法直接返回一些值以作为操作是否成功执行的判断条件,支持的类型如下:

  • int、Integer
  • long、Long
  • boolean、Boolean

其中int和long会返回影响的行数,boolean会返回操作是否成功(影响的行数大于0则为true,否则为false),比如:

public interface EmployeeMapper {

    int addEmp(Employee employee);
    long updateEmp(Employee employee);
    boolean deleteEmpById(Integer id);
}

获取自增主键的值

在刚才的新增操作中,因为数据表的id列是自增的,所以我们无需设置id的值,但有时候,我们需要知道新增的数据id是多少,这个时候该怎么办呢?我们只需要在 insert 标签中设置useGeneratedKeys属性为true即可:

<insert id="addEmp" parameterType="com.wwj.mybatis.bean.Employee"
        useGeneratedKeys="true" keyProperty="id">

  insert into tbl_employee(last_name, gender, email)
  values (#{lastName}, #{gender}, #{email})
</insert>

keyProperty属性用于指定主键id需要被封装到哪个属性上,此时我们执行新增操作:

Employee employee = new Employee(null"jack"'1'"jack@qq.com");
employeeMapper.addEmp(employee);
System.out.println(employee);

id值将会被自动封装到Employee的id属性上,结果如下:

Employee(id=3, lastName=jack, gender=1, email=jack@qq.com)

参数处理

我们发现,只要在接口上声明参数,就能够在映射文件中通过 #{} 取出参数值,那么这一过程又有哪些讲究呢?

对于单个参数的情况,MyBatis不会做特殊的处理,比如这样的一个接口方法:

Employee getEmpById(Integer id);

映射文件:

<select id="getEmpById" resultType="employee">
  select *
  from tbl_employee
  where id = #{id}
</select>

这里就可以通过 #{id} 取出id值,事实上,对于一个参数的情况,你可以随意编写参数名,比如 #{a}#{1} 都是没有问题的。

若是接口方法有多个参数,比如:

Employee getEmpByIdAndLastName(Integer id,String lastName);

这个时候映射文件中的参数名就不能随便写了:

<select id="getEmpByIdAndLastName" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where id = #{param1}
  and last_name = #{param2}
</select>

对于多个参数,MyBatis会将参数封装到一个Map集合中,并以param1、param2、...作为每个参数的键,而 #{} 实质上是从Map中取出参数值,所以需要指定键为param1和param2;我们还可以通过参数值的索引进行获取:

<select id="getEmpByIdAndLastName" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where id = #{0}
  and last_name = #{1}
</select>

原理是一样的。

相信大家能够发现这种方式的弊端,首先不够见名知意,其次,当参数过多时,会发生顺序上的混乱,为此,我们需要使用 命名参数  。命名参数能够自定义MyBatis在封装参数值时的键名,用法如下:

Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("lastName") String lastName);

此时我们就能够通过这两个参数名获取参数值了:

<select id="getEmpByIdAndLastName" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where id = #{id}
  and last_name = #{lastName}
</select>

若是接口方法中的参数是一个Bean:

Employee getEmpById(Employee employee);

则在映射文件中可以直接通过Bean中的属性名获取属性值。

而如果接口方法中的参数是一个Map:

Employee getEmpById(Map<String,Object> map);

则在映射文件中仍然可以直接通过Map中的键名获取键值。

最后需要注意,如果接口方法中的参数是List、Set或者数组,则MyBatis会将集合或者数组中的数据封装到Map中,而且键名也会特殊处理,比如:

Employee getEmpById(List<Integer> ids);

那么若是想取出集合中的第一个id值,必须这样取:#{list[0]}  ,键名定义如下:

  • Collection:Collection的子类集合都可以通过 collection 获取值
  • List:List集合可以通过 collectionlist 获取值
  • Set:Set集合可以通过 collectionset 获取值
  • 数组:数组可以通过 array 获取值

对于参数的取值,前面我们一直使用 #{} 获取,而事实上,我们还能够通过 ${} 获取参数值,那么它俩有啥区别呢?它俩其实没有太大区别,唯一的差异在于使用 ${} 取值能够在生成的sql语句上查看到参数的值,而使用 #{} 就只能看到占位符,所以使用 #{} 的安全性更高,若是想输出MyBatis生成的sql语句,需要在全局配置文件中配置:

<setting name="logImpl" value="STDOUT_LOGGING" />

#{} 输出的sql:

select * from tbl_employee where id = ? and last_name = ?

${} 输出的sql:

select * from tbl_employee where id = 1 and last_name = jack

select标签

在数据表的操作中,数查询操作最为频繁,所以我们来详细了解一下用于查询的select标签。

对于单个数据的查询结果,我们只需要指定resultType即可,倘若查询到的是多个值,该如何编写呢?首先是接口方法:

List<Employee> getEmpsByLastNameLike(@Param("lastName") String lastName);

然后编写映射文件:

<select id="getEmpsByLastNameLike" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where last_name like #{lastName}
</select>

resultType只需要指定为集合中的元素类型即可,编写业务代码:

List<Employee> emps = employeeMapper.getEmpsByLastNameLike("%c%");
for (Employee emp : emps) {
    System.out.println(emp);
}

执行结果:

Employee(id=1, lastName=jack, gender=1, email=jack@qq.com)
Employee(id=3, lastName=jack, gender=1, email=jack@qq.com)

若是想将一条数据封装成一个Map集合,其中键为列名,值为数据值,该如何实现呢?首先是接口方法:

Map<String,Object> getEmpByIdReturnMap(@Param("id") Integer id);

编写配置:

<select id="getEmpByIdReturnMap" resultType="java.util.Map">
  select *
  from tbl_employee
  where id = #{id}
</select>

将resultType指定为Map类型,当然也可以使用别名 map ,编写业务代码:

Map<String, Object> map = employeeMapper.getEmpByIdReturnMap(1);
System.out.println(map);

执行结果:

{gender=1, last_name=jack, id=1, email=jack@qq.com}

若是想将多条数据都封装到一个Map里,其中键为当前数据的主键,值为数据对象,则首先编写接口方法:

@MapKey("id")
Map<Integer,Employee> getEmpsByLastNameReturnMap(@Param("lastName") String lastName);

其次进行配置:

<select id="getEmpsByLastNameReturnMap" resultType="java.util.Map">
  select *
  from tbl_employee
  where last_name like #{lastName}
</select>

resultType仍然指定为Map类型,只需要在接口方法标上@MapKey("id")注解即可,意为指定Employee中的id属性为Map的键名,编写业务代码:

Map<Integer, Employee> map = employeeMapper.getEmpsByLastNameReturnMap("%c%");
System.out.println(map);

执行结果:

{1={gender=1, last_name=jack, id=1, email=jack@qq.com}, 
3={gender=1, last_name=jack, id=3, email=jack@qq.com}}

以上就是本文针对MyBatis基础的全部内容了,希望对大家有帮助。

本文作者:汪伟俊 为Java技术迷专栏作者 投稿,未经允许请勿转载。

🍁


关注Java技术迷看更多干货





更多精彩推荐

 牛逼至极!用这个神器看代码太舒服了~
☞ 推荐一款 IDEA 神器,人工智能帮你写代码,再也不用加班啦 Java后端开发者的福音,接口开发利器来袭,有点厉害! Win10最新蓝屏死机Bug大规模爆发,已有数亿人中招 好家伙!30 行代码实现蚂蚁森林自动收能量,低调别让好友知道

 苹果这个神级功能竟然被用来搞黄色?已有人被抓!

点分享点点赞点在看
: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存