1. 简介

Java程序都是通过JDBC连接数据库,但是只定义了接口规范,具体的实现交给各个数据库厂商去实现,因为每个数据库都有其特殊性。所以JDBC是一种桥接模式

这里为什么说JDBC是一种桥接模式呢?为例避免JDBC的缺陷出现了ORM模型,比如Hibernate,提供的是一种全表映射模型。因为Hibernate的一些缺陷,比如:

  • Hibernate屏蔽了SQL,只能全表映射,更新时需要发送所有的字段
  • 无法根据不同的条件组装不同的SQL
  • 对多表关联和复杂SQL查询支持较差,需要自己写SQL,返回后,需要自己将数据组装为POJO
  • 不能有效地支持存储过程
  • 虽然有HQL,但是性能较差。

因此出现了MyBatis,一个半自动的框架。之所以称之为半自动,是因为它需要手动提供POJO、SQL和映射关系,而全表映射的Hibernate只需要提供POJO和映射关系即可,因为Hibernate无需编写SQL,所以开发效率优于MyBatis。

2. MyBatis入门

MyBatis的核心组件:

  • SqlSessionFactoryBuilder(构造器):它会根据配置信息或者代码来生成SqlSessionFactory(工厂接口)
  • SqlSessionFactory:依靠工厂来生成SqlSession
  • SqlSession:是一个既可以发送SQL去执行并返回结果,也可以获取Mapper的接口
  • SQL Mapper:它是MyBatis新设计的组件,是由一个java接口和XML文件(或注解)构成的,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返回结果。

2.1 构建SqlSessionFactory

每个MyBatis的应用都是以SqlSessionFactory的实例为中心,可以通过SqlSessionFactoryBuilder获得,注意SqlSessionFactory是一个工厂接口而不是实现类,其任务是创建SqlSession。SqlSession类似于一个JDBC的Connection对象。MyBatis提供了两种模式去创建SqlSessionFactory:一种是XML配置的方式,另一种是代码方式。要尽量的使用配置文件,这样一方面可以避免硬编码,一方面方便日后配置人员修改。

org.apache.ibatis.session.Configuration在MyBatis中将以一个Configuration类对象的形式存在,存在于整个MyBatis应用的生命期中,以便重复读取和运用。我们可以一次性解析一次配置的XML保存到Configuration对象中。在MyBatis中提供了两个SqlSessionFactory的实现类,DefaultSqlSessionFactory和SqlSessionManager。目前使用的是DefaultSqlSessionFactory。

2.1.1 使用XML方式构建

这里以一个简易的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>
    <!--定义别名 -->
    <typeAliases>
        <typeAlias alias="role" type="com.learn.chapter2.po.Role"/>
    </typeAliases>
    <!--定义数据库信息,默认使用development数据库构建环境-->
    <environments default="development">  
        <environment id="development">  
            <!--采用jdbc事务管理 -->
            <transactionManager type="JDBC">
                <property name="autoCommit" value="false"/>
            </transactionManager>
            <!--配置数据库链接信息  -->
            <dataSource type="POOLED">  
                <property name="driver" value="com.mysql.jdbc.Driver" />  
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />  
                <property name="password" value="root" />
            </dataSource>  
        </environment>  
    </environments>  
    <!--定义映射器 -->
     <mappers>
         <mapper resource="com/learn/chapter2/mapper/roleMapper.xml"/>
     </mappers>
</configuration>

然后用代码实现创建SqlSessionFactory:

String resource="mybatis-config.xml";
InputStream inputStream=null;
inputStream=Resources.getResourceAsStream(resource);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);

这里创建了一个XML文件输入流,用SqlSessionFactoryBuilder读取XML的信息 来创建SqlSessionFactory的对象。MyBatis的解析程序会将mybatis-config.xml文件配置信息解析到Configuration类对象里面,然后利用SqlSessionFactoryBuilder读取这个对象,为我们创建SqlSessionFactory。

2.1.2 使用代码方式构建

不推荐这种方式,因为修改环境的时候,要重新编译代码,不利于维护。

     //构建数据库连接池
        PooledDataSource dataSource=new PooledDataSource();
        dataSource.setDriver("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        //构建数据库事务方式
        TransactionFactory transactionFactory=new JdbcTransactionFactory();
        //创建数据库运行环境
        Environment environment=new Environment("development",transactionFactory,dataSource);
        //构建Configuration对象
        Configuration configuration=new Configuration(environment);
        //注册一个MyBatis上下文别名
        configuration.getTypeAliasRegistry().registerAlias("role",Role.class);
        //加入一个映射器
        configuration.addMapper(RoleMapper.class);
        //使用SqlSessionFactoryBuilder构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(configuration);
        return sqlSessionFactory;

采用代码方式一般是需要加入自己特性的时候才会用到,例如数据源配置的信息要求是加密的时候,我们需要把它转化出来。大部分情况下不建议用这个方式来创建SqlSessionFactory

2.1.2 创建SqlSession

在MyBatis中SqlSession接口的实现类有两个,分别是DefaultSqlSession和SqlSessionManager。我们构建了SqlSessionFactory,然后生成门面接口SqlSession。SqlSession接口类似于一个JDBC中的Connection接口对象,我们需要保证每次用完正确关闭它。

SqlSession的用途主要是获取映射器以及直接通过命名信息去执行SQL返回结果。

2.1.3 映射器

由Java接口和XML文件(或注解)共同组成,作用如下:

  • 定义参数类型
  • 描述缓存
  • 描述SQL语句
  • 定义查询结果和POJO的映射关系

使用XML文件配置方式实现Mapper,第一步给出Java接口:

public interface RoleMapper {
    public Role getRole(Long id);
    public int deleteRole(Long id);
    public int insertRole(Role role);
}

第二步给出一个映射文件:

<?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.learn.chapter2.mapper.RoleMapper">
    <select id="getRole"  resultType="role" parameterType="long">
        select id,role_name as roleName,note from t_role where id=#{id}
    </select>
</mapper>
  • MyBatis会读取这个配置文件,生成映射器
  • 定义一个命名空间为com.learn.chapter2.mapper.RoleMapper的SQL Mapper,这个命名空间和我们定义的接口的全限定名要一致
  • 用一个select元素定义一个查询SQL,id是getRole,和我们接口中的方法名是一致的
  • #{id}为这条SQL的参数。而SQL列的别名和POJO的属性名称保持一致。那么MyBatis就会把这条语句查询的结果自动映射到我们需要的POJO属性上

现在可以使用SqlSession来获取这个Mapper:

RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);
Role role=roleMapper.getRole(1L);

 2.1.4 Java注解方式实现Mapper

不推荐注解的方式,如果系统简单的话,使用注解方式也不失为一个好办法。

注册这个接口为映射器:

2.1.4 一些疑问

这要注意的是在MyBatis中保留这iBatis,通过命名空间+SQL id 的方式发送SQL并返回数据的形式,而不需要获取映射器:

2.2 生命周期

SqlSessionFactoryBuilder:只存在于方法的局部,作用是生成SessionFactory对象。

SqlSessionFactory:存在于MyBatis应用的整个生命周期中,唯一责任是创建SqlSession,要采用单例模式。

SqlSession:一个会话,相当于JDBC的一个Connection对象,他的生命周期应该是在请求数据库处理事务的过程中。它是一个线程不安全的对象。

Mapper:一个接口,没有任何实现类,作用是发送SQL。因此它应该在一个SqlSession事务方法之内,是一个方法级别的东西。

2.3 实例

代码清单2-11:log4j.properties

log4j.rootLogger=DEBUG, stdout
log4j.logger.org=BEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: m%n

 其中参数的解释见博文:http://www.cnblogs.com/alipayhutu/archive/2012/06/21/2558249.html 

其次,构建SessionFactory,我们需要配置文件,代码:

<?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>
    <!--定义别名 -->
    <typeAliases>
        <typeAlias alias="role" type="com.learn.chapter2.po.Role"/>
    </typeAliases>
    <!--定义数据库信息,默认使用development数据库构建环境-->
    <environments default="development">  
        <environment id="development">  
            <!--采用jdbc事务管理 -->
            <transactionManager type="JDBC">
                <property name="autoCommit" value="false"/>
            </transactionManager>
            <!--配置数据库链接信息  -->
            <dataSource type="POOLED">  
                <property name="driver" value="com.mysql.jdbc.Driver" />  
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />  
                <property name="password" value="root" />
            </dataSource>  
        </environment>  
    </environments>  
    <!--定义映射器 -->
     <mappers>
         <mapper resource="com/learn/chapter2/mapper/roleMapper.xml"/>
     </mappers>
</configuration>

 构建SqlSessionFactory

package com.learn.chapter2.util;

import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.logging.Log;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class SqlSessionFactoryUtil {
    //SqlSessionFactory对象
    private static SqlSessionFactory sqlSessionFactory=null;
    //类线程锁
    private static final Class Class_LOCK=SqlSessionFactoryUtil.class;
    /**
     * 私有化构造参数
     */
    private SqlSessionFactoryUtil(){}
    
    /**
     * 构建SqlSessionFactory
     */
    public static SqlSessionFactory initSqlSessionFactory(){
        String resource="mybatis-config.xml";
        InputStream inputStream=null;
        try{
            inputStream=Resources.getResourceAsStream(resource);
        }catch (Exception e) {
            Logger.getLogger(SqlSessionFactoryUtil.class.getName()).log(Level.SEVERE, null,e);
        }
        synchronized (Class_LOCK) {
            if(sqlSessionFactory==null){
                sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
            }
        }
        return sqlSessionFactory;
    }
    /**
     * 打开SqlSession
     */
    public static SqlSession openSqlSession(){
        if(sqlSessionFactory==null){
            initSqlSessionFactory();
        }
        return sqlSessionFactory.openSession();
    }
}

上面要注意的部分:

1. 类线程锁:private static final Class Class_LOCK=SqlSessionFactoryUtil.class;

实现一个POJO类——Role.java

package com.learn.chapter2.po;

public class Role {
    private Long id;
    private String roleName;
    private String note;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getRoleName() {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note;
    }
}

 还需要一个映射器的描述:

<?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.learn.chapter2.mapper.RoleMapper">
    <select id="getRole"  resultType="role" parameterType="long">
        select id,role_name as roleName,note from t_role where id=#{id}
    </select>
    <insert id="insertRole" parameterType="role">
        insert into t_role(role_name,note) values (#{roleName},#{note})
    </insert>
    <delete id="deleteRole" parameterType="long">
        delete from t_role where id=#{id}
    </delete>
</mapper>

 定义相应的接口:

package com.learn.chapter2.mapper;

import com.learn.chapter2.po.Role;

public interface RoleMapper {
    public Role getRole(Long id);
    public int deleteRole(Long id);
    public int insertRole(Role role);
}

 使用一个main方法来测试:

package com.learn.chapter2.main;

import org.apache.ibatis.session.SqlSession;

import com.learn.chapter2.mapper.RoleMapper;
import com.learn.chapter2.po.Role;
import com.learn.chapter2.util.SqlSessionFactoryUtil;

public class Chapter2Main {
    public static void main(String[] args){
        SqlSession sqlSession=null;
        try{
            sqlSession=SqlSessionFactoryUtil.openSqlSession();
            RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);
            Role role=new Role();
            role.setRoleName("testName");
            role.setNote("testRole");
            roleMapper.insertRole(role);
            roleMapper.deleteRole(1L);
            sqlSession.commit();
        }catch (Exception e) {
            System.err.println(e.getMessage());
            sqlSession.rollback();
        }finally{
            if(sqlSession!=null){
                sqlSession.close();
            }
        }
    }
}

 

posted on 2017-07-05 10:47  f91og  阅读(519)  评论(0编辑  收藏  举报