Mybatis工作原理

简单来说,他跟你直接用一个sqlUtil的实现是一样,只不过很多复杂的util优化的事情,提前有其他程序员做了。Mybatis是一个映射封装,他与你用util的区别就是,他将在代码块中的sql存在统一的xml文件也就是sqlmaper中。同时他将你执行sql的传参也就是执行变量进行了通配,然后映射到你的model中。Mybatis大概的执行过程:通过factory方法获取sqlsession----通过MapperProxy代理到dao--执行底层数据库操作,简单说就是跟楼上说的“据经过controller 再经过service 然后执行service中的相关方法并关联到mapper 再执行mapper.xml中的sql语句=== @象厂喜剧 ”我们以JDBC为例看看他们的区别:JDBC:(1) 加载JDBC驱动,建立并获取数据库连接 ,创建statement对象(2) 设置SQL语句的传入参数(3) 执行SQL语句并获得查询结果(4) 对查询结果进行转换处理并将处理结果返回(5) 释放资源Mybatis:1:使用连接池,datasource,在驱动并连接的这个过程中优化并解耦 JDBC第一步其实从效率角度来看是不合适的,因为无论什么数据库都不可能支撑随机和庞大的连接数,而且不可避免的存在连接浪费的情况,Mybatis就封装了这些优化的方法。2:统一sql存取到XML 如果代码写在java块中,在团队合作中很可能出现两个交叉业务的代码使用类似的sql语句,而开发人员的工作本身没有交集,那就代表sql语句肯定是无法复用的。而且对sql的修改,就代表着对java文件的修改,需要重新编译和打包部署(比如常见的状态值更改,sql修改随着业务变化必然存在修改)。 mybatis将sql统一存取到xml中,就算存在业务交叉,但因为统一配置的缘故,sql在xml中一目了然,两个跨team的程序员可以看到对方的sql,来判断自己是否需要重用。并且使用xml配置可以减少代码编译。 还有就是在java中拼写长sql太恶心了。3:参数和结果集映射 sql的方式需要传入参数,如果存在多条件“或类型”的查询(列表查询的查询条件允许空),那就代表你必须传参进行sql拼接,就算使用xml的方式也不行。要么每个业务独立配置xml中的sql,要么还是写入java代码中,或者以工具的方式进行自动拼接。 Mybatis使用映射的方式,方便model管理参数,同时以解析器的方式将参数动态拼接到sql(sqlmaper里那些标签),由于是model映射,连查询结果都可以统一映射,方便取出和运算。而且mybatis对查询结果集进行了缓存处理,使得重复查询进一步进行了优化。4:对多重复sql进行复用封装 比如模板方法,将常用sql模块化,直接调用。比如通用的save和getID之类的,只有表名和字段名有变化。发布于 2016-04-17​赞同 49​​添加评论​分享​收藏​感谢​收起​更多回答wuxinliulei做好自己87 人赞同了该回答Mybatis原名Ibatis,在2011年从Ibatis2.x升级到Mybatis 3.X,并将项目地址从Apache迁移到了Google code,事实上我们看MyBatis的类全路径名,还是保留了Apache和Ibatis的的包前缀import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; 不过MyBatis的配置文件以及操作类和实现方式都有了很大变化,这里我们重点讲述的是Mybatis,不是Ibatis;Mybatis的配置文件一共由两类:一类用于指定数据源、事务属性以及其他一些参数配置信息(通常是一个独立的文件,可以称之为全局配置文件);另一类则用于 指定数据库表和程序之间的映射信息(可能不止一个文件,我们称之为映射文件)这些文件的名字并没有确定的要求;只是要最从特定的dtd的xml文件约束,即xml标签需要符合要求;

<mappers>
    <!-- 注册userMapper.xml文件, 
    userMapper.xml位于com.test.mapping这个包下,所以resource写成com/test/mapping/userMapper.xml-->
    <mapper resource="com/test/mapping/userMapper.xml"/>
</mappers>
复制代码
上述就是MyBatis的数据源,事务属性,以及映射文件的索引; select * from users where id=#{id} 上面是数据库表与程序之间的映射文件,定义了一个根据id来获取User对象的sqlpackage com.test.domain;

/**

  • users表所对应的实体类 */ public class User {

    //实体类的属性和表的字段名称一一对应 private int id; private String name; private int age;

    public int getId() { return id; }

    public void setId(int id) { this.id = id; }

    public String getName() { return name; }

    public void setName(String name) { this.name = name; }

    public int getAge() { return age; }

    public void setAge(int age) { this.age = age; }

    @Override public String toString() { return "User [id=" + id + ", name=" + name + ", age=" + age + "]"; } } 问题:mybatis是怎么在程序中顺利的找到sqlmapper的,这个的流程是怎么样??// mybatis的配置文件 String resource = "conf.xml"; // 使用类加载器加载mybatis的配置文件(它也加载关联的映射文件) InputStream is = Test1.class.getClassLoader().getResourceAsStream(resource); // 构建sqlSession的工厂 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); 题主问的sqlmapper可以理解为两种组件,一种是mapping映射文件,通过id名来获取相应的sql语句,操作数据库;一种是sql的返回对象,resultType="com.test.domain.User" 这个就是返回的sql结果映射成为具体的POJO(Plain Ordinary Java Object)对象;两个重要的类即:org.apache.ibatis.session.SqlSessionFactory;org.apache.ibatis.session.SqlSession;package org.apache.ibatis.session;

import java.sql.Connection;

public interface SqlSessionFactory {

SqlSession openSession();

SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level);

SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection);

Configuration getConfiguration();

} 在构建SqlSessionFactory类的时候,将会对数据源及事务配置进行解析,具体在org.apache.ibatis.builder.xml.XMLConfigBuilder类org.apache.ibatis.builder.BaseBuilder类XMLConfigBuilder类是解析产生org.apache.ibatis.Session.Configuration类的的具体类,Configuration类中将保存中所有的配置;mybatis的源代码解析(1)--xml文件解析 - 王久勇 - 博客园这篇博客介绍了一些xml文件解析的基本;具体mybatis的xml解析使用到了XPath方式,具体解析过程参看https://zhuanlan.zhihu.com/p/31418285其实一般各种轮子都会有一个解析XML后信息的专用存储类,比如Config.Java,xxxConf.java,都是在启动组件时解析XML配置以用作程序中使用的。引用网络上的一段源代码public class Test1 {

public static void main(String[] args) throws IOException {
    //mybatis的配置文件
    String resource = "conf.xml";
    //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
    InputStream is = Test1.class.getClassLoader().getResourceAsStream(resource);
    //构建sqlSession的工厂
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
    //使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件)
    //Reader reader = Resources.getResourceAsReader(resource); 
    //构建sqlSession的工厂
    //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
    //创建能执行映射文件中sql的sqlSession
    SqlSession session = sessionFactory.openSession();
    /**
     * 映射sql的标识字符串,
     * me.gacl.mapping.userMapper是userMapper.xml文件中mapper标签的namespace属性的值,
     * getUser是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
     */
    String statement = "me.gacl.mapping.userMapper.getUser";//映射sql的标识字符串
    //执行查询返回一个唯一user对象的sql
    User user = session.selectOne(statement, 1);
    System.out.println(user);
}
复制代码

} 通过跟踪源代码可以看到SqlSession通过mapper映射的id来查找数据的方法;org.apache.ibatis.session.defaults.DefaultSqlSession类public List selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); List result = executor. query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset();
} } org.apache.ibatis.session.Configuration类public MappedStatement getMappedStatement(String id) { return this.getMappedStatement(id, true); } protected final Map<String, MappedStatement> mappedStatements = new StrictMap("Mapped Statements collection"); public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) { if (validateIncompleteStatements) { buildAllStatements(); } return mappedStatements.get(id); } 其实就是根据一个map映射,key就是定义mapping时候的id来拿到的;至此,------------------------------上述org.apache.ibatis.session.defaults.DefaultSqlSession类对象中的 selectList方法中的executor对象,在默认情况下,即没有设置settings的cache和executor属性时,默认使用的org.apache.ibatis.executor.CachingExecutor类public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; } 所以调用到了public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } 在真正查询时先查询cache,可以看到这个cache层级在MappedStatement上,也就是在单个Sql上;若查到,则直接返回,无则通过jdbc查询,且返回结果public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, key, parameterObject, boundSql); if (!dirty) { cache.getReadWriteLock().readLock().lock(); try { @SuppressWarnings("unchecked") List cachedList = (List) cache.getObject(key); if (cachedList != null) return cachedList; } finally { cache.getReadWriteLock().readLock().unlock(); } } List list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578. Query must be // not synchronized to // prevent deadlocks return list; } } return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } 上述的使用方式是未使用代理的方式,这样需要我们自行openSession并且关闭Session;SqlSession session = null; try { session = sessionFactory.openSession(); /** * 映射sql的标识字符串, com.test.mapping.userMapper是userMapper. * xml文件中mapper标签的namespace属性的值, * getUser是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL */ String statement = "com.test.mapping.userMapper.getUser";// 映射sql的标识字符串 // 执行查询返回一个唯一user对象的sql User user = session.selectOne(statement, 1); System.out.println(user); } catch (Exception e) { // TODO: handle exception } finally { if (session != null) { session.close(); } } 事实上如果我们使用SqlSessionManager来管理,那么开启和关闭Session操作都不用我们来处理了。final SqlSessionManager sqlSessionManager = SqlSessionManager.newInstance(sessionFactory); String statement = "com.test.mapping.userMapper.getUser";// 映射sql的标识字符串 User user = sqlSessionManager.selectOne(statement, 1); System.out.println(user); 下面是Interceptor类实现,开启和关闭操作都交由了private class SqlSessionInterceptor implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get(); if (sqlSession != null) { try { return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } else { final SqlSession autoSqlSession = openSession(); try { final Object result = method.invoke(autoSqlSession, args); autoSqlSession.commit(); return result; } catch (Throwable t) { autoSqlSession.rollback(); throw ExceptionUtil.unwrapThrowable(t); } finally { autoSqlSession.close(); } } } } 如果使用Mapper方式来操作SQL,就是利用动态代理,可以避免我们手写mapper的id字符串,将查找sql过程和执行sql过程放到了代理处理中,更优雅些,不过大体流程就是这些,改变了查找sql的步骤,通过Mapper的方法名来查找对应的sql的 如果想学习Java工程化、高性能及分布式、深入浅出。性能调优、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级架构进阶群:180705916,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值