MyBatis


MyBatis

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。MyBatis是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架
MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

MyBatis最强大的特性之一就是它的动态语句功能。如果您以前有使用JDBC或者类似框架的经历,您就会明白把SQL语句条件连接在一起是多么的痛苦,要确保不能忘记空格或者不要在columns列后面省略一个逗号等。动态语句能够完全解决掉这些痛苦。

MyBatis 优点

  1. 简单易学
  2. 灵活
  3. 降低sql与程序代码的耦合
  4. 提供映射标签,支持对象与数据库的orm字段关系映射
  5. 提供对象关系映射标签,支持对象关系组建维护
  6. 提供xml标签,支持编写动态sql

MyBatis 缺点

  1. 编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此
  2. SQL语句依赖于数据库,导致数据库移植性较差
  3. 二级缓存机制不佳

MyBatis 核心配置文件

MyBatis核心配置文件是一个xml文件
配置环境(开发、测试、生产)
配置数据库连接信息
配置连接池
设置MyBatis运行参数
配置映射器(mappers)

示例:


    
    
        
        
            
            
            
            
            
                
                
                
                
            
        
    

配置环境(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者共享相同 Schema 的多个生产数据库, 想使用相同的 SQL 映射。许多类似的用例。

不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。

所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:

每个数据库对应一个 SqlSessionFactory 实例
为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:

SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties);
如果忽略了环境参数,那么默认环境将会被加载,如下所示:

SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties);
环境元素定义了如何配置环境。

注意这里的关键点:

默认的环境 ID(比如:default=”development”)。
每个 environment 元素定义的环境 ID(比如:id=”development”)。
事务管理器的配置(比如:type=”JDBC”)。
数据源的配置(比如:type=”POOLED”)。
默认的环境和环境 ID 是一目了然的。随你怎么命名,只要保证默认环境要匹配其中一个环境ID。

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):

JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。例如:



NOTE如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

这两种事务管理器类型都不需要任何属性。它们不过是类型别名,换句话说,你可以使用 TransactionFactory 接口的实现类的完全限定名或类型别名代替它们。

public interface TransactionFactory {
void setProperties(Properties props);
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
任何在 XML 中配置的属性在实例化之后将会被传递给 setProperties() 方法。你也需要创建一个 Transaction 接口的实现类,这个接口也很简单:

public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
使用这两个接口,你可以完全自定义 MyBatis 对事务的处理。

数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

许多 MyBatis 的应用程序将会按示例中的例子来配置数据源。然而它并不是必须的。要知道为了方便使用延迟加载,数据源才是必须的。
有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):

UNPOOLED– 这个数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择。 不同的数据库在这方面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理想的。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是JDBC驱动中可能包含的数据源类)。
url – 这是数据库的 JDBC URL 地址。
username – 登录数据库的用户名。
password – 登录数据库的密码。
defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
作为可选项,你也可以传递属性给数据库驱动。要这样做,属性的前缀为“driver.”,例如:

driver.encoding=UTF8
这将通过DriverManager.getConnection(url,driverProperties)方法传递值为 UTF8 的 encoding 属性给数据库驱动。

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。

除了上述提到 UNPOOLED 下的属性外,会有更多属性用来配置 POOLED 的数据源:

poolMaximumActiveConnections – 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
poolTimeToWait – 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。
poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。
poolPingEnabled – 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。
poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
JNDI– 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性:

initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。
data_source – 这是引用数据源实例位置的上下文的路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给初始上下文。比如:

env.encoding=UTF8
这就会在初始上下文(InitialContext)实例化时往它的构造方法传递值为 UTF8 的 encoding 属性。

通过需要实现接口 org.apache.ibatis.datasource.DataSourceFactory , 也可使用任何第三方数据源,:

public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory 可被用作父类来构建新的数据源适配器,比如下面这段插入 C3P0 数据源所必需的代码:

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

public C3P0DataSourceFactory() {
this.dataSource = new ComboPooledDataSource();
}
}
为了令其工作,为每个需要 MyBatis 调用的 setter 方法中增加一个属性。下面是一个可以连接至 PostgreSQL 数据库的例子:

Mapper 映射文件

MyBatis 的真正强大在于它的映射语句,也是它的魔力所在
所有的SQL语句都要定义要Mapper文件中

常用标签:

  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

示例:

<?xml version="1.0" encoding="UTF-8" ?>



insert into dept
values(#{deptno}, #{dname}, #{loc})



insert into dept
values(#{deptno}, #{dname}, #{loc})



insert into dept values(#{deptno}, #{dname}, #{loc})

select seq.nextval from dual




update dept set
dname=#{dname}, loc=#{loc} where deptno=#{deptno}



delete from dept where
deptno=#{xx}


Mapper文件的定义和根节点

<?xml version="1.0" encoding="UTF-8"?>


 

Mapper必须在核心配置文件中注册

 
        
 

insert

标签,用于执行insert into。

属性说明:

  • id 引用的名称
  • parameterType 将要传入语句的参数的完全限定类名或别名,可选,可以是Map、Pojo或普通类型
  • useGeneratedKeys 是否有自动增长列
  • keyProperty 自动增长列的字段名

示例:

 
        insert  into dept(dname,loc) values ( #{dname},#{loc})
  

案例:


public Dept findDeptById(int id);//接口


public void addDept(@Param("id") int id,@Param("name") String name,@Param("loc") String loc);

        insert  into dept(deptno,dname,loc) values ( #{id},#{name},#{loc})


public int updateDept(Dept dept);

        update  dept set dname=#{dname},loc=#{loc} where deptno=#{deptno}


public void addDeptByEntity(Dept dept);

        insert into dept(deptno,dname,loc) values(#{deptno},#{dname},#{loc})
        
            select myseq.nextval//这是我数据库定义的序列 from dual
        

说明:

从Map或Pojo中取出key或property值的语法 :#{key|property}

如果要使用Oracle的序列为字段赋值,使用selectKey子标签:

  1. resultType 返回的类型
  2. keyProperty 将selectKey的返回值填充到指定的字段
  3. order 执行的顺序,可选值为before和after

简单来说befor 在插入数据前就替换了bean的id并插入,表中与bean中使用的是序列的值
after 在插入数据后替换了bean的id,此时使用的是bean的值,不是序列的id,表中的id值是由bean指定的。而bean的id值确是序列的值

update和delete

update语句

    
        update  dept set dname=#{dname},loc=#{loc} where deptno=#{deptno}
    

delete语句


        delete from dept where deptno=#{deptno}

select

示例:


resultMap

如果Pojo的属性名和表中的字段名不一致,需要使用resultMap标签映射属性名和字段名

示例:

实体类:
public class Dept {
    private int deptno;
    private String dname;
    public String loc;
}

    
    
    
 
 

MyBatis APl

SqlSession:会话,代表与数据库的一次会话
SqlSessionFactory:用于创建SqlSession的工厂
SqlSessionFactoryBuilder:创建创建SqlSessionFactory的构建器

SqlSession

SqlSession的常用方法

int insert(String statement, Object parameter);
int update(String statement, Object parameter);
int delete(String statement, Object parameter);
 List selectList(String statement, Object parameter);
 T selectOne(String statement, Object parameter);
void commit();
void rollback();
void close();

MyBatis以接口方式编程

MyBatis可以以接口方式编程,只需要定义接口无需要实现,同时,也不再使用SqlSession来执行sql语句。

示例:


        delete from dept where deptno=#{deptno}

测试类:
  @Test
    public void test7() throws IOException {
        Reader reader=Resources.getResourceAsReader("config.xml");
        SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession session=factory.openSession();
        DeptDao deptDao=session.getMapper(DeptDao.class);
        deptDao.deleteDept(75);
        session.commit();
        session.close();
    }