Mybatis

Mybatis简介

Mybatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为Mybatis 。2013年11月迁移到Github。ibatis一词来源于“internet”和“abatis”的组合,是一个基于Java的ORM持久层框架。ibatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。
ORM的全称是Object/Relation Mapping。当使用一种面向对象的编程语言来进行应用开发时,从项目一开始就采用面向对象分析、设计和编程,但到了持久层数据库访问时,又必须重返关系数据库的访问方式,于是出现了ORM工具,ORM框架是面向对象程序设计语言与关系型数据库发展不同步的中间解决方案。jdbc/dbutils/springdao,hibernate/springorm,mybaits同属于ORM解决方案之一。

Mybatis架构

这里写图片描述

框架架构讲解:

(1)加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。

(2)SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。

(3) SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。

(4)结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。

这里写图片描述

  1. mybatis配置SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
  2. 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂 。
  3. 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
  4. mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
  5. MappedStatement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个MappedStatement对象,sql的id即是Mappedstatement的id。
  6. MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过MappedStatement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
  7. MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过MappedStatement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

入门

安装

下载mybatis jar包置lib文件夹下,或maven构建项目,pom.xml配置方式如下:

  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.5</version>
  </dependency>

SqlMapConfig.xml配置构建SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。

SqlMapConfig.xml 配置

<!DOCTYPE configuration 
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- <properties resource="db.properties"></properties> -->
    <!--别名-->
    <typeAliases>
        <package name="com.yt.modules.model"/>
    </typeAliases>

    <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://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="student"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/userMapper.xml"/>
    </mappers>
</configuration>

其中environment可以配置多个,比如测试、开发和生产可能在不同的环境下,每个environment都有着一个SqlSessionFactory实例。mappers 元素则是包含一组 mapper 映射器(这些 mapper 的 XML 文件包含了 SQL 代码和映射定义信息)。

从配置文件中构建SqlSessionFactory实例

InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

从SqlSessionFactory中获取SqlSession

SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。

SqlSession sqlSession = sqlSessionFactory.openSession();

简单示例

数据库

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `sex` char(3) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

实体类:

package com.yt.modules.model;

public class User {
    private Integer id;
    private String name;
    private String sex;
    private Integer age;

    public User() {
        super();
    }
    public User(Integer id, String name, String sex, Integer age) {
        super();
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
    }
}

SqlMapConfig.xml配置如上,mybatis的mapper映射配置如下(mappers/userMapper.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="userMapper">
  <select id="selectUser" resultType="user">
    SELECT * FROM USER
  </select>
</mapper>

测试:

public class SqlSessionFactoryUtil {

    public static void main(String[] args){
        SqlSession sqlSession = null;
        try {
            InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            List<User> list = sqlSession.selectList("userMapper.selectUser");
            System.out.println(list);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }
}

命名空间

命名空间(Namespaces)在之前版本的 MyBatis 中是可选的,容易引起混淆因此是没有益处的。现在的命名空间则是必须的,目的是希望能比只是简单的使用更长的完全限定名来更进一步区分语句。
命名解析:为了减少输入量,MyBatis 对所有的命名配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。

  • 完全限定名(比如“mappers.userMapper.selectUser”)将被直接查找并且找到即用。
  • 短名称(比如“userMapper.selectUser”)如果全局唯一也可以作为一个单独的引用。如果不唯一,有两个或两个以上的相同名称(比如“mappers.userMapper”和“mappers.oracle.userMapper”),那么使用时就会收到错误报告说短名称是不唯一的,这种情况下就必须使用完全限定名。

SqlMapConfig.xml配置详解

Mybatis配置文件结构

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properties)信息。文档的顶层结构如下:

  • configuration
    • properties 属性
    • settings 设置
    • typeAliases 类型别名
    • typeHandlers 类型处理器
    • objectFactory 对象工厂
    • plugins 插件
    • environments 环境
      • environment 环境变量
        • transactionManager 事务管理器
        • dataSource 数据源
    • databaseIdProvider 数据库厂商标识
    • mappers 映射器

properties

这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。

db.properties:java属性文件配置

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis

mybatis-config.xml:properties子元素配置

<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</properties>
<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

Java类:properties对象设置

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
Properties props = new Properties();
props.setProperty("password","654321");
//Properties对象设置
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, props);

mybatis加载属性的顺序(可以覆盖):

  • 在 properties 元素体内指定的属性首先被读取。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。

从Mybatis 3.4.2开始,可以为一个占位符指定一个默认值:

<properties resource="db.properties">
    <!-- 开启使用默认值 -->
    <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>
<dataSource type="POOLED">
    <!-- 当username属性不存在,使用ut_user-->
    <property name="username" value="${username:ut_user}"/>
</dataSource>

settings

Mybatis极为重要的调整设置,它会改变Mybatis的运行时行为。

设置参数描述有效值默认值
cacheEnabled该配置影响的所有映射器中配置的缓存的全局开关。true / falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。true / falsefalse
aggressiveLazyLoading当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods).true / falsefalse (true in ≤3.4.1)
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’ 的日志等级必须设置为 WARN)FAILING: 映射失败 (抛出 SqlSessionException)NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定驱动等待数据库响应的秒数。任意正整数Not Set (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。任意正整数Not Set (null)
safeRowBoundsEnabled允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。true / falseFalse
safeResultHandlerEnabled允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。true / falseTrue
mapUnderscoreToCamelCase是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。true / falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。SESSION / STATEMENTSESSION
jdbcTypeForNull当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType enumeration. Most common are: NULL, VARCHAR and OTHEROTHER
lazyLoadTriggerMethods指定哪个对象的方法触发一次延迟加载。A method name list separated by commasequals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成的默认语言。A type alias or fully qualified class name.org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandlerSpecifies the TypeHandler used by default for Enum. (Since: 3.4.5)A type alias or fully qualified class name.org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。true / false false
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始)true / falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。Any StringNot set
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J / LOG4J / LOG4J2 / JDK_LOGGING / COMMONS_LOGGING / STDOUT_LOGGINGNO_LOGGING
proxyFactory指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。CGLIB / JAVASSISTJAVASSIST (MyBatis 3.3 or above)
vfsImpl指定VFS的实现自定义VFS的实现的类全限定名,以逗号分隔。Not set
useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始)true / falsetrue
configurationFactory指定一个提供Configuration实例的类. 这个被返回的Configuration实例是用来加载被反序列化对象的懒加载属性值. 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始)类型别名或者全类名.Not set

settings配置示例:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

typeAliases

类型别名是为Java类型设置一个短的名字,它只和XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余。XML配置有二种方式:

<!--为每个全限定名指定别名-->
<typeAliases>
    <typeAlias alias="author" type="com.yt.modules.model.Author"/>
    <typeAlias alias="user" type="com.yt.modules.model.User"/>
</typeAliases>
<!--指定包下的类的别名为类首字母小写-->
<typeAliases>
  <package name="com.yt.modules.model"/>
</typeAliases>

使用指定包名配置时,若想自定义可以使用注解@Alias配置。此外,Mybatis也内建了许多常见Java类型的相应类型别名,它们对大小写不敏感。

别名映射的JAVA类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

typeHandlers

Mybatis在预处理语句(PreparedStatement)中设置一个参数或从结果集取出一个值都会使用类型处理器。

objectFactory

MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现,实现ObjectFactory接口或继承DefaultObjectFactory。

public class MyObjectFactory extends DefaultObjectFactory {
    /*
     * 使用默认构造方法 
     */
    public <T> T create(Class<T> type) {
        return super.create(type);
    }

    /*
     * 使用参数构造方法
     */
    @Override
    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return super.create(type, constructorArgTypes, constructorArgs);
    }
    /*
     * objectFactory属性设置
     */
    @Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
    }

    @Override
    protected Class<?> resolveInterface(Class<?> type) {
        return super.resolveInterface(type);
    }

    @Override
    public <T> boolean isCollection(Class<T> type) {
        return super.isCollection(type);
    }

}
<!--初始化ObjectFactory实例后,property会被传递给setProperties方法-->
<objectFactory type="com.yt.modules.util.MyObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>

plugins

environments

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置,每个环境对应着一个SqlSessionFactory实例。

<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED"><!--UNPOOLED、POOLED、JNDI-->
                <property name="driver" value="${driver}"></property>
                <property name="url" value="${url}"></property>
                <property name="username" value="${username}"></property>
                <property name="password" value="${password}"></property>
            </dataSource>
        </environment>
    </environments>

default : 默认的环境id;
environment:定义一个环境;
transactionManager:事务处理器(JDBC和MANAGED类型),当与spring集成时无需配置,由spring容器管理;
dataSource:数据源配置(UNPOOLED、POOLED、JNDI)。

databaseIdProvider

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。

<!--DB_VENDOR 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进行设置-->
<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>        
  <property name="Oracle" value="oracle" />
  <property name="MySQL" value="mysql"/>
</databaseIdProvider>

在有 property时,DB_VENDOR databaseIdProvider 的将被设置为第一个能匹配数据库产品名称的属性键对应的值,如果没有匹配的属性将会设置为 “null”。

mappers

mybatis 的行为已经由上述元素配置完了,mappers配置mybatis的Sql映射文件位置。可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。

<mappers >
    <!--相对于类路径的资源引用-->
    <mapper resource="mappers/UserMapper.xml"/>
</mappers>
<mappers>
    <!--完全限定资源定位符(基本不用)-->
    <mapper url="file:///D:/mappers/UserMapper.xml"/>
</mappers>
<mappers>
    <!--使用接口类全限定名-->
    <mapper class="com.yt.modules.dao.UserMapper"/>
</mappers>  
<mappers>
    <!--使用包名-->
    <package name="com.yt.modules.dao"/>
</mappers>

Mapper XML文件

SQL 映射文件的顶级元素(按照它们应该被定义的顺序):

  • cache – 给定命名空间的缓存配置。
  • cache-ref – 其他命名空间缓存配置的引用。
  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
  • parameterMap – 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。
  • sql – 可被其他语句引用的可重用语句块。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

select

<select 
        id="selectUser" 
        parameterType="int" 
        resultMap="userResultMap"
        resultType="map"
        flushCache="false"
        useCache="true" 
        timeout="10000"
        fetchSize="256"
        statementType="PREPARED"
        resultSetType="FORWARD_ONLY"

        databaseId="mysql"
        resultOrdered="true"
        resultSets="user"
        >
        <!-- 
         parameterType 参数类型
         resultMap 外部resultMap的命名引用 
         resultType 返回的类的全限定名或别名,与resultMap不能同时使用
         fulshCache 为true时会清空本地缓存和二级缓存
         useCache 为true时会被二级缓存,默认true
         timeout 等待数据库返回请求结果的秒数,默认值为unset(依赖驱动)
         fetchSize 每次批量返回的结果行数,默认值为unset(依赖驱动)
         statementType 值为STATEMENT PREPARED CALLABLE,默认值为PREPARED
         resultSetType 结果集类型,FORWARD_ONLY SCROLL_SENSITIVE SCROLL_INSENSITIVE,默认值为unset(依赖驱动)   
         databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略
         resultOrdered 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false
         resultSets 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的

    -->

insert

<insert 
        id="insertUser"
        parameterType="int"
        statementType="STATEMENT"
        timeout="1000"
        flushCache="true"
        databaseId="mysql"
        useGeneratedKeys="true"
        keyColumn="user_id"
        keyProperty="userId"
        >
        <!-- useGeneratedKeys使用JDBC的getGeneratedKeys方法来获取由数据库内部产生的主键(数据库支持自动生成主键的字段,如MySQL、SQL Server)
             keyProperty将通过getGeneratedKeys或insert中selectKey生成的主键设置到属性中
             keyColumn将生成的主键值设置到表的对应列
     -->
     <selectKey keyProperty="userId" 
                keyColumn="user_id"
                resultType="int" 
                order="BEFORE" 
                statementType="STATEMENT" 
                databaseId="oracle"
                >
                <!-- oracle数据库通过序列生成主键 -->
                select SEQUENCE_USER.nextval as id from dual
                </selectKey>
    <!-- 
            order 这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 ,这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。
         -->           

update

<update 
        id="updateUser"
        parameterType="int"
        statementType="STATEMENT"
        timeout="1000"
        flushCache="true"
        databaseId="mysql"
        useGeneratedKeys="true"
        keyColumn="user_id"
        keyProperty="userId"
        >
        <selectKey 
                keyProperty="userId" 
                keyColumn="user_id"
                resultType="int" 
                order="BEFORE" 
                statementType="STATEMENT" 
                databaseId="oracle"
                >
                <!-- oracle数据库通过序列生成主键 -->
                select SEQUENCE_USER.nextval as id from dual
                </selectKey>
    </update>

delete

<delete 
        id="deleteUser"
        parameterType="int"
        statementType="STATEMENT"
        timeout="1000"
        flushCache="true"
        databaseId="mysql"
        >
    </delete>

sql

这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化。

属性值可以用于包含的refid属性或者包含的字句里面的属性值

<sql id="userColumns">${alias}.id,${alias}.name,${alias}.sex,${alias}.age</sql>
<sql id="sometable">${tablename}</sql>
<sql id="someinclude">from <include refid="${include_target}"></include></sql>

使用上述定义的sql

<select id="selectUser" resultType="map">
    SELECT
        <include refid="userColumns"><property name="alias" value="u"/></include>
        <include refid="someinclude"><property name="include_target" value="sometable"/>
        </include> u
</select>

cache

ResultMap

resultMap 元素是 Mybatis 中最重要最强大的元素。它就是让你远离 90%的需要从结果 集中取出数据的 JDBC 代码的那个东西, 而且在一些情形下允许你做一些 JDBC 不支持的事 情。ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们 的关系。

<select id="selectUser" resultType="map">
    SELECT * FORM USER
</select>

上述查询的结果的每条数据会被封装成一个Map集合,列名作为Map集合的key,而列的值作为Map的value。但Map集合并不能很好的描述一个领域模型,因此可以返回一个javabean。

<select id="selectUser" resultType="com.yt.modules.model.User">
    SELECT * FORM USER
</select>

Mybatis会将查询到的数据的列和需要返回的对象(User)的属性逐一匹配赋值,如果查询到的数据的列和需要返回的对象的属性不一致,则Mybatis就不会自动赋值,这时,就可以使用resultMap处理。

<!--对象与数据库映射-->
<resultMap id="userResultMap" type="com.yt.modules.model.User">
        <!-- 主键 -->
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="sex" column="sex"/>
        <result property="age" column="age"/>
</resultMap>

也可以使用构造方法注入属性

 <resultMap type="com.yt.modules.model.User" id="userMap">
    <constructor>
        <idArg column="id" javaType="_int"/>
        <arg column="name" javaType="string"/>
        <arg column="sex" javaType="string"/>
        <arg column="age" javaType="_int"/>
    </constructor>
</resultMap>
一对一映射

一个人只有一张身份证,它们的关系是一一对应,可以创建person和card二张表

CREATE TABLE `card` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(18) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(18) DEFAULT NULL,
  `sex` varchar(18) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `card_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `card_id` (`card_id`),
  CONSTRAINT `person_ibfk_1` FOREIGN KEY (`card_id`) REFERENCES `card` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
public class Card implements Serializable{
    private Integer id;
    private String card;
}

public class Person implements Serializable{
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    private Card card;
}

cardMapper.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="cardMapper">
    <select id="selectCardById" parameterType="int" resultType="card">
        SELECT * FROM card WHERE id=#{id}   
    </select>
  </mapper>

personMapper.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="personMapper">
    <resultMap type="com.yt.modules.model.Person" id="personMap">
        <id property="id" column="id"/>
        <result property="name" column="name" />
        <result property="sex" column="sex" />
        <result property="age" column="age" />

        <!-- 一对一关联映射:association -->
        <association 
            property="card" 
            column="card_id"
            select="cardMapper.selectCardById"
            javaType="com.yt.modules.model.Card"
            />
            <!--
                 property表示Person中的属性
                 column表示数据库中对应的字段 
                 javaType表示property的类型
                 select表示执行一条SQL语句,column作为参数,将结果封装到property属性中 ,使用<select id="...">中id
            -->
    </resultMap>

    <select id="selectPersonById" parameterType="int" resultMap="personMap">
        SELECT * FROM person WHERE id=#{id}
    </select>
  </mapper>
一对多映射

一对多是常见的关系,如一个班级有多个学生,一个学生只属于一个班级,创建clazz和student二张表

CREATE TABLE `clazz` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(10) NOT NULL COMMENT '班级编号',
  `name` varchar(10) NOT NULL COMMENT '班级名称',
  KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` varchar(2) DEFAULT NULL,
  `clazz_id` int(11) DEFAULT NULL,
  KEY `id` (`id`),
  KEY `clazz_id` (`clazz_id`),
  CONSTRAINT `student_ibfk_1` FOREIGN KEY (`clazz_id`) REFERENCES `clazz` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
public class Clazz {
    private Integer id;
    private String code;
    private String name;
    private List<Student> students;
}
public class Student {
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    private Clazz clazz;
}

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="studentMapper">
    <resultMap type="com.yt.modules.model.Student" id="studentMap">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="sex" column="sex"/>
        <result property="age" column="age"/>
        <!-- 一对一关联映射:association -->
        <association 
            property="clazz" 
            javaType="com.yt.modules.model.Clazz"
            column="clazz_id"
            select="clazzMapper.selectClazzById"
            >
            <id property="id" column="id"/>
            <result property="code" column="code"/>
            <result property="name" column="name"/>
        </association>
    </resultMap>
    <select id="selectStudentByClazzId" parameterType="int" resultType="student">
        SELECT * FROM student WHERE clazz_id=#{id}
    </select>
        <select id="selectStudentId" parameterType="int" resultMap="studentMap">
        SELECT * FROM student WHERE id=#{id}
    </select>
  </mapper>

clazzMapper.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="clazzMapper">
    <resultMap type="com.yt.modules.model.Clazz" id="clazzMap">
        <id property="id" column="id"/>
        <result property="code" column="code"/>
        <result property="name" column="name"/>
        <!-- 一对多关联映射:collection fetchType="lazy"表示懒加载 -->
        <!-- column 作为查询的参数,id是clazz的id字段值 -->
        <collection 
            property="students" 
            javaType="ArrayList"
            column="id"
            ofType="com.yt.modules.model.Student"
            select="studentMapper.selectStudentByClazzId"
            >
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="sex" column="sex"/>
            <result property="age" column="age"/>
        </collection>
    </resultMap>
    <select id="selectClazzById" parameterType="int" resultMap="clazzMap">
        SELECT * FROM clazz WHERE id=#{id}
    </select>
  </mapper>
多对多映射

动态SQL

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

  • if
  • choose(when,otherwise)
  • trim(where,set)
  • foreach
  • bind

if

通过条件控制where子句的条件,网页中通过多条件查询时就体现了if的作用。

<select id="queryUserByIf" parameterType="user" resultType="user" >
    SELECT * FORM user WHERE name=#{name}
    <if test="age != null">
        AND age = #{age}
    </if>
    <if test="sex != null">
        AND sex = #{sex}
    </if>
</select>

choose,when,otherwise

有些时候,我们不想用到所有的条件语句,而只想从中择其一二,choose类似于JAVA中的switch。

<select id="queryUserByWhen" parameterType="user" presultType="user">
    SELECT * FORM user WHERE name=#{name}
    <choose>
        <when test="age != null">
            AND age = #{age}
        </when>
        <when test="sex != null">
            AND sex = #{sex}
        </when>
        <otherwise>
            AND id = #{id}
        </otherwise>
    </choose>
</select>

where,trim,set

对于第一个if示例中,若name=#{name}也有判断条件时,若第一个条件不成立,后面的条件成立,将会出现”WHERE AND”,或都不成立时出现”WHERE”,这并不是我们希望的。where元素提供了简单的处理,where元素知道只有在一个以上的if条件成立时才插入“WHERE”子句,而且也会去除多余的“AND”或“OR”。

<select id="queryUserByWhere" parameterType="user" resultType="user" >
    SELECT * FORM user
    <where>
        <if test="name != null">
            name=#{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="sex != null">
            AND sex = #{sex}
        </if>
    </where>
</select>

如果 where 元素没有按正常套路出牌,还可以通过自定义 trim 元素来定制我们想要的功能,与where元素等价的自定义trim元素为:

<select id="queryUserByTrim" parameterType="user" resultType="user" >
    SELECT * FORM user
    <trim prefix="WHERE" prefixOverrides="AND ">   <!-- AND后面空格不可忽略 -->
        <if test="name != null">
            name=#{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="sex != null">
            AND sex = #{sex}
        </if>
    </trim>
</select>

类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的,也会消除无关的逗号。

<update id="updateUserBySet">
    update user
    <set>
        <if test="name != null">
            name=#{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="sex != null">
            sex = #{sex}
        </if>
    </set>
    WHERE id=#{id}
</update>

等价的trim

<update>
    update user
    <trim prefix="SET" suffixOverrides=",">
        <if test="name != null">
            name=#{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="sex != null">
            sex = #{sex}
        </if>
    </trim>
    WHERE id=#{id}
</update>

foreach

动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。

<select id="queryPostIn" parameterType="list" resultType="user">
        SELECT * FROM user
        WHERE id in
        <foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
            #{item}
        </foreach>
    </select>

你可以将任何可迭代对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符,并且不会偶然地附加多余的分隔符。当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字典(或者Map.Entry对象的集合)时,index是键,item是值。

bind

bind元素可以从OGNL表达式中创建一个变量并将其绑定到上下文。

<select id="" parameterType="user" resultType="user">
    <bind name="pattern" value="'%'+_parameter.getName()+'%'"/>
    SELECT * FROM user 
    WHERE name LIKE #{pattern} 
</select>

动态SQL防注入

Mybatis提供了两种支持动态 sql 的语法:#{} 以及 ${},可以用来传递参数

select * from ${PLAT}.user where username=#{name}
select * from ${PLAT}.user where username=${name}

${}与#{}的区别

Mybatis在对SQL语句进行预编译之前,会对SQL进行动态解析。在动态SQL解析阶段,#{}和${}会有不同的表现:

  • #{}:被解析为JDBC预编译语句(prepared statement)的参数占位符?
  • ${}:被变量的值替换成纯粹的String

前面的sql可能被动态解析成如下:

select * from root.user where username=?
select * from root.user where username=smith

综上所述,${}预编译之前已经不存在变量了,而每个#{}会被解析成预编译语句的占位符。

预编译

Mybatis默认是开启预编译的,在动态解析完SQL语句后,会使用PreparedStatement对象抽象预编译语句,编译后产生的 PreparedStatement 对象会被缓存下来,下次对于同一个SQL,可以直接使用这个缓存的 PreparedStatement 对象,然后将参数一起发送给DBMS处理。

SQL注入

SQL注入原理:sql注入只对sql语句的准备(编译)过程有破坏作用,而PreparedStatement是已经编译完成了,执行阶段只是把输入串作为数据处理,而不再对sql语句进行解析、准备,因此也就避免了sql注入问题。

因此#{}可以防止SQL注入问题,而${}的参数替换在预编译之前由Mybatis的动态SQL解析完成,极易被SQL注入,如:

参数:"simth" and 1=1
select * from user where username=${name}

解析后:
select * from user where username="simth" and 1=1

如果使用#{name}则如下:

预编译语句:select * from user where username=?
执行SQLselect * from user where username="\"simth\" and 1=1"

对于表名参数则必须要使用${},这是由于表名是字符串类型,如果使用#{},则会出现如下情况:

参数:user
预编译SQL:select * from ?;
执行SQL:select * from 'user';

显然SQL会执行异常,因此对于表名的参数需要开发者自己做相关的数据校验,避免攻击者SQL注入。

JAVA API

SqlSession是Mybatis最重要的JAVA接口之一,它的实现类有DefaultSqlSession和SqlSessionManager,SqlSessions 是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对 象 包 含 创 建 SqlSession 实 例 的 所 有 方 法 。 而 SqlSessionFactory 本 身 是 由 SqlSessionFactoryBuilder 创建的, 当Mybatis与一些依赖注入框架(如Spring或者Guice)同时使用时,SqlSessions将被依赖注入框架所创建。

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder有9个build方法:

SqlSessionFactory build(Configuration config)
SqlSessionFactory build(InputStream inputStream)
SqlSessionFactory build(InputStream inputStream, String environment)
SqlSessionFactory build(InputStream inputStream, Properties properties)
SqlSessionFactory build(InputStream inputStream, String env, Properties props)
SqlSessionFactory build(Reader reader)
SqlSessionFactory build(Reader  reader, String environment)
SqlSessionFactory build(Reader reader, Properties properties)
SqlSessionFactory build(Reader reader, String env, Properties props)

environment表示XML配置中的environment Id,当不指定时使用environments默认id。

获取SqlSessionFactory

String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);

Resources工具类用于获取classpath路径下的文件,主要方法如下:

URL getResourceURL(String resource)
URL getResourceURL(ClassLoader loader, String resource)
InputStream getResourceAsStream(String resource)
InputStream getResourceAsStream(ClassLoader loader, String resource)
Properties getResourceAsProperties(String resource)
Properties getResourceAsProperties(ClassLoader loader, String resource)
Reader getResourceAsReader(String resource)
Reader getResourceAsReader(ClassLoader loader, String resource)
File getResourceAsFile(String resource)
File getResourceAsFile(ClassLoader loader, String resource)
InputStream getUrlAsStream(String urlString)
Reader getUrlAsReader(String urlString)
Properties getUrlAsProperties(String urlString)
Class classForName(String className)

通过Configuration实例创建SqlSessionFactory

BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:mysql://localhost:8080/mytest");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");

TransactionFactory transactionFactory = new JdbcTransactionFactory();

Environment environment = new Environment("development", transactionFactory, dataSource);

Configuration configuration = new Configuration(environment);
configuration.setLazyLoadingEnabled(true);
configuration.setEnhancementEnabled(true);
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
configuration.getTypeAliasRegistry().registerAlias(Post.class);
configuration.getTypeAliasRegistry().registerAlias(Author.class);
configuration.addMapper(BoundBlogMapper.class);
configuration.addMapper(BoundAuthorMapper.class);

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configuration);

SqlSessionFactory

SqlSessionFactory 有六个方法可以用来创建 SqlSession 实例。可选参数:

  • Transaction (事务): 你想为 session 使用事务或者使用自动提交(通常意味着很多 数据库和/或JDBC驱动没有事务)?
  • Connection (连接): 你想 MyBatis 获得来自配置的数据源的连接还是提供你自己

SqlSessionFactory的方法:

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();

默认的openSession方法没有参数,他创建的SqlSession有如下特性:

  • 会开启一个事务(也就是不自动提交)
  • 连接对象会从由活动环境配置的数据源实例中得到。
  • 事务隔离级别将会使用驱动或数据源的默认设置。
  • 预处理语句不会被复用,也不会批量处理更新。

autoCommit可用于设置是否自动提交;
Connection用于用户传入自定义连接;
TransactionIsolationLevel是一个Java枚举类型,支持NONE,READ_UNCOMMITTED,READ_COMMITTED,REPEA TABLE_READ,SERIALIZA BLE 5个事务等级。
ExecutorType也是一个Java枚举类型:

  • ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。
  • ExecutorType.REUSE: 这个执行器类型会复用预处理语句。
  • ExecutorType.BATCH: 这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是 必须的,来保证一个简单并易于理解的行为。

SqlSession

SqlSession是Mybatis的关键对象,是执行持久化操作的对象,它是应用程序与持久化存储层之间执行交互操作的一个单线程对象。
SqlSession的常用方法如下:

int insert(String statement)
int insert(String statement, Object parameter)

int update(String statement)
int update(String statement, Object parameter)

int delete(String statement)
int delete(String statement, Object parameter)

<T> T selectOne(String statement)
<T> T selectOne(String statement, Object parameter)

<E> List<E> selectList(String statement)
<E> List<E> selectList(String statement, Object parameter)
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)

<K,V> Map<K,V> selectMap(String statement, String mapKey)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)

void select (String statement, ResultHandler<T> handler)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)

//当ExecutorType.BATCH作为ExecutorType使用时可以采用此方法刷新(执行)存储在JDBC驱动类中的批量更新语句
List<BatchResult> flushStatements()
//清理Session级缓存,SqlSession 实例有一个本地缓存在执行 update,commit,rollback 和 close 时被清理
void clearCache()
Configuration getConfiguration()
Connection getConnection()
void close()

/*
 *当选择了自动提交或外部事务管理器,以下方法没有效果 
 */
void commit()
void commit(boolean force)
void rollback()
void rollback(boolean force)

<T> T getMapper(Class<T> type)

ResultHandler对象用于处理查询返回的复杂结果集,通常用于多表查询。
RowBounds对象可用于分页,它的二个属性:offset指略过指定数量的记录,limit指当前页显示多少条数据。

int offset = 10;
int limit = 5;
RowBounds rowBounds = new RowBounds(offset,limit);

SqlSession直接使用insert,update,delete,select没有类型安全,getMapper(Class<T> type)返回mapper接口的代理对象,该对象关联了SqlSession对象,开发者可以通过该对象直接调用方法操作数据库。下面展示方法如何映射到SqlSession:

public interface UserMapper {
  // (User) selectOne("selectUser",id);
  User selectUser(int id); 
  // (List<User>) selectList(“selectUsers”)
  List<User> selectUsers();
  // (Map<Integer,User>) selectMap("selectUsers", "id")
  @MapKey("id")
  Map<Integer, User> selectUsers();
  // insert("insertUser", user)
  int insertUser(User user);
  // updateUser("updateUser", user)
  int updateUser(User user);
  // delete("deleteUser",5)
  int deleteUser(int id);
}

总之, 每个映射器方法签名应该匹配相关联的 SqlSession 方法, 而没有statement参数, 但是方法名必须匹配映射语句的 ID。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值