概述
- MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的ORM持久层框架
- MyBatis 消除了几乎所有的 JDBC 代码,参数的手动设置以及结果集的检索
- MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录
- ORM(Object/Ralational Mapping)即对象/关系映射。是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,并且提供了一种机制,通过JavaBean对象去操作数据库表中的数据,不同的持久层框架ORM是不同的
- 持久化:即把数据保存到可永久保存的存储设备中(如磁盘,数据库等)
- 持久层:dao/mapper层就是持久层
核心接口和类
每个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心
- 首先获取SqlSessionFactorBuilder对象,可以根据XML配置文件或Configuration类的实例构建该对象
- 然后获取SqlSessionFactory对象,该对象实例可以通过SqlSessionFactoryBuilder对象来获得
- 最后获取SqlSession实例,SqlSession对象中完全包含以数据库为背景的所有执行SQL操作的方法,可以用该实例来直接执行已映射的SQL语句
- SqlSessionFactoryBuilder
- SqlSessionFactoryBuilder负责构建SqlSessionFactory,并且提供了多个build()方法的重载
- 配置信息可以以三种形式提供给SqlSessionFactoryBuilder的build()方法,InputStream(字节流)、Reader(字符流)、Configuration(类)
- InputStream inputStream= Resources.getResourceAsStream(“mybatis/mybatis.xml”);
- SqlSessionFactoryBuilder的最大特点:用过即丢。一旦创SqlSessionFactory对象之后,这个类就不需要存在了,因此SqlSessionFactoryBuilder的最佳范围就是存在于方法体内,也就是局部变量而已
- SqlSessionFactory
- SqlSessionFactory提供的openSession()方法来获取SqlSession的实例
- openSession()方法的参数为boolean值时,若传入为true表示关闭事务控制,自动提交;false表示开始事务控制,若不传参,默认为true
- SqlSessionFactory对象一旦创建,就会在整个应用运行过程中始终存在,没有理由去销毁或再创建它,并且再应用运行中也不建议多次创建SqlSessionFactory。因此SqlSessionFactory的最佳作用域是Application,即随着应用的生命周期一同存在,那么这种存在于整个应用运行期间,并且同时只有一个对象实例的模式称之为单例模式
- SqlSession
- SqlSession是用于执行持久化操作的对象,类似于JDBC中的Connection。它提供了面向数据库执行SQL命令所需的所有方法,可以通过SqlSession实例直接运行已映射的SQL语句
- SqlSession对应着一次数据库会话。由于数据库会话不是永久的,因此SqlSession的生命周期也不应该是永久的,相反,再每一次访问数据库都需要创建它(这里并不是说SqlSession只能执行一次SQL,SqlSession完全可以执行多次,但是若关闭了SqlSession,那么就需要重新创建它),创建SqlSession的地方只有一个,那就是SqlSessionFactory对象的openSession()方法
- 每个线程都有自己的SqlSession实例,SqlSession实例不能被共享,也不是线程安全的。因此最佳的作用域范围是request作用域或者方法体作用域内
- 使用方式
- 1.通过SqlSession实例来直接执行已映射的SQL语句
- 2.基于mapper接口方式操作数据
操作步骤
- 读取配置文件
- InputStream inputStream= Resources.getResourceAsStream(“mybatis/mybatis.xml”);
- 获取一个工厂类
- SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
- 获取一个sqlSession会话
- SqlSession session = factory.openSession();
- 获取Mapper的接口对象
- 接口类型 对象名 = session.getMapper(接口类.class);
- 调用接口中的方法
- List list = 对象名.方法();
- 事务提交
- session.commit();
- 关闭会话
- session.close();
- 注释
<!-- SQL外的注释:使用XML的标准注释 -->
<select id="selectUser" resultType="com.xl.entity.UserEntity">
SELECT
/* SQL中的注释:采用SQL多行注释 */
id, name, status
FROM
users
</select>
MyBatis核心(全局)配置文件(mybatis-config.xml)
MyBatis 的配置文件包含了影响 MyBatis 行为的设置(settings)和属性(properties)信息
- <configuration>:配置文件的根元素节点,子元素需按顺序书写
- <properties>:配置属性文件
- resource属性:外部指定properties属性文件
- <property>
- name属性:属性名
- value属性:属性值
- 使用不在一个地方配置执行顺序为
- 在 properties 元素体内指定的属性首先被读取
- 然后根据 properties 元素中的 resource 属性读取全类名下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性
- 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性
- <settings>:设置MyBatis运行中的全局行为
- <setting>
- name:属性名
- cacheEnabled:对在此配置文件下的所有cache二级缓存进行全局性开关设置,默认true
- lazyLoadingEnabled:全局性设置懒加载,默认true,如果设为fasle,则所有相关联的都会被初始化加载
- autoMappingBehavior:MyBatis对于<resultMap>自动映射的匹配级别为 NONE|PARTIAL|FULL
- NUNE:禁止自动匹配
- PARTIAL:默认。自动匹配所有属性(要求是列名和javaBean属性名一致),有内部嵌套(association,collection)的除外
- FULL:自动匹配所有
- aggressivelazyLoading:属性按需加载
- value:属性值
- name:属性名
- <setting>
- <typeAliases>:为Java类型命名别名(别名处理器)
- alias:别名
- type:实体类全类名
- 也可@Alias注解为其指定一个别名
- MyBatis已经为许多常见的 Java 类型内建了相应的类型别名。它们都是大写不敏感的,我们在起别名的时候千万不要占用已有的别名
- <typeHandlers>:类型处理器
- 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型
- <plugins>:插件
- 插件通过动态代理机制,可以介入四大对象的任何一个方法的执行
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
- <environments>:配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,该元素节点下可以配置多个environment子元素节点,但是必须指定其中一个默认运行环境(通过default指定)
- <environment>:配置MyBatis的一套运行环境,
- id:指定当前环境的唯一标识
- <transactionManager>:事务管理
- type:MyBatis有两种事物管理类型,即JDBC、MANAGED
- 设置其属性为JDBC,直接使用JDBC的提交和回滚功能,依赖于从数据源获得来连接来管理事务的生命周期
- MANAGED:不提交或回滚一个连接、让容器来管理事务的整个生命周期
- 可自定义事物管理类型:实现TransactionFactory接口,type=全名/别名
- type:MyBatis有两种事物管理类型,即JDBC、MANAGED
- <dataSource>:数据源(库)配置
- type:有三种数据源类型(UNPOOLED、POOLED、JNDI)
- UNPOOLED:不使用连接池
- POOLED:使用连接池
- JNDI: 在EJB 或应用服务器这类容器中查找指定的数据源
- 自定义:实现DataSourceFactory接口,定义数据源的获取方式
- <property>
- name属性:属性名
- value属性:属性值
- type:有三种数据源类型(UNPOOLED、POOLED、JNDI)
- <environment>:配置MyBatis的一套运行环境,
- <databaseIdProvider>:根据不同的数据库厂商执行不同的语句
- type:DB_VENDOR //使用MyBatis提供的VendorDatabaseIdProvider解析数据库厂商标识。也可以实现DatabaseIdProvider接口来自定义
- 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进行设置。由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短
- Property-name:数据库厂商标识
- Property-value:为标识起一个别名,方便SQL语句使用databaseId属性引用
- 匹配规则
- 如果没有配置databaseIdProvider标签,那么databaseId=null
- 如果配置了databaseIdProvider标签,使用标签配置的name去匹配数据库信息,匹配上设置databaseId=配置指定的值,否则依旧为null
- 如果databaseId不为null,他只会找到配置databaseId的sql语句
- MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库databaseId 属性的所有语句。如果同时找到带有 databaseId 和不带databaseId 的相同语句,则后者会被舍弃
- type:DB_VENDOR //使用MyBatis提供的VendorDatabaseIdProvider解析数据库厂商标识。也可以实现DatabaseIdProvider接口来自定义
- <mappers>:映射器作用是告诉MyBatis去哪里找到SQL映射文件(该文件内容是开发者定义的映射SQL语句),整个项目可以有一个或多个SQL映射文件
- <mapper/>:获取SQL映射文件
- resource:使用类资源路径获取资源
- class:使用全类名获取,要求接口和映射文件同包同名
- url:使用URL获取资源
- <package>:批量获取映射文件
- name:文件所在包名 //要求接口和映射文件同包同名
- <mapper/>:获取SQL映射文件
SQL映射文件(mapper.xml)
- 映射文件指导着MyBatis如何进行数据库增删改查
- <mapper> //映射文件的根元素节点,只有一个属性namespace
- namespace:用于区分不同的mapper,全局唯一,绑定DAO接口,即面向接口编程。当namespace绑定某一接口之后(一个接口对应一个映射文件),可以不用写该接口的实现类,MyBatis会通过接口的完整限定名查找到对应的mapper配置来执行SQL语句,因此namespace的属性值必须要和接口同名
- <insert> //映射插入语句 <update> //映射更新语句 <delete> //映射删除语句
- id:命名空间的唯一识别符,可以被用来引用这条语句,与接口中的方法名一致
- parameterType:参数类型,可不传MyBatis会根据TypeHandler自动推断
- flushCache:有语句被调用,会导致本地缓存和二级缓存被清空,默认为true
- timeout:在抛出异常前,驱动程序等待数据库返回请求结果的秒数,默认为unset
- statementType:默认为PREPARED映射类型,也可设置为STATEMENT映射类型,CALLABLE映射类型
- useGeneratedKeys:MyBatis 使用 JDBC 中的 getGeneratedKeys 方法来取出由数据库内部生成的主键(MySQL),默认为false,delete中无效
- keyProperty:指定能够唯一识别对象的属性,MyBatis通过getGeneratedKeys方法的返回值或insert语句的selectKey子元素设置它的键值,默认为unset,delete中无效
- keyColumn:通过生成的键值设置表中列名,当主键列不是表中的第一列时需设置,在某些数据库(PostgreSQL)是必须的,delete中无效
- databaseId:与<databaseIdProvider>的value值对应,若配置了databaseIdProvider,MyBatis会加载所有不带databaseId或匹配当前databaseId的语句,如果带或者不带都有,则不带的会被忽略
- <selectKey> //为语句传参
- KeyProperty:selectKey语句结果应该被设置的目标属性
- KeyColumn:匹配属性的返回结果集中的列名称
- resultType:结果的类型
- order:设置为BEFORE,表示先执行selectKey中的语句,设置为AFTER,表示先执行外层语句
- statementType:默认为PREPARED映射类型,也可设置为STATEMENT映射类型,CALLABLE映射类型
- #{key}:获取参数的值,预编译到SQL中,安全,类似JDBC的PreparedStatement可防止SQL注入,返回字符串带引号 ‘’
- ${key}:获取参数的值,拼接到SQL中,有SQL注入问题
- <select> //映射查询语句,标签内些查询语句
- id:命名空间的唯一识别符,可以被用来引用这条语句
- parameterType:参数类型,可不传MyBatis会根据TypeHandler自动推断
- resultType:返回值类型,resultType和resultMap不能同时存在,二取其一
- resultMap:对外部resultMap的引用,对应外部resultMap的id,表示返回结果映射到哪一个resultMap上,它的应用场景一般是数据库字段与对象属性不一致或者需要做复杂的联合查询以便自由控制映射结果
- flushCache:表示任何时候语句被调用,都不会去清空本地缓存和二级缓存,默认为false
- useCache:是否开启二级缓存,默认为true
- timeout:在抛出异常前,驱动程序等待数据库返回请求结果的秒数,默认为unset
- fetchSize:给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值,默认为unset(依赖驱动)
- statementType:默认为PREPARED映射类型,也可设置为STATEMENT映射类型,CALLABLE映射类型
- resultSetType:FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)
- databaseId:与上一个标签databaseId意义相同
- resultOrdered:这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
- resultSets:这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔
- <include> //导入<sql>中的SQL块
- refid:<sql>的id名
- <sql> //可以重用的SQL块,被其他语句引用(include来引用)
- id:引用标识
- 可为sql语句片段或完整语句,也可为动态sql语句
- <resultMap> //用来描述数据库结果集和对象的对应关系
- id:<select>中resultMap的id名称
- type:需要映射到的类对象
- 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)
- <constructor> //将查询结果作为参数注入到实例的构造方法中
- <idArg/> //标记结果作为 ID
- <arg/> //标记结果作为普通参数
- <id> //设置主键(可多个)
- column:数据表的列,使用{key1=column1,key2=column2…}的形式传递多列数据
- property:将column属性指定的列结果映射到对象的哪个属性
- javaType:实体类中字段类型
- jdbcType:表中字段类型
- <result> //配置映射关系
- column:数据表的列
- property:将column属性指定的列结果映射到对象的哪个属性
- javaType:实体类中字段类型
- jdbcType:表中字段类型
- <association> //处理查询结果中关联其他对象的情况,映射JavaBean的某个复杂类型的属性
- fetchType:eager立即加载/lazy延迟加载
- columnPrefix:祛除查询结果内列名的指定前缀
- javaType:完整的Java类名或者别名,若映射带一个JavaBean,则MyBatis通常会自行检测到其他类型,若映射到一个HashMap,则应该明确指定javaType
- 嵌套查询方式(分段查询) //指的是通过执行另外一个 SQL 映射语句来返回所关联的对象类型
- column:指定数据表的列
- property:将column属性指定的列结果映射到对象的哪个属性
- select:调用<select>查询当前属性的值
- 嵌入结果集方式 //指的是通过联表查询将所需的所有字段内容先查询出来,再通过级联属性映射来创建复杂类型的结果对象
- property:映射到列结果的字段或属性
- javaType:完整的Java类名或者别名
- resultMap:引用外部resultMap
- <id> //设置主键
- <result> //配置映射关系
- <collection> //处理查询结果中关联其他对象的情况,返回复杂类型的集
- fetchType=eager立即加载/lazy延迟加载
- 嵌套查询方式(分步查询)
- column:指定数据表的列
- property:映射到列结果的字段或属性
- select:调用目标的方法查询当前属性的值
- javaType:完整的Java类名或者别名
- 嵌入结果集方式
- column:指定数据表的列
- property:映射到列结果的字段或属性
- javaType:完整的Java类名或者别名
- ofType:完整的Java类名或者别名,即集合中包含的类型(可不写)
- <id> //设置主键
- <result> //配置映射关系
- <discriminator> 鉴别器//很像Java语言中的switch 语句,允许用户根据查询结果中指定字段的不同取值来执行不同的映射规则
- column:数据表的列
- <case> //基于某些值的结果映射
- value :查询结果中指定字段的不同取值
- resultMap:外部映射规则名
- resultType:内部写映射规则
- <cache> //配置给定命名空间的二级缓存
- eviction:缓存的回收策略
- LRU – 最近最少使用的:移除最长时间不被使用的对象(默认)
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
- flushInterval:缓存刷新间隔(缓存多长时间清除一次,默认不清空)毫秒为单位
- readOnly:缓存是否只读,默认false
- true:MyBatis认为所有从缓存中读取数据库的操作都是只读操作不会修改数据,为加快获取速度,直接会将数据在缓存中的引用交给用户
- MyBatis觉得获取的数据可修改,MyBatis会利用序列化&反序列的技术克隆一份新的数据给你
- size:缓存存放多少元素
- type:指定第三方或自定义缓存的全类名,常见第三方缓存EHCache,Redis,Memcache,需导入jar包与配置文件
- eviction:缓存的回收策略
- <cache-ref> //从其它命名空间引入缓存配置
- namespace:外部缓存全类名
主键生成方式
- 若数据库支持自动生成主键的字段,则可以设置useGeneratedKeys=”true”,然后再把keyProperty 设置到目标属性上
- 若数据库不支持自增型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用
参数处理
- 单个参数
- 可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可直接使用这个参数,不需要经过任何处理
- 多个参数
- 任意多个参数,都会被MyBatis重新包装成一个Map传入
- 接口中方法参数只能有一个
- parameterType=“Integer[]” 接收数组
- 使用@Param(“参数名”)为参数命名,key指定
- POJO
- 当这些参数属于我们业务POJO时,我们直接传递POJO
- #{}拓展用法:参数也可以指定一个特殊的数据类型
- #{property,javaType=double,jdbcType=NUMERIC,numericScale=2}
- 参数位置支持的属性:javaType、jdbcType、mode(存储过程)、numericScale、resultMap、typeHandler、jdbcTypeName、expression
- javaType:通常可以从参数对象中来去确定
- jdbcType:通常需要在某种特定的条件下被设置,在我们数据为null的时候,有些数据库可能不能识别myBatis对null的默认处理,比如Oracle
- numericScale:设置小数点后保留位数
- mode:属性允许指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像在获取输出参数时所期望的那样
- MyBatis两个内置参数
- _parameter:代表整个参数
- 单个参数:_parameter就是这个参数
- 多个参数:参数会被封装成一个map,_parameter就是代表这个map
- _databaseId:如果配置了DateBaseIdProvider标签,_databaseId代表当前数据库的别名
- _parameter:代表整个参数
动态SQL
- 动态 SQL是MyBatis强大特性之一。极大的简化我们拼装SQL的操作
- 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似
- MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作
- OGNL对象图导航语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。 类似于EL,SpEL等
- 访问对象属性:person.name
- 调用方法:person.getName()
- 调用静态属性/方法:@java.lang.Math @PI @java.util.UUID @randomUUID()
- 调用构造方法:new com.bean.Person(‘admin’).name
- 运算符:+,-*,/,%
- 逻辑运算符:in,not in,>,>=,<,<=,==,!=
- xml中特殊符号如”,>,<等这些都需要使用转义字符
特殊符号 转义字符
& &
< <
> >
" " //双引号
' ' //单引号
a<=b a <=b 或者 a <![CDATA[ <= ]]>b
a>=b a >=b 或者 a <![CDATA[ >= ]]>b
a!=b a <![CDATA[ <> ]]>b 或者 a <![CDATA[!= ]]>b
<where>
- 简化SQL中的where,如果有条件,条件的第一个and或者or会自动取消,如果无条件,where关键字会取消.
<set>
- 解决动态更新语句,
<update id="updateEmp">
update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
UPDATE_DATE = SYSDATE
<!--set语句之后一定别忘记加逗号-->
</set>
where id=#{id}
</update>
<if>
- 相当于Java中的if语句
<select id="getEmpsByConditionIf" resultType="com.mybatis.bean.Employee">
select * from tbl_employee
<where>
<!--
遇见特殊符号应写转义字符或写在 <![CDATA[ 和 ]]> 区间内(语句前加空格)
-->
<if test="id=='0'.toString()"><!--转换-->
and id=#{id}
</if>
<if test="lastName!=null && lastName!=""">
and last_name like #{lastName}
</if>
<if test="email!=null and email.trim()!=""">
and email=#{email}
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
and gender=#{gender}
</if>
</where>
</select>
<choose>,<when>,<otherwise>
- 相当于Java中的switch语句
<select id="getEmpsByConditionChoose" resultType="com.mybatis.bean.Employee">
select * from tbl_employee
<where>
<!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 -->
<choose>
<when test="id!=null">
id=#{id}
</when>
<when test="lastName!=null">
last_name like #{lastName}
</when>
<when test="email!=null">
email = #{email}
</when>
<otherwise>
gender = 0
</otherwise>
</choose>
</where>
</select>
<trim>
- 可以灵活的去除多余的关键字 也可添加
<select id="getEmpsByConditionTrim" resultType="com.mybatis.bean.Employee">
select * from tbl_employee
<!-- 后面多出的and或者or where标签不能解决
prefix="":前缀:trim标签体中是整个字符串拼串后的结果。
prefix给拼串后的整个字符串加一个前缀
prefixOverrides="":
前缀覆盖: 去掉整个字符串前面多余的字符
suffix="":后缀
suffix给拼串后的整个字符串加一个后缀
suffixOverrides=""
后缀覆盖:去掉整个字符串后面多余的字符
-->
<!-- 自定义字符串的截取规则 -->
<trim prefix="where" suffixOverrides="and">
<if test="id!=null">
id=#{id} and
</if>
<if test="lastName!=null && lastName!=""">
last_name like #{lastName} and
</if>
<if test="email!=null and email.trim()!=""">
email=#{email} and
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</trim>
</select>
<foreach>
- 迭代一个集合,通常用于in条件
<select id="getEmpsByConditionForeach" resultType="com.mybatis.bean.Employee">
select * from tbl_employee
<!--
collection:指定要遍历的集合:list类型的参数会特殊处理封装在map中,map的key就叫list
item:将当前遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引。遍历list的时候是index就是索引,item就是当前值
遍历map的时候index表示的就是map的key,item就是map的值
#{变量名}就能取出变量的值也就是当前遍历出的元素
-->
<foreach collection="ids" item="item_id" separator="," open="where id in(" close=")">
#{item_id}
</foreach>
</select>
示例:
List<ForDownZip> oneDownFun(List<String> bizKeysList);
<foreach collection="list" item="bizKeysList" open="(" separator="," close=")">
#{bizKeysList}
</foreach>
List<ForDownZip> oneDownFun(@Param("comCodeList") List<String> comCodeList);
<foreach collection="comCodeList" item="comCode" open="(" separator="," close=")">
#{comCode}
</foreach>
<bind>
- 可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
<select id="getEmpsTestInnerParameter" resultType="com.mybatis.bean.Employee">
<bind name="_lastName" value="'%'+lastName+'%'"/>
select * from emp where last_name like #{_lastName}
</select>
OGNL表达式:
e1 or e2
e1 and e2
e1 == e2,e1 eq e2
e1 != e2,e1 neq e2
e1 lt e2:小于
e1 lte e2:小于等于,其他gt(大于),gte(大于等于) 7 e1 in e2
e1 in e2
e1 not in e2
e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
!e,not e:非,求反
e.method(args)调用对象方法
e.property对象属性值
e1[ e2 ]按索引取值,List,数组和Map
@class@method(args)调用类的静态方法
@class@field调用类的静态字段值
<sql>
<!--
sql片段 解决SQL中重复的代码冗余,可以提取出来放在sql片段中
1. <sql> 定义sql片段
id 唯一标识
2. <include> 在SQL中引用SQL片段片段
refid 需要引用的SQL片段的id
<property 声明变量, 就可以在SQL片段中动态调用,让不同的SQL调用同一个SQL片段达到不同的功能
name 变量名
value 变量值
一般情况使用${}在sql片段中引用.一单引用了,一定保证每个include都声明了该变量
-->
<sql id="selectEmp">
select ${columns} from emp
</sql>
<select id="QueryEmp4" resultType="Emp">
<include refid="selectEmp">
<property name="columns" value="*"></property>
</include>
</select>
缓存机制
- MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制,缓存可以极大的提升查询效率
- MyBatis系统中默认定义了两级缓存
- 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启
- 二级缓存需要手动开启和配置,它是基于namespace级别的缓存,如果对表的操作查询可能有牵扯到多个namespace,那么得到的数据就可能是错误的
- 为了提高扩展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存
- 一级缓存
- 一级缓存(local cache)即本地缓存, 作用域默认为sqlSession
- 当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空
- 当表数据进行DML操作的时,缓存数据也会清空
- 一级缓存不能被关闭, 但可以调用 sqlSession.clearCache() 来清空本地缓存, 或者改变缓存的作用域
- 不同的sqlSession具有不同的缓存区域,即不同作用域的缓存互不影响
- 在mybatis3.1之后, 可以配置本地缓存的作用域
- localCacheScope:默认为SESSION,缓存一个会话下执行的所有查询,设置为STATEMENT,可以禁用一级缓存
- 二级缓存
- 二级缓存(second level cache)即全局作用域缓存,二级缓存默认不开启,需要手动配置
- 二级缓存要求POJO实现Serializable接口
- 二级缓存在 SqlSession 关闭或提交之后才会生效
- 不同 SqlSession 之间,同一个Mapper数据共享
- 打开二级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings> - 工作机制
- 一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果会话关闭,一级缓存中的数据就会被保存在二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容
- 不同namespace查春的数据会放在自己对应的map(缓存)中 查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭之后,一级缓存的数据才会转移到二级缓存中
- 缓存命中率
- 命中率的取值范围 [0.1),衡量查询性能的重要参数
- 命中率=命中个数/(命中的数+脱靶的数)
- 用了缓存后会增加查询的效率,但是会降低增删改的效率
- 第一次查询:无缓存,脱靶+1,总数+1、第二次查询:有缓存,命中+1,总数+1
MyBatis-Spring整合
- 整合关键配置:将MyBatis-config.xml的配置整合到Spring的配置文件中
<context:property-placeholder location="classpath:conf/*.properties" system-properties-mode="NEVER"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${mysql.driverName}"></property>
<property name="url" value="${mysql.url}"></property>
<property name="username" value="${mysql.username}"></property>
<property name="password" value="${mysql.password}"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定mybatis全局配置文件位置 -->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
<!--指定数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!--mapperLocations:所有sql映射文件所在的位置 -->
<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
<!--typeAliasesPackage:批量别名处理-->
<property name="typeAliasesPackage" value="com.gc.bean"></property>
</bean>
<!--自动的扫描所有的mapper的实现并加入到ioc容器中 -->
<bean id="configure" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 调用的sqlSessionFactory,这里是String类型,可省略 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!– basePackage:指定包下所有的mapper接口实现自动扫描并加入到ioc容器中 -->
<property name="basePackage" value="com.gc.dao"></property>
</bean>
MyBatis-逆向工程
- MyBatis Generator简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增删改查,以及QBC风格的条件查询。但多表连接、存储过程等这些复杂sql的定义需要我们手工编写
- 使用步骤
- 编写MBG的配置文件
- <jdbcConnection> 配置数据库连接信息
- <javaModelGenerator> 配置javaBean的生成策略
- <sqlMapGenerator> 配置sql映射文件生成策略
- <javaClientGenerator> 配置Mapper接口的生成策略
- <table> 配置要逆向解析的数据表
- 运行代码生成器生成代码
- 注:Context标签
- targetRuntime=“MyBatis3“可以生成带条件的增删改查
- targetRuntime=“MyBatis3Simple“可以生成基本的增删改查
- 如果再次生成,建议将之前生成的数据删除,避免xml向后追加内容出现的问题
- 编写MBG的配置文件
- MBG配置文件
<?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>
<context id="Tables" targetRuntime="MyBatis">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
// 数据库连接信息配置
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/test" userId="root" password="123456">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
// javaBean的生成策略
<javaModelGenerator targetPackage="com.gc.bean" targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="true" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
// 映射文件的生成策略
<sqlMapGenerator targetPackage="com.gc.mapper" targetProject=".\conf">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
// mapper接口生成策略
<javaClientGenerator type="XMLMAPPER" targetPackage="com.gc.mapper" targetProject=".\src">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
//数据表与javaBean的映射
<table tableName="books" domainObjectName="Book"></table>
<table tableName="dept" schema="" enableCountByExample="false"
enableDeleteByExample="false" enableUpdateByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>
- 生成器代码
public static void main(String[] args) 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);
}
- 测试
@Test
public void test01(){
SqlSession openSession = build.openSession();
DeptMapper mapper = openSession.getMapper(DeptMapper.class);
DeptExample example = new DeptExample();
//所有的条件都在example中封装
Criteria criteria = example.createCriteria();
//select id, deptName, locAdd from tbl_dept WHERE
//( deptName like ? and id > ? )
criteria.andDeptnameLike("%部%");
criteria.andIdGreaterThan(2);
List<Dept> list = mapper.selectByExample(example);
for (Dept dept : list) {
System.out.println(dept);
} }
MyBatis-插件开发
-
MyBatis在四大对象的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果
-
MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用
-
默认情况下,MyBatis 允许使用插件来拦截的方法调用包括
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
-
pageHelper分页插件
- 导包(jsqlparser-1.1.jar,pagehelper-5.1.2.jar)
- 在全局配置文件中注册
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 针对的数据库 --> <property name="helperDialect" value="mysql"/> <!-- 分页合理化 低于1页面,高于最后一页如何处理 --> <property name="reasonable" value="true"/> <!-- 查询没有数据时的处理 --> <property name="pageSizeZero" value="true"/> </plugin> </plugins>- PageHelper.startPage(curPage, pageSize); 放在查询之前,PageInfo info=new PageInfo(list);放在查询之后,不能有其他语句
-
开发步骤
- 编写插件实现Interceptor接口,并使用@Intercepts注解完成插件签名
@Intercepts({ @Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class) }) public class MyFirstPlugin implements Interceptor{}- 在全局配置文件中注册插件
<plugins> <plugin interceptor="com.mybatis.dao.MyFirstPlugin"> <property name="username" value="root"/> <property name="password" value="123456"/> </plugin> <plugin interceptor="com.atguigu.mybatis.dao.MySecondPlugin"> </plugin> </plugins>- 插件原理
- 按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理
- 多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链
- 目标方法执行时依次从外到内执行插件的intercept方法
- 多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值
- Interceptor接口
- public Object intercept(Invocation invocation) throws Throwable //拦截目标方法执行
- public Object plugin(Object target) //生成动态代理对象,可以使用MyBatis提 供的Plugin类的wrap方法
- public void setProperties(Properties properties) //注入插件配置时设置的属性
- 从代理链中分离真实被代理对象
//1、分离代理对象。由于会形成多次代理,所以需要通过一个while 循环分离出最终被代对象,从而方便提取信息 MetaObject metaObject = SystemMetaObject.forObject(target); while (metaObject.hasGetter("h")) { Object h = metaObject.getValue("h"); metaObject = SystemMetaObject.forObject(h); } //2、获取到代理对象中包含的被代理的真实对象 Object obj = metaObject.getValue("target"); //3、获取被代理对象的MetaObject方便进行信息提取 MetaObject forObject = SystemMetaObject.forObject(obj);
本文深入探讨MyBatis框架的核心概念,包括ORM映射、核心接口、配置与使用方式,以及动态SQL、缓存机制、插件开发等内容,旨在帮助读者全面理解MyBatis的工作原理。

701

被折叠的 条评论
为什么被折叠?



