简介

从mybatis的包,可以看出,它是一个ORM框架:

  • 解决了jdbc相关的衔接,比如数据连接池、数据集的解释,数据库事务;
  • 也解决了数据集到对象的映射;
  • 另包括日志,以及对以上各个环节的缓存支持等等辅助模块。

以会话的形态供外界调用。

应用

mybatis的类库从SqlSessionFactoryBuilder开始,需要读取一个符合标准的xml配置文件。文件内容主要包括environments(比如数据源data source等),mappers(对象到JDBC的映射)

SqlSessionFactoryBuilder根据data source设置创建sql session工厂,这个工厂初始化后能被反复调用。

常规的调用过程如下:
session工厂能反复打开session——>session获取mapper——>调用mapper里定义业务接口(通常就是SQL处理)——>关闭session。通常有一些公用方法可以抽象出来, 比如session的commit和close,因此可以用代理类将session和mapper通过InvocationHandler进行关联。

代码示例

以下示例展示mybatis同时采用多个数据源,实际应用就是读写分离或业务分库。

(1)添加mybatis的支持

<dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.2.8</version>
</dependency>

(2)准备好数据库环境,更多Mysql环境搭建点这里
准备测试数据:

-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'tao');

(3)程序开发
- 数据库配置信息文件db:

hd.jdbc.driverClassName=com.mysql.jdbc.Driver
hd.jdbc.url=jdbc:mysql://192.168.142.128:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
hd.jdbc.username=root
hd.jdbc.password=dadyz30h

ho.jdbc.driverClassName=com.mysql.jdbc.Driver
ho.jdbc.url=jdbc:mysql://192.168.142.128:3306/test2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
ho.jdbc.username=root
ho.jdbc.password=dadyz30h

- Mybatis配置文件mybatis.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>
    <properties resource="db"/>

    <environments default="HO">
        <environment id="HD">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${hd.jdbc.driverClassName}" />
                <property name="url" value="${hd.jdbc.url}" />
                <property name="username" value="${hd.jdbc.username}" />
                <property name="password" value="${hd.jdbc.password}" />
            </dataSource>
        </environment>
         <environment id="HO">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${ho.jdbc.driverClassName}" />
                <property name="url" value="${ho.jdbc.url}" />
                <property name="username" value="${ho.jdbc.username}" />
                <property name="password" value="${ho.jdbc.password}" />
            </dataSource>
        </environment>       
    </environments>   

    <mappers>
        <mapper class="UserMapper"/>
      </mappers>
</configuration>

- 用户类:

public class User {
    private int uid;
    private String name;
    public int getUid() {
        return uid;
    }
    public void setUid(int uid) {
        this.uid = uid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return String.format("id:%d/name:%s", uid, name);
    }
}

- 用户对象业务处理:

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

public interface UserMapper extends Mapper{
    @Insert("insert into user(name) values(#{name})") 
    public void insertUser(User user); 

    @Update("update user set name=#{name} where uid=#{uid}") 
    public void updateUser(User user); 

    @Select("select * from user where uid=#{id}") 
    public User getUserById(int id); 

    @Delete("delete from t_user where uid=#{id}") 
    public void deleteUser(int id); 
}

- mappers接口类:

public interface Mapper {
}

- Mapper工厂类:

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.commons.io.IOUtils;
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 enum MapperFactory {

    HD {
        private SqlSessionFactory sqlSessionFactory;

        @Override
        public <T> T createMapper(Class<? extends Mapper> clazz) {
            return createMapper(clazz, this);
        }

        @Override
        protected void createSqlSessionFactory() {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, this.name());
        }

        @Override
        public SqlSessionFactory getSqlSessionFactory() {
            return sqlSessionFactory;
        }

    },
    HO {
        private SqlSessionFactory sqlSessionFactory;
        @Override
        public <T> T createMapper(Class<? extends Mapper> clazz) {
            return createMapper(clazz, this);
        }

        @Override
        protected void createSqlSessionFactory() {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, this.name());
        }

        @Override
        public SqlSessionFactory getSqlSessionFactory() {
            return sqlSessionFactory;
        }
    };

    /**
     * Create a mapper of environment by Mapper class
     * @param clazz Mapper class
     * @param environment A datasource environment
     * @return a Mapper instance
     */
    public abstract <T> T createMapper(Class<? extends Mapper> clazz);

    /**
     * Create SqlSessionFactory of environment
     */
    protected abstract void createSqlSessionFactory();

    /**
     * get SqlSessionFactory
     */
    public abstract SqlSessionFactory getSqlSessionFactory();

    private static InputStream inputStream = null;
    static {
        try {
            inputStream = Resources.getResourceAsStream("mybatis.xml");
            HO.createSqlSessionFactory();
            inputStream = Resources.getResourceAsStream("mybatis.xml");
            HD.createSqlSessionFactory();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> T createMapper(Class<? extends Mapper> clazz, MapperFactory MapperFactory) {
        SqlSession sqlSession = MapperFactory.getSqlSessionFactory().openSession();
        Mapper mapper = sqlSession.getMapper(clazz);
        return (T)MapperProxy.bind(mapper, sqlSession);
    }


    private static class MapperProxy implements InvocationHandler {
        private Mapper mapper;
        private SqlSession sqlSession;

        private MapperProxy(Mapper mapper, SqlSession sqlSession) {
            this.mapper = mapper;
            this.sqlSession = sqlSession;
        }

        private static Mapper bind(Mapper mapper, SqlSession sqlSession) {
            return (Mapper) Proxy.newProxyInstance(mapper.getClass().getClassLoader(),
                    mapper.getClass().getInterfaces(), new MapperProxy(mapper, sqlSession));
        }

        /**
         * execute mapper method and finally close sqlSession
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object object = null;
            try {
                object = method.invoke(mapper, args);
                sqlSession.commit();
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                sqlSession.close();
            }
            return object;
        }
    }
}

注意mapper工厂类的写法,利用枚举解藕data source name,session factory的引用更加独立和简便。

- 调用类:

public class T {

    /**
     * @param args
     */
    public static void main(String[] args) {
        UserMapper mapper = MapperFactory.HD.createMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);

        user.setName("tao_new");
        UserMapper mapper_new = MapperFactory.HD.createMapper(UserMapper.class);
        System.out.println(user);
        mapper_new.updateUser(user);

        //--------------
        UserMapper mapper2 = MapperFactory.HO.createMapper(UserMapper.class);
        User user2 = mapper2.getUserById(1);
        System.out.println(user2.getName());
    }
}