Mybatis教程及使用配置


一.Mybatis简介



MyBatis由Clinton Begin 在2002 年创建,其后,捐献给了Apache基金会,成立了iBatis 项目。2010 年5 月,将代码库迁至Google Code,并更名为MyBatis。



MyBatis 是一个可以自定义SQL、存储过程和高级映射的持久层框架。MyBatis 只使用简单的XML 和注解来配置和映射基本数据类型、Map 接口和POJO 到数据库记录。相对Hibernate和Apache OJB等“一站式”ORM解决方案而言,Mybatis 是一种“半自动化”的ORM实现。



二.从JDBC到Mybatis



1.  JDBC查询数据库数据,一般需要以下七个步骤:



(1)  加载JDBC驱动



(2)  建立并获取数据库连接



(3)  创建 JDBC Statements 对象



(4)  设置SQL语句的传入参数



(5)  执行SQL语句并获得查询结果



(6)  对查询结果进行转换处理并将处理结果返回



(7)  释放相关资源(关闭Connection,关闭Statement,关闭ResultSet)



实现JDBC有七个步骤,哪些步骤是可以进一步封装的,减少我们开发的代码量?



2.  JDBC演变到Mybatis过程



第一步优化:连接获取和释放



问题描述:通过JDBC的方式数据库连接频繁的开启和关闭本身就造成了资源的浪费,影响系统的性能;但是现在连接池多种多样,可能存在变化,有可能采用DBCP的连接池,也有可能采用容器本身的JNDI数据库连接池。



解决问题:数据库连接的获取和关闭我们可以使用数据库连接池来解决资源浪费的问题,通过连接池就可以反复利用已经建立的连接去访问数据库了,减少连接的开启和关闭的时间。



同时通过DataSource进行隔离解耦,统一从DataSource里面获取数据库连接,DataSource具体由DBCP实现还是由容器的JNDI实现都可以,将DataSource的具体实现通过让用户配置来应对变化。



第二步优化:SQL统一存取



问题描述:通过JDBC的方式,可读性很差,不利于维护以及做性能调优;改动Java代码需要重新编译、打包部署。不利于取出SQL在数据库客户端执行(取出后还得删掉中间的Java代码,编写好的SQL语句写好后还得通过+号在Java进行拼凑)。



     解决问题:把SQL语句统一集中放到配置文件,通过SQL语句的key值去获取对应的SQL语句。



第三步优化:传入参数映射和动态SQL



问题描述:很多情况下,我们都可以通过在SQL语句中设置占位符来达到使用传入参数的目的,这种方式本身就有一定局限性,它是按照一定顺序传入参数的,要与占位符一一匹配。但是,如果我们传入的参数是不确定的(比如列表查询,根据用户填写的查询条件不同,传入查询的参数也是不同的,有时是一个参数、有时可能是三个参数),那么我们就得在后台代码中自己根据请求的传入参数去拼凑相应的SQL语句,这样的话还是避免不了在Java代码里面写SQL语句的命运。



解决问题:通过专门的SQL解析器来解析SQL语句,SQL语句经过解析后就可以动态的生成符合上下文的SQL语句,同时使用#变量名#表示占位符变量,使用$变量名$表示非占位符变量



第四步优化:结果映射和结果缓存



问题描述:执行SQL语句、获取执行结果、对执行结果进行转换处理、释放相关资源是一整套下来的。假如是执行查询语句,那么执行SQL语句后,返回的是一个ResultSet结果集,这个时候我们就需要将ResultSet对象的数据取出来,不然等到释放资源时就取不到这些结果信息了。我们从前面的优化来看,以及将获取连接、设置传入参数、执行SQL语句、释放资源这些都封装起来了,只剩下结果处理这块还没有进行封装,如果能封装起来,每个数据库操作都不用自己写那么一大堆Java代码,直接调用一个封装的方法就可以搞定了



解决问题:一般对执行结果的有哪些处理,有可能将结果不做任何处理就直接返回,也有可能将结果转换成一个JavaBean对象返回、一个Map返回、一个List返回等等,结果处理可能是多种多样的。从这里看,我们必须告诉SQL处理器两点:第一,需要返回什么类型的对象;第二,需要返回的对象的数据结构怎么跟执行的结果映射,这样才能将具体的值copy到对应的数据结构上。



      接下来,我们可以进而考虑对SQL执行结果的缓存来提升性能。缓存数据都是key-value的格式,那么这个key怎么来呢?怎么保证唯一呢?即使同一条SQL语句几次访问的过程中由于传入参数的不同,得到的执行SQL语句也是不同的。那么缓存起来的时候是多对。但是SQL语句和传入参数两部分合起来可以作为数据缓存的key值。



第五步优化:解决重复SQL语句问题



问题描述:将所有SQL语句都放到配置文件中,这个时候会遇到一个SQL重复的问题,几个功能的SQL语句其实都差不多,有些可能是SELECT后面那段不同、有些可能是WHERE语句不同。有时候表结构改了,那么我们就需要改多个地方,不利于维护。



解决问题:通过将SQL片段模块化,将重复的SQL片段独立成一个SQL块,然后在各个SQL语句引用重复的SQL块,这样需要修改时只需要修改一处即可。



 3.  优化的总结



对JDBC的优化和封装:



(1) 使用数据库连接池对连接进行管理



(2) SQL语句统一存放到配置文件



(3) SQL语句变量和传入参数的映射以及动态SQL



(4) 动态SQL语句的处理



(5) 对数据库操作结果的映射和结果缓存



(6) SQL语句的重复使用



三.框架整体设计



1.       总体流程



 

(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)返回结果处理


   将最终的处理结果返回


 


2.       功能架构设计


 


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


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


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


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


 


 


 


3.       框架架构设计




框架架构讲解:


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


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


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


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


  


 


四.Mybatis入门


每一个Mybatis应该都是以一个SqlSessionFactory实例为中心的,一个SqlSessionFactory实例都可以使用SqlSessionFactoryBuilder来创造。从配置类中创造的定制SqlSessionFactoryBuilder 实例,可以使用XML配置文件来生成一个SqlSessionFactory 实例。


1.       从xml中创造SqlSessionFactory


MyBatis 有一个Resources 通用类,类中有许多方法可以简单地从类路径和其他地址中加载资源。

String resource = "org/mybatis/example/Configuration.xml"; 
   
    

     Reader reader = Resources.getResourceAsReader(resource); 
   
    

     sqlMapper = new SqlSessionFactoryBuilder().build(reader);


 


XML 文件包含了许多MyBatis 的核心设置,包括一个获取数据库连接(Connection)实例的数据源(DataSource),一个决定事务作用域和操作的TransactionManager。全部的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> 
   
    

     <environments default="development"> 
   
    

     <environment id="development"> 
   
    

     <transactionManager type="JDBC"/> 
   
    

     <dataSource type="POOLED"> 
   
    

     <property name="driver" value="${driver}"/> 
   
    

     <property name="url" value="${url}"/> 
   
    

     <property name="username" value="${username}"/> 
   
    

     <property name="password" value="${password}"/> 
   
    

     </dataSource> 
   
    

     </environment> 
   
    

     </environments> 
   
    

     <mappers> 
   
    

     <mapper resource="org/mybatis/example/XXMapper.xml"/> 
   
    

     </mappers> 
   
    

     </configuration>


2.       不使用xml文件新建SqlSessionFactory


DataSource dataSource = DataSourceFactory.getDataSource(); 
   
    

     TransactionFactory transactionFactory = new JdbcTransactionFactory(); 
   
    

     Environment environment = 
   
    

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

     Configuration configuration = new Configuration(environment); 
   
    

     configuration.addMapper(XXMapper.class); 
   
    

     SqlSessionFactory sqlSessionFactory = 
   
    

     new SqlSessionFactoryBuilder().build(configuration);


3.       使用SqlSessionFactory获取SqlSession


通过一个SqlSessionFactory,就可以获取一个SqlSession实例,SqlSession包含了针对数据库执行语句的每一个方法,直接使用SqlSession执行已经映射的每一个SQL语句:


SqlSession session = sqlSessionFactory.openSession(); 
   
    

     try { 
   
    

     XXMapper mapper = session.getMapper(XXMapper.class); 
   
    

     XX  xx= mapper.selectXx (101); 
   
    

     } finally { 
   
    

     session.close();


五.探究SQL映射语句


接下来先看一个简单的demo,来完成SqlSession的调用过程

<?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.coin.dao.PersonDao"> 
   
    

              <select id= 
    "getPerson" resultType=     "Person     " parameterType=     "long">    
    

                 SELECT id,name 
   
    

                 FROM T_Person 
   
    

                 WHERE id = #{id} 
   
    

     </select></mapper> 
   
    

     可以用下列语句简单地针对Mapper 接口进行调用上面文件,代码如下: 
   
    

     PersonDao mapper = session.getMapper(PersonDao.class); 
   
    

     Person p= mapper.getPerson (1);


还有一个关于Mapper 类的技巧。它们的映射语句完全不需要使用XML 来配置,可以使用JAVA 注解方式来取代。比如,上面的XML 语句可以替换为:


public interface PersonDao { 
   
    

     @Select("SELECT id,name 
   
    

                 FROM T_Person 
   
    

                 WHERE id = #{id} 
   
    

     ") 
   
    

     Person  
    getPerson (int id);

 


总结:注解是非常简单明了的,但是JAVA 注解既有局限性,在语句比较复杂的情况下又比较容易混乱。所以,如果你的语句比较复杂,最好还是使用XML 来映射语句。


六.作用域和生命周期


1.       SqlSessionFactoryBuilder


这个类可以被初始,使用和丢弃,因为如果已经创建好一个SqlSessionFactory后就不用再保留它,所以SqlSessionFactoryBuilder的最好作用域是方法体内的。


2.       SqlSessionFactory


一旦创建,SqlSessionFactory 就会在整个应用过程中始终存在。所以没有理由去销毁和再创建它,一个应用运行中也不建议多次创建SqlSessionFactory。因此SqlSessionFactory最好的作用域是Application。可以结合使用Google Guice 或Spring 来进行依赖反射。这些框架允许你生成管理器来管理SqlSessionFactory 的单例生命周期。


3.       SqlSession


每个线程都有自己的SqlSession 实例,SqlSession 实例是不能被共享,也是不是线程安全的。因此最好使用Request 作用域或者方法体作用域。比如说在Servlet 中的HttpSession 中。如果你正在使用WEB 框架,应该让SqlSession 跟随HTTP 请求的相似作用域。也就是说,在收到一个HTTP 请求过后,打开SqlSession,等返回一个回应以后,立马关掉这个SqlSession。关闭SqlSession 是非常重要的。你必须要确保SqlSession 在finally 方法体中正常关闭。可以使用下面的标准方式来关闭:


SqlSession session = sqlSessionFactory.openSession(); 
   
    

     try { 
   
    

     // do work 
   
    

     } finally { 
   
    

     session.close(); 
   
    

     使用这种模式来贯穿你的所有代码,以确保所有数据库资源都被完全关闭。[这是假定不是使用你自己的数据库连接,而是使用MyBatis 来管理你的数据库连接资源]。 
   
    
     4.       Mapper实例    
    

     Mapper是一种创建用于绑定映射语句的接口。Mapper接口的实例是用SqlSession来获得的,所以Mapper 实例作用域像SqlSession 一样,使用请求作用域。在方法被调用的时候调用Mapper实例,然后使用后,就会自动销毁掉,而不需要使用明确的注销。下面例子演示如何操作: 
   
    

     SqlSession session = sqlSessionFactory.openSession(); 
   
    

     try { 
   
    

     PersonDao mapper = session.getMapper(PersonDao.class); 
   
    

     // do work 
   
    

     } finally { 
   
    

     session.close();


七.Mapper的XML配置文件


Mapper的XML配置文件包含一些设置和属性,用于增强Mybatis的动作。Configuration的各个节点配置如下:




1.       属性(properties)


JAVA 属性文件就可以配置直观的、可代替的属性,或者是属性项的子项。比如: 
   
    

     <properties resource="org/mybatis/example/config.properties"> 
   
    

     <property name="username" value="test"/> 
   
    

     <property name="password" value="test/> 
   
    

     </properties> 
   
    

     通过动态配置,这些属性都可以用替换整个文件的值。例如: 
   
    

     <dataSource type="POOLED"> 
   
    

     <property name="driver" value="${driver}"/> 
   
    

     <property name="url" value="${url}"/> 
   
    

     <property name="username" value="${username}"/> 
   
    

     <property name="password" value="${password}"/> 
   
    

     </dataSource>


例子中的usrname 和password 将被属性文件中设置的值所替换, driver 和value 属性也将被config.properties 文件中值所替换,这为配置提供了多种选择。


属性值也可以设入到SqlSessionBuilder.build()方法中,例如:

SqlSessionFactory factory = 
   
    

     sqlSessionFactoryBuilder.build(reader, props); 
   
    

     // ... or ... 
   
    

     SqlSessionFactory factory = 
   
    

     sqlSessionFactoryBuilder.build(reader, environment, props);

如果一个属性项在多个地方出现,那MyBatis 将按以下顺序加载: