美文网首页MyBatis
MyBatis 初窥(二)

MyBatis 初窥(二)

作者: 沈先生的影子 | 来源:发表于2020-11-11 10:02 被阅读0次

    一、MyBatis 实际使用案例

    编程式使用

      JavaApi编程的方式使用。

    1.引入Mybatis jar 包
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.4-snapshot</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.21</version>
            </dependency>
    
    2.创建一个全局配置文件,里面是MyBatis一些核心行为控制,比如mybatis-config.xml。这里只定义的数据源和Mapper映射器路径。
    <?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.properties"></properties>
        <settings>
            <!-- 打印查询语句 -->
            <setting name="logImpl" value="STDOUT_LOGGING" />
    
            <!-- 控制全局缓存(二级缓存),默认 true-->
            <setting name="cacheEnabled" value="true"/>
    
            <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false  -->
            <setting name="lazyLoadingEnabled" value="true"/>
            <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
            <setting name="aggressiveLazyLoading" value="true"/>
            <!--  Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
            <!--<setting name="proxyFactory" value="CGLIB" />-->
            <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
            <!--
                    <setting name="localCacheScope" value="STATEMENT"/>
            -->
            <setting name="localCacheScope" value="SESSION"/>
        </settings>
    
        <typeAliases>
            <typeAlias alias="blog" type="com.dia.domain.Blog" />
        </typeAliases>
    
    <!--    <typeHandlers>
            <typeHandler handler="com.dia.type.MyTypeHandler"></typeHandler>
        </typeHandlers>-->
    
        <!-- 对象工厂 -->
    <!--    <objectFactory type="com.dia.objectfactory.GPObjectFactory">
            <property name="gupao" value="666"/>
        </objectFactory>-->
    
    <!--    <plugins>
            <plugin interceptor="com.dia.interceptor.SQLInterceptor">
                <property name="gupao" value="betterme" />
            </plugin>
            <plugin interceptor="com.dia.interceptor.MyPageInterceptor">
            </plugin>
        </plugins>-->
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <mapper resource="BlogMapper.xml"/>
            <mapper resource="BlogMapperExt.xml"/>
        </mappers>
    
    </configuration>
    
    
    properties文件
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
    jdbc.username=root
    jdbc.password=123456
    

    3.编写映射文件:Mapper.xml,通常来说对应一张业务表,我们会在这个mapper文件里编写增删改查的SQL语句,以及参数和结果集。

    <?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.dia.mapper.BlogMapper">
    
        <resultMap id="BaseResultMap" type="blog">
            <id column="bid" property="bid" jdbcType="INTEGER"/>
    <!--
            <result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.dia.type.MyTypeHandler"/>
    -->
            <result column="name" property="name" jdbcType="VARCHAR"/>
            <result column="author_id" property="authorId" jdbcType="INTEGER"/>
        </resultMap>
    
        <select id="selectBlogById" resultMap="BaseResultMap" statementType="PREPARED" >
            select * from blog where bid = #{bid}
        </select>
    
    </mapper>
    

    4.使用Mybatis执行一个查询。

      mybati的目的就是简化JDBC的操作,那么它必须要提供一个可执行增删改查的对象,在MyBatis里是SqlSession接口,他是和数据库建立的一个连接/会话。
      问题来了,SqlSession是如何创建的?Mybatis核心行为的控制(各种配置项以及开启缓存等)都在全局的配置文件中,所以必须基于全局配置文件创建,mybatis 提供了一个工厂类来创建SqlSession。

    
    public interface BlogMapper {
        /**
         * 根据主键查询文章
         * @param bid
         * @return
         */
        public Blog selectBlogById(Integer bid);
    }
    
    
    public class BlogExampleTest {
    
        @Test
        public void TestExample() throws IOException {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            SqlSession session = sqlSessionFactory.openSession();
            try {
                Blog blog = (Blog)session.selectOne("com.dia.mapper.BlogMapper.selectBlogById",new Integer(1));
                System.out.println(blog.getBid()+">>>>"+blog.getName()+">>>>>"+blog.getAuthorId());
            } finally {
                session.close();
            }
        }
    }
    
    =====================================
    Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@223191a6]
    ==>  Preparing: select * from `blog` where bid = ? 
    ==> Parameters: 1(Integer)
    <==    Columns: bid, name, author_id
    <==        Row: 1, RabbitMQ延时消息, 1001
    <==      Total: 1
    
    

      这样的调用方式,解决了重复代码、资源管理、SQL耦合、结果集映射的四大问题。

    还存在问题:

    1、Statement是硬编码,维护起来不方便;
    2、不能在编译时候进行类型检查,如果namespace或者StatementID出错,只能在运行时报错。

    mybatis给我们推荐了一种新的方法:定义一个Mapper接口的方式。这个接口全路径必须跟Mapper.xml里面的namespace对应起来,方法也要跟StatementID一一对应。

            @Test
        public void TestExample() throws IOException {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            SqlSession session = sqlSessionFactory.openSession();
            try {
    
    //            Blog blog = (Blog)session.selectOne("com.dia.mapper.BlogMapper.selectBlogById",new Integer(1));
    //            System.out.println(blog.getBid()+">>>>"+blog.getName()+">>>>>"+blog.getAuthorId());
                BlogMapper mapper = session.getMapper(BlogMapper.class);
                Blog blog = mapper.selectBlogById(1);
    
            } finally {
                session.close();
            }
        }
    ============================================
    Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3023df74]
    ==>  Preparing: select * from `blog` where bid = ? 
    ==> Parameters: 1(Integer)
    <==    Columns: bid, name, author_id
    <==        Row: 1, RabbitMQ延时消息, 1001
    <==      Total: 1
    
    

    二、MyBatsi的核心对象及其生命周期

    SqlSessinFactoryBuilder

      用来构建SqlSessionFactory的,然而SqlSessionFactory单例即可,完成构建SqlSessionFactory也就没有存在的意义了,生命周期局限于方法内部。

    SqlSessionFactory(单例)

      SqlSessionFactory用于构建SqlSession,应用程序每次访问数据库都需要创建一个会话。我们一直需要会话的存在,所以SqlSessionFactory的生命周期也是我们整个程序的生命周期(作用域是应用作用域)。创建SqlSession只需要一个人来做就行了,否则会产生混乱和浪费资源。所以采用单例模式。

    SqlSession

      SqlSession是一个会话,因为它不是线程安全的,不能在线程间共享。所以我们要在请求开始的时候创建一个SqlSession,在请求结束或者说方法执行完毕之后要及时关闭它(一次请求或者操作中)。

    Mapper

      其实是一个被代理对象,在SqlSession中获取

    BlogMapper mapper = session.getMapper(BlogMapper.class);
    

    他的作用是发送SQL来操作数据库的数据,它应该在一个SqlSession事务方法之内。

    mybatis-核心对象生命周期

    三、核心配置解读

    Configuration

      配置类Configuration。它贯穿MyBatis执行流程的每个环节。这个类下面还有很多属性,跟其他的子标签也能对应上。

    properties

      第一个一级标签properties,用来配置参数信息,比如最常见的数据库信息。
      为了避免直接把参数写死在xml文件中,我们可以单独吧一些数据信息单独放在properties文件中,用properties标签引进来,然后在xml文件中使用${}引入即可。
      可以使用resource引用应用里面的相对路径,也可以使用url指定本地服务器或者网络的绝对路径。

    setting

    mybatis的一些核心配置


    image.png image.png image.png image.png

    typeAliases

      typeAliases是类型的别名,跟linux系统的别名是一样的。可以为Bean创建别名,也可以指定单个类,或者指定一个package。
    用法如下:

        <typeAliases>
            <typeAlias alias="blog" type="com.dia.domain.Blog" />
        </typeAliases>
    

    配置了别名在mapper就可以使用其别名了:

        <resultMap id="BaseResultMap" type="blog">
            <id column="bid" property="bid"  />
    <!--
            <result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.dia.type.MyTypeHandler"/>
    -->
            <result column="name" property="name"  />
            <result column="author_id" property="authorId" />
        </resultMap>
    

    typeHandler

      由于Java类型和数据库类型并不是一一对应的,比如(String与Varchar、char、text),所以我们要把对象转换为数据库对象的值,同理也需要把数据库对象的值转换为Java对象,这两个方向的转换,需要用到typeHandler。
      当参数类型和返回值是一个对象时,我们没有做任何配置,为什么对象里面一个String属性,可以转换为数据库里面的VARCHAR?
      这是因为Mybatis已经内置了很多TypeHandler(在type包下),他们全部都注册在TypeHandlerRegistry中,他们都继承了抽象类BaseTypeHandler,泛型就是要处理的Java数据类型。
      这个也是为什么大部分类型都不需要处理。当我们查询数据和登记数据,做数据的类型转换时,就会自动调用对应Typehanlder方法。

    objectFactory

      当我们把数据库返回的结果集转换为实体类的时候,需要创建对象的实例,由于我们不知道需要处理的类是什么,有哪些属性和类型。我们不能直接new Object(),只能通过反射来实现,objectFactory就是用来创建对象的工厂。

    public interface ObjectFactory {
    
      // 设置参数的时候调用
      default void setProperties(Properties properties) {
        // NOP
      }
    
      // 创建对象(无参数构造函数)
      <T> T create(Class<T> type);
    
      // 创建对象(有参数构造函数)
      <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
    
      // 判断是否集合
      <T> boolean isCollection(Class<T> type);
    
    }
    

      ObjectFactory有一个默认的实现类DefaultObjectFactory。创建对象的方法最终都调用了instantiateClass(),这里能看到反射的代码。
      默认情况,所有的对象都由DefaultObjectFactory实现。

    plugin

      插件机制是mybatis很强大的功能,mybatis预留了插件的接口,让Mybatis更容易扩展。

    environment

      environment标签用来管理数据库的环境,比如我们可以有开发环境、测试环境、生产数据库。(其实就是管理数据源的东西)

    transactionManager

      如果配置成JDBC,则会使用Connection对象的Commit()、rollback()、close()管理事务。
      如果配置为managed,会把事务交给容器管理,比如JBOSS、WEBLOGIC。我们跑的如果是本地环境,如果配置为mannaged就没有事务。

    dataSource

      数据源即是数据的来源,一个数据源就对应一个数据库。在java里面是他是对数据库连接的一个抽象。
      一般的数据源都会包括连接池管理的功能,所以很多时候也把DataSource直接成为连接池,也就是带连接池的数据源。

    为什么要用连接池?

      除了连接池之外,大家应该也听过很多其他的池,比如线程池、内存池、对象池,池化技术的目的大多一样。
      如果没有连接池,那么每个用户、每次会话连接数据库都需要直接创建和释放连接,这个过程是需要消耗时间和资源的,并且会消耗应用和服务器的性能。
      如果采用池化技术,在应用程序里关闭连接的时候,物理连接比并没有被关闭,而是回到连接池里面。
      从这个角度来考虑,一般的连接池都会有初始连接数、最大连接数、回收时间等等,提供提前创建、资源重用、数量控制、超时管理等等。
      Mybatis自带两种数据源,unpooled和pooled,也可以配置成其他数据库,比如C3P0、HIkari等等。市面上流行的数据源,一般都有连接池功能。
      在跟Spring集成的时候,事务和数据源都会交给Spring来管理,不再使用Mybatis配置的数据源。

    mapper

      <mapper>标签配置的是映射器,也就是Mapper.xml的路径,这些配置的目的就是在mybatis启动的时候,去扫描这些映射器,创建映射关系。

    有四种表达方式:

    1.使用相对与类路径的资源引用(resource)

    <mappers>
            <mapper resource="xxxMapper.xml">
    </mappers>
    

    2.使用完全限定资源定位符(绝对路径)(URL)

    <mappers>
            <mapper resource="file://app/xx/mappers/xxxMapper.xml">
    </mappers>
    

    3.使用映射器接口实现类的完全限定类名

    <mappers>
            <mapper class="com.dia.xx.mapper.xxxMapper">
    </mappers>
    

    4.将包内的映射器接口实现全部注册为映射器(最常用)

    <mappers>
            <mapper class="com.dia.xx.mapper">
    </mappers>
    

    批量插入以及动态SQL还有原生的插件等等

      这些东西都是日常使用的,直接进入官网

    相关文章

      网友评论

        本文标题:MyBatis 初窥(二)

        本文链接:https://www.haomeiwen.com/subject/whrjbktx.html