【Mybatis】基于XML配置的基本使用

文章依照官网文档所写,使用的mybatis版本为3.5.4,使用数据库为mysql
本文以使用案例为主,基础性讲解较少,请结合文档阅读。也是初学者,能力有限,有错麻烦指正,谢谢
本文所用代码上传至个人GitHub,全部运行方法都在测试实例中,数据库生成与数据添加代码在sql.sql内,为了演示不同的功能,不同功能的测试代码和配置文件完全分离。

Getting Start

本章内容的测试代码在test/java/basic中,使用到的配置文件在src/main/resources/basic

基本使用

运行测试实例basic.Test方法所示,从结果可以看出,运行要先通过如下几步

  1. SqlSessionFactoryBuilder创建工厂构建类;
  2. 通过Resources获取配置文件的路径的Reader对象
  3. 将配置文件传给工厂构建类,通过build方法创建工厂;
  4. 调用openSession创建SqlSession
  5. 通过SqlSession获取dao的代理对象,调用代理对象方法的数据库操作方法;或者直接调用SqlSession的数据库操作方法

生命周期

对于上述几个对象的使用约束如下:

  1. SqlSessionFactoryBuilder:这个对象可以 随意被创建、使用或者丢弃,不需要一直持有这个对象;
  2. SqlSessionFactory:一旦创建,这个工厂需要在应用的整个周期内存活;
  3. SqlSession:每个线程持有一个SqlSession,不要在线程之间共享SqlSession,并且每次用完之后要记得关闭掉,可以使用java8提供的try-catch-resource语法;
  4. Mapper Instances:建议在方法内使用Mapper对象,即在方法中获取,用完就丢弃。

Configuration配置

即传入SqlSessionFactoryBuilder.build方法构建SqlSessionFactory的xml配置文件的书写规则,其约束为

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

其根标签为configuration,直接子标签包括propertiessettingstypeAliasestypeHandlersobjectFactorypluginsenvironmentsatabaseIdProvidermappers共9个,这些标签的配置必须按照上述顺序依次配置,例如typeHandlers要配置在objectFactory之前

配置XML——properties

本章内容的测试代码在test/java/xml/properties中,使用到的配置文件在src/main/resources/xml/properties中。实验结果如下
这个element无非就是给配置文件设定属性键值对,以便后续配置根据key获取value

属性

属性指的是跟标签上的配置,如<properties resource="xxx">resource指的就是这个标签的一个属性

resource代表着从本地文件里读取配置信息

获取值

通过${key}来获取即可

获取默认值

${username:root},可以看到本章使用的配置信息里没有username,那么${username:root}的含义就是如果没有username,那么就使用root,默认这个功能是不开启的,需要设置<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>即可使用这个功能,可以看到分隔符默认是:,可以通过<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>将分隔符改为?:,即${username?:root}

配置XML——settings

涉及内容过多,暂时略过

配置XML——typeAliases

本章内容的测试代码在test/java/xml/typeAliases中,使用到的配置文件在src/main/resources/xml/typeAliases中。实验结果如下
typeAliases是给类名起个别名,这些别名可以用在mapper.xml文件里,因为每次都使用全限定类名就很麻烦,使用别名可方便很多
mybatis里有很多内置的typeAliase,例如_float指的java类为基本数据类型float,而float指的是引用数据类型Float

配置XML——typeHandlers

本章内容的测试代码在test/java/xml/typeHandlers中,使用到的配置文件在src/main/resources/xml/typeHandlers中。实验结果如下

typeHandlers定义了java类型和jdbc类型的转换关系,如代码中所示,AuthorName类是一个单独的类,我们希望其能对应数据库author中的name这一列,那么AuthorNameHandler就是用于处理这两者之间的转换关系的,例如

  1. AuthorName selectAuthorNameById(int id);方法是希望通过id获取一个AuthorName对象,执行该方法可以看到控制台有打印通过列名调用getNullableResult,说明TypeHandler生效;
  2. Author selectByAuthorNameObj(AuthorName authorName);方法是希望传入一个AuthorName对象,从而获取Author对象,注意此时mapper文件中的sql为select * from author where name=#{authorName};,其中#{authorName}就会导致AuthorNameHandlersetNonNullParameter方法调用;
  3. 当然也可以构造一个可以处理多个类型的handler,例如public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E>
  4. AuthorNameHandler头上挂的注解都可以通过xml标签里指定,并且xml标签里配置的优先级更高,会覆盖注解,如文件中所示;
  5. EnumTypeHandler就是处理Enum类型的映射关系,如果查询结果为MALE,那么就会取出对应Javatype枚举类型SexMALE对象。通过看源码可知,数据库sex列类型可以为字符类型或者enum类型,因为结果都是通过rs.getString(columnName);获取的。
  6. EnumOrdinalTypeHandler是另一种处理Enum类型的映射关系,如果数据库内返回对象为数字0,那么就会取出对应Javetype枚举类型中的第一个枚举对象;只不过这个功能需要在mybatis-config.xml里手动指定,否则默认使用的是EnumTypeHandler
  7. AuthorNameHandler可以在Mapper文件中单独使用,后面会讲到。

配置XML——objectFactory

本章内容的测试代码在test/java/xml/objectFactory中,使用到的配置文件在src/main/resources/xml/objectFactory中。
Each time MyBatis creates a new instance of a result object, it uses an ObjectFactory instance to do so. 从结果可以看出,创建Author对象的时候,打印了两次interface java.util.List,这是因为返回结果是一个,但内部仍然调用的是selectList方法,然后取list里的第0个元素作为返回值,至于为啥打印两边,这是因为无参构造方法create也会调用有参的create构造方法

配置XML——plugins

本章内容的测试代码在test/java/xml/plugins中,使用到的配置文件在src/main/resources/xml/plugins中。
实例代码参考自MyBatis-Plugins,功能为拦截了StatementHandlerprepare方法,获取了执行方法的信息,并修改了sql语句
要小心使用这些方法

配置XML——environments

示例略,详情请看文档,该标签用于配置环境

  1. 两种事务处理器,JDBC与MANAEGED;
  2. 三种内置数据源,pooled、unpooled以及JNDI;同时mybatis支持第三方数据源,例如C3P0和druid

配置XML——databaseIdProvider

示例略,详情请看文档,该标签用于多数据库支持,举例来讲,实际应用场景有可能会在多种类型的数据库中切换。
MyBatis之databaseIdProvider多数据库支持

配置XML——mappers

示例略,mapper就是配置mapper.xml的路径,有两类方法

直接配置xml路径

  1. 配置路径
<mapper resource="xml/plugins/dao/BlogMapper.xml"/>
  1. 配置url
<mapper url="file:///D:/code/mybatis/how2mybatis_xml/src/main/resources/xml/plugins/dao/BlogMapper.xml"/>

配置接口类

  1. 直接配置全限定类名,这种方法需要注意的是,mybatis也会同时加载classpath:dao/BlogMapper.xml,如果找不到该文件,并且接口内方法也没有注解的话,就会报错
<mapper class="dao.BlogMapper"/>
  1. 直接配置包名,要求与配置全限定类名一样,也会加载与类同名的xml配置文件
<package name="dao"/>

Mapper XML——select

Mapper里用于进行操作的标签,select标签上有很多属性,这些属性多数与JDBC相关,例如fetchSize以及resultSetType等,自行百度。其中最重要的属性为

  1. id:指明mapper接口的具体的方法;
  2. parameterType:指明方法的参数类型,Mybatis (ParameterType) 如何传递多个不同类型的参数;使用参数的方法后面会介绍,里面通过索引获取参数的方法好像已经失效了。不要这么用。
  3. resultType:指明方法返回类型;
  4. resultMap:指明方法返回map;

Mapper XML——insert, update and delete

本章内容的测试代码在test/java/mapper/insert中,使用到的配置文件在src/main/resources/mapper/insert中。
Mapper里用于执行增删改操作的标签,实验结果不言自明
文档里提到了selectKey这个标签,我觉得应该用不到
实验完之后记得把数据库恢复原样,以便后续实验

delete from how2mybatis.blog where id>=5;
ALTER TABLE how2mybatis.blog auto_increment=5;

Mapper XML——sql

本章内容的测试代码在test/java/mapper/sql中,使用到的配置文件在src/main/resources/mapper/sql中。
改标签的功能就是定义一些可以复用的sql片段,不同的sql片段可以相处嵌套

Mapper XML——parameter

本章内容的测试代码在test/java/mapper/parameter中,使用到的配置文件在src/main/resources/mapper/parameter中。
这不是一个标签,这是取值从bean对象取值而构建sql的语法,使用就无非是#$
mybatis中#与$的区别
两者的区别在于,#会认为这是个参数,$会直接进行字符串替换,例如select * from ${tabelName}就会将${tabelName}直接替换成tableName的值;而select * from ${tabelName}则会被编译成select * from ?,这种语句是没法进行setParameter从而成功执行的。

该案例里使用了五个方法,可以看到我在AuthorMapper里的参数添加了@Param这个注解。这是因为默认情况下,java8的反射机制是无法获取方法的参数名的,例如某个方法method(String name),通过反射的方法获取该方法的参数名只能获得arg0,因此需要手动配置编译模式,如Java8获取参数名称所说的方法。修改完之后记得rebuild工程,否则用的还是修改前的class文件
但由于我懒= =懒得折腾这个了,我就用了另一种方法,给参数直接挂上注解,从而让mybatis框架识别参数名。接下来重点说#

如果方法传入参数只有1个

对于传入的是存在内置typeHandler的对象(如基本数据类型int,float等):

  • 如果传入的参数带了@Param('a') String arg,那么#{a}就代表了arg本身,如果使用#{asdf}会报错,告诉你可用的参数名只有aparam1
  • 如果不带@Param注解,如Date date,那么#{}里不管写什么,都能读取到这个date;虽然起什么名都行,但乱起名终归是个坏习惯,最好保证参数名相同。

如果传入的是不存在存在内置typeHandler的对象(javabean):

  • 如果传入的参数带了@Param('a')Bean arg,那么#{a}就代表了对象arg本身,对其可以使用内置的handler或者自定义的typeHandler。同时#{a.prop}就代表其prop属性;
  • 如果传入的参数不带注解Bean bean,那么#{arg}会去搜索这个javabean的arg属性,即getArg()。注意,即使此时开启了-parameters参数,#{bean}指的也不是这个对象bean,仍然是去找叫做bean的属性;

如果传入的参数不止1个

假设传递参数为AuthorName authorName,int id,注意此时假设开启了-parameters编译模式

  • ${authorName}${id}指的就是authorName这个对象和id这个整数,对于javabean也不会去认为authorName是一个属性,你可以通过#{authorName.prop}获取其Prop属性;
  • 如果不开启-parameters功能,就只能通过@Param注解实现上面的功能了;
    无论怎样,#都会自动调用对应的handler,例如,如果传入的参数是一个int类型,那么IntegerTypeHandler这个内置的处理器会被调用。

mapper XML——resultMap

本章内容的测试代码在test/java/mapper/resultMap中,使用到的配置文件在src/main/resources/mapper/resultMap中。

  1. resultMap里仍然可以用各种handler;
  2. idresult没什么大的不同,The only difference between the two is that id will flag the result as an identifier property to be used when comparing object instances.
  3. constructor标签的作用就是调用带参构造方法来创建新对象,constructor的子标签idArgarg的标签属性name指的就是构造函数的参数名,需要注意的是,如果不开启-parameters编译,则需要加@Param我没有开启-parameters,后面不再复述
  4. association处理的是has-one这种关系,例如,每个Blog对象都持有一个Author对象,association里面也是一个小型的resultMap,有Nested Select、Nested Results和Multiple ResultSets三种方式,Nested Select就是再调用另外一个select方法,Nested Select可以开启懒加载。
  5. 讨论对象唯一性问题,在本章代码中,selectAllBlogsWithNestedSelectAssociation,返回一个blog的列表,每个blog都has one author,由于使用的是nested select ,第一条博客和第二条博客持有的author对象是同一个;而selectAllBlogsWithNestedResultsAssociation使用的是nested result,第一条博客和第二条博客持有的author对象不是同一个;
  6. collection处理的是has-many这种关系,例如,一个Author对象可能会发布了多条Blog,其也有Nested Select、Nested Results和Multiple ResultSets三种方式,我只展示了Nested Select的用法;
  7. 讨论对象唯一性问题,selectAuthorsByIdWithNestedSelectColleciton使用了nested select collection语法,从数据库中读取全部作者,并且让每个author持有作者id小于等于自己作者id的全部blog,这说明2号author会持有1号作者和2号作者的blog。从结果可以看出,即使使用的是select,1号author持有的blog,与2号持有的1号作者的blog也不是同一对象。这说明collection语法中总会新建对象;
  8. discriminator这个标签是用来作为判别器使用的,有点类似于Java里的switch语法,MyBatis系列(十三):使用discriminator鉴别器映射。注意事项:Javatype是用于指定以什么类型进行比较的;如果discriminator里有一个case满足条件了,那么这个resultMap之外的discriminator标签外面的其他result或者id都会失效,doc原文如下
<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
    <case value="3" resultMap="vanResult"/>
    <case value="4" resultMap="suvResult"/>
  </discriminator>
</resultMap>

In this example, MyBatis would retrieve each record from the result set and compare its vehicle type value. If it matches any of the discriminator cases, then it will use the resultMap specified by the case. This is done exclusively, so in other words, the rest of the resultMap is ignored

  1. extends属性可以引入另一个resultMap除去discriminator的部分

Mapper XML——autoMapping

本章内容的测试代码在test/java/mapper/autoMapping中,使用到的配置文件在src/main/resources/mapper/autoMapping中。这段代码只显示了autoMapping为full时候的危险。

NONE disables auto-mapping. PARTIAL will only auto-map results with no nested result mappings defined inside. FULL will auto-map result mappings of any complexity (containing nested or otherwise).

  1. 在join表达式中,autoMapping的运行模式如None一样,即不指定的参数不会被映射;
  2. 示例代码里的风险在于,association里的自动读取了结果,从而把blog的id映射到author的id上了。

动态Sql——if

本章内容的测试代码在test/java/dynamicSql/dynamicIf中,使用到的配置文件在src/main/resources/dynamicSql/dynamicIf中。

动态Sql——where set trim

本章内容的测试代码在test/java/dynamicSql/dynamicWhereSet中,使用到的配置文件在src/main/resources/dynamicSql/dynamicWhereSet中。
where和set都是根据判定结果自动在头添加where或者set,并且处理首个判定成功条件内容开头的AND |OR或者最后一个判定成功条件末尾的,
而我们可以通过trim对其定制化,prefixsuffix属性代表着在头或者尾添加的内容,prefixOverrides代表首个判定成功的表达式的开头需要被自动处理的;suffixOverrides则表示最后一个判定成功的表达式的末尾需要被处理的内容,注意这两个override对空格都是敏感的。

动态Sql——foreach

本章内容的测试代码在test/java/dynamicSql/dynamicForeach中,使用到的配置文件在src/main/resources/dynamicSql/dynamicForeach中。

  1. foreach可以用于iteratable对象,比如listsetindex代表序号,item代表持有的对象,当然数组对象也可;
  2. foreach可以用于Map或者Collection of Map.Entry对象,比如listsetindex代表key,item代表value;

动态Sql——bind

本章内容的测试代码在test/java/dynamicSql/dynamicBind中,使用到的配置文件在src/main/resources/dynamicSql/dynamicBind中。
就是在标签里进行运算然后进行参数绑定,看代码不言自明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值