mybatis

mybatis

这里写图片描述

一、介绍

1、关于mybatis

mybatis是一个持久层的框架,是apache下的顶级项目。它让程序将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要的sql语句。mybatis可以将preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)

2、相关概念

  • SqlMapConfig.xml:mybatis的全局配置文件,配置数据源、事务等mybatis运行环境。
  • SqlSessionFactory(会话工厂):根据配置文件配置工厂,创建SqlSession。一旦被创建就应该在应用的运行期间一直存在,最简单的就是使用单例模式或者静态单例模式。
  • SqlSession(会话):是一个面向用户的接口,用来操作数据库(发出sql),每个线程都应该有它自己的 SqlSession 实例。
  • Executor(执行器):是一个接口(基本执行器、缓存执行器),SqlSession内部通过执行器操作数据库。
  • mappedstatement(底层封装对象):对操作数据库存储封装,包括sql语句,输入参数、输出结果类型。

3、执行过程

这里写图片描述

首先根据mybatis配置文件SqlMapConfig.xml加载mybatis运行环境,使用SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂(一般用单例),然后通过SqlSessionFactory会话工厂创建SqlSession,SqlSession是一个面向用户接口,提供操作数据库方法。通过SqlSession调用Executor,通过Executor调用MapperedStatement,进行参数设置、执行sql、结果映射。如果需要提交事务,需要执行SqlSession的commit方法,然后释放资源,关闭SqlSession。

二、核心配置

1、结构

MyBatis的核心配置文件包含了影响MyBatis行为甚深的设置(settings) 和属性(properties) 信息,存在于SqlMapConfig.xml文件中,文档的结构如下:

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

2、properties

将数据库连接参数单独配置到db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值,在SqlMapConfig.xml中就不需要对数据库连接参数硬编码,方便对参数进行统一管理,其它xml可以引用db.properties。
db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
<properties resource="db.properties"/>

mybatis加载属性的顺序

  • 在properties元素体内定义的属性首先被读取
  • 然后会读取properties传递的属性,它会覆盖已读取的同名属性。
  • 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

因此,通过parameter传递的属性具有最高优先级,resource或url加载的属性次之,最低优先级的是properties元素体内定义的属性。

3、settings

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。
















































































































































































设置参数描述有效值默认值
cacheEnabled 该配置影响的所有映射器中配置的缓存的全局开关。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods). true | false false (true in ≤3.4.1)
multipleResultSetsEnabled 是否允许单一语句返回多结果集(需要兼容驱动)。 true | false true
useColumnLabel 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。 true | false true
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior 指定发现自动映射目标未知列(或者未知属性类型)的行为。

  • NONE: 不做任何反应

  • WARNING: 输出提醒日志 (‘org.apache.ibatis.session.AutoMappingUnknownColumnBehavior’ 的日志等级必须设置为 WARN)

  • FAILING: 映射失败 (抛出 SqlSessionException)

NONE, WARNING, FAILING NONE
defaultExecutorType 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 设置超时时间,它决定驱动等待数据库响应的秒数。 任意正整数 Not Set (null)
defaultFetchSize 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。 任意正整数 Not Set (null)
safeRowBoundsEnabled 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。 true | false False
safeResultHandlerEnabled 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。 true | false True
mapUnderscoreToCamelCase 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 true | false False
localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 SESSION | STATEMENT SESSION
jdbcTypeForNull 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER OTHER
lazyLoadTriggerMethods 指定哪个对象的方法触发一次延迟加载。 A method name list separated by commas equals,clone,hashCode,toString
defaultScriptingLanguage 指定动态 SQL 生成的默认语言。 A type alias or fully qualified class name. org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler Specifies 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 | false false
logPrefix 指定 MyBatis 增加到日志名称的前缀。 Any String Not set
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING Not set
proxyFactory 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。 CGLIB | JAVASSIST JAVASSIST (MyBatis 3.3 or above)
vfsImpl 指定VFS的实现 自定义VFS的实现的类全限定名,以逗号分隔。 Not set
useActualParamName 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始) true | false true
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>

mybatis默认没有开启延迟加载,需要在SqlMapConfig.xml中setting配置。

<settings>
    <!--默认false,false:所有相关联的都会被初始化加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--默认true,true:懒加载的对象可能被任何懒属性全部加载-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

4、typeAliases

在mapper.xml文件中,定义有很多statement,需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型,如果使用全路径,不方便进行开发,所以定义别名来方便我们的开发。

<!-- 别名定义 -->
<typeAliases>
    <!-- 针对单个别名定义
        type:类的路径
        alias:别名
     -->
    <!-- <typeAlias type="com.mybatis.po.User" alias="user"/> -->
    <!-- 批量定义别名 
        指定包名,mybatis自动扫描包中的pojo类,自动定义别名,别名就是类名(首字母大写或小写都可以),如果加@Alias(value="User")注解,则以注解为别名
    -->
    <package name="com.mybatis.po"/>
</typeAliases>

mybatis默认支持的别名

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal

5、typeHandlers

mybatis使用typeHandlers类型处理器完成jdbc类型和java类型的转换。

默认支持的类型处理器

类型处理器Java类型JDBC类型
BooleanTypeHandlerBoolean,boolean任何兼容的布尔值
ByteTypeHandlerByte,byte任何兼容的数字或字节类型
ShortTypeHandlerShort,short任何兼容的数字或短整型
IntegerTypeHandlerInteger,int任何兼容的数字和整型
LongTypeHandlerLong,long任何兼容的数字或长整型
FloatTypeHandlerFloat,float任何兼容的数字或单精度浮点型
DoubleTypeHandlerDouble,double任何兼容的数字或双精度浮点型
BigDecimalTypeHandlerBigDecimal任何兼容的数字或十进制小数类型
StringTypeHandlerStringCHAR和VARCHAR类型
ClobTypeHandlerStringCLOB和LONGVARCHAR类型
NStringTypeHandlerStringNVARCHAR和NCHAR类型
NClobTypeHandlerStringNCLOB类型
ByteArrayTypeHandlerbyte[]任何兼容的字节流类型
BlobTypeHandlerbyte[]BLOB和LONGVARBINARY类型
DateTypeHandlerDate(java.util)TIMESTAMP类型
DateOnlyTypeHandlerDate(java.util)DATE类型
TimeOnlyTypeHandlerDate(java.util)TIME类型
SqlTimestampTypeHandlerTimestamp(java.sql)TIMESTAMP类型
SqlDateTypeHandlerDate(java.sql)DATE类型
SqlTimeTypeHandlerTime(java.sql)TIME类型
ObjectTypeHandler任意其他或未指定类型
EnumTypeHandlerEnumeration类型VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)

6、environments

environments用来配置连接数据库的参数,MyBatis 可以配置成适应多种环境, 这种机制有助于将 SQL 映射应用于多种数据库之中,尽管可以配置多个环境, 但每个 SqlSessionFactory 实例只能选择其一。

<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
    <environment id="development">
        <!-- 使用jdbc事务管理,事务控制由mybatis-->
        <transactionManager type="JDBC" />
        <!-- 数据库连接池,由mybatis管理-->
        <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>

7、mappers

告诉 MyBatis 到哪里去找映射文件。

1)加载方法
<!-- Using classpath relative resources -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>
<!-- Using url fully qualified paths -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
<!-- Using mapper interface classes -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<!-- Register all interfaces in a package as mappers -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
2)加载方式
<!-- 加载映射文件 -->
<mappers>
    <!--单个加载-->
    <mapper resource="sqlmap/User.xml" />
    <mapper resource="mapper/UserMapper.xml" />

    <!-- 批量加载mapper
        指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载,
        遵循一定规范,需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在同一个目录下
        上边规范的前提是:使用的是mapper代理方法
    -->
    <package name="com.mybatis.mapper"/>
</mappers>

8、其它

1)objectFactory

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

2)plugins

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。 默认情况下,MyBatis 允许使用插件来拦截的方法调用。

3)databaseIdProvider

MyBatis 可以根据不同的数据库厂商执行不同的语句, 这种多厂商的支持是基于映射语句中的databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有databaseId 和不带 databaseId 的相同语句, 则后者会被舍弃。 为支持多厂商特性只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider
即可:

<databaseIdProvider type="DB_VENDOR" />

三、Mapper配置

1、结构

<?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="User">

</mapper>

最外层的当然是mapper元素,mapper中有如下元素:

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

2、select

1)使用

通过select元素执行数据库查询,有如下常用属性:

  • id:标志映射文件中的sql,将sql语句封装到mappedStatement对象中,所以id称为statement的id。
  • parameterType:指定输入参数的类型,这里指定int型。
  • resultType:select指定resultType表示为输出结果中单条记录映射成的java对象,多条记录也只指定单条记录映射成的java对象。
<!--在映射文件中配置很多sql语句-->
<!--需求:通过id查询用户表的记录-->
<select id="findUserById" parameterType="_int" resultType="com.wenxue.study.domain.User">
    select * from user where id=#{id}
</select>

#{}表示一个占位符,#{id}中的id表示接收输入的参数,参数名称就是id,如果输入的参数就是简单类型,#{}中的参数名可以任意,可以value或其它名称。

2)属性

select元素中的所有属性:

属性描述
id在命名空间中唯一的标识符, 可以被用来引用这条语句。
parameterType将会传入这条语句的参数类的完全限定名或别名。 这个属性是可选的, 因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数, 默认值为 unset。
parameterMap这是引用外部 parameterMap 的已经被废弃的方法。 使用内联参数映射和 parameterType 属性。
resultType从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形, 那应该是集合可以包含的类型, 而不能是集合本身。 使用 resultType 或 resultMap, 但不能同时使用。
resultMap外部 resultMap 的命名引用。 结果集的映射是 MyBatis 最强大的特性, 对其有一个很好的理解的话, 许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType, 但不能同时使用。
flushCache将其设置为 true, 任何时候只要语句被调用, 都会导致本地缓存和二级缓存都会被清空, 默认值:false。
useCache将其设置为 true, 将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前, 驱动程序等待数据库返回请求结果的秒数。 默认值为 unset(依赖驱动) 。
fetchSize这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为 unset(依赖驱动) 。
statementTypeSTATEMENT, PREPARED 或 CALLABLE 的一个。 这会让 MyBatis 分别使用 Statement, PreparedStatement 或CallableStatement, 默认值:PREPARED。
resultSetTypeFORWARD_ONLY, SCROLL_SENSITIVE 或SCROLL_INSENSITIVE 中的一个, 默认值为 unset (依赖驱动) 。
databaseId如果配置了 databaseIdProvider, MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有, 则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了, 这样的话当返回一个主结果行的时候, 就不会发生有对前面结果集的引用的情况。 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。 默认值: false 。
resultSets这个设置仅对多结果集的情况适用, 它将列出语句执行后返回的结果集并每个结果集给一个名称, 名称是逗号分隔的。

3、insert、update和delete

1)使用

通过insert、update和delete元素进行数据库增、改和删。

<!-- useGeneratedKeys="true" keyProperty="id" 用于将自动生成的主键返回给id属性-->
<insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
    insert into user(name,age,sex) values(#{name},#{age},#{sex})
</insert>

<update id="updateUser" parameterType="user">
    update user set age=#{age} where id=#{id}
</update>

<delete id="deleteUser" parameterType="_int">
    delete from user where id=#{id}
</delete>

对于inset、update和delete操作,编写方法时,要进行事务提交,不然不会写到数据库,提交方式:

sqlSession.commit();
2)属性
属性描述
id命名空间中的唯一标识符, 可被用来代表这条语句。
parameterType将要传入语句的参数的完全限定类名或别名。 这个属性是可选的, 因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数, 默认值为 unset。
parameterMap这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
flushCache将其设置为 true, 任何时候只要语句被调用, 都会导致本地缓存和二级缓存都会被清空, 默认值:true(对应插入、 更新和删除语句) 。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为unset(依赖驱动) 。
statementTypeSTATEMENT, PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement, 默认值:PREPARED。
useGeneratedKeys(仅对 insert 和 update 有用) 这会令 MyBatis 使用JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 的关系数据库管理系统的自动递增字段) , 默认值:false。
keyProperty(仅对 insert 和 update 有用) 唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列, 也可以是逗号分隔的属性名称列表。
keyColumn(仅对 insert 和 update 有用) 通过生成的键值设置表中的列名, 这个设置仅在某些数据库(像PostgreSQL) 是必须的, 当主键列不是表中的第一列的时候需要设置。 如果希望得到多个生成的列, 也可以是逗号分隔的属性名称列表。
databaseId如果配置了 databaseIdProvider, MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有, 则不带的会被忽略。

4、sql

1)简单定义

使用sql元素来定义可重用的sql代码段,用include元素引入sql代码段。

<sql id="query">
    select * from user
</sql>

<sql id="desc">
    order by id desc
</sql>

<select id="queryDesc" resultType="user">
    <include refid="query"/>
    <include refid="desc"/>
</select>
2)属性设置

还可以设置属性值来使得定义的sql代码段不断变化。

<sql id="userColumns"> ${alias}.id,${alias}.name,${alias}.age</sql>
<select id="selectUsers" resultType="map">
    select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from user t1
    cross join some_table t2
</select>
3)嵌套属性

属性值还可以设置在include元素的refid参数中:

<sql id="sometable">
    ${prefix}Table
</sql>

<sql id="someinclude">
    from
    <include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
    select
    field1, field2, field3
    <include refid="someinclude">
        <property name="prefix" value="Some"/>
        <property name="include_target" value="sometable"/>
    </include>
</select>

5、resultMap

1)介绍
(1)resultType与resultMap

resultType:使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。

  • 如果查询出来的列名和pojo中的属性名不一致,不会创建pojo对象。
  • 只要查询出来的列名和pojo中的属性有一个一致,则会创建pojo对象。

查询出来的结果只有一行且一列,才可以用简单类型进行输出映射。不管输出的pojo单个对象还是一个列表(list中包含pojo),在mapper.xml中指定的类型是一样的,都是pojo类型,在mapper.java指定的方法返回值类型不一样,一个是pojo类型,一个是List。

resultMap:如果查询出来的列名和pojo属性名不一致,我们则需要定义一个resultMap对列名和pojo属性名之间做一个映射关系。

  • 定义resultMap
  • 使用resultMap作为statement的输出映射类型。
(2)resultMap结构
  • constructor:类在实例化时,用来注入结果到构造方法中
    • idArg:ID 参数,标记结果作为 ID 可以帮助提高整体效能
    • arg:注入到构造方法的一个普通结果
  • id:一个 ID 结果,标记结果作为 ID 可以帮助提高整体效能
  • resul:注入到字段或 JavaBean 属性的普通结果
  • association:级联使用,代表一对一关系。
  • collection:级联使用,代表一对多关系。
  • discriminator:级联使用,鉴别器根据实际选择示例,可以通过特定条件确定结果集。
(3)resultMap属性
属性描述
id当前命名空间中的一个唯一标识,用于标识一个result map.
type类的全限定名, 或者一个类型别名 (内置的别名可以参考上面的表格).
autoMapping如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。这个属性会覆盖全局的属性autoMappingBehavior。默认值为:unset。
2)简单使用

定义resultMap将取别名的查询结果和User类中的属性进行映射:

<resultMap type="user" id="userResultMap">
    <id column="userid" property="id"/>
    <result column="username" property="name"/>
</resultMap>

<select id="findUserByIdResultMap" parameterType="_int" resultMap="userResultMap">
    select id userid,name username from user where id=#{id}
</select>

resultMap属性:指定定义的resultMap的id,如果这个resultMap在其它的mapper文件,前边需要加namespace。

resultMap元素: type属性是resultMap最终映射的java类型对象类型,可以使用别名,id属性是对resultMap的唯一标识。

id元素:表示查询结果集中的唯一标识。column是查询出来的列名,property是type指定的pojo类型中的字段名,最终resultMap对column和property做一个映射关系。

result元素:是对普通列映射定义。column是查询出来的列名,property是type指定的pojo类型中的字段名。

小结:使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。如果查询出来的列名和pojo的属性名不一致,则需要定义一个resultMap对列名和pojo属性名之间做一个映射关系。

3)association
(1)介绍

association元素处理表与表之间一对一的关系,有以下通用属性:

属性描述
property映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的 property JavaBeans 的属性, 那么就会使用。 否则 MyBatis 将会寻找给定名称的字段。 这两种情形你可以使用通常点式的复杂属性导航。比如,你可以这样映射 一 些 东 西 :“ username ”, 或 者 映 射 到 一 些 复 杂 的 东 西 : “address.street.number” 。
javaType一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名的列表) 。 如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如javaType果你映射到的是HashMap,那么你应该明确地指定 javaType 来保证所需的行为。
jdbcType在这个表格之前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 需要对插入, 更新和删除操作可能为空的列进行处理。这是 JDBC 的需要, jdbcType 而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定这个类型-但 仅仅对可能为空的值。
typeHandler我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的 typeHandler

关于对多个表的联合查询,mybatis提供两种方式来加载:

  • Nested Select:通过执行另一个返回预期复杂类型的映射sql语句。
  • Nested Results:通过嵌套结果映射来处理联接结果集(joined results)的重复子集。
(2)嵌套查询

在resultMap元素中嵌套了一个查询语句,它会引用第一次查询的值继续查询,总共会经过n+1次查询。

属性描述
column用来传给另一个表的值所在列,一般为两个表关联的主键。对于复合主键可以指定多个列名,通过column="{prop1=col1,prop2=col2}" 语法来传递给嵌套查询语句。这会引起 prop1和prop2以参数对象形式来设置给目标嵌套查询语句,此时嵌套查询parameterType为map。
select另外一个查询语句的id,可以加载这个属性映射需要的复杂类型。获取的 在column属性中指定的列的值将被传递给目标select语句作为参数。
fetchType可选的,有效值为 lazy和eager。 如果使用了,它将取代全局配置参数lazyLoadingEnabled。
<mapper namespace="Card">
    <select id="findCardByUserId" parameterType="int" resultType="card">
            select * from card where userid=#{userid}
    </select>
</mapper>
<resultMap type="user" id="userCard">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="age" property="age"/>
    <result column="sex" property="sex"/>
    <association property="card" column="id" select="Card.findCardByUserId"/>
</resultMap>
<select id="findUserByIdAll" parameterType="_int" resultMap="userCard">
    select * from user where id=#{id}
</select>

通过id查找到某个用户,然后通过它的id调用嵌套的查询语句查找它对应的身份证card,会进行两次查询。

这种方式很简单, 但是对于大型数据集合和列表将不会表现很好,问题就是我们熟知的 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:

  • 执行了第一个单独的 SQL 语句来获取结果列表(就是1次查询)。
  • 对返回的n条记录,都会执行另一个查询语句来为每个加载细节(就是n次查询)。

例如:

<select id="getAllUserAll" resultMap="userCard">
    select * from user
</select>

这个问题会导致成百上千的 SQL 语句被执行,通常不是期望的。MyBatis 能延迟加载这样的查询就是一个好处,因此可以分散这些语句同时运行的消耗。然而,如果加载一个列表之后迅速迭代来访问嵌套的数据,此时就会调用所有的延迟加载,这样的行为可能是很糟糕的,所以还有另外一种方法。

(3)嵌套结果

在一个resultMap中嵌套了一个resultMap,用来映射另一个类的属性,它使用数据库的连接查询语句进行查询,只会进行一次查询。

属性描述
resultMap这是结果映射的 ID,可以映射关联的嵌套结果到一个合适的对象图中。这 是一种替代方法来调用另外一个查询语句。这允许你联合多个表来合成到 resultMap 一个单独的结果集。这样的结果集可能包含重复,数据的重复组需要被分 解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis 让你“链接”结果映射,来处理嵌套结果。
columnPrefix当连接多表时,你将不得不使用列别名来避免ResultSet中的重复列名。指定columnPrefix允许你映射列名到一个外部的结果集中。
notNullColumn默认情况下,子对象仅在至少一个列映射到其属性非空时才创建。 通过对这个属性指定非空的列将改变默认行为,这样做之后Mybatis将仅在这些列非空时才创建一个子对象。 可以指定多个列名,使用逗号分隔。默认值:未设置(unset)。
autoMapping如果使用了,当映射结果到当前属性时,Mybatis将启用或者禁用自动映射。 该属性覆盖全局的自动映射行为。 注意它对外部结果集无影响,所以在select or resultMap属性中这个是毫无意义的。 默认值:未设置(unset)。
<mapper namespace="Card">
    <resultMap type="card" id="cardResult">
        <id property="id" column="cid"/>
        <result property="name" column="cname"/>
        <result property="sex" column="csex"/>
        <result property="userid" column="id"/>
    </resultMap>
</mapper>
<resultMap type="user" id="userCard2">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="age" property="age"/>
    <result column="sex" property="sex"/>
    <association property="card" resultMap="Card.cardResult"/>
</resultMap>
<select id="findUserByIdAllResultMap" parameterType="_int" resultMap="userCard2">
    select user.*,card.id cid,card.name cname,card.sex csex from user,card where user.id=card.userid and user.id=#{id}
</select>

通过id查找到某个用户,使用的是嵌套结果方式,和使用嵌套查询的效果一样,观察日志可以发现,我们这次只进行了一次查询。上面的association标签是引用了一个外部的ResultMap,这使得这个外部resultMap具有可重用性。然而如果我们不需要重用,可以直接将映射关系嵌套在association元素中。

<resultMap type="user" id="userCard3">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="age" property="age"/>
    <result column="sex" property="sex"/>
    <association property="card">
        <id property="id" column="cid"/>
        <result property="name" column="cname"/>
        <result property="sex" column="csex"/>
        <result property="userid" column="id"/>
    </association>
</resultMap>
4)collection
(1)介绍

association元素处理表与表之间一对多的关系,collection元素的作用几乎和association是相同的,所以我们更多关注于它们的不同。collection中多了ofType属性,这个属性用来区分与类的属性property,它代表的是集合属性中包含的类型。

(2)嵌套查询
<mapper namespace="Book">
    <select id="findBookByUserId" parameterType="int" resultType="book">
        select * from book where userid=#{userid}
    </select>
</mapper>
<resultMap type="user" id="userBook">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="age" property="age"/>
    <result column="sex" property="sex"/>
    <collection property="books" ofType="book" column="id" select="Book.findBookByUserId"/>
</resultMap>
<select id="findUserByIdBookSelect" resultMap="userBook">
    select * from user where id=#{id}
</select>

通过id查找某个用户,然后通过它的id查找出用户拥有的书籍(每个用户有多本书)。books是user类的一个属性,是List<Book>类型,所以ofType对应的是Book类型,亦即ofType="book"

(3)嵌套结果

在一个resultMap中嵌套了一个resultMap,用来映射另一个类的属性,它使用数据库的连接查询语句进行查询,只会进行一次查询。

<mapper namespace="Book">
    <resultMap type="book" id="bookResultMap">
        <id column="bid" property="id"/>
        <result column="bname" property="name"/>
        <result column="bpublish" property="publish"/>
    </resultMap>
</mapper>
<resultMap type="user" id="userBook2">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="age" property="age"/>
    <result column="sex" property="sex"/>
    <collection property="books" ofType="book" resultMap="Book.bookResultMap"/>
</resultMap>
<select id="findUserByIdBookResultMap" resultMap="userBook2">
    select user.*,book.id bid,book.name bname,book.publish bpublish from user,book where user.id=book.userid and user.id=#{id}
</select>

通过id查找到某个用户,使用的是嵌套结果方式,和使用嵌套查询的效果一样。它使用的是引用外部resultMap,我们也可以直接在collection建立映射关系。

5)discriminator

实际生活中我们往往有一个基类,然后派生出一些类,这些派生类的属性具有一些差异。discriminator元素用来处理一条数据库查询可能会返回包括各种不同的数据类型的结果集,就像switch语句,根据给定的值选取resultMap。

<resultMap id="userResultMap" type="com.wenxue.study.domain.user">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <result property="sex" column="sex" />
    <discriminator javaType="int" column="sex">
        <case value="0" resultMap="maleResultMap" />
        <case value="1" resultMap="femaleResultMap" />
    </discriminator>
</resultMap>

discriminator定义了javaType说明它用整型作为参数,column对应的是sex列,case则是它的分支,当sex=0时,采用maleResultMap;当sex=2时采用femaleResultMap;如果没有任何一个匹配,则仅使用discriminator外部的映射。

6)小结

rsultType:

  • 作用:将查询结果按照sql列名pojo属性名一致性映射到pojo中。
  • 场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resulType将每一条记录映射到pojo中,在前面遍历list(list中是pojo)即可。

resultMap:使用association和collection完成一对一和一对多高级映射。
association:

  • 作用:将关联查询信息映射到一个pojo对象中。
  • 场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。

使用resultType无法将查询结果映射到pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap。

collection:

  • 作用:将关联查询结果信息映射到list集合中。
  • 场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样做的目的也是方便对查询结果集进行遍历查询。如果使用resultType无法将查询结果映射到list集合中。

6、cache

1)、介绍

MyBatis 包含一个非常强大的查询缓存特性,可以非常方便地配置和定制。默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,需要在 SQL 映射文件中添加一行:

<cache/>

字面上看就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句将会被缓存。
  • 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
  • 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
  • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

所有的这些属性都可以通过缓存元素的属性来修改。比如:

<cache
    eviction="FIFO"
    flushInterval="60000"
    size="512"
    readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。

可用的收回策略有:

  • LRU – 最近最少使用的:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    默认的是 LRU。

flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。

2)自定义缓存

使用自定义缓存:除了这些自定义缓存的方式, 你也可以通过实现你自己的缓存或为其他第三方缓存方案 创建适配器来完全覆盖缓存行为。

<cache type="com.domain.something.MyCustomCache"/>

这个示例展示了如何使用一个自定义的缓存实现。type属性指定的类必须实现org.mybatis.cache.Cache 接口。这个接口是 MyBatis 框架中很多复杂的接口之一,但是简单给定它做什么就行。

public interface Cache {
    String getId();
    int getSize();
    void putObject(Object key, Object value);
    Object getObject(Object key);
    boolean hasKey(Object key);
    Object removeObject(Object key);
    void clear();
}

要配置你的缓存, 简单和公有的 JavaBeans 属性来配置你的缓存实现, 而且是通过 cache 元素来传递属性, 比如, 下面代码会在你的缓存实现中调用一个称为 “setCacheFile(String file)” 的方法:

<cache type="com.domain.something.MyCustomCache">
    <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>

你可以使用所有简单类型作为 JavaBeans 的属性,MyBatis 会进行转换。 And you can specify a placeholder(e.g. ${cache.file}) to replace value defined at configuration properties.

从3.4.2版本开始,MyBatis已经支持在所有属性设置完毕以后可以调用一个初始化方法。如果你想要使用这个特性,请在你的自定义缓存类里实现 org.apache.ibatis.builder.InitializingObject 接口。

public interface InitializingObject {
  void initialize() throws Exception;
}

记得缓存配置和缓存实例是绑定在 SQL 映射文件的命名空间是很重要的。因此,所有 在相同命名空间的语句正如绑定的缓存一样。 语句可以修改和缓存交互的方式, 或在语句的 语句的基础上使用两种简单的属性来完全排除它们。默认情况下,语句可以这样来配置:

<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

因为那些是默认的,你明显不能明确地以这种方式来配置一条语句。相反,如果你想改 变默认的行为,只能设置 flushCache 和 useCache 属性。比如,在一些情况下你也许想排除 从缓存中查询特定语句结果,或者你也许想要一个查询语句来刷新缓存。相似地,你也许有 一些更新语句依靠执行而不需要刷新缓存。

参照缓存:回想一下上一节内容, 这个特殊命名空间的唯一缓存会被使用或者刷新相同命名空间内的语句。也许将来的某个时候,你会想在命名空间中共享相同的缓存配置和实例。在这样的 情况下你可以使用 cache-ref 元素来引用另外一个缓存。

<cache-ref namespace="com.someone.application.data.SomeMapper"/>

6、动态sql

1)if

根据if语句选择是否使用某个片段。

<select id="findActiveBlogWithTitleLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = ‘ACTIVE’
    <if test="title != null">
        AND title like #{title}
    </if>
</select>

这条语句提供了一个可选的文本查找类型的功能,如果没有传入title的值,那么所有处于“ACTIVE”状态的BLOG都会返回,反之若传入了title,那么就会把模糊查找出title内容的BLOG结果返回。

2)choose (when, otherwise)

如果不是每个片段都要被使用,我们只想使用多个中的一个片段,就需要choose语句实现多选一的效果。

<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = ‘ACTIVE’
    <choose>
        <when test="title != null">
            AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
            AND author_name like #{author.name}
        </when>
        <otherwise>
            AND featured = 1
        </otherwise>
    </choose>
</select>

提供了title就按title查找,提供了author就按author查找,若两者都没有提供,就返回所有符合条件的BLOG。

3)trim (where, set)

假如所有的条件都不满足或者第一个条件不满足,就会查询失败,因为多一个where或and。

<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG 
    <where> 
        <if test="state != null">
             state = #{state}
        </if> 
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
            AND author_name like #{author.name}
        </if>
    </where>
</select>

where 元素知道只有在一个以上的if条件有值的情况下才去插入WHERE子句。而且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
    ... 
</trim>

prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它带来的结果就是所有在 prefixOverrides 属性中指定的内容将被移除,并且插入 prefix 属性中指定的内容。类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。比如:

<update id="updateAuthorIfNecessary">
    update Author
    <set>
        <if test="username != null">username=#{username},</if>
        <if test="password != null">password=#{password},</if>
        <if test="email != null">email=#{email},</if>
        <if test="bio != null">bio=#{bio}</if>
    </set>
    where id=#{id}
</update>

set 元素会动态前置 SET 关键字,同时也会消除无关的逗号,因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。等价的自定义 trim 元素:

<trim prefix="SET" suffixOverrides=",">
    ...
</trim>
4)foreach

对一个集合进行遍历,通常是在构建 IN 条件语句的时候,当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字典(或者Map.Entry对象的集合)时,index是键,item是值。例如查询多个id:id in (1,2,3)id=1 or id=2 or id=3

<if test="ids!=null">
    <!--使用foreach遍历传入的ids
        collection:指定输入对象中集合属性
        item:每个遍历生成对象中
        open:开始遍历时拼接的串
        close:结束遍历时拼接的串
        seperator:遍历的两个对象中需要拼接的串
    -->
    <foreach collection="ids" item="user_id" open="AND(" close=")" separator="or">
        <!--每个遍历需要拼接的串-->
        id=#{user_id}
    </foreach>
</if>

传入参数

public class UserQueryVo{
    private List<Integer> ids;
}
5)bind

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

<select id="selectBlogsLike" resultType="Blog">
    <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
    SELECT * FROM BLOG
    WHERE title LIKE #{pattern}
</select>
6)Multi-db vendor support

一个配置了“_databaseId”变量的 databaseIdProvider 对于动态代码来说是可用的,这样就可以根据不同的数据库厂商构建特定的语句。比如下面的例子:

<insert id="insert">
    <selectKey keyProperty="id" resultType="int" order="BEFORE">
        <if test="_databaseId == 'oracle'">
            select seq_users.nextval from dual
        </if>
        <if test="_databaseId == 'db2'">
            select nextval for seq_users from sysibm.sysdummy1"
        </if>
    </selectKey>
    insert into users values (#{id}, #{name})
</insert>

四、原始dao开发

1、介绍

原始dao的方式开发需要编写dao接口和实现类,在dao实现类中注入一个SqlSessionFactory工厂。

2、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="User">
    <select id="findUserById" parameterType="_int" resultType="com.wenxue.study.domain.User">
        select * from user where id=#{id}
    </select>

    <insert id="insertUser" parameterType="user">
        insert into user(name,age,sex) values(#{name},#{age},#{sex})
    </insert>

    <update id="updateUser" parameterType="user">
        update user set age=#{age} where id=#{id}
    </update>

    <delete id="deleteUser" parameterType="_int">
        delete from user where id=#{id}
    </delete>

    <select id="getAllUser" resultType="user">
        select * from user
    </select>
</mapper>

3、dao接口

public interface UserDao {
    public User findUserById(int id);
    public void insertUser(User user);
    public void updateUser(User user);
    public void deleteUser(int id);
    public List<User> getAllUser();
}

4、dao实现

public class UserDaoImp implements UserDao{

    private SqlSessionFactory sqlSessionFactory;

    public UserDaoImp(SqlSessionFactory sqlSessionFactory) {
        super();
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public User findUserById(int id) {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        User user=sqlSession.selectOne("User.findUserById", id);
        sqlSession.close();
        return user;
    }

    @Override
    public void insertUser(User user) {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        sqlSession.insert("User.insertUser",user);
        sqlSession.commit();
        sqlSession.close();
    }

    @Override
    public void updateUser(User user) {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        sqlSession.update("User.updateUser",user);
        sqlSession.commit();
        sqlSession.close();
    }

    @Override
    public void deleteUser(int id) {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        sqlSession.delete("User.deleteUser",id);
        sqlSession.commit();
        sqlSession.close();
    }

    @Override
    public List<User> getAllUser() {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        List<User> userList=sqlSession.selectList("User.getAllUser");
        return userList;
    }
}

5、测试

public class DaoTest {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws IOException{
        InputStream inputStream=Resources.getResourceAsStream("SqlMapConfig.xml");
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testFindUserById(){
        UserDao userDao=new UserDaoImp(sqlSessionFactory);
        User user=userDao.findUserById(1);
        System.out.println(user.toString());
    }
}

6、问题

  • dao接口实现类方法中存在大量的模板方法。
  • 调用SqlSession方法时将statement的id硬编码了。
  • SqlSession方法传入变量使用泛型,即使变量类型传入错误,编译阶段也不会报错,不利于开发。

五、mapper代理开发

1、介绍

之前是使用完全限定名来映射sql语句,但是它是使用字符串常量进行操作的,容易出错,不够安全,所以我们要使用另外一种方式来实现,也就是使用Mapper开发。Mapper开发可以找到要使用的mapper接口类,然后映射到同一个包下同名的mapper配置文件,从中查找id与方法名一样的sql语句。

开发规范:

  • 在UserMapper.xml中namespace等于UserMapper接口地址
<mapper namespace="com.mybatis.mapper.UserMapper">
  • UserMapper.java接口中的方法名和UserMapper.xml中statement的id一致
  • UserMapper.java接口中的方法输入参数类型和UserMapper.xml中statement中的parameterType指定的类型一致。
  • UserMapper.java接口中的方法返回值类型和UserMapper.xml中statement中的resultType指定的类型一致。

2、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.wenxue.study.mapper.UserMapper">
    <select id="findUserById" parameterType="_int" resultType="com.wenxue.study.domain.User">
        select * from user where id=#{id}
    </select>

    <insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyProperty="id"> 
        insert into user(name,age,sex) values(#{name},#{age},#{sex})
    </insert>

    <update id="updateUser" parameterType="user">
        update user set age=#{age} where id=#{id}
    </update>

    <delete id="deleteUser" parameterType="_int">
        delete from user where id=#{id}
    </delete>

    <select id="getAllUser" resultType="user">
        select * from user
    </select>
</mapper>

3、mapper接口

package com.wenxue.study.mapper;

import java.util.List;

import com.wenxue.study.domain.User;

public interface UserMapper {
    public User findUserById(int id);
    public void insertUser(User user);
    public void updateUser(User user);
    public void deleteUser(int id);
    public List<User> getAllUser();
}

4、测试

public class MapperTest {
    private SqlSessionFactory sqlSessionFactory;
    private UserMapper userMapper;
    private SqlSession sqlSession;

    @Before
    public void setUp() throws IOException{
        InputStream inputStream=Resources.getResourceAsStream("SqlMapConfig.xml");
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
        sqlSession=sqlSessionFactory.openSession();
        userMapper=sqlSession.getMapper(UserMapper.class);
    }

    @After
    public void setDown(){
        sqlSession.commit();
    }

    @Test
    public void testFindUserById(){
        User user=userMapper.findUserById(1);
        System.out.println(user.toString());
    }

    @Test
    public void testInsertUser(){
        User user=new User();
        user.setName("ard");
        user.setAge(28);
        user.setSex('女');
        userMapper.insertUser(user);
        System.out.println(user.toString());
    }

    @Test
    public void testUpdateUser(){
        User user=new User();
        user.setId(26);
        user.setAge(8);
        userMapper.updateUser(user);
    }

    @Test
    public void testDeleteUser(){
        userMapper.deleteUser(26);
    }

    @Test
    public void testGetAllUser(){
        List<User> userList=userMapper.getAllUser();
        for(User user:userList){
            System.out.println(user.toString());
        }
    }
}

5、与dao方式比较

  • mapper配置文件和之前dao方式基本一样,只是namespace不同。
  • mapper接口与dao接口基本一样,但是mapper接口没有实现类。
  • 调用SqlSession方法直接获取mapper接口实例,传入的是类名,如果传入错误,就会报错,利于我们开发。

小结:所以说只要我们遵循mapper规范,按照mapper方式开发,就可以大大简化程序员的工作,也就是我们常说的约定优于配置

六、逆向工程

1、介绍

mybatis提供逆向工程,可以根据数据库表生成mybatis执行所需要的代码(mapper.java,mapper.xml,po.java)。

2、generatorConfig.xml

注意:要修改连接数据库信息,pojo的位置,mapper配置文件的位置,mapper接口的位置,指定的数据库表。

<?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>
    <classPathEntry
        location="D:\install\java\maven_local\mysql\mysql-connector-java\5.1.42\mysql-connector-java-5.1.42.jar" />
    <context id="mysqlTables" targetRuntime="MyBatis3">

        <commentGenerator>
            <!-- 是否去除自动生成的注释true:是,false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!-- 数据库连接的信息 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/ssm"
            userId="root" password="root">
        </jdbcConnection>

        <!-- 默认为false,把jdbc、decimal和numeric类型解析为Integer,为true时把jdbc decimal和numeric类型解析成java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

         <!-- 生成model模型,对应的包,存放位置可以指定具体的路径,如/ProjectName/src,也可以使用MAVEN来自动生成 -->
        <javaModelGenerator targetPackage="com.wenxue.po"
            targetProject="ProjectName/src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

         <!--对应的xml mapper文件  -->
        <sqlMapGenerator targetPackage="com.wenxue.mapper" targetProject="ProjectName/src/main/resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!-- 对应的mapper接口 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="com.webxue.mapper" targetProject="ProjectName/src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

         <!-- 这里是数据库的表名table_user 以及 POJO类的名字User -->
        <table tableName="user"></table>
    </context>

</generatorConfiguration>

3、使用

右击generatorConfig.xml文件,选择Run->Run Mybatis Generator运行即可,如果没有这个选项,则需要安装mybatis generator插件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值