我是靠谱客的博主 玩命小伙,最近开发中收集的这篇文章主要介绍mybatis的两大配置文件,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

全局配置 mybatis-config.xml

  1. porperties属性

    可以配置一些运行参数,可以放一些porperties或者xml文件,比如:

    <properties>
    	<property resource = "db.properties"></property>
    </properties>
    
  2. settings设置

    能够配置的内容比较多,能够影响Mybatis底层的运行,一般只修改一些常用的规则,比如自动映射、驼峰命名规则、级联规则、缓存、Executor类型等,比如:

    <settings>
        
     	<!--打印查询语句 -->      
    	<setting name = "logImpl" value = "STDOUT_LOGGING"/>
        
     	<!--延迟加载开关,解决N+1问题,默认值为false-->   
     	<setting name = "lazyLoadingEnabled" value = "true"/>
        
        <!--层级加载开关,默认值3.4.1之后为false-->
     	<setting name = "aggresiveLazyLoading" value = "true" />
        
        <!--一级缓存开关,默认值session,也就是开启,修改值为STATEMENT则会关闭一级缓存-->
        <setting name = "localCacheScope" value = "SESSION"/>
        
        <!--二级缓存开关,默认开启,另外也可以在映射器中使用<cache/>标签进行开启,在特定的Sql标签修改useCache为false可以关闭该statement的二级缓存-->
        <setting name = "cacheEnabled" value = "true"/>
        
    </settings>
    
  3. typeAliases类型别名

    定义类的别名,简化使用,通常使用扫描的方式,如果别名重复使用注解@Aliases进行区分,比如:

    <typeAliases>
    	<typeAlias alias = "use" type = "com.xxx.User"></typeAlias>
        <package name = "com.xxx"/>
    </typeAliases>
    
  4. typeHandlers类型转换器

    将Java类型和Jdbc类型进行转换,有系统定义和自定义,自定义的需要实现BaseTypeHandler接口并且在这里进行注册,或者在映射器中指定,比如:

    <!-- 配置文件中指定 -->
    <typeHandlers>
    	<typeHandler handler = "com.xxx.typehandler.SexTypeHandler" javaType = "com.xxx.pojo.SexEnum" jdbcType = "INTEGER"/>
        <package name = "com.xxx.typehandler"/>
    </typeHandlers>
    
    <!-- 映射器中指定-->
    <resultMap id = "userResultMap" type = "user">
    	<result property = "sex" column = "sex" typeHandler = "com.xxx.typehandler.SexTypeHandler"></result>
    </resultMap>
    
  5. objectFactory对象工厂

    对象工厂是在创建结果集时通过反射来创建实例对象,可以通过实现ObjectFactory接口或者继承DefaultObjectFactory来自定义返回规则,通常不需要配置,

    <ObjectFactory type = "com.xxx.objectfactory.MyObjectFactory">
    	<property name = "" value = ""></property>
    </ObjectFactory>
    
  6. plugin插件

    Mybatis中的插件拦截的四个对象Executor、ParameterHandler、ResultSetHandler、StatementHandler,自定义的插件需要实现Interceptor接口,并且在配置中注册。关于插件的运行原理和分页插件的使用在后续文章中在写。

  7. enviroments运行环境

    主要是配置数据库信息,可以配置多个数据库,一般而言只需要配置一个就行了。包括了事务管理器和数据源

    ​ 事务管理器:Mybatis提供了两个工厂类,:JdbcTransactionFactory和ManagedTransactionFactory,JDBC以jdbc的方式对数据库的提交和回滚进行操作,而MANAGED把事务交给容器处理,提交和回滚方法不用任何操作,默认情况下会关闭连接。

    ​ 数据源:提供了PooledDataSourceFactory、UnpooledDataSourceFactory、JndiDataSourceFactory三个工厂类,POOLED使用连接池的思想对Connection管理;UNPOOLED采用非数据库连接池的管理方式,每次请求都会打开一个新的数据库连接;JNDI是为了能在如EJB或者应用服务器这类容器中使用。

    在与Spring集成之后数据库连接和事务都托管给了Spring。

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value=""/>
                <property name="url" value=""/>
                <property name="userName" value=""/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    
  8. databaseIdProvider数据库厂商标识

    查询时可以支持多数据库,当SQL语句中的databaseID被配置了的时候,系统会优先取和数据库配置一致的SQL,如果没有,则取没有databaseID的SQL执行,可以把它当做默认值,value为数据库别名,可以通过这个别名标注那一条SQL语句适用于哪种数据库运行,比如:

    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
        <property name="DB2" value="db2"/>
    </databaseIdProvider>
    
    <select id = "getUserById" parameterType = "int" resultType = "user" databaseId = "mysql">
    	select id uname as name note from t_user where 1 = 1 and id = #{id}
    </select>
    <select id = "getUserById" parameterType = "int" resultType = "user" databaseId = "oracle">
    	select id uname as name note from t_user where id = #{id}
    </select>
    <!-- 这样当数据库切换时,就会自动选择对应的sql进行查询-->
    
  9. mapper映射器

    映射器namespace = 接口的权限名,SELECT|UPDATE|INSERT|DELETE标签的id则对应接口的方法名,映射器的引入通常有下面几种方法:

    <mappesr>
        
    	<!--使用类名注册-->
        <mapper class = "com.xxx.mapper.UserMapper"/>
       
    	<!--使用包名注册,常用-->
        <package name = "com.xxx.mapper"/>
        
    	<!--使用文件路径引入-->
        <mapper resource = "com/xxx/mapper/UserMapper.xml"/>
    
    </mappers>
    
    

    和spring集成之后映射器的扫描托管到spring的配置文件中,在springboot中则使用注解@MapperScan(“packagename”)进行扫描。

    Mapper映射器

    1. 缓存cache

      跟Hibernate一样,Mybatis也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口。

      Mybatis跟缓存相关的类都在cache包中,定义了一个Cache接口,并且只有一个默认的实现类PerpetualCache底层使用HashMap实现,这个对象一定会创建,也叫基础缓存,除此之外,Mybatis还定义了很多缓存的装饰器,通过这些装饰器,可以对基础缓存扩展很多额外的功能,比如回收策略、日志记录、定时刷新等。总体上分三类:基本缓存,淘汰算法缓存、装饰器缓存。下面对这些缓存做个说明:

      缓存实现类描述作用装饰条件
      基本缓存缓存基本实现类默认是PerpetualCache,也可以自定义比如RedisCache/EhCache等具备基本功能的缓存类
      LruCacheLRU策略的缓存删除最近最少使用的缓存eviction=“LRU”(默认)
      FifoCacheFIFO策略的缓存删除最先入队的缓存eviction=“FIFO”
      SoftCache WeakCaceh带清理策略的缓存通过JVM的软引用和弱引用来实现缓存,当JVM内存不足时,会自动清理这些缓存,基于SoftReference和WeakReferenceeviction=“SOFT|WEAK”
      LoggingCache带日志功能的缓存比如:输出缓存命中率基本
      SynchronizedCache同步缓存基于Synchronized关键字实现,解决并发问题基本
      BlockingCache阻塞缓存通过在get/put方式中加锁,保证只有一个线程操作缓存,基于JAVA重入锁实现bloking=“true”
      SerializedCache支持序列化的缓存将对象序列化以后存入缓存,取出时反序列化readOnly=false(默认)
      ScheduledCache定时调度的缓存在进行get/put/remove/getSize等操作前,判断缓存时间是否超过了设置的最长缓存时间(默认是1小时),如果是则清空缓存,也就是每隔一段时间清空一次缓存flushInterval不为空
      TransactionalCache事务缓存在二级缓存中使用,可一次存入多个缓存,移除多个缓存在TransactionalCacheManager中用Map维护对应关系
      • 一级缓存

        一级缓存也叫本地缓存Local Cache,是SqlSession层面的缓存,默认开启。既然是在SqlSession中进行缓存,DefaultSqlSession中只有两个对象属性:Configuration和Executor,而前者是全局性的,所以一级缓存是在Executor中维护的,三种执行器的父类BaseExecutor中的构造函数中就初始化了PerPetual对象。

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gy0PLLM3-1592171443328)(C:UsersdellDesktop学习资料markdownmarkword-imgimage-20200615051703746.png)]

        在同一个Session中共享,不同Session不能共享。

        一级缓存的存入:是在BaseExecutor的query()方法中的queryFromDataBase()方法中存入的。

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IYdnuVZa-1592171443331)(C:UsersdellDesktop学习资料markdownmarkword-imgimage-20200615042308068.png)]

        一级缓存的清空:同一个会话中,更新或者删除操作会导致缓存被清空,如果select标签的flushCache=true时,则查询也会清空缓存。在BaseEexcutor中的update()方法中调用了clearLocalCache()方法来清空。

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JYg63oOB-1592171443333)(C:UsersdellDesktop学习资料markdownmarkword-imgimage-20200615042649393.png)]

        一级缓存存在的问题:如果其他会话更新数据,会导致本会话从缓存中读取到过时的数据。要解决这个问题只能用范围更大的二级缓存。

      • 二级缓存

        二级缓存是用来解决一级缓存不能跨会话共享的问题的,是namespace级别的,只要是同一个接口里面相同的方法,都可以共享,声明周期和应用同步。

        如果开启了二级缓存,那么使用到缓存的时候,肯定是先查询二级缓存,二级缓存没有的时候才去一级缓存中查,一级缓存没有的时候就到数据库去查。

        一级缓存是放在Executor中的,二级缓存的范围比它大,Mybatis中对Executor做了个装饰,使用CashingExecutor来维护,也就是如果使用二级缓存,则在创建Executor对象时会对Executor进行装饰,查询时判断CashingExecutor中是否有缓存结果,有就从该缓存中返回,没有就委派给真正的执行器执行查询,比如说SimpleExecutor,再到该执行器中的一级缓存中查。

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kl7nDI5s-1592171443336)(C:UsersdellAppDataRoamingTyporatypora-user-imagesimage-20200615054152012.png)]

        注意:全局配置文件中二级缓存的总开关默认开启,如果一个Mapper要使用二级缓存,还要单独打开它自己的开关。

        <!-- mapper中声明该namespace使用二级缓存-->
        <cache type = "org.apache.ibatis.cache.impl.PerpetualCache" size = "1000" eviction = "LRU" flushInterval = "12000" readOnly = "false"/>
        <!-- size表示最多缓存的对象个数,eviction表示回收策略,flushInterval表示自动刷新时间-->
        
    2. select

      首先来看一个简单的使用:

    select emp_id as id name mobile note from t_emp where id = #{id} ```
    • ​ 简单说一下各个属性的含义:namespace为接口的全限定名,id为接口中对应的方法,parameterType为传入参数的类型,resultType为结果映射的类型…

    • 多个参数的传递方式:使用map,使用注解,使用javabean。

      使用map:代码可读性降低,用的比较少

      List<Employee> selectByMap(Map<String,Object> map);
      
    select emp_id as id name mobile note from t_emp where name like concat('%',#{name},"%") and mobile like concat('%',#{lastNumber},'%') ```
     使用注解:一般参数少于5个时,简单使用,可读性高,参数过多调用起来不方便,显得繁琐复杂
    
     ```java
    

    List selectByAnnotation(@Param(“name”)String name,@Param(“lastNumber”)int lastNumber);
    ```

     ```xml
     <select id = "selectByMap" resultType = "emploee">
     	select emp_id as id name mobile note from t_emp 
         where name like concat('%',#{name},"%") 
         and mobile like concat('%',#{lastNumber},'%')
     </select>
     ```
    
     使用javabean:参数多于5个时使用比较方便
    
     ```java
     public class EmploeeParam{
         private String name;
         private int lastNumber;
         ...
    

    }
    ```

     ```xml
    
    select emp_id as id name mobile note from t_emp where name like concat('%',#{name},"%") and mobile like concat('%',#{lastNumber},'%') ```
    • 分页参数RowBounds

    RowBounds是Mybatis内置的分页类,有两个参数,offset表示从哪一行开始,limit表示限制条数,在接口方法上加上这个参数直接使用,比如:

       List<Employee> selectByAnnotation(@Param("name")String name,@Param("lastNumber")int lastNumber,RowBounds rb);
    
       @Autowired
       private EmployeeService service;
       @RequestMapping("/emps_like")
       @ResponseBody
       public ModelAndView getEmps(@RequestParam("name")String name,@RequstParam("lastNumber")int lastNumber) {
           ModelAndView mv = new ModelAndView();
           RowBounds rb = new RowBounds(0,10);
           List<Employee> list = service.selectByAnnotation(name,lastNumber,rb);
           mv.put("emps",list)
           return mv;
       }
       ```
    
    3. #### insert
    
     - 简单的使用
    
    ```xml
       <mapper namespace = "com.xxx.dao.EmployeeMapper">
    	<insert id = "insertEmp" parameterType = "employee">
       		insert into t_emp(emp_id,name, sex,mobile, note) 
               values(#{id},#{name},#{sex},#{mobile},#{note})
       	</insert>
    </mapper>
       ```
    
     - 主键回填
    
       无论支持还是不支持主键自增的数据库都可以使用selectKey来实现主键回填,比如:
    
       ```xml
    <!--mysql 注意order要设置为after,获取递增主键值用的是函数LAST_INSERT_ID()-->
       <insert id="insertUser" parameterType="user" >
        	<selectKey keyProperty="id" order="after " resultType="int" >
               		select LAST_INSERT_ID()
           	</selectKey>
           	insert into t_user(t_name,sex) values (#{name},#{sex})
       </insert>
       
       <!--oracle 注意order要设置为before,因为要先从序列中获取值,然后将值作为主键插入到数据库中-->
       <insert id="insertUser" parameterType="user" >
        	<selectKey keyProperty="id" order="before " resultType="int" >
               		select t_id.nextval as id from dual
        	</selectKey>
           	insert into t_user(t_id,t_name,sex) values (#{t_id},#{name},#{sex})
    </insert>
       ```
    
       对于支持自增主键的数据库MySQL,也可以使用下面这种方式实现主键回填,设置useGeneratedKeys属性为true,keyProperty设置生成主键映射到那个属性上,keyColum是映射到那个字段上。
    
       ```java
       void insertUser(User user);
       ```
    
       ```xml
       <insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
            insert into t_user(t_name,sex) values (#{name},#{sex})
       </insert>
       ```
    
       对于不支持主键自增的数据库Oracle,就通过通用方式selectKey属性和标签来完成。
    
       这里对selectKey标签中的属性简单说明一下:
    
       | 属性          | 描述                                                         |
       | ------------- | ------------------------------------------------------------ |
       | keyProperty   | selectKey语句结果应该被设置的目标属性                        |
       | resultType    | 结果的类型                                                   |
       | keyColumn     | 匹配属性的返回结果集中的列名称                               |
       | order         | 如果为before,那么它会先执行selectKey语句设置keyProperty然后执行插入语句,如果为after则相反 |
       | statementType | 使用哪种语句类型,默认为PREPARED。                           |
    
    4. #### update|delete
    
     这两个比较简单,对于批量更新和删除见后面的动态sql一节。
    
    5. #### resultMap
    
     resultMap主要用途有两个:第一个就是当数据库字段名和Java对象属性名不一致是使用,第二个就是用于高级查询,也就是一对多,多对多。代码示例:
    
     ```xml
         <resultMap id="employee" type="employee">
             <id column="id" property="id"/> <!-- 主键列-->
             <result column="real_name" property="realName"/>
          <result column="sex" property="sex"/>
             <result column="birthday" property="birthday"/>
          <result column="mobile" property="mobile"/>
             <result column="email" property="email"/>
          <result column="POSITION" property="position"/>
             <result column="note" property="note"/>
             <!-- association完成一对一的映射-->
             <association property="workCard"  column="id"  select="com.dao.WorkCardDao.getWorkCardByEmpId"/>
             <!-- collection完成一对多的映射-->
             <collection property="employeeTaskList" column="id" fetchType="lazy" ofType="employeeTask" select="com.dao.EmployeeTaskDao.getEmployeeTaskByEmpId"/>
             <!--鉴别器-->
             <discriminator javaType="long" column="sex">
                 <case value="1" resultMap="maleHealthFormMapper"/>
                 <case value="0" resultMap="femaleHealthFormMapper"/>
             </discriminator>
         </resultMap>
     
      <resultMap id="maleHealthFormMapper" type="maleEmployee" extends="employee">
             <association property="maleHealthForm" column="id" select="com.dao.MaleHealthFormDao.getMaleHealthForm"/>
      </resultMap>
     
         <resultMap id="femaleHealthFormMapper" type="femaleEmployee" extends="employee">
             <association property="femaleHealthForm" column="id" select="com.dao.FemaleHealthFormDao.getFemaleHealthForm"/>
         </resultMap>
     
         <select id="getEmployee" parameterType="long" resultMap="employee">
             select id,real_name as realName,sex,birthday,moblie,email,POSITION,note from t_emploee
             where id = #{id}
         </select>
    
    1. sql片段

    通常是把一些反复使用的SQL片段比如列名,定义在sql标签中,在其他标签中就可以通过引入的方式使用,比如:

       <sql id = "empCols">
        id,real_name,sex,birthday,moblie,email,POSITION,note 
       </sql>
    
       <select id="getEmployee" parameterType="long" resultMap="employee">
               select 
           	<include refid = "empCols"/> <!--引入-->
           	from t_emploee
               where id = #{id}
       </select>
    

    另外sql标签还支持变量传递,比如:

    <sql id = "empCols">									${alias}.id,${alias}.real_name,${alias}.sex,${alias}.birthday,${alias}.moblie,${alias}.email,${alias}.POSITION,${alias}.note 
    </sql>
    <!--定义的alias变量的值就是表的别名-->
    <select id="getEmployee" parameterType="long" resultMap="employee">
         select 
        	<include refid = "empCols">
                <property name = "alias" value = "e"></property>
            </include>
        	from t_emploee e
            where id = #{id}
    </select>
    
    1. 级联查询

      Mybatis中的级联分三种:鉴别器、一对一和一对多,分别对应着resultMap中的:discriminator、association和collection属性,

      • 嵌套查询:会多执行一些SQL语句导致数据库资源的损耗和系统性能的下降,也就是N+1问题,解决方案就是打开延迟加载开关:

        <resultMap id="employee" resultType = "employee">
           <association property="workCard"  			      column="id"select="com.xxx.dao.WorkCardDao.getWorkCardByEmpId"/>
           <collection property="employeeTaskList" column="id" fetchType="lazy" 			select="com.xxx.dao.EmployeeTaskDao.getEmployeeTaskByEmpId"/>
        </resultMap>       
        <!--就是在查询employee表的时候通过select属性值(statementId)定位到另外的一个查询上,进行了第二次查询,看上去只有一个查询雇员信息的操作,其实后台分别执行了对雇员表、工号表、雇员任务表的查询 -->
        
        <!--解决方案:
        	1.全局性配置:lazyLoadingEnabled是否开启延迟加载,aggressiveLazyLoading是否采用层级加载。
        	2.语句配置:fetchType属性可以处理全局定义无法处理的问题,进行自定义。注意该选项只在级联元素association和collection中存在,fetchType=eager 获得当前POJO后立即加载对应的数据,fetchType=lazy 获得当前POJO后延迟加载对应的数据
         注意:如果配置了fetchType属性,那么它会忽略上述的两个全局配置项。
        -->
        
      • 嵌套结果查询:基于表连接实现级联,会导致SQL变复杂,所需要的配置也比较复杂,一次性取出所有数据会造成内存浪费,给日后的维护工作带来不便,一般用于比较简单,关联不多的场景下。

        <resultMap id="employee2" type="employee">
                <id column="id" property="id"/>
                <result column="real_name" property="realName"/>
                <result column="sex" property="sex"/>
                <result column="brithday" property="birthday"/>
             <result column="mobile" property="mobile"/>
                <result column="email"  property="email"/>
             <result column="position" property="position"/>
                <association property="workCard"  javaType="workCard" column="id">
                 <id column="wc_id" property="id"/>
                    <result column="id" property="empId"/>
                 <result column="wc_real_name" property="realName"/>
                    <result column="wc_department" property="department"/>
                 <result column="wc_mobile" property="mobile"/>
                    <result column="wc_position" property="position"/>
                 <result column="wc_note" property="note"/>
                </association>
                <collection property="employeeTaskList" ofType="employeeTask" column="id" >
                    <id column="et_id" property="id"/>
                    <result column="id" property="empId"/>
                    <result column="et_task_name" property="taskName"/>
                    <result column="et_note" property="note"/>
                    <association property="task" javaType="task" column="et_task_id">
                        <id column="t_id" property="id"/>
                        <result column="t_title" property="task.title"/>
                        <result column="t_context" property="task.context"/>
                        <result column="t_note" property="note"/>
                    </association>
                </collection>
                <discriminator javaType="int" column="sex">
                    <case value="1" resultMap="maleEmployeeMapper2"/>
                    <case value="0" resultMap="femaleEmployeeMapper2"/>
                </discriminator>
        </resultMap>
        

    动态SQL

    1. 概念:动态Sql可以让我们在xml映射文件中以标签的形式编写动态sql,完成逻辑判断和动态拼接Sql的功能。

    2. 九中动态Sql标签:if|where|set|foreach|choose|when|otherwise|trim|bind

      具体使用方法后面文章在写

    3. 执行原理:使用OGNL从Sql参数对象中计算表达式的值,根据表达式的值动态拼接Sql。

    
    
```

动态SQL

  1. 概念:动态Sql可以让我们在xml映射文件中以标签的形式编写动态sql,完成逻辑判断和动态拼接Sql的功能。

  2. 九中动态Sql标签:if|where|set|foreach|choose|when|otherwise|trim|bind

    具体使用方法后面文章在写

  3. 执行原理:使用OGNL从Sql参数对象中计算表达式的值,根据表达式的值动态拼接Sql。


最后

以上就是玩命小伙为你收集整理的mybatis的两大配置文件的全部内容,希望文章能够帮你解决mybatis的两大配置文件所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部