MyBatis配置文件解析与MyBatis实例演示


Posted in Java/Android onApril 07, 2022

MyBatis介绍

MyBatis是一个持久层的ORM框架,使用简单,学习成本较低。可以执行自己手写的SQL语句,比较灵活。但是MyBatis的自动化程度不高,移植性也不高,有时从一个数据库迁移到另外一个数据库的时候需要自己修改配置,所以称只为半自动ORM框架

传统JDBC和Mybatis相比的弊病

传统JDBC

@Test
public  void test() throws SQLException {
    Connection conn=null;
    PreparedStatement pstmt=null;
    try {
        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.创建连接
        conn= DriverManager.
                getConnection("jdbc:mysql://localhost:3306/mybatis_example", "root", "123456");


        // SQL语句
        String sql="select id,user_name,create_time from t_user where id=?";

        // 获得sql执行者
        pstmt=conn.prepareStatement(sql);
        pstmt.setInt(1,1);

        // 执行查询
        //ResultSet rs= pstmt.executeQuery();
        pstmt.execute();
        ResultSet rs= pstmt.getResultSet();

        rs.next();
        User user =new User();
        user.setId(rs.getLong("id"));
        user.setUserName(rs.getString("user_name"));
        user.setCreateTime(rs.getDate("create_time"));
        System.out.println(user.toString());
    } catch (Exception e) {
        e.printStackTrace();
    }
    finally{
        // 关闭资源
        try {
            if(conn!=null){
                conn.close();
            }
            if(pstmt!=null){
                pstmt.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

传统JDBC的问题如下:

1.数据库连接创建,释放频繁造成西戎资源的浪费,从而影响系统性能,使用数据库连接池可以解决问题。
2.sql语句在代码中硬编码,造成代码的不已维护,实际应用中sql的变化可能较大,sql代码和java代码没有分离开来维护不方便。
3.使用preparedStatement向有占位符传递参数存在硬编码问题因为sql中的where子句的条件不确定,同样是修改不方便/
4.对结果集中解析存在硬编码问题,sql的变化导致解析代码的变化,系统维护不方便。

mybatis对传统的JDBC的解决方案

1、数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库链接。

2、Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

3、向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。

4、对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

Mybaits整体体系图

MyBatis配置文件解析与MyBatis实例演示

 

MyBatis配置文件解析与MyBatis实例演示

一个Mybatis最简单的使用例子如下:

public class App {
    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        Reader reader;
        try {
            //将XML配置文件构建为Configuration配置类
            reader = Resources.getResourceAsReader(resource);
            // 通过加载配置文件流构建一个SqlSessionFactory  DefaultSqlSessionFactory
            SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
            // 数据源 执行器  DefaultSqlSession
            SqlSession session = sqlMapper.openSession();
            try {
                // 执行查询 底层执行jdbc
                //User user = (User)session.selectOne("com.tuling.mapper.selectById", 1);

                UserMapper mapper = session.getMapper(UserMapper.class);
                System.out.println(mapper.getClass());
                User user = mapper.selectById(1L);
                System.out.println(user.getUserName());
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                session.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

mybatis-config.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 扫描属性文件.properties  -->
    <properties resource="db.properties"></properties>


    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

   <plugins>
       <plugin interceptor="com.tuling.plugins.ExamplePlugin" ></plugin>
    </plugins>

    <environments default="development">
        <environment id="development">
           <transactionManager type="JDBC"/>
            <!--//  mybatis内置了JNDI、POOLED、UNPOOLED三种类型的数据源,其中POOLED对应的实现为org.apache.ibatis.datasource.pooled.PooledDataSource,它是mybatis自带实现的一个同步、线程安全的数据库连接池 一般在生产中,我们会使用c3p0或者druid连接池-->
            <dataSource type="POOLED">
            <property name="driver" value="${mysql.driverClass}"/>
            <property name="url" value="${mysql.jdbcUrl}"/>
            <property name="username" value="${mysql.user}"/>
            <property name="password" value="${mysql.password}"/>
        </dataSource>
        </environment>
    </environments>



    <mappers>
        <!--1.必须保证接口名(例如IUserDao)和xml名(IUserDao.xml)相同,还必须在同一个包中-->
        <package name="com.tuling.mapper"/>

        <!--2.不用保证同接口同包同名
         <mapper resource="com/mybatis/mappers/EmployeeMapper.xml"/>

        3.保证接口名(例如IUserDao)和xml名(IUserDao.xml)相同,还必须在同一个包中
        <mapper class="com.mybatis.dao.EmployeeMapper"/>

        4.不推荐:引用网路路径或者磁盘路径下的sql映射文件 file:///var/mappers/AuthorMapper.xml
         <mapper url="file:E:/Study/myeclipse/_03_Test/src/cn/sdut/pojo/PersonMapper.xml"/>-->

    </mappers>
</configuration>

总结下就是分为下面四个步骤:

  • 从配置文件(通常是XML文件)得到SessionFactory;
  • 从SessionFactory得到SqlSession;
  • 通过SqlSession进行CRUD和事务的操作;
  • 执行完相关操作之后关闭Session。

MyBatis 源码编译

1.下载mybatis源码

下载地址:https://github.com/mybatis/mybatis-3

MyBatis配置文件解析与MyBatis实例演示

我下载的最新的 mybatis-3-mybatis-3.4.6,下载完后解压。打开pom.xml

<parent>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-parent</artifactId>
    <version>30</version>
    <relativePath />
  </parent>

发现mybatis源码依赖mybatis-parent 所以编译前要先下载mybatis-parent

2.下载mybatis-parent源码

下载地址:https://github.com/mybatis/parent

MyBatis配置文件解析与MyBatis实例演示

下载的mybatis-parent版本要和mybatis源文件pom.xml 版本一致。

3.编译mybatis-parent源码

切换到你下载的mybatis-parent目录:

mvn clean install

3.编译mybatis源码

切换到你下载的mybatis源码目录:

mvn clean 

mvn install -Dmaven.test.skip=true

如果出现如下错误:

MyBatis配置文件解析与MyBatis实例演示

打开pom.xml 文件注释掉 maven-pdf-plugin 插件

<!--
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pdf-plugin</artifactId>
      </plugin>
      -->

然后重新编译

4.导入IDEA

导入方式就不多说,按maven项目导入即可!导入成功后:

MyBatis配置文件解析与MyBatis实例演示

如果希望在spring源码中引入你自己的这份源码,可以做如下操作

1.修改你mybatis源码的pom的 这样可以和官方的区分开来

<version>3.5.3-xsls</version>

2.这样你在spring源码中就可以引入这份mybatis源码了.

  • 如果引入mybatis-spring 同样需要做1、3步骤
compile("org.mybatis:mybatis-spring:2.0.3-xsls")  
compile("org.mybatis:mybatis:3.5.3-xsls")

3.当然,如果你想在spring这边看到你mybatis源码相关的注释,还得在mybatis源码的pom里面加入plugin,使它生成 jar 的同时 生成 sources 包

<plugin>
  <artifactId>maven-source-plugin</artifactId>
  <version>3.0.1</version>
  <configuration>
    <attach>true</attach>
  </configuration>
  <executions>
    <execution>
      <phase>compile</phase>
      <goals>
        <goal>jar</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Mybatis启动流程分析

String resource = "mybatis-config.xml";
//将XML配置文件构建为Configuration配置类
reader = Resources.getResourceAsReader(resource);
// 通过加载配置文件流构建一个SqlSessionFactory  DefaultSqlSessionFactory
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);

通过上面代码发现,创建SqlSessionFactory的代码在SqlSessionFactoryBuilder中,进去一探究竟:

//整个过程就是将配置文件解析成Configration对象,然后创建SqlSessionFactory的过程
//Configuration是SqlSessionFactory的一个内部属性
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
    
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

下面我们看下解析配置文件过程中的一些细节。
先给出一个配置文件的例子:

<?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>
    <!--SqlSessionFactoryBuilder中配置的配置文件的优先级最高;config.properties配置文件的优先级次之;properties标签中的配置优先级最低 -->
    <properties resource="org/mybatis/example/config.properties">
      <property name="username" value="dev_user"/>
      <property name="password" value="F2Fa3!33TYyg"/>
    </properties>

    <!--一些重要的全局配置-->
    <settings>
    <setting name="cacheEnabled" value="true"/>
    <!--<setting name="lazyLoadingEnabled" value="true"/>-->
    <!--<setting name="multipleResultSetsEnabled" value="true"/>-->
    <!--<setting name="useColumnLabel" value="true"/>-->
    <!--<setting name="useGeneratedKeys" value="false"/>-->
    <!--<setting name="autoMappingBehavior" value="PARTIAL"/>-->
    <!--<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>-->
    <!--<setting name="defaultExecutorType" value="SIMPLE"/>-->
    <!--<setting name="defaultStatementTimeout" value="25"/>-->
    <!--<setting name="defaultFetchSize" value="100"/>-->
    <!--<setting name="safeRowBoundsEnabled" value="false"/>-->
    <!--<setting name="mapUnderscoreToCamelCase" value="false"/>-->
    <!--<setting name="localCacheScope" value="STATEMENT"/>-->
    <!--<setting name="jdbcTypeForNull" value="OTHER"/>-->
    <!--<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>-->
    <!--<setting name="logImpl" value="STDOUT_LOGGING" />-->
    </settings>

    <typeAliases>

    </typeAliases>

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!--默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果-->
            <!--如果某些查询数据量非常大,不应该允许查出所有数据-->
            <property name="pageSizeZero" value="true"/>
        </plugin>
    </plugins>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://10.59.97.10:3308/windty"/>
                <property name="username" value="windty_opr"/>
                <property name="password" value="windty!234"/>
            </dataSource>
        </environment>
    </environments>

    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql" />
        <property name="Oracle" value="oracle" />
    </databaseIdProvider>

    <mappers>
        <!--这边可以使用package和resource两种方式加载mapper-->
        <!--<package name="包名"/>-->
        <!--<mapper resource="./mappers/SysUserMapper.xml"/>-->
        <mapper resource="./mappers/CbondissuerMapper.xml"/>
    </mappers>

</configuration>

下面是解析配置文件的核心方法:

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      //解析properties标签,并set到Configration对象中
      //在properties配置属性后,在Mybatis的配置文件中就可以使用${key}的形式使用了。
      propertiesElement(root.evalNode("properties"));
      
      //解析setting标签的配置
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      //添加vfs的自定义实现,这个功能不怎么用
      loadCustomVfs(settings);
        
      //配置类的别名,配置后就可以用别名来替代全限定名
      //mybatis默认设置了很多别名,参考附录部分
      typeAliasesElement(root.evalNode("typeAliases"));
        
      //解析拦截器和拦截器的属性,set到Configration的interceptorChain中
      //MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
      //Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
        //ParameterHandler (getParameterObject, setParameters)
        //ResultSetHandler (handleResultSets, handleOutputParameters)
        //StatementHandler (prepare, parameterize, batch, update, query)
      pluginElement(root.evalNode("plugins"));
      
      //Mybatis创建对象是会使用objectFactory来创建对象,一般情况下不会自己配置这个objectFactory,使用系统默认的objectFactory就好了
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
       
      //设置在setting标签中配置的配置
      settingsElement(settings);
   
      //解析环境信息,包括事物管理器和数据源,SqlSessionFactoryBuilder在解析时需要指定环境id,如果不指定的话,会选择默认的环境;
      //最后将这些信息set到Configration的Environment属性里面
      environmentsElement(root.evalNode("environments"));
        
      //
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        
      //无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。解析typeHandler。
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析Mapper
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

MyBatis配置文件解析与MyBatis实例演示

上面解析流程结束后会生成一个Configration对象,包含所有配置信息,然后会创建一个SqlSessionFactory对象,这个对象包含了Configration对象。

简单总结

对于MyBatis启动的流程(获取SqlSession的过程)这边简单总结下:

  • SqlSessionFactoryBuilder解析配置文件,包括属性配置、别名配置、拦截器配置、环境(数据源和事务管理器)、Mapper配置等;解析完这些配置后会生成一个Configration对象,这个对象中包含了MyBatis需要的所有配置,然后会用这个Configration对象创建一个SqlSessionFactory对象,这个对象中包含了Configration对象;

这里解析的东西比较多,大致概况:会把所有的信息都解析到Configration对象中,比较简单不多介绍。

Java/Android 相关文章推荐
总结一些Java常用的加密算法
Jun 11 Java/Android
Java实现多线程聊天室
Jun 26 Java/Android
SpringBoot 拦截器妙用你真的了解吗
Jul 01 Java/Android
gateway网关接口请求的校验方式
Jul 15 Java/Android
logback如何自定义日志存储
Aug 30 Java/Android
JVM的类加载器和双亲委派模式你了解吗
Mar 13 Java/Android
Java 超详细讲解IO操作字节流与字符流
Mar 25 Java/Android
Spring Boot 使用 Spring-Retry 进行重试框架
Apr 24 Java/Android
Android开发手册TextInputLayout样式使用示例
Jun 10 Java/Android
Android Gradle 插件自定义Plugin实现注意事项
Jun 16 Java/Android
Java服务调用RestTemplate与HttpClient的使用详解
Jun 21 Java/Android
Java 深入探究讲解简单工厂模式
springboot用户数据修改的详细实现
Apr 06 #Java/Android
Java中API的使用方法详情
Java 获取Word中所有的插入和删除修订的方法
springboot应用服务启动事件的监听实现
Apr 06 #Java/Android
Java十分钟精通进阶适配器模式
mapstruct的用法之qualifiedByName示例详解
Apr 06 #Java/Android
You might like
PHP 正则表达式小结
2015/02/12 PHP
PHP将session信息存储到数据库的类实例
2015/03/04 PHP
详解PHP中的 input属性(隐藏 只读 限制)
2017/08/14 PHP
picChange 图片切换特效的函数代码
2010/05/06 Javascript
转换json格式的日期为Javascript对象的函数
2010/07/13 Javascript
jquery 追加tr和删除tr示例代码
2013/09/12 Javascript
javascript中Array数组的迭代方法实例分析
2015/02/04 Javascript
微信小程序(应用号)简单实例应用及实例详解
2016/09/26 Javascript
js下载文件并修改文件名
2017/05/08 Javascript
微信小程序实现滑动删除效果
2017/05/19 Javascript
JS实现点击复选框变更DIV显示状态的示例代码
2017/12/18 Javascript
jQuery EasyUI 折叠面板accordion的使用实例(分享)
2017/12/25 jQuery
微信小程序icon组件使用详解
2018/01/31 Javascript
ES6 迭代器(Iterator)和 for.of循环使用方法学习(总结)
2018/02/08 Javascript
从零开始搭建一个react项目开发
2018/02/09 Javascript
echarts实现地图定时切换散点与多图表级联联动详解
2018/08/07 Javascript
vue 中滚动条始终定位在底部的方法
2018/09/03 Javascript
vue-cli 打包后提交到线上出现 &quot;Uncaught SyntaxError:Unexpected token&quot; 报错
2018/11/06 Javascript
详解js动态获取浏览器或页面等容器的宽高
2019/03/13 Javascript
微信小程序停止其他视频播放当前视频的实例代码
2019/12/25 Javascript
微信小程序实现点击导航条切换页面
2020/11/19 Javascript
python中json格式数据输出的简单实现方法
2016/10/31 Python
Pycharm设置去除显示的波浪线方法
2018/10/28 Python
python买卖股票的最佳时机(基于贪心/蛮力算法)
2019/07/05 Python
TensorFlow车牌识别完整版代码(含车牌数据集)
2019/08/05 Python
Python自动化导出zabbix数据并发邮件脚本
2019/08/16 Python
在pytorch中查看可训练参数的例子
2019/08/18 Python
Python进程Multiprocessing模块原理解析
2020/02/28 Python
HTML5 input新增type属性color颜色拾取器的实例代码
2018/08/27 HTML / CSS
聪明的粉丝购买门票的地方:TickPick
2018/03/09 全球购物
英国家庭家具、照明和花园家具购物网站:Furniture123
2018/12/31 全球购物
连锁酒店店长职责范本
2014/02/13 职场文书
2016中秋节月饼促销广告语
2016/01/28 职场文书
导游词之西湖雷峰塔
2019/09/18 职场文书
Django开发RESTful API实现增删改查(入门级)
2021/05/10 Python
彻底解决MySQL使用中文乱码的方法
2022/01/22 MySQL