java框架之mybatis

Posted 狗吃鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java框架之mybatis相关的知识,希望对你有一定的参考价值。

一、简介

1、概念

  • mybatis 是一个半自动轻量级的一个 orm 框架

2、作用

  • 将 java 与 sql 分离,解决了 jdbc 的硬编码问题,方便 sql 的修改;
  • sql 由开发人员控制,更加方便 sql 的调优;

3、快速开始

(1)原始方法

  • 建一个全局配置文件,里面是数据源等运行环境的信息;
  • 建立一个sql 的映射文件,并将这个文件注册到全局的配置中;
  • 根据全局的配置文件获得一个 sqlsessionfactory;
  • 通过 factory 获得 sqlsession(非线程安全),一个sqlsession 就是和数据库的一次会话,用完需要关闭;
  • sqlsession通过 sql 的唯一标识符调用方法执行;
  • 注意数据库的字段名和bean的字段名要相同才能顺利映射;

(2)接口式编程

  • 定义一个接口,接口中定义操作数据库的方法;
  • 将接口和sql的映射文件绑定:xml的namespace是接口全限定名,sql的id是接口中的方法名;
  • 在 java 程序中通过session获得借口的实现类(代理对象)对象;
  • 通过对象调用方法执行相应的sql;

 

二、配置文件

1、全局配置文件

(1)xml 中引入 dtd 约束

(2)properties标签:url引入网络或者磁盘的资源;resource引入 classpath 下的资源;

(3)settings标签:设置一些mybatis运行时的重要参数

(4)typeAliases标签:就是给 java 类起别名用的

  • 别名不分大小写;
  • 可以单独起也可以批量起;
  • 默认别名就是类名;
  • 也可以使用注解来起别名(@alias);
  • java 中的基本数据类型和一些常用的类已经取好了别名(基本类型就是前面加 _ ;引用类型就是原类名)

(5)typeHandlers标签:

  • 就是处理 java 类型和数据库类型的转换的;
  • 常用的类型 mybatis 都给默认处理了,不用我们自己去做;
  • 当然是支持自定义的了;

(6)plugins标签:作用就是拦截四大对象,改变他们的默认行为

(7)environments标签:

  • 用来配置环境的,可以配置多个环境,在default属性里可以指定默认的环境;
  • 每个环境都应包含:transactionManager 和 dataSource

(8)databaseIdProvider标签:

  • 为mybatis提供多数据库厂商的支持;
  • 厂商标识在数据库的驱动里;
  • 标签内通过property标签指定数据库的别名;
  • 在相应的sql语句的标签上用databaseId引入数据库的别名;
  • 注意:主配置文件里的标签的书写的顺序是有要求的,databaseIdProvider应该写在envir 和 mapper 之间;

(9)mappers标签:

  • 作用就是注册 sql 映射;
  • 里面具体的 mapper 的属性有三种写法:
  • resource:classpath 路径下;
  • url:网络资源或者磁盘资源;
  • class:指定对应的映射的接口(条件是必须同包同名);
  • 批量的注册直接用package 标签(条件是必须同包同名);
<?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="conf/dbconfig.properties"></properties>
    
    <!-- <settings>
        <setting name="" value="" />
    </settings>

    <typeAliases>
        单独起别名
        <typeAlias type="" alias="" />
        批量起别名
        <package name="" />
    </typeAliases>

    <typeHandlers>
        <typeHandler handler="" />
    </typeHandlers>

    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>     -->
    
    <environments default="development">
        <!-- <environment id="test">
            <transactionManager type=""></transactionManager>
            <dataSource type=""></dataSource>
        </environment> -->
        <environment id="development">
            <transactionManager type="JDBC" />
            <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>
    
    <databaseIdProvider type="DB_VENDOR">
        <property name="Oracle" value="oracle" />
        <property name="mysql" value="mysql" />
    </databaseIdProvider>
    
    <mappers>
        <mapper resource="conf/TuserMapper.xml" />
    </mappers>
</configuration>
技术图片

2、sql映射文件概述

(1)增删改测试

  • int、long 和 boolean 类型的返回值我们可以直接用;
  • 增删改后一定要 commit;

(2)数据插入获得自增主键

  • mysql:支持自增主键
  1. useGeneratedKeys 和 keyProperty 属性配合使用即可获取到自增主键的值
  • oracle:不支持自增主键,靠序列来完成
  1. 在要执行的sql前用 selectKey 标签来实现
  2. 相关的三个属性分别是 keyProperty、resultType 和 order;
  3. 有两种写法分别是 order 的属性值为 before 和 after;通常使用 before

3、映射文件的参数处理

(1)单个参数

  • 单个参数可以在 #{}中随意写;

(2)多个参数

  • 多个参数默认在其中写 param1 和 param2 等;也可以用注解@param 指定名称;多个参数本质是封装在一个map中;
  • 业务相关的数据直接封装在 pojo 中;
  • 关联性不高且使用的频率不高的话的可以放在一个map里;在map 里可以随意指定键名;
  • 同时多个参数如果使用的频率很高的话还可以封装为一个 TO 对象;
  • 集合和数组的特殊处理:参数可以写 collection、list 和 array;

(3)参数处理源码分析

  • 参数封装成map 的过程:总结起来就是参数多的时候就封装成 map 集合,有@param注解的参数的键值就是指定的值,否则就是默认的;

(4)关于 #{} 和 ${}两种取值的方法:

  • #{}是以预编译的形式来取参数的值的,可以防注入;而${}是直接拼接的;
  • 大多数的情况采用 #{},但在原生的 jdbc 不支持占位符的地方可以使用 ${},比如order by ${age};
  • #{}进行参数设置的时候可以指定一些规则,比如:#{email,jdbcType.null},mybatis自动将 null值映射为jdbc的other类型,这个类型oracle不支持会报错;

4、映射文件的select 元素

(1)resultType

  • resultType的作用就是指定结果集的封装的规则;
  • 结果集封装为 list类型直接写 list里数据类型即可;
  • 封装为 map 单条记录直接写 map 的别名;多条记录写pojo 的类型(map的值是pojo),map的键值在相应的方法上面用注解@mapKey来指定pojo中的属性;

(2)resultMap(关联查询 | 分步查询 | 延迟加载 | 集合属性封装)

  • 配置列名属性名映射
  • resultMap也是用来定义结果集的封装规则的(表的列名和pojo 的属性名的对应关系);
  • resultMap标签两个属性 type是pojo类型,id是唯一标识;表中的主键列用id 封装,普通列用 result 封装;
  • 用resultMap实现关联查询(前提pojo包装pojo):
  1. 第一种方法是直接写 property 的时候用属性点属性的方式就可以了;
  2. 第二种方法是在 resultMap 里用 association 标签实现关联查询,两个属性 property 和 javatype;
  • association 标签用法
  • 可以用来实现关联查询;
  • 用 association 实现分步查询:select属性指定 mapper方法,column 属性指定传入方法的参数;
  • 关联查询时,如果想按需加载,就可以在全局 settings 里设置lazyloading 为true,实现get相应的pojo 的时候再发出sql向数据库查询;
  • collection 标签用法
  • 用 collection 标签实现集合类型属性的封装,属性 property为相应的集合类型的属性名,而ofType 是集合的泛型的类型;
  • collection 实现分步查询和延迟加载:
  1. 实际上还是 select 将两个接口方法、两个sql关联,再用 column 属性传入参数;
  2. 多个参数的传递可以用map,就是这样的形式:{key1=col1,key2=col2};
  3. fetchType属性可以设置延迟加载;
  • discriminator鉴别器根据列的值做出不同的处理:还是写在 resultMap里,内含case标签指定各种值的处理方法(封装方法),其中指定封装数据的类型时,type 和 map 必须二选一;这个东西用的少;
<?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.wc.mapper.TuserMapper">
    <!-- <resultMap type="tuser" id="map1">
        <id property="uid" column="tid"/> 
        <result property="uname" column="uname"/>
        <result property="age" column="age"/>
        关联查询
        <association property="dept" javaType="department">
            <id property="did" column="did"/> 
            <result property="dname" column="dname"/>
        </association>
    </resultMap> -->
    
    <resultMap type="tuser" id="map2">
        <id property="uid" column="tid"/> 
        <result property="uname" column="uname"/>
        <result property="age" column="age"/>
        <result property="did" column="did"/>
        <!-- select step by step -->
        <association property="dept"
            select="com.wc.mapper.DepartmentMapper.getDeptById" column="did">
            <id property="did" column="did"/> 
            <result property="dname" column="dname"/>
        </association>
    </resultMap>
    <select id="getById" resultMap="map2">
        select * from tuser where tid = #{id}
    </select>
</mapper>

 

技术图片

三、动态sql

1、if 判断

(1)OGNL(对象图导航语言)

  • 有点类似于 jsp 中的 el 表达式,可以用来取参数的值并作一些基本的运算;

(2)if 判断

  • if 判断拼接 where 条件存在的问题:就是可能会出现where后面直接是一个 and的情况
  1. 第一种方法:就是where 后面紧跟一个 1=1;
  2. 第二种方法:使用where 标签,使用时注意条件的后就不要写 and 了;
  3. 第三种方法:使用trim 标签,可以通过属性设置前缀后缀和前后缀的覆盖问题,where 前后多出的 and(or)都能解决;
  • set 标签用来封装修改条件,作用就是去除 set 的时候多余的逗号,当然用 trim 标签也可以实现,与if结合使用可以实现动态更新;

2、choose 分支选择

(1)语法:choose----when----when----otherwise-----choose

(2)理解:类似于 java 中带break 的 switch语句,只匹配一个条件;

3、foreach 遍历

(1)遍历集合拼接 where 条件(如:in 集合)

  • 几个常用的属性 collection、item、separator、open、close、index(map的时候就是map的key);

(2)实现数据的批量插入

  • mysql 第一种:在 values 后面用 foreach 遍历拼接 sql;
  • mysql 第二种:直接整个插入语句来循环,需要在数据库的 url 后面传参开启多条语句执行的支持;
  • oracle 第一种:用 plsql ,在begin 和 end 中来遍历;
  • oracle 第二种:循环用 select 语句将集合中的值从伪表中查出,再用查出的结果进行插入;

4、其他

(1)内置参数:

  • 除了我们传入的参数,还有内置的参数,这里的内置参数可以取到也可以判断
  • _parameter:封装所有的参数,传入的参数一个就是这个参数本身,传入多个参数就是一个 map;
  • databaseId:全局配置文件里面配置了 databaseproviderid这里可以取到;
  • _parameter 和 if结合可以用来进行 where条件的拼接;
  • databaseId 和 if 结合使用可以用来在一个 select 元素里写两个数据库的 sql;

(2)bind 标签:作用就是将传入的参数赋值给一个变量方便以后的调用,连个属性name 和 value;

(3)sql 片段:

  • 用 sql 标签抽取可以重用的sql 片段方便以后在其他的sql 里用include标签用id引用,如查询字段的重用;
  • include 支持自定义 property ,在sql标签的内部就能用${}取来使用;

 

四、缓存

1、介绍

(1)作用:就是提升查询的效率;

(2)分类:

  • 一缓(本地缓存):session 级别的缓存,默认开启,session关闭则失效;
  • 二缓(全局缓存):namespace级别的缓存,要手动开启;

2、一级缓存

(1)一级缓存失效的四种情况

  • sqlsession 不同,不同的session 之间不能共享数据;
  • 参数不同的情况;
  • 两次相同的查询之间有 增删改 的操作;
  • 第二次查询之前手动清空缓存;

3、二级缓存

(1)作用:可以实现不同的 session 之间的数据共享,一个 namespace(一个接口)对应一个map(二级缓存);

(2)原理:

  • 一次会话中查询的数据会默认保存在一级缓存中;
  • 当对应的会话关闭的时候,如果开启了二级缓存,在session关闭清空缓存数据之前会将数据存到二级缓存中;
  • 需要注意的是如果 session 没有关闭,一级缓存的数据是不会自动存到二级缓存中的;

(3)使用:总开关(主配文件里的 setting)和分开关(sql映射文件里用 cache标签来开启)都要打开;

4、缓存原理和设置

(1)缓存相关设置

  • 全局的 cacheEnabled 和 select 标签的 useCache 都是和二级缓存相关的设置,不会影响一级缓存;
  • flashCache属性在增删改的标签里是默认开启,在 select 标签里是默认关闭的,这个属性为 true的话会清空一二级的缓存;
  • session 的 clearcache 方法只会清除当前 session 的一级缓存;
  • 全局 LocalcacheScope 可以用来关闭一级缓存(一般不用);

(2)原理图示

技术图片技术图片?

5、第三方缓存整合(ehcache)

(1)步骤:

  • 导入缓存的包和整合包;
  • 引入ehcache配置文件;
  • 直接在mapper xml 中用 cache 标签引用;
  • 另一个 mapper 可以用 cache-ref 标签来引用其他 mapper 的缓存策略;

 

五、整合spring(SSM整合)

1、导包

(1)核心包:spring包、springmvc包、mybatis包、spring-mybatis整合包

(2)其他包:日志包、连接池包

2、mybatis 配置

(1)主要就是配全局 settings 等;

3、springMVC 配置

(1)controller 扫描;

(2)视图解析器;

(3)注解驱动;

(4)默认servlet 处理;

4、spring 配置

(1)包扫描;

(2)数据源的配置;

(3)事务管理(基于注解的事务);

(4)sqlSessionFactory配置;

(5)配置可以批量执行的 sqlSession;

(6)配置Mapper 接口的扫描(不可少);

5、web.xml配置

(1)全局参数配置 spring配置文件的路径;

(2)配置spring 监听;

(3)配置 springmvc 的DispatcherServlet;

(4)spring框架会自动加载 web-inf 下的配置文件,classpath 路径下的配置文件需要配置路径;

(5)所有配置文件的示例

 

六、逆向工程

1、概念

  • 就是根据数据库表自动生成 bean、mapper 和相应的映射文件;
  • 逆向工程常用的方法有:配置 eclipse 插件生成;使用java 工程来生成;

2、使用

(1)导包

(2)配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- targetRuntime="MyBatis3Simple":生成简单版的CRUD
        MyBatis3:豪华版    -->
  <context id="DB2Tables" targetRuntime="MyBatis3">
      <!-- jdbcConnection:指定如何连接到目标数据库 -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true"
        userId="root"
        password="123456">
    </jdbcConnection>

    <!--  -->
    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

    <!-- javaModelGenerator:指定javaBean的生成策略 
    targetPackage="test.model":目标包名
    targetProject="MBGTestProjectsrc":目标工程
    -->
    <javaModelGenerator targetPackage="com.atguigu.mybatis.bean" 
            targetProject=".src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <!-- sqlMapGenerator:sql映射生成策略: -->
    <sqlMapGenerator targetPackage="com.atguigu.mybatis.dao"  
        targetProject=".conf">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

    <!-- javaClientGenerator:指定mapper接口所在的位置 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.dao"  
        targetProject=".src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

    <!-- 指定要逆向分析哪些表:根据表要创建javaBean -->
    <table tableName="tbl_dept" domainObjectName="Department"></table>
    <table tableName="tbl_employee" domainObjectName="Employee"></table>
  </context>
</generatorConfiguration>
技术图片

(3)运行生成器 java 代码

public void testMbg() throws Exception {
    List<String> warnings = new ArrayList<String>();
    boolean overwrite = true;
    File configFile = new File("mbg.xml");
    ConfigurationParser cp = new ConfigurationParser(warnings);
    Configuration config = cp.parseConfiguration(configFile);
    DefaultShellCallback callback = new DefaultShellCallback(overwrite);
    MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
            callback, warnings);
    myBatisGenerator.generate(null);
}
技术图片

3、注意事项

  • mybatis 的SSM集成和逆向工程的具体使用方法和示例代码 github 上官方目录都有;
  • 可以生成简单查询和复杂查询的代码,复杂查询的方式类似于 hibernate 的 QBC;
  • or 条件的拼接
EmployeeExample example = new EmployeeExample();
Criteria criteria = example.createCriteria();
criteria.andLastNameLike("%e%");
criteria.andGenderEqualTo("1");

Criteria criteria2 = example.createCriteria();
criteria2.andEmailLike("%e%");
example.or(criteria2);
技术图片

 

七、运行原理

技术图片技术图片?

1、sqlsessionfactory 初始化

  • 把配置文件所有的信息解析并保存在 configuration 中,并返回一个包含 configuration 的 defaultSqlSessionFactory 对象;
  • 一个 mapperedStatement 代表一个增删改查的信息;

2、sqlsession 对象获取

  • 返回一个 defaultSqlSession 对象,里面包含了 configuration 和 executor;
  • executor的两次包装:二级缓存开启的话会有一次包装;拦截器链会有一次包装;

3、获取接口代理对象

  • 调用 getMapper 方法实际上是从一个 mapperproxyfactory 的工厂类中获得 mapperproxy 对象,里面包含了defaultsqlsession;

4、查询实现

技术图片技术图片?

  • 注意:在四大对象的创建过程中都会有插件的介入;

 

八、插件扩展

1、插件开发

(1)原理

  • 四大对象的创建不是直接返回的,而是 intercepter 调用了 plugin 方法包装生成代理对象;
  • 我们可以使用插件对四大对象进行包装生成代理对象,这样就可以拦截到四大对象的每一个执行;

(2)简单实现--单个插件

  • 写一个类继承intercepter ,重写 intercept(拦截)、plugin(包装)、和 setProperties(属性设置)方法;
  • 用 @intercepts 注解给插件签名;
  • 在全局配置文件中引用插件;

(3)多个插件:拦截同一个对象同一个方法

  • 包装:顺序包装;
  • 执行:逆序执行;
  • 特点:多个插件是对目标对象进行层层包装的;

(4)开发示例:

技术图片技术图片?

(5)PageHelper 插件

  • 实现分页相关功能
  • 使用:导包-----主配置文件注册插件------mapper查询前设置参数;
  • 说明:参数设置后可以返回一个 page 对象其中包含很多分页信息;也可以用pageinfo包装查询结果; 

2、扩展

(1)批量执行sql语句

  • 普通使用:就是在 openSession 传入 executorType;
  • 与 Spring 集成:
<!--配置一个可以进行批量执行的sqlSession  -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
    <constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>
  • mybatis 主配置文件的setting 里有一个批量执行的开关,但是不建议开启,因为会影响所有的sql执行; 

(2)mybatis 调用存储过程

  • 带游标的存储过程的创建(oracle分页查询):存储过程语法、游标使用;
  • 调用过程:select 标签设置 statementtype参数;{call proname(args)};游标的封装用到resultMap;

(3)自定义类型处理器处理枚举类型

  • 枚举类型:name(名)和 ordinal(suoyin);
  • 使用 mybatis 自带的类型处理器:只能保存名字或者索引中的一个;
  • 自定义:类继承typehandler;重写相应方法(实质就是调用ps和rs 的 get 和 set 方法);主配置文件中注册;
  • 说明:枚举的本质就是一个类;枚举的值本质就是枚举对象;枚举当然也可以有构造方法;
  • 注意:保存数据的时候可以针对具体的字段设置 typehandler;select 查询的时候也可以针对具体字段在 resultMap中指定typeHandler,但是必须保证保存和查询的typeHandler是一致的

以上是关于java框架之mybatis的主要内容,如果未能解决你的问题,请参考以下文章

Java之SSM框架整合-案例IDEA版(一篇文章精通系列)Spring+SpringMVC+MyBatis(整合源代码)

深入理解java:4.3.1. 框架编程之MyBatis---SQL语句执行的完整流程

Java框架之Mybatis

Java框架之Mybatis

Java SSM框架之MyBatis3MyBatis之动态SQL

Java数据持久层框架 MyBatis之背景知识二