MyBatis学习笔记

MyBatis主配置文件

1. Properties

可以通过resource属性指定properties配置文件,而后,在配置文件中可以通过${}的方式访问properties里的数据

2. Settings

可以通过setting标签来设置属性。
例:
下面的配置可以将数据库中的下划线式命名法映射到POJO类的驼峰命名法。

<setting name="mapUnderscoreToCamelCase" value="true" />
---------------------------------------------------------------------------------------
效果:
last_name ===> lastName
3. typeAliases

可以通过配置来给类的的全类名取别名
配置别名的两种方式:

  1. 通过typeAlias标签:
<typeAlias type="全类名" alias="别名"/>

alias标签可以省略,若没设置alias属性的话,则别名默认为类名

  1. 通过package标签:
<package name="包名"/>

指定包名里的所有类都会被设置默认别名,可能出现的问题是,如果指定包里存在着全类名不同但类名相同的类,使用别名时会出现冲突
此时可以使用@Alias注解来为需要的类配置新的别名,从而解决冲突

PS:别名不区分大小写
PS2:mybatis内置别名

别名 类型
_byte byte
_short short
_int int
_integer int
_long long
_float float
_double double
_boolean boolean
string String
byte Byte
short Short
int Integer
integer Integer
long Long
float Float
double Double
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
map Map
typeHandler

类型处理器:将数据库的数据类型与Java的数据类型一一对应,mybatis3.4以后就自带处理了,这里就不多学了。

plugins

插件

environments

环境标签,可以通过environment来配置一个具体的环境。在存在多个不同的环境时,可以通过environments的default属性来指定所使用的环境。
environment标签中,有两个标签
transactionManager:事务管理器
    type:事务管理器的类型
可选值有JDBC(JdbcTransactionFactory)和MANAGED(ManagedTransactionFactory)
实际上这两个都是实现了TransactionFactory接口的类,通过实现TransactionFactory接口,也可以做到自定义事务管理器,此时type指定为类的全类名。

dataSource:数据源
    type:数据源类型
可选值有UNPOOLED(UnpooledDataSourceFactory),POOLED(PooledDataSourceFactory),JNDI(JndiDataSourceFactory)。实质上是实现了DataSourceFactory接口的类。

<environments default="mysql">
        <environment id="mysql">
            <transactionManager type="jdbc"></transactionManager>
            <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="yousql">
            <transactionManager type="managed"></transactionManager>
            <dataSource type="unpooled"></dataSource>
        </environment>
    </environments>
databaseIdProvider

作用:为sql语句选择使用的数据库,实现了多厂商支持
type:获得各数据库厂商的标识,例如MySQL,Oracle,SQL Server
property子标签可以给这些标识加上别名,然后在编写sql语句的时候就可以在属性指定databaseId这一属性。

    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
        <property name="SQL Server" value="sqlserver"/>
    </databaseIdProvider>
=======================================================
用例:
    <select id="selectOneUser" parameterType="_iNT" resultType="user" databaseId="mysql">
        select * from tb_user where user_id = #{userId}
    </select>
mappers

作用:将写好的sql映射文件注册到mybatis配置文件。
注册映射文件的方法:

  1. 通过mapper标签:
  • resource:类路径
  • url:网络路径或磁盘路径
  • class:对应接口的路径
    PS:使用class属性时,必须要有与接口对应的sql映射文件,而且必须和接口放在同一目录下。
  1. 通过package标签:
    name:指定包名,批量注册指定包里的所有接口
  2. 不写映射文件,直接通过注解完成sql语句。

PS:如果通过mapper标签的class属性或者通过package标签来注册,同时又想实现接口和映射文件分离,那么可以通过在项目的资源目录resouces下创建和接口相同的包名,用来存放映射文件。这样做的原理是,这两个目录所指向的其实是同一个目录。

映射文件

增删改
  1. mybatis允许增删改定义以下返回值
    int,long,boolean(基础类型、包装类)
  2. 可以不写parameterType
    但是写了可以限制传入的参数。
  3. 如果使用无参的openSession(),增删改操作时,需要手动提交。
  4. 获取自增主键的值
    插入命令时,可以通过将useGeneratedKeys的值设为true,并且用keyProperty来指定用来返回主键值的JavaBean的一个属性。
参数处理
  1. 当传入一个参数时,mybatis不会对参数进行处理。
    因此不管#{}里面填的是什么,都能取到唯一的参数

  2. 当传入多个参数时,mybatis会将参数封装成map,如果不指定key的话,则map默认的key值为param1.....paramN,value值则为传入的参数值。
    因此取值的时候可以通过#{默认的key值}或者#{索引}来找到对应的值。
    但是也可以通过@param("设定的key")来给map设定key值。
    用例:

User seleceByIdAndName(@Param("userId") int id, @Param("lastName") String name);
  1. 如果传入的多个参数正好是业务逻辑的模型,可以将这些参数封装成一个POJO对象传入

  2. 如果传入的多个参数不是业务逻辑的模型,且使用频率不高时,可以构造一个map

  3. 如果传入的多个参数不是业务逻辑的模型,但是使用频率很高时,可以编写TO(Transfer Object)数据传输对象。
    Page {
    int page;
    int index;
    }

  4. 当传入的是Collection、List或者Array数组时:
    Collection使用collection
    List使用list、collection
    Array使用array
    这几个并不能使用通用的param1等

mybatis封装参数的过程。
  • 判断参数的数量,如果为参数为0直接返回。
#{}和${}的区别

两者都是用来获取map中的值或者是POJO类中的属性。
#{}采用预编译的方式,将里面的值设置到sql语句中,类似于PreparedStatement。
${}采用字符串拼接的方式,将里面的值与sql语句进行拼接,有安全问题。

如果遇到原生JDBC都不支持占位符的情况下
比如分表,表名,排序。

select * from ${year}_salary;
select * from ${table_name};

这种情况下只能只用${}

通常情况下#{}比${}要安全,能用#{}的情况下选择用#{}

#{}在取值的时候还能加上其他参数,这里暂时先不学了。

返回值处理

通过resultType来指定返回值的类型

  1. 返回值是List<User>
    此时resultType应该指定的是List中元素的类型,mybatis会把每一条记录封装成user对象,然后返回集合。
  2. 返回类型为Map<String, Object>
    此时resultType是map,mybatis把记录拆成键值的方式封装在map里
  3. 返回类型是Map<String, User>
    此时resultType应指定为User,因为mybatis是要将记录封装在User对象中,但是如果只指定了resultType,在运行时会报错,报错信息为:
Expected one result (or null) to be returned by selectOne(), but found: 2

需要返回的参数超出了mybatis能返回的数量,这时可以通过@MapKey来给接口中的方法打上注解,可以将JavaBean中的一个属性设定为另一个参数。

自定义返回类型

resultMap:不能和resultType同时使用,自定义封装规则

  • type:指定自定义规则的JavaBean
  • id:该resultMap的唯一标识符
    子标签:
  • <id>:添加主键的映射
  • <result>:添加其他字段的映射
    用column属性指定数据库中的字段,property属性指定JavaBean中的成员。
    id也可以用result替代,但是id的优化优于result
    字段与JavaBean中成员名相同的,可以不进行映射。

当一个类中包含其他类的引用时,使用级联属性封装结果集

JavaBean(省略成员方法):
======================================
public class User {
    private Integer userId;
    private String lastName;
    private String sex;
    private Integer age;
    private Class aClass;
}
public class Class {
    private int id;
    private String className;
}
映射文件:
======================================
<resultMap id="DifUser" type="ltc.model.User">
  <id property="userId" column="user_id"/>
  <result property="aClass.id" column="d_id"/>
  <result property="aClass.className" column="class_name"/>
</resultMap>
<select id="getFullUserById" resultMap="DifUser">
  select user_id,last_name,sex,age,d_id,class_name
  from tb_user u, tb_class c where d_id = id and user_id = #{id}
</select>

这里将d_id字段的值交给了aClass的id属性,class_name交给了className属性。

当一个类中包含其他类的引用时,使用Association封装结果集
JavaBean同上,映射文件改为:

<resultMap id="Association" type="ltc.model.User">
    <id property="userId" column="user_id"/>
    <result property="lastName" column="last_name"/>
    <result column="sex" property="sex"/>
    <result column="age" property="age"/>
    <association property="aClass" javaType="ltc.model.Class">
        <id column="d_id" property="id"/>
        <result column="class_name" property="className"/>
    </association>
</resultMap>
<select id="getFullUserById" resultMap="Association">
    select user_id,last_name,sex,age,d_id,class_name
    from tb_user u, tb_class c where d_id = id and user_id = #{id}
</select>

association:

  • property中的是JavaBean中被选为Association的属性,这里是aClass
  • javaType中的是该属性的Java类型。

这里不把其他字段映射的话,获取不到值

使用association进行分步查询
步骤:

  1. 通过传入的userId值来获得user的详细信息
  2. 通过user信息中d_id来查询班级信息
  3. 将班级信息封装给user的aClass属性
<resultMap id="step" type="ltc.model.User">
    <id property="userId" column="user_id"/>
    <association property="aClass" select="ltc.dao.ClassMapper.getClassById" column="d_id">
    </association>
</resultMap>
<select id="getFullUserByIdStep" resultMap="step">
    select * from tb_user where user_id = #{id}
</select>

association中:

  • select属性指定了要执行的查询语句
  • column指定了执行语句所需的参数

分布查询的延迟加载:
仅当关联的属性被使用时,才执行分步的查询语句。
通过在mybatis配置文件中配置lazyLoadingEnabled和aggressiveLazyLoading属性。

该项设为true时,启动延迟加载
<setting name="lazyLoadingEnabled" value="true"/>
该项设为true时,在延迟加载时,一次性加载所有的属性,否则,只加载需要的属性。
<setting name="aggressiveLazyLoading" value="false"/>

返回值中包含集合
返回值中包含集合的时候,使用collection标签来对记录进行封装。

  • property:封装成的属性
  • ofType:分装成的Java类型
    collection内的配置与以往相同。
<resultMap id="mycol" type="ltc.model.Class">
    <id column="id" property="id"/>
    <result column="class_name" property="className"/>
    <collection property="users" ofType="ltc.model.User">
        <id column="user_id" property="userId"/>
        <result column="last_name" property="lastName"/>
        <result column="sex" property="sex"/>
        <result column="age" property="age"/>
    </collection>
</resultMap>
<select id="getClassByIdPlus" resultMap="mycol">
    select c.id id,c.class_name class_name,u.user_id user_id,
        u.last_name last_name, u.sex sex, u.age age
    from tb_class c
    left join tb_user u
    on c.id = u.d_id
    where c.id=#{id};
</select>

包含集合的分步查询
步骤:

  1. 用班级id获取到班级的信息
  2. 再用班级id在用户表里查找用户。
<resultMap id="classStep" type="ltc.model.Class">
    <id column="id" property="id"/>
    <result column="class_name" property="className"/>
    <collection property="users" select="ltc.dao.ClassMapper.getAllUserById" 
            column="id" fetchType="lazy" >
    </collection>
</resultMap>
<select id="getClassByIdStep" resultMap="classStep">
    select * from tb_class where id = #{id}
</select>
<select id="getAllUserById" resultType="ltc.model.User">
    select * from tb_user where d_id = #{id}
</select>
  • fetchType:加载方式:延迟加载或立即加载
    PS:当collection里的column要传递多个参数时,可以使用{key1=value1,key2=value2}的方式书写
<collection property="users" select="ltc.dao.ClassMapper.getAllUserById" column="{id=id}">
</collection>

注:这里第一次看视频教程的时候,视频上用的collection,但是我自己敲的时候不小心敲成association,但是也成功查询到了结果,因此目前有点分不清association和collection了。

动态SQL

if标签的使用

<if test=""></if>
与jstl表达式类似,不过OGNL里test支持的元素更多。
当满足test属性里的条件时,对应的sql语句片段被拼接

select * from tb_user
where
<!--test属性里的userId是直接从参数里取得值-->
<if test="userId != null">
    user_id = #{userId}
</if>
<!--特殊符号应该使用转义符-->
<if test="lastName != null &amp;&amp; lastName != &quot;&quot;">
    and last_name like #{lastName}
</if>
<if test="sex != null ">
    and sex = #{sex}
</if>
<if test="age != null">
    and age = #{age}
</if>

常用的字符转义符

字符 十进制 转义字符
" &#34; &quot;
& &#38; &amp;
< &#60; &lt;
> &#62; &gt;
不断开空格(non-breaking space) &#160; &nbsp;
where标签

去掉拼接后字符串前多余的and或者or

<select id="getUsersWithDynamicSqlWhere" resultType="ltc.model.User">
    select * from tb_user
    <where>
        <if test="userId != null">
            user_id = #{userId}
        </if>
        <if test="lastName != null &amp;&amp; lastName != &quot;&quot;">
             and last_name like #{lastName}
        </if>
        <if test="sex != null ">
            and sex = #{sex}
        </if>
        <if test="age != null">
            and age = #{age}
        </if>
    </where>
</select>

PS:

  • where标签会自动在拼接体前面加上where
  • where标签不能去除后面重复的and或or
trim标签
  • prefix:前缀,在拼接体前面加上的前缀
  • prefixOverrides:前缀覆盖,去掉拼接体前面多余的字符串
  • suffix:后缀,在拼接体后面加上的后缀
  • suffixOverrides:后缀覆盖,去掉拼接体后面多余的字符串
select * from tb_user
<trim prefix="where" suffixOverrides="and || or">
    <if test="userId != null">
        user_id = #{userId} and
    </if>
    <if test="lastName != null &amp;&amp; lastName != &quot;&quot;">
        last_name like #{lastName} and
    </if>
    <if test="sex != null ">
        sex = #{sex} and
    </if>
    <if test="age != null">
        age = #{age}
    </if>
</trim>
choose标签

当满足某一分支的时候选择对应的sql语句,类似于switch-case语句,按顺序判断

select * from tb_user
<where>
    <choose>
        <when test="userId != null">
            user_id = #{userId}
        </when>
        <when test="lastName != null">
            last_name like #{lastName}
        </when>
        <when test="sex != null">
            sex = #{sex}
        </when>
        <when test="age != null">
            age = #{age}
        </when>
        <!--当条件都不满足的时候返回所有user信息-->
        <otherwise>
        </otherwise>
    </choose>
</where>
set标签

用于更新字段,当用if标签判断需要拼接的字符串时,可能出现多余的",",这个时候可以用set标签来取出多余的","。

<update id="updateUserById">
    update tb_user
    <set>
        <if test="lastName != null">
            last_name = #{lastName},
        </if>
        <if test="sex != null">
            sex = #{sex},
        </if>
        <if test="age != null">
            age = #{age}
        </if>
    </set>
    <where>
        user_id = #{userId}
    </where>
</update>
foreach标签

通过遍历可以把集合里的元素生成一个字符串

  • collection:用来选中传来的集合参数
  • item:表示集合中每个元素
  • separator:用来分个每个元素的符号
  • open:在集合生成的字符串前面添加的字符串
  • close:在集合生成的字符串后面添加的字符串
  • index:表示遍历过程中,集合的索引(遍历map时,表示key值)
<select id="getUsersWithDynamicSqlForeach" resultType="ltc.model.User">
    select * from tb_user
    <where>
        user_id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </where>
</select

使用foreach标签插入多条记录

  1. 通过mysql语句支持 insert into table_name values(),(),()的特点,用foreach标签拼接出来插入多条记录的单sql语句。
<insert id="insertUsersWithDynamicSqlForeach">
    insert into tb_user(last_name, sex, age) values
    <foreach collection="users" item="user" separator="," >
        (#{user.lastName}, #{user.sex}, #{user.age})
    </foreach>
</insert>
  1. 通过foreach拼接处多条用分号分个的insert语句
<insert id="insertUsersWithDynamicSqlForeach">
    <foreach collection="users" item="user" separator=";">
        insert into tb_user(last_name, sex, age)
            values(#{user.lastName}, #{user.sex}, #{user.age})
    </foreach>
</insert>

PS:这个方法不限于insert语句,也可以用于其他语句,但是使用这个方法必须在数据库连接url的属性后添加allowMultiQueries=true。

两个内置对象
  1. _parameter
    表示传入sql语句的参数,多个参数时,这些参数会被封装成map,此时_parameter指代的就是这个map
  2. _databaseId
    表示当前数据库的id,可以通过这个对象判断当前使用的数据库
<select id="getUserTestInnerParamater" resultType="ltc.model.User">
    <!--当当前数据库为mysql时,执行这个语句-->
    <if test="_databaseId == mysql">
        select * from tb_user
        <!--当传入参数不为空时,才添加查找的条件-->
        <if test="_parameter != null">
            <where>
                last_name = #{lastName}
            </where>
        </if>
    </if>
    <!--其他数据库时执行的语句-->
    <if test="_databaseId == oracle">
    </if>
</select>
bind标签

可以将OGNL表达式的值绑定到一个变量

<bind name="" value="'%' + lastName + '%'"/>
sql标签
  1. sql:抽取sql片段,以便后面重用
  2. include:引入sql标签声明的sql片段。
  3. include还可以通过property子标签设置自定义属性,可以在sql标签里通过${}直接使用。
<insert id="insertUsersWithDynamicSqlForeach">
    <foreach collection="users" item="user" separator=";">
        insert into tb_user(
        <include refid="insertColumn"></include>
        )
        values(#{user.lastName}, #{user.sex}, #{user.age})
    </foreach>
</insert>
<sql id="insertColumn">
    last_name, sex, age
</sql>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容

  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,425评论 0 4
  • 最近学习MyBatis这个轻量型持久层框架,感觉入门很简单,但是深层次细节配置很多。本篇笔记从 配置文件->例子入...
    Super超人阅读 643评论 0 1
  • 《小吴同学的Mybatis学习之路》点击此处查看完整项目 1.Mybatis_01_HelloWorld接口式编程...
    吴里庆庆阅读 325评论 0 0
  • 《深入浅出MyBatis技术原理与实战》2016年版本 读书笔记 第一章 MyBatis简介 1.ORM模型:对象...
    GunnerAha阅读 544评论 0 3
  • MyBatis mybatis-3.4.6 jdk1.8+ 一、mybatis入门 1.依赖jar包 pom.xm...
    Ernest_Chou阅读 894评论 0 0