MyBatis

来源:互联网 发布:网络拓扑结构的特点 编辑:程序博客网 时间:2024/05/01 23:31

MyBatis是什么?

MyBatis是一款一流的支持自定义SQL、存储过程和高级映射的持久化框架。MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去设置参数和获取检索结果。MyBatis能够使用简单的XML格式或者注解进行来配置,能够映射基本数据元素、Map接口和POJOs(普通java对象)到数据库中的记录。


简介

每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。


用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpath resource),但你可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。

MyBatis工作流程

(1)加载配置并初始化
触发条件:加载配置文件
配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)接收调用请求
触发条件:调用Mybatis提供的API
传入参数:为SQL的ID和传入参数对象
处理过程:将请求传递给下层的请求处理层进行处理。
(3)处理操作请求 触发条件:API接口层传递请求过来
传入参数:为SQL的ID和传入参数对象
处理过程:
(A)根据SQL的ID查找对应的MappedStatement对象。
(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。
(C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
(E)释放连接资源。
(4)返回处理结果将最终的处理结果返回。


功能架构



我们把Mybatis的功能架构分为三层:

1、API接口层:

提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

2、数据处理层:

负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。

3、基础支撑层:

负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。  


从XML中构建SqlSessionFactory

[java] view plaincopy
  1. String resource = "com/accp/mybatis/data/Configuration.xml";    
  2. Reader reader = Resources.getResourceAsReader(resource);    
  3. sqlMapper = new SqlSessionFactoryBuilder().build(reader);    

MyBatis主配置文件

MyBatis配置文件中大标签configuration下子标签包括:

configuration

|--- properties

|--- settings

|--- typeAliases

|--- typeHandlers

|--- objectFactory

|--- plugins

|--- environments

|--- |--- environment

|--- |--- |--- transactionManager

|--- |--- |__ dataSource

|__ mappers


XML <configuration>配置文件包含对MyBatis系统的核心设置,包含获取数据库连接实例的数据源和决定事务范围和控制的事务管理器。

[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE configuration  
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">  
  5. <configuration>  
  6.   
  7.     <!--在下面的datasource中可以使用占位符来引用属性文件中的值-->  
  8.     <properties resource="properties/datasource.properties" />  
  9.   
  10.     <settings>  
  11.         <setting name="cacheEnabled" value="true" />  
  12.         <setting name="lazyLoadingEnabled" value="true" />  
  13.         <setting name="multipleResultSetsEnabled" value="true" />  
  14.         <setting name="useColumnLabel" value="true" />  
  15.         <setting name="useGeneratedKeys" value="false" />  
  16.         <setting name="defaultExecutorType" value="SIMPLE" />  
  17.         <setting name="defaultStatementTimeout" value="25000" />  
  18.     </settings>  
  19.       
  20.     <!-- 配置别名 类型别名是Java 类型的简称。它仅仅只是关联到XML 配置,简写冗长的JAVA 类名。-->  
  21.     <typeAliases>  
  22.         <typeAlias type="com.accp.mybatis.model.Blog" alias="Blog"/>  
  23.     </typeAliases>  
  24.     <!-- 使用这个配置,"Blog"就能在任何地方代替"com.accp.mybatis.model.Blog"被使用。-->  
  25.       
  26.     <!--  
  27.     java 中一些简单类型的别名  
  28.         别名  映射的类型   
  29.         _byte  byte   
  30.         _long  long   
  31.         _short  short   
  32.         _int   int    
  33.         _integer   int    
  34.         _double  double   
  35.         _float   float    
  36.         _boolean  boolean   
  37.         string  String   
  38.         byte  Byte   
  39.         long  Long   
  40.         short  Short   
  41.         int   Integer   
  42.         integer   Integer   
  43.         double  Double   
  44.         float   Float    
  45.         boolean  Boolean   
  46.         date  Date   
  47.         decimal  BigDecimal   
  48.         bigdecimal  BigDecimal   
  49.         object  Object   
  50.         map  Map   
  51.         hashmap  HashMap   
  52.         list  List    
  53.         arraylist  ArrayList   
  54.         collection  Collection   
  55.         iterator  Iterator  
  56.     -->  
  57.       
  58.   
  59.     <!-- 每个数据库(environment)对应一个SqlSessionFactory 实例-->  
  60.     <environments default="accp">  
  61.     <!--   
  62.         environments对于一个应用可能连接多个数据库,那就需要配置不同的环境来连接不同的数据库,  
  63.         每一个SqlSessionFactory对应一个environments也可以设置不同的环境应用于开发或测试的环境如果环境被忽略,  
  64.         那么默认环境将会被加载,也就是default="development"的作用了  
  65.     -->  
  66.         <environment id="accp">  
  67.             <transactionManager type="JDBC" />  
  68.             <!--   
  69.                 transactionManager设置事物的管理类型是 type=”[JDBC|MANAGED]”  
  70.                 JDBC使用datasource的连接来管理事物范围。  
  71.                 MANAGED自己不进行事物的提交和回滚,依靠容器来管理事物,设置closeConnection为false,取消自动关闭连接   
  72.             -->  
  73.             <dataSource type="POOLED">  
  74.             <!--  
  75.                 dataSource设置数据源[UNPOOLED|POOLED|JNDI]  
  76.                 POOLED:每次被请求时简单打开和关闭连接  
  77.                 POOLED:JDBC 连接对象的数据源连接池的实现,用来避免创建新的连接实例时必要的初始连接和认证时间。  
  78.                 poolMaximumActiveConnections –  在任意时间存在的活动(也就是正在使用)连接的数量。默认值:10   
  79.                 poolMaximumIdleConnections –  任意时间存在的空闲连接数。  
  80.                 oolMaximumCheckoutTime –  在被强制返回之前,池中连接被检查的时间。默认值:20000 毫秒(也就是 20 秒)  
  81.                 poolTimeToWait – 这是给连接池一个打印日志状态机会的低层次设置,还有重新尝试获得连接,这些情况下往往需要很长时间(为了避免连接池没有配置时静默失败)。 默认值:20000 毫秒(也就是 20 秒)  
  82.                 poolPingQuery –  发送到数据的侦测查询,用来验证连接是否正常工作,并且准备接受请求。默认是“NO PING QUERY SET”,这会引起许多数据库驱动连接由一个错误信 息而导致失败。   
  83.                 poolPingEnabled  –  这是开启或禁用侦测查询。如果开启,你必须用一个合法的SQL语句(最好是很快速的)设置 poolPingQuery 属性。默认值:false。  
  84.                 poolPingConnectionsNotUsedFor  –  这是用来配置 poolPingQuery 多次时间被用一次。这可以被设置匹配标准的数据库连接超时时间,来避免不必要的侦测。默认值:0 (也就是所有连接每一时刻都被侦测-但仅仅当 poolPingEnabled 为 true 时适用)。    
  85.                 JNDI –  这个数据源的实现是为了使用如 Spring 或应用服务器这类的容器,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。  
  86.                 initial_context  –  这个属性用来从初始上下文中寻找环境(也就是initialContext.lookup(init ial——context))。这是个可选属性,如果被忽略,那么data_source 属性将 会直接以 init ialContext 为背景再次寻找。   
  87.                 data_source  –  这是引用数据源实例位置的上下文的路径。它会以由 init ial_context查询返回的环境为背景来查找,如果 init ial_context 没有返回结果时,直接以初始上下 文为环境来查找。  
  88.             -->  
  89.                 <property name="driver" value="${driver}" />  
  90.                 <property name="url" value="${url}" />  
  91.                 <property name="username" value="${username}" />  
  92.                 <property name="password" value="${password}" />  
  93.                 <property name="driver.encoding" value="${encoding}" />  
  94.             </dataSource>  
  95.         </environment>  
  96.     </environments>  
  97.   
  98.     <!--mappers是告诉MyBatis 去哪寻找映射SQL 的语句。可以使用类路径中的资源引用,或者使用字符,输入确切的URL 引用。-->  
  99.     <mappers>  
  100.         <mapper resource="com/accp/mybatis/data/BlogMapper.xml" />  
  101.     </mappers>  
  102. </configuration>  

从SqlSessionFactory中获取SqlSession

通过以下代码获得SqlSession实例。
[java] view plaincopy
  1. SqlSession session = sqlMapper.openSession();  

MyBatis 真正强大之处就在这些映射语句,也就是它的魔力所在。对于它的强大功能,SQL 映射文件的配置却非常简单。

如果您比较SQL 映射文件配置与JDBC 代码,您很快可以发现,使用SQL 映射文件配置可以节省95%的代码量。MyBatis 被创建来专注于SQL,但又给您自己的实现极大的空间。


需要配置的基本元素


1. cache – 配置给定模式的缓存

2. cache-ref – 从别的模式中引用一个缓存

3. resultMap – 这是最复杂而却强大的一个元素了,它描述如何从结果集中加载对象

4. sql – 一个可以被其他语句复用的SQL 块

5. insert – 映射INSERT 语句

6. update – 映射UPDATE 语句

7. delete – 映射DELEETE 语句

8. select - 映射SELECT语句



配置注意


SQL 映射XML 文件只有一些基本的元素需要配置,并且要按照下面的顺序来定义
写好SQL语句映射文件后,需要在MyBAtis主配置文件mappers标签中引用!
例如:

[html] view plaincopy
  1. <!--mappers是告诉MyBatis 去哪寻找映射SQL 的语句。可以使用类路径中的资源引用,或者使用字符,输入确切的URL 引用。-->  
  2. <mappers>  
  3.     <mapper resource="com/accp/mybatis/data/BlogMapper.xml" />  
  4. </mappers>  


当Java接口与XML文件在一个相对路径下时,可以不在myBatis配置文件的mappers中声明。



SQL 


这个元素可以被用来定义可重用的SQL代码段,可以包含在其他语句中。

例子请看下面select中例子



SELECT

mybatis select是mybatis 中最常用的元素之一。

对简单的查询,select 元素的配置是相当简单的:

[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="com.accp.mybatis.model.Blog">  
  6.       
  7.     <!--可重用的SQL代码段-->  
  8.     <sql id="blog_column">id,title,author_id as authorId</sql>  
  9.       
  10.     <select id="selectBlog_as_map" parameterType="int" resultType="hashmap">  
  11.         select <include refid="blog_column"/> from Blog where id = #{id}  
  12.     </select>  
  13.       
  14. </mapper>  

 这个语句被称作selectBlog_as_map,使用一个int (或Integer)类型的参数,并返回一个HashMap类型的对象

#{id}告诉mybatis创建了一个PreparedStatement(预处理语句)参数。

在JDBC中,类似的代码如下:

[java] view plaincopy
  1. String selectBlog_as_map = “select * from Blog where id =?”;   
  2. PreparedStatement ps = conn.prepareStatement(selectBlog_as_map);   
  3. ps.setInt(1,id);   

测试代码:
[java] view plaincopy
  1. public static void selectBlogAsMap(int id) {  
  2.     SqlSession session = sqlMapper.openSession();  
  3.     Map<String, Object> map = session.selectOne("selectBlog_as_map",id);  
  4.     System.out.println(map);  
  5.     session.close();  
  6. }  


SELECT的属性还有很多的属性可以配置,具体的如下:属性描述取值默认id在这个模式下唯一的标识符,可被其它语句引用  parameterType传给此语句的参数的完整类名或别名  resultType语句返回值类型的整类名或别名。注意,如果是集合,
那么这里填写的是集合的项的整类名或别名,而不是集合本身的类名。
(resultType 与resultMap 不能并用)  resultMap引用的外部resultMap 名。结果集映射是MyBatis 中最强大的特性。
许多复杂的映射都可以轻松解决。(resultType 与resultMap 不能并用)  flushCache如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为falsetrue|falsefalseuseCache如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false
timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定true|falsefalsetimeout设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定正整数未设置fetchSize设置一个值后,驱动器会在结果集数目达到此数值后,
激发返回,默认为不设值,由驱动器自己决定正整数驱动器决定statementTypestatement,preparedstatement,callablestatement。
预准备语句、可调用语句STATEMENT
PREPARED
CALLABLEPREPAREDresultSetTypeforward_only,scroll_sensitive,scroll_insensitive
只转发,滚动敏感,不区分大小写的滚动FORWARD_ONLY
SCROLL_SENSITIVE
SCROLL_INSENSITIVE驱动器决定




























INSERT

简单的insert语句:

[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="com.accp.mybatis.model.Blog">  
  6.   
  7.     <insert id="insertBlog" parameterType="Blog">  
  8.         insert into Blog  
  9.             (id,title,author_id)  
  10.         values  
  11.             (#{id},#{title},#{authorId})  
  12.     </insert>  
  13.   
  14. </mapper>  

对于insert如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后把keyProperty 设成对应的列,就搞定了。

例如Blog表已经对 id 使用了自动生成的列类型,那么语句如下:

[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="com.accp.mybatis.model.Blog">  
  6.   
  7.     <insert id="insertBlog" parameterType="Blog" useGeneratedKeys=”true” keyProperty=”id”>  
  8.         insert into Blog  
  9.             (title,author_id)  
  10.         values  
  11.             (#{title},#{authorId})  
  12.     </insert>  
  13.       
  14. </mapper>  

还可以使用selectKey元素。下面例子,使用mysql数据库nextval('student')为自定义函数,用来生成一个key。

例如:

[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="com.accp.mybatis.model.Blog">  
  6.     <insert id="insertBlog" parameterType="Blog" >  
  7.         <selectKey keyProperty="studentID" resultType="String" order="BEFORE">    
  8.            select nextval('id')    
  9.         </selectKey>    
  10.         insert into Blog  
  11.             (id,title,author_id)  
  12.         values  
  13.             (#{id},#{title},#{authorId})  
  14.     </insert>   
  15. </mapper>  


insert语句属性配置细节:

属性描述取值默认id在这个模式下唯一的标识符,可被其它语句引用  parameterType传给此语句的参数的完整类名或别名  flushCache如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为falsetrue|falsefalseuseCache如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false
timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定true|falsefalsetimeout设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定正整数未设置fetchSize设置一个值后,驱动器会在结果集数目达到此数值后,激发返回,默认为不设值,由驱动器自己决定正整数驱动器决定statementTypestatement,preparedstatement,callablestatement。
预准备语句、可调用语句STATEMENT
PREPARED
CALLABLEPREPAREDuseGeneratedKeys

告诉MyBatis 使用JDBC 的getGeneratedKeys 方法来获取数据库自己生成的主键(MySQL、SQLSERVER 等

关系型数据库会有自动生成的字段)。默认:false

true|falsefalsekeyProperty

标识一个将要被MyBatis 设置进getGeneratedKeys 的key 所返回的值,或者为insert 语句使用一个selectKey

子元素。

  

selectKey语句属性配置细节:

属性描述取值keyPropertyselectKey 语句生成结果需要设置的属性。 resultType生成结果类型,MyBatis 允许使用基本的数据类型,包括String 、int类型。 order可以设成BEFORE 或者AFTER,如果设为BEFORE,那它会先选择主键,
然后设置keyProperty,再执行insert语句;如果设为AFTER,它就先运行insert 语句再运行selectKey 语句,
通常是insert 语句中内部调用数据库(像Oracle)内嵌的序列机制。BEFORE
AFTERstatementType像上面的那样, MyBatis 支持STATEMENT,PREPARED和CALLABLE 的语句形式,
 对应Statement ,PreparedStatement 和CallableStatement 响应STATEMENT
PREPARED
CALLABLE

UPDATE,DELETE

update 简单例子:
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="com.accp.mybatis.model.Blog">  
  6.     <update id="updateBlog" parameterType="Blog">    
  7.        UPDATE Blog    
  8.             SET title = #{title},     
  9.                 author_id = #{author.id},    
  10.          WHERE id = #{id};  
  11.     </update>    
  12. </mapper>  

delete 简单例子:
[html] view plaincopy
  1. <delete id="deleteBlog" parameterType="Blog">    
  2.       DELETE FROM BLOG WHERE ID = #{id}    
  3. </delete>  

update、delete语句属性配置细节:

属性描述取值默认id在这个模式下唯一的标识符,可被其它语句引用  parameterType传给此语句的参数的完整类名或别名  flushCache如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为falsetrue|falsefalseuseCache如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false
timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定true|falsefalsetimeout设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定正整数未设置fetchSize设置一个值后,驱动器会在结果集数目达到此数值后,激发返回,默认为不设值,由驱动器自己决定正整数驱动器决定statementTypestatement,preparedstatement,callablestatement。
预准备语句、可调用语句STATEMENT
PREPARED
CALLABLEPREPARED

Parameters

这个元素说的直白点就是定义参数。注意一个语句中只能有一个参数。
所以参数类型在以后的使用中,可能需要复杂的类型,比如hashmap,一个复杂的对象等。例如:

[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="com.accp.mybatis.model.Blog">  
  6.       
  7.     <insert id="insertBlog" parameterType="Blog">  
  8.         insert into Blog  
  9.             (id,title,author_id)  
  10.         values  
  11.             (#{id},#{title},#{author.id})  
  12.     </insert>  
  13. </mapper>  


如果blog类型的参数对象传递到了语句中,id、title和author属性将会被查找,然后它们的值就被传递到预处理语句的参数中。
这点对于传递参数到语句中非常好。但是对于参数映射也有一些其他的特性。
首先,像MyBatis的其他部分,参数可以指定一个确定的数据类型。
[html] view plaincopy
  1. #{property,javaType=int,jdbcType=NUMERIC}  
像MyBatis的剩余部分,javaType通常可以从参数对象中来确定,除非对象是一个HashMap。那么javaType应该被确定来保证使用正确类型处理器。
注意:如果null被当作值来传递,对于所有可能为空的列,JDBC Type是需要的。也可以通过阅读PreparedStatement. setNull()方法的JavaDocs文档来研究它。
为了以后自定义类型处理器,你可以指定一个确定的类型处理器类(或别名),比如:
[html] view plaincopy
  1. #{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}  
尽管它看起来繁琐,但是实际上是你很少设置它们其中之一。
对于数值类型,对于决定有多少数字是相关的,有一个数值范围。
[html] view plaincopy
  1. #{height,javaType=double,jdbcType=NUMERIC,numericScale=2}  
mode属性允许你指定IN,OUT或INOUT参数。如果参数为OUT或INOUT,参数对象属性的真实值将会被改变,就像你期望你需要你个输出参数。如果mode为OUT(或INOUT),而且jdbcType为CURSOR(也就是Oracle的REFCURSOR),你必须指定一个resultMap来映射结果集到参数类型。要注意这里的javaType属性是可选的,如果左边的空白是jdbcType的CURSOR类型,它会自动地被设置为结果集。
[html] view plaincopy
  1. #{department,  
  2.     mode=OUT,  
  3.     jdbcType=CURSOR,  
  4.     javaType=ResultSet,  
  5.     resultMap=departmentResultMap}  
字符串替换
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速,也是首选的做法,有时你只是想直接在SQL语句中插入一个不改变的字符串。比如,像ORDER BY,你可以这样来使用:
ORDER BY ${columnName}
这里MyBatis不会修改或转义字符串。

重要:接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。


resultMap

resultMap 元素是MyBatis中最重要最强大的元素。它就是让你远离90%的需要从结果集中取出数据的JDBC代码的那东西,而且在一些情形下允许你做一些JDBC不支持的事情。事实上,编写相似于对复杂语句联合映射这些等同的代码,也许可以跨过上千行的代码。ResultMap的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们的关系。

[java] view plaincopy
  1. package com.accp.mybatis.model;  
  2.   
  3. public class Blog {  
  4.     private Integer id;  
  5.     private String title;  
  6.     private Integer authorId;  
  7.   
  8. //省略get和set方法  
  9. }  

基于JavaBean的规范,上面这个类有3个属性:id,title和authorId。这些在select语句中会精确匹配到列名。
这样的一个JavaBean可以被映射到结果集,就像映射到HashMap一样简单。
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="com.accp.mybatis.model.Blog">    
  6.       
  7.     <select id="selectBlog_by_id" parameterType="int" resultType="Blog">  
  8.         select * from Blog where id = #{id}  
  9.     </select>  
  10.   
  11. </mapper>  

这些情况下,MyBatis会在幕后自动创建一个ResultMap,基于属性名来映射列到JavaBean的属性上。如果列名没有精确匹配,你可以在列名上使用select字句的别名(一个标准的SQL特性)来匹配标签。
ResultMap最优秀的地方你已经了解了很多了,但是你还没有真正的看到一个。只是出于示例的原因,让我们来看看最后一个示例中外部的resultMap是什么样子的,这也是解决列名不匹配的另外一种方式。

[html] view plaincopy
  1. <resultMap id="Blog_result" type="Blog" >  
  2.     <id column="id" property="id" />  
  3.     <result column="title" property="title"/>  
  4.     <result column="author_id" property="authorId"/>  
  5. </resultMap>  



引用它的语句使用resultMap属性就行了(注意我们去掉了resultType属性)。比如:
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="com.accp.mybatis.model.Blog">  
  6.   
  7.   
  8.     <resultMap id="Blog_result" type="Blog" >  
  9.         <id column="id" property="id" />  
  10.         <result column="title" property="title"/>  
  11.         <result column="author_id" property="authorId"/>  
  12.     </resultMap>    
  13.   
  14.     <!-- resultType与resultMap不能同时使用 -->     
  15.     <select id="selectBlog_by_id" parameterType="int" resultMap="Blog_result">  
  16.         select * from Blog where id = #{id}  
  17.     </select>  
  18.   
  19. </mapper>  

MyBatis的创建基于这样一个思想:数据库并不是您想怎样就怎样的。虽然我们希望所有的数据库遵守第三范式或BCNF(修正的第三范式),但它们不是。如果有一个数据库能够完美映射到所有应用程序,也将是非常棒的,但也没有。结果集映射就是MyBatis为解决这些问题而提供的解决方案。例如,我们如何映射下面这条语句?


[html] view plaincopy
  1. <select id="selectBlog_by_id" parameterType="int" resultMap="Blog_result">  
  2.     select  
  3.         b.id,  
  4.         b.title,  
  5.         b.author_id,  
  6.         a.id,  
  7.         a.username,  
  8.         a.password,  
  9.         a.email,  
  10.         a.bio  
  11.     from  
  12.         Blog b left join Author a  
  13.         on  
  14.         b.author_id = a.id  
  15.     where  
  16.         b.id = #{id}  
  17. </select>  

[html] view plaincopy
  1. <resultMap type="Blog" id="Blog_result">  
  2.   
  3.     <id column="id" property="id" />  
  4.     <result column="title" property="title"/>  
  5.       
  6.     <!-- 映射关联的对象 -->  
  7.     <association property="author" javaType="Author">  
  8.         <id column="author_id" property="id"/>  
  9.         <result column="username" property="username"/>  
  10.         <result column="password" property="password"/>  
  11.         <result column="email" property="email"/>  
  12.         <result column="bio" property="bio"/>  
  13.     </association>  
  14.       
  15. </resultMap>  


resultMap


resultMap属性:type为java实体类;id为此resultMap的标识。

resultMap可以设置的映射:


1. constructor – 用来将结果反射给一个实例化好的类的构造器

a) idArg – ID 参数;将结果集标记为ID,以方便全局调用
b) arg –反射到构造器的通常结果


2. id – ID 结果,将结果集标记为ID,以方便全局调用


3. result – 反射到JavaBean 属性的普通结果


4. association – 复杂类型的结合;多个结果合成的类型

a) nested result mappings – 几resultMap 自身嵌套关联,也可以引用到一个其它上


5. collection –复杂类型集合a collection of complex types


6. nested result mappings – resultMap 的集合,也可以引用到一个其它上


7. discriminator – 使用一个结果值以决定使用哪个resultMap

a) case – 基本一些值的结果映射的case 情形

i. nested result mappings –一个case 情形本身就是一个结果映射,因此也可以包括一些相同的元素,也可以引用一个外部resultMap。


è最佳实践:逐步地生成resultMap,单元测试对此非常有帮助。如果您尝试一下子就生成像上面这样巨大的resultMap,可能会出错,并且工作起来非常吃力。从简单地开始,再一步步地扩展,并且进行单元测试。使用框架开发有一个缺点,它们有时像是一个黑合。为了确保达到您所预想的行为,最好的方式就是进行单元测试。这对提交bugs也非常有用。


下面一部分将详细说明每个元素。


id、result

id、result是最简单的映射,id为主键映射;result其他基本数据库表字段到实体类属性的映射。

[html] view plaincopy
  1. <resultMap type="Blog" id="Blog_result">  
  2.     <id column="id" property="id" />  
  3.     <result column="title" property="title"/>  
  4. </resultMap>  

这些是结果映射最基本内容。id和result都映射一个单独列的值到简单数据类型(字符串,整型,双精度浮点数,日期等)的单独属性或字段。

这两者之间的唯一不同是id表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射)。


id、result语句属性配置细节:

属性

描述

property

映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的JavaBeans的属性,那么就会使用。

否则MyBatis将会寻找给定名称的字段。这两种情形你可以使用通常点式的复杂属性导航。

比如,你可以这样映射一些东西:“username”,或者映射到一些复杂的东西:“address.street.number”。

column

从数据库中得到的列名,或者是列名的重命名标签。

这也是通常和会传递给resultSet.getString(columnName)方法参数中相同的字符串。

javaType

一个Java类的完全限定名,或一个类型别名(参加上面内建类型别名的列表)。

如果你映射到一个JavaBean,MyBatis通常可以断定类型。

然而,如果你映射到的是HashMap,那么你应该明确地指定javaType来保证所需的行为。

jdbcType

在这个表格之后的所支持的JDBC类型列表中的类型。JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。

这是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC编程,你需要指定这个类型-但仅仅对可能为空的值。

typeHandler

我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。

这个属性值是类的完全限定名或者是一个类型处理器的实现,或者是类型别名。 

支持的JDBC类型

MyBatis支持如下的JDBC类型:

BIT

FLOAT

CHAR

TIMESTAMP

OTHER

UNDEFINED

TINYINT

REAL

VARCHAR

BINARY

BLOB

NVARCHAR

SMALLINT

DOUBLE

LONGVARCHAR

VARBINARY

CLOB

NCHAR

INTEGER

NUMERIC

DATE

LONGVARBINARY

BOOLEAN

NCLOB

BIGINT

DECIMAL

TIME

NULL

CURSOR


constructor <构造方法>

[html] view plaincopy
  1. <resultMap type="Blog" id="Blog_result_cs">  
  2.     <constructor>  
  3.         <idArg column="id" javaType="int"/>  
  4.         <arg column="title" javaType="String"/>  
  5.     </constructor>  
  6. </resultMap>  

对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性可以起作用,而且像绝大多数的领域模型,指令也许是你想使用一成不变的类的地方。通常包含引用或查询数据的表很少或基本不变的话对一成不变的类来说是合适的。构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis也支持私有属性和私有JavaBeans属性来达到这个目的,但是一些人更青睐构造方法注入。Constructor(构造方法)元素支持这个。

[java] view plaincopy
  1. package com.accp.mybatis.model;  
  2.   
  3. public class Blog {  
  4.     private Integer id;  
  5.     private String title;  
  6.     private Integer authorId;  
  7.   
  8.     public Blog() {  
  9.     }  
  10.   
  11.     public Blog(Integer id, String title, Integer authorId) {  
  12.         super();  
  13.         System.out.println("使用有参Blog");  
  14.         this.id = id;  
  15.         this.title = title;  
  16.         this.authorId = authorId;  
  17.     }  
  18.     //........  
  19. }  

为了向这个构造方法中注入结果,MyBatis需要通过它的参数的类型来标识构造方法。Java没有自查(或反射)参数名的方法。所以当创建一个构造方法元素时,保证参数是按顺序排列的,而且数据类型也是确定的。


association<关联映射>

[html] view plaincopy
  1. <!-- 映射关联的对象 -->  
  2. <association property="author" javaType="Author">  
  3.     <id column="author_id" property="id"/>  
  4.     <result column="username" property="username"/>  
  5.     <result column="password" property="password"/>  
  6.     <result column="email" property="email"/>  
  7.     <result column="bio" property="bio"/>  
  8. </association>  


关联元素处理“有一个”类型的关系。比如,在我们的示例中,一个博客有一个用户。关联映射就工作于这种结果之上。你指定了目标属性,来获取值的列,属性的java类型(很多情况下MyBatis可以自己算出来),如果需要的话还有jdbc类型,如果你想覆盖或获取的结果值还需要类型控制器。
关联中不同的是你需要告诉MyBatis如何加载关联。MyBatis在这方面会有两种不同的方式:
(1) 嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型。
(2) 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。
首先,然让我们来查看这个元素的属性。

属性

描述

property

映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的JavaBeans的属性,那么就会使用。否则MyBatis将会寻找给定名称的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你可以这样映射一些东西:“username”,或者映射到一些复杂的东西:“address.street.number”。

column

来自数据库的列名,或重命名的列标签。这和通常传递给resultSet.getString(columnName)方法的字符串是相同的。

注意:要处理复合主键,你可以指定多个列名通过column=”{prop1=col1,prop2=col2}”这种语法来传递给嵌套查询语句。这会引起prop1和prop2以参数对象形式来设置给目标嵌套查询语句。

javaType

一个Java类的完全限定名,或一个类型别名(参加上面内建类型别名的列表)。如果你映射到一个JavaBean,MyBatis通常可以断定类型。然而,如果你映射到的是HashMap,那么你应该明确地指定javaType来保证所需的行为。

jdbcType

在这个表格之前的所支持的JDBC类型列表中的类型。JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。这是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC编程,你需要指定这个类型-但仅仅对可能为空的值。

typeHandler

我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理器的实现,或者是类型别名。


联合嵌套选择(Nested Select for Association

select

通过这个属性,通过ID引用另一个加载复杂类型的映射语句。从指定列属性中返回的值,将作为参数设置给目标select 语句。表格下方将有一个例子。注意:在处理组合键时,您可以使用column={prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套语句。这就会把prop1prop2设置到目标嵌套语句的参数对象中。


 

SELECT<联合查询>

[html] view plaincopy
  1. <resultMap type="Blog" id="Blog_result">  
  2.     <association property="author" column="author_id"  
  3.         javaType="Author" select="selectAuthorById" />  
  4. </resultMap>  
  5.   
  6. <select id="selectAuthorById" parameterType="int" resultType="Author">  
  7.     select * from Author where id = #{id}  
  8. </select>  
  9.   
  10. <!--  
  11.     select关联,建议在一对一的情况下使用。  
  12.     在此处,如果selectBlogById返回多个Blog,将会带来N+1问题  
  13.  -->  
  14. <select id="selectBlogById" parameterType="int" resultMap="Blog_result">  
  15.     select * from Blog where id = #{id}  
  16. </select>  


 

我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描述了“selectAuthorById”语句应该被用来加载它的author属性。
其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。
这种方式很简单,但是对于大型数据集合和列表将不会表现很好。问题就是我们熟知的“N+1查询问题”。概括地讲,N+1查询问题可以是这样引起的:
    (1)你执行了一个单独的SQL语句来获取结果列表(就是“+1”)。
    (2)对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。

关联的嵌套结果

 

resultMap  

这是结果映射的ID,可以映射关联的嵌套结果到一个合适的对象图中。这是一种替代方法来调用另外一个查询语句。这允许你联合多个表来合成到一个单独的结果集。这样的结果集可能包含重复,数据的重复组需要被分解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis让你“链接”结果映射,来处理嵌套结果。例子会很容易来仿照,这个表格后面也有一个示例。

[html] view plaincopy
  1. <select id="selectBlog" parameterType="int" resultMap="blogResult">  
  2.     select  
  3.         B.id as blog_id,  
  4.         B.title as blog_title,  
  5.         B.author_id as blog_author_id,  
  6.         A.id as author_id,  
  7.         A.username as author_username,  
  8.         A.password as author_password,  
  9.         A.email as author_email,  
  10.         A.bio as author_bio  
  11.     From Blog B left outer join Author A on B.author_id = A.id  
  12.     where B.id = #{id}  
  13. </select>  


注意这个联合查询,以及采取保护来确保所有结果被唯一而且清晰的名字来重命名。这使得映射非常简单。现在我们可以映射这个结果:

[html] view plaincopy
  1. <resultMap id="blogResult" type="Blog">  
  2.     <id property=”blog_id” column="id" />  
  3.     <result property="title" column="blog_title"/>  
  4.     <association property="author" column="blog_author_id"  
  5.     javaType="Author" resultMap=”authorResult”/>  
  6. </resultMap>  
  7. <resultMap id="authorResult" type="Author">  
  8.     <id property="id" column="author_id"/>  
  9.     <result property="username" column="author_username"/>  
  10.     <result property="password" column="author_password"/>  
  11.     <result property="email" column="author_email"/>  
  12.     <result property="bio" column="author_bio"/>  
  13. </resultMap>  


非常重要:在嵌套结果映射中id元素扮演了非常重要的角色。应该通常指定一个或多个属性,它们可以用来唯一标识结果。实际上就是如果你不使用它(id元素),但是会产生一个严重的性能问题,不过MyBatis仍然可以正常工作。选择的属性越少越好,它们可以唯一地标识结果。主键就是一个显而易见的选择(即便是联合主键)。
现在,上面的示例用了外部的结果映射元素来映射关联。这使得Author结果映射可以重用。然而,如果你不需要重用它的话,或者你仅仅引用你所有的结果映射合到一个单独描述的结果映射中。你可以嵌套结果映射。这里给出使用这种方式的相同示例:

[html] view plaincopy
  1. <resultMap id="blogResult" type="Blog">  
  2.     <id property=”blog_id” column="id" />  
  3.     <result property="title" column="blog_title"/>  
  4.     <association property="author" column="blog_author_id"  
  5.         javaType="Author">  
  6.         <id property="id" column="author_id"/>  
  7.         <result property="username" column="author_username"/>  
  8.         <result property="password" column="author_password"/>  
  9.         <result property="email" column="author_email"/>  
  10.         <result property="bio" column="author_bio"/>  
  11.         </association>  
  12. </resultMap>  


collection<集合>

[html] view plaincopy
  1. <collection property="posts" ofType="Post">  
  2.     <id property="id" column="id"/>  
  3.     <result property="subject" column="subject"/>  
  4.     <result property="body" column="body"/>  
  5. </collection>  


集合元素的作用几乎和关联是相同的。实际上,它们也很相似。
我们来继续上面的示例,一个博客只有一个作者。但是博客有很多文章。在博客类中,这可以由下面这样的写法来表示:
private List<Post> posts;
要映射嵌套结果集合到List中,我们使用集合元素。就像关联元素一样,我们可以从连接中使用嵌套查询,或者嵌套结果。

集合的嵌套查询

首先,让我们看看使用嵌套查询来为博客加载文章。

[html] view plaincopy
  1. <resultMap type="Blog" id="Blog_result">  
  2.     <association property="author" column="author_id"  
  3.         javaType="Author" select="selectAuthorById" />  
  4.           
  5.     <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" />  
  6. </resultMap>  
  7.   
  8. <select id="selectBlogPost" resultType="Post" parameterType="int">  
  9.     select * from Post where blog_id=#{id}  
  10. </select>   
  11.   
  12. <select id="selectAuthorById" parameterType="int" resultType="Author">  
  13.     select * from Author where id = #{id}  
  14. </select>  
  15.   
  16. <!--  
  17.     select关联,建议在一对一的情况下使用。  
  18.     在此处,如果selectBlogById返回多个Blog,将会带来N+1问题  
  19.  -->  
  20. <select id="selectBlogById" parameterType="int" resultMap="Blog_result">  
  21.     select * from Blog where id = #{id}  
  22. </select>  

首先,你应该注意我们使用的是集合元素。然后要注意那个新的“ofType”属性。这个属性用来区分JavaBean(或字段)属性类型和集合包含的类型来说是很重要的。所以你可以读出下面这个映射:
[html] view plaincopy
  1. <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" />  

读作:“在Post类型的ArrayList中的posts的集合。”

集合的嵌套结果

[html] view plaincopy
  1. <select id="selectBlog" parameterType="int" resultMap="blogResult">  
  2.     select  
  3.         B.id as blog_id,  
  4.         B.title as blog_title,  
  5.         B.author_id as blog_author_id,  
  6.         P.id as post_id,  
  7.         P.subject as post_subject,  
  8.         P.body as post_body,  
  9.     from Blog B  
  10.         left outer join Post P on B.id = P.blog_id  
  11.     where B.id = #{id}  
  12. </select>  

现在用文章映射集合映射博客,可以简单写为:

[html] view plaincopy
  1. <resultMap id="blogResult" type="Blog">  
  2.     <id property=”id” column="blog_id" />  
  3.     <result property="title" column="blog_title"/>  
  4.     <collection property="posts" ofType="Post">  
  5.         <id property="id" column="post_id"/>  
  6.         <result property="subject" column="post_subject"/>  
  7.         <result property="body" column="post_body"/>  
  8.     </collection>  
  9. </resultMap>  

要记得id元素的重要性。
如果你引用更长的形式允许你的结果映射的更多重用,你可以使用下面这个替代的映

[html] view plaincopy
  1. <resultMap id="blogResult" type="Blog">  
  2.     <id property=”id” column="blog_id" />  
  3.     <result property="title" column="blog_title"/>  
  4.     <collection property="posts" ofType="Post" resultMap=”blogPostResult”/>  
  5. </resultMap>  
  6. <resultMap id="blogPostResult" type="Post">  
  7.     <id property="id" column="post_id"/>  
  8.     <result property="subject" column="post_subject"/>  
  9.     <result property="body" column="post_body"/>  
  10. </resultMap>  

discriminator鉴别器

[html] view plaincopy
  1. <discriminator javaType="int" column="draft">  
  2.     <case value="1" resultType="DraftPost"/>  
  3. </discriminator>  

有时一个单独的数据库查询也许返回很多不同(但是希望有些关联)数据类型的结果集。鉴别器元素就是被设计来处理这个情况的,还有包括类的继承层次结构。鉴别器非常容易理解,因为它的表现很像Java语言中的switch语句。
定义鉴别器指定了column和javaType属性。列是MyBatis查找比较值的地方。JavaType是需要被用来保证等价测试的合适类型(尽管字符串在很多情形下都会有用)。比如:

[html] view plaincopy
  1. <resultMap id="vehicleResult" type="Vehicle">  
  2.     <id property=”id” column="id" />  
  3.     <result property="vin" column="vin"/>  
  4.     <result property="year" column="year"/>  
  5.     <result property="make" column="make"/>  
  6.     <result property="model" column="model"/>  
  7.     <result property="color" column="color"/>  
  8.     <discriminator javaType="int" column="vehicle_type">  
  9.         <case value="1" resultMap="carResult"/>  
  10.         <case value="2" resultMap="truckResult"/>  
  11.         <case value="3" resultMap="vanResult"/>  
  12.         <case value="4" resultMap="suvResult"/>  
  13.     </discriminator>  
  14. </resultMap>  
  15. <resultMap id="carResult" type="Car">  
  16.     <result property=”doorCount” column="door_count" />  
  17. </resultMap>  

上面示例中,MyBatis会从结果集中得到每条记录,然后比较它的vehicle类型的值。如果它匹配任何一个鉴别器的实例,那么就使用这个实例指定的结果映射。
还有另外一种语法来做简洁的映射风格。比如:


[html] view plaincopy
  1. <resultMap id="vehicleResult" type="Vehicle">  
  2.     <id property=”id” column="id" />  
  3.     <result property="vin" column="vin"/>  
  4.     <result property="year" column="year"/>  
  5.     <result property="make" column="make"/>  
  6.     <result property="model" column="model"/>  
  7.     <result property="color" column="color"/>  
  8.     <discriminator javaType="int" column="vehicle_type">  
  9.         <case value="1" resultType="carResult">  
  10.             <result property=”doorCount” column="door_count" />  
  11.         </case>  
  12.         <case value="2" resultType="truckResult">  
  13.             <result property=”boxSize” column="box_size" />  
  14.             <result property=”extendedCab” column="extended_cab" />  
  15.         </case>  
  16.         <case value="3" resultType="vanResult">  
  17.             <result property=”powerSlidingDoor” column="power_sliding_door" />  
  18.         </case>  
  19.         <case value="4" resultType="suvResult">  
  20.             <result property=”allWheelDrive” column="all_wheel_drive" />  
  21.         </case>  
  22.     </discriminator>  
  23. </resultMap>  

0 0