记录一下学习总结,方便以后遗忘查看 复习使用
先介绍一些概念。。。。。
MyBatis是一个开源的数据持久层框架。它内部封装了通过JDBC访问数据库的操作,支持普通的SQL查询,存储过程和高级映射。几乎消除了所有JDBC代码和参数的手工设置以及结果集的检索。
ORM(Object/Ralational Mapping)即对象/关系映射。是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,并且提供了一种机制,通过JavaBean对象去操作数据库表中的数据。
MyBatis就是ORM的解决方案。
MyBatis的三个基本要素:
核心接口和类;MyBatis核心配置文件(mybatis-config.xml);SQL映射文件(mapper.xml);每个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心,
首先获取SqlSessionFactorBuilder对象,可以根据XML配置文件或Configuration类的实例构建该对象。然后获取SqlSessionFactory对象,该对象实例可以通过SqlSessionFactoryBuilder对象来获得。有了SqlSessionFactory对象之后,就可以进而获取SqlSession实例,SqlSession对象中完全包含以数据库为背景的所有执行SQL操作的方法,可以用该实例来直接执行已映射的SQL语句。SqlSessionFactoryBuilder:
SqlSessionFactoryBuilder负责构建SqlSessionFactory,并且提供了多个build()方法的重载;配置信息可以以三种形式提供给SqlSessionFactoryBuilder的build()方法,InputStream(字节流)、Reader(字符流)、Configuration(类);SqlSessionFactoryBuilder的最大特点:用过即丢。一旦创建SqlSessionFactory对象之后,这个类就不需要存在了,因此SqlSessionFactoryBuilder的最佳范围就是存在于方法体内,也就是局部变量而已;SqlSessionFactory:
SqlSessionFactory简单的理解就是创建SqlSession的工厂。所有的MyBatis应用都是以SqlSessionFactory实例为核心,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder对象来获得。有了它之后,就可以用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作用域或者方法体作用域内。 SqlSession的两种使用方式: 1.通过SqlSession实例来直接执行已映射的SQL语句2.基于mapper接口方式操作数据mybatis-config.xml文件的元素节点是有一定顺序的,节点位置若不按照顺序排位,则XML会报错(这里写了大部分的节点,想了解更多,可以看下官方文档)。
建议:所用更改全局设置时或者使用区全局设置时,在核心配置文件里setting设置一下,即使它是默认的,避免更新造成一些设置失效。
一个接口对应一个映射文件
SQL映射文件是个XML文件,其中包含的元素如下: mapper:映射文件的根元素节点,只有一个属性namespace; namespace:用于区分不同的mapper,全局唯一。 绑定DAO接口,即面向接口编程。当namespace绑定某一接口之后,可以不用写该接口的实现类, MyBatis会通过接口的完整限定名查找到对应的mapper配置来执行SQL语句,因此namespace的命名必须要跟接口同名 cache:配置给定命名空间的缓存 cache-ref:从其它命名空间引入缓存配置 resultMap:用来描述数据库结果集和对象的对应关系 sql:可以重用的SQL块,也可以被其他语句引用 insert:映射插入语句 update:映射更新语句 delete:映射删除语句 select:映射查询语句在这里记录下mapper.xml查询方式
假设我有两张表 用户表(user) 和 用户性别表(usergender): 其中 user表有id、name、gender字段 gender是int类型 usergender表有 id gender字段
user表中有一下信息: id name gender 1 武大 0 2 李二 1 3 王三 0 4 刘四 1
usergender表中有如下信息: id gender 0 女 1 男
有如下信息:
<?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="cn.zd.dao.UserDao"> <!-- MyBatis会通过接口的完整限定名查找到对应的mapper配置来执行SQL语句,因此namespace的命名必须要跟接口同名 注意:不要忘记在核心配置文件(mybatis-config.xml)中配置mapper.xml文件 --> <!-- 实体类及接口不再列举 这里给出方法 --> <!-- 查询单个记录 --> <!-- User getUserById(Integer id); --> <select id="getUserById" resultType="cn.zd.entity.User" parameterType="integer"> SELECT * FROM `user` where `id` = #{id} </select> <!-- 查询多条记录 --> <!-- List<User> getUserList(); --> <select id="getUserList" resultType="cn.zd.entity.User"> SELECT * FROM `user` </select> <!-- 根据Id删除用户信息 --> <!-- int delUser(Integer id); --> <delete id="delUser"> delete from user where id = #{id} </delete> <resultMap id="userInfo" type="cn.zd.entity.User"> <id column="id" property="id" /> <result column="name" property="name" /> <result column="gender" property="gender" /> <association property="userGender" javaType="cn.zd.entity.UserGender"> <id column="ugid" property="id"/> <result column="ugg" property="gender" /> </association> </resultMap> <!-- 改造 List<User> getUser(); 使用连表查询 --> <!-- 注意mapper.xml文件同一命名空间id不可重复 这里只是记录 --> <select id="getUser" resultMap="userInfo"> SELECT u.id,u.name,u.gender,ug.id as ugid,ug.gender as ugg FROM `user` as u,`usergender` as ug where u.gender = ug.id </select> <!-- MyBatis实现批量保存: mysql设置数据库连接属性: allowMultiQueries:在一条语句中,允许使用";"来分隔多条查询(true/false) 默认为false --> //这种分号分割可以用于其他的批量操作 <insert id="add"> <foreach collection="list" item="user" separator=";"> insert into user(id,name) values(#{user.id},#{user.name}) </foreach> </insert> //推荐使用 <insert id="add"> insert into user(id,name) values <foreach collection="list" item="user" separator=","> (#{user.id},#{user.name}) </foreach> </insert> <!-- 属性: id:命名空间中的唯一表示符,可以被用来引用这条语句。 parameterType:表示查询传入参数的类型的完全限定名或别名。它支持基础数据类型和复杂数据类型。(这里未使用) resultType:直接返回结果类型,包括基础数据类型和复杂数据类型 resultMap:对外部resultMap的引用,对应外部resultMap的id,表示返回结果映射到哪一个resultMap上,它的应用场景一般是数据库字段与对象属性不一致或者需要做复杂的联合查询以便自由控制映射结果 resultType和resultMap不能同时存在,二取其一 resultMap的三个映射级别:(在MyBatis核心配置文件(mybatis-config.xml)中配置autoMappingBehavior) - NUNE:禁止自动匹配 - PARTIAL:默认。自动匹配所有属性,有内部嵌套的 (association,collection)的除外 - FULL:自动匹配所有 如果要传入多个参数:使用@Param为参数注解 - 若不使用,在传入多个参数的时候,则会报错,报错信息类似于Parameter'参数名'not found,因Mybatis的参数类型为Map,若使用@Param注解参数,那么就会记录指定的参数名为key,若参数前没有家@Param,那么就会使用"param"+他的序号作为map的key,所以进行多参数入参时,若没有使用@Param指定参数,那么就映射的SQL语句中获取不到#{参数名}而报错。 在mapper.xml中,不仅可以使用#{}获取传过的值 也可使用${}或取传过的值 ,本文后面会有详细介绍 association:映射JavaBean的某个复杂类型的属性,比如JavaBean类,即JavaBean内部嵌套一个复杂数据类型,这种情况就属于复杂类型的关联 //用于处理一对一的关联关系 属性:javaType:完整的Java类名或者别名,若映射带一个JavaBean,则MyBatis通常会自行检测到其他类型,若映射到一个HashMap,则应该明确 指定javaType; property:映射数据库列的实体对象的属性 collection:作用和association差不多,只不过返回的是一个集合(这里不再记录) 属性:ofType:完整的Java类名或者别名,即集合做包含的类型 property:映射数据库列的实体对象的属性 sql标签:可重用的sql片段,方便引用 //经常查询的列名 或者插入的列名 //include来引用 include还可以自定义一些property,sql标签内部就能使用自定义的属性 ${} 不能使用#{} <sql id=""></sql> --> </mapper>以上的mapper.xml只是列举了一些简单的查询 记录常用的属性
实现动态SQL所需的元素:
if:利用if实现简单的条件选择choose(when,otherwise):相当于Java中的switch语句where:简化SQL中的whereset:解决动态更新语句trim:可以灵活的去除多余的关键字 也可添加foreach:迭代一个集合,通常用于in条件//动态SQL并不复杂 这里不再赘述
MyBatis ${}和#{}
#{}:可以获取map中的值或者pojo对象属性的值 ${}:可以获取map中的值或者pojo对象属性的值
区别:
#{}:是以预编译的形式,将参数设置到sql语句中; 类似JDBC的 PreparedStatement;可防止SQL注入。
${}: 取出的值直接拼装在sql语句中
大多情况下,取参数的方式都应该使用#{}, 对于原生SQL不支持占位符的地方,就可以使用${}取值 比如:表名、排序。。。#{}拓展用法:
1.可以规定参数的一些规则: javaType、jdbcType、mode(存储过程)、numericScale、resultMap、typeHandler、jdbcTypeName、expression jdbcType:通常需要在某种特定的条件下被设置, 在我们数据为null的时候,有些数据库可能不能识别myBatis对null的默认处理, 比如Oracle(报错:jdbcType:OTHER 无效的类型),因为MyBatis对所有的null都映射的是原生jdbc OTHER类型 如果是Oracle,例:#{字段名,jdbcType=NULL}由于全局配置中:jdbcTypeForNull=OTHER,Oracle不支持
解决方案:#{字段名,jdbcType=NULL}
在maBaits中mybatis-config.xml中settings下配置
在这里顺便记录下MyBatis两个内置参数:
_parameter:代表整个参数 单个参数:_parameter就是这个参数 多个参数:参数会被封装成一个map,_parameter就是代表这个map_databaseId: 如果配置了DateBaseIdProvider标签,_databaseId代表当前数据库的别名。。。。缓存
MyBatis缓存机制:
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便的配置和定制,缓存可以极大的提高查询效率MyBatis系统默认定义了两级缓存
一级缓存和二级缓存
一级缓存是基于PerpetualCache(MyBatis自带)的HashMap本地缓存,作用范围是Session域内
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个sqlSession中,不同的sqlSession中的缓存是互相不能读取的。默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。一级缓存是一直开启的,SqlSession级别的一个map,数据库同一次会话期间查询到的数据会放在本地缓存中,以后如果需要相同的数据,直接从缓存中拿,没必要再次查询数据库 一级缓存失效情况: 1.SqlSession不同 2.SqlSession相同,查询条件不同 3.SqlSession相同,再次查询之jian执行了增删改操作 4.SqlSession相同,手动清除一级缓存二级缓存就是global caching,它超出session范围之外,可以被所有SqlSession共享
二级缓存(全局缓存)需要手动开启和配置,它是基于namespace级别的缓存 一个namespace对应一个二级缓存
工作机制: 1.一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中 2.如果会话关闭,一级缓存中的数据就会被保存在二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容 3.不同namespace查春的数据会放在自己对应的map(缓存)中 查出的数据都会被默认先放在一级缓存中,,只有会话提交或者关闭之后,一级缓存的数据才会转移到二级缓存中 使用: 1. 开启二级缓存 这是在核心配置文件里打开二级缓存的总开关 注意:仅仅只是开启, <setting name="cacheEnabled" value="true" /> 为false,会关闭二级缓存 不会关闭一级缓存 建议显式配置(显式的指定每个我们需要的更改配置的值,即使它是默认的,防止版本更新带来的问题) 2. 去mapper.xml中配置使用二级缓存 在xml文件中配置<cache></cache>标签 属性:eviction:缓存的回收策略 flushInterval:缓存刷新间隔(缓存多产时间清除一次,默认不清空) 毫秒为单位 readOnly:缓存是否只读 (true/false) 默认false; true:只读;MyBatis认为所有从缓存中读取数据库的操作都是只读操作不会修改数据,MyBatis为了加快获取获取速度,直接 会将数据在缓存中的引用交给用户 false:非只读;MyBatis觉得获取的数据可修改,MyBatis会利用序列化&反序列的技术克隆一份新的数据给你 size:缓存存放多少元素 type:指定自定义缓存的全类名 实现Cache接口 3.我们的POJO需要实现序列化接口 Serializable和缓存有关的设置/属性:
1.<setting name="cacheEnabled" value="true" /> 为false,会关闭二级缓存 不会关闭一级缓存 2.每个select标签都有useCache="true" false 不使用缓存 (一级缓存仍然使用,二级缓存关闭) 3.每个增删改标签都有一个flushCache="true" 执行完毕之后清除缓存(一级缓存清空,二级缓存也会清空) 查询标签有一个flushCache="false" 一旦为true 每次查询都会清空缓存 4.sqlSession.clearCache(); 只是清除当前session的一级缓存 5.MyBatis3.3之后有一个设置, localCacheScope:本地缓存作用域(一级缓存SESSION),当前会话的所有数数据都会保存在会话缓存中 STATEMENT:可以禁用一级缓存 6.为了提高扩展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存不能滥用二级缓存,二级缓存也有弊端,从MyBatis默认二级缓存是关闭的就可以看出来。二级缓存是建立在namespace下的,如果对表的操作查询可能有牵扯到多个namespace,那么得到的数据就可能是错误的。
在数据库我们查询员工工作表、和员工信息表,在查询员工信息的时候我们需要把员工对应的工作也查询出来,那么这个员工信息及工作是被二级缓存缓存在了员工信息对应的命名空间里的,这个时候要是有人来修改员工工作的岗位,是不会影响到员工信息的缓存,那么我们再次查询的时候,拿到的是之前缓存的数据,但是其实这个数据过时的,并不是我们真正需要的数据的。