Mybatis
概念
框架
- 是软件开发过程中的一套解决方案,不同的框架解决不同的问题
- 优点:框架封装了很多细节,使开发者可以使用极简的方式实现功能,大大提高开发效率
三层架构
- 表现层:用于展示数据,Spring MVC
- 业务层:处理业务需求,Spring
- 持久层:操作数据库,Mybatis
持久层技术解决方案
JDBC技术:
- Connection
- PreparedStatement
- ResultSet
Spring的JdbcTemplate:Spring中对JDBC的简单封装
Apache的DBUtils:对JDBC的简单封装
JDBC是规范,以上这些只是工具类
Mybatis
Mybatis是一个优秀的基于java的持久层框架,它内部封装了JDBC,使开发者只需要关注SQL语句本身,而不需要花费精力去加载驱动、创建连接、创建Statement等复杂的过程。
Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由Mybatis框架执行SQL并将结果映射为Java对象并返回
采用 ORM 思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
ORM思想:Object Relational Mapping 对象关系映射,把数据库表和实体类及实体类的属性对应起来,通过操作实体类完成数据库表操作
Mybatis入门
Mybatis环境搭建
第一步:创建maven工程并导入坐标
1
2
3
4
5
6<!--Mybatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>第二步:创建实体类和dao的接口
第三步:创建Mybatis的主配置文件
SqlMapConfig.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30"1.0" encoding="UTF-8" xml version=
<!-- Mybatis的主配置文件 -->
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置Mysql的环境 -->
<environment id="mysql">
<!-- 配置事务的类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/nogizaka/dao/UserDao.xml"></mapper>
</mappers>
</configuration>第四步:创建映射配置文件
UserDao.xml
1
2
3
4
5
6
7
8
9
10
11
12
13"1.0" encoding="UTF-8" xml version=
<mapper namespace="com.nogizaka.dao.UserDao">
<!-- 配置查询所有 -->
<!-- id 对应 查询方法名称-->
<!-- resultType 对应 查询结果封装的对象-->
<select id="findAll" resultType="com.nogizaka.domain.User">
select * from user;
</select>
</mapper>
注意
- 在Mybatis中把持久层的操作接口名称和映射文件也叫做:Mapper
- Mybatis的映射配置文件位置必须和dao接口的包结构相同
- 映射配置文件的mapper标签
namespace
属性的取值**必须是dao接口的全限定类名,不能使用别名 - 映射配置文件的操作配置(select),id属性取值必须是dao接口的方法名
创建步骤
- 第一步: 读取配置文件
- 第二步: 创建SqlSessionFactory工厂
- 第三步: 使用工厂生产SqlSession对象
- 第四步: 使用SqlSession创建Dao接口的代理对象
- 第五步: 使用代理对象执行方法
- 第六步: 释放资源
1 | // 1. 读取配置文件 |
注意
(第一步)读取配置文件:直接写绝对路径和相对路径都不合适,一般采用以下两种方式
- 使用类加载器,它只能读取类路径的配置文件
- 使用ServletContext对象的getRealPath()获取全局路径
(第二步)创建SqlSessionFactory:使用了构建者模式
构建者模式:把对象的创建细节隐藏,使用者直接调用方法即可拿到对象
(第三步)使用工厂生产SqlSession对象:使用了工厂模式
工厂模式:降低类之间的依赖关系,降低耦合度
(第四步)使用SqlSession对象创建Dao的代理对象:使用了代理模式
代理模式:在不修改源码的基础上,对已有方法进行增强
不要忘记在映射配置中告知mybatis要封装到哪个实体类中
- 配置的方式:在
resultType
属性指定实体类的全限定类名
- 配置的方式:在
使用注解
- 在Dao接口的方法上,使用
@Select("..")
注解,并且指定执行SQL语句 - 同时需要在
SqlMapConfig.xml
中的mapper配置中,使用class属性
指定Dao接口
的全限定类名
CRUD
1 | <mapper namespace="com.nogizaka.dao.UserDao"> |
#{}与${}的区别
#{}表示一个占位符号
通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,#{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类型值,#{}括号中可以是 value 或其它名称。
${}表示拼接 sql 串
通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}括号中只能是 value。
获取新增用户id的返回值
使用
<selectKEy>
1
2
3
4
5
6
7
8<!--插入数据-->
<insert id="addUser" parameterType="com.nogizaka.domain.User">
<!-- 配置插入操作后,获取插入数据的id值-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username,address,sex,birthday) values (#{username},#{address},#{sex},#{birthday});
</insert>
arameterType
- Mybatis使用OGNL表达式解析对象字段的值,#{}或者${}中为Pojo的属性名称
OGNL表达式:Object Graphic Navigation Language 对象导航图语言
通过对象的取值方法来获取数据,在写法上把get和省略了
- 类中获取名称:user.getUsername()
- OGNL中获取名称:user.username
mybatis在parameterType中已经提供了属性所属的类,所以此时不需要写对象名
标签中的属性
Select标签中的属性
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap | 这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。 |
resultType | 从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。 |
resultMap | 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
useCache | 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。 |
fetchSize | 这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为未设置(unset)(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 中的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖驱动)。 |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。 |
resultSets | 这个设置仅对多结果集的情况适用。它将列出语句执行后返回的结果集并给每个结果集一个名称,名称是逗号分隔的。 |
Insert, Update, Delete 标签的属性
属性 | 描述 | |
---|---|---|
id | 命名空间中的唯一标识符,可被用来代表这条语句。 | |
parameterType | 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset) | 。 |
parameterMap | 这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。 | |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。 | |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。 | |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 | |
useGeneratedKeys | (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 | |
keyProperty | (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认值:未设置(unset)。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 | |
keyColumn | (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望使用多个生成的列,也可以设置为逗号分隔的属性名称列表。 | |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
ResultMap
- 查询的列名和实体类的属性名称不一致:
- 方法一:使用sql语言中的起别名,如
Select id as userId
,(执行效率高) - 方法二:
resultMap
标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。(开发效率高)
- 方法一:使用sql语言中的起别名,如
- 在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。
1 | <resultMap id="userMap" type="com.nogizaka.domain.User"> |
SqlMapConfig中的配置
SqlMapConfig.xml配置内容和顺序
1 | -properties(属性) |
properties属性
- properties属性:
- 可以在标签内部配置连接数据库的信息,也可通过属性引用外部配置文件信息
- resource属性: 用于指定配置文件的位置,是按照类路径的写法,并且必须存在于类路径下
- url属性;要求按照url的写法来写地址:协议 主机 端口 URI
- 可以在标签内部配置连接数据库的信息,也可通过属性引用外部配置文件信息
1 | <!--方法一:标签内部配置连接数据库的信息--> |
typeAliases属性
- typeAliases必须按照configuration的配置顺序进行配置
配置domain中类的别名
- type属性:指定是实体类全限定类名
- alias属性:指定别名,指定别名后,不区分大小写
package :用于指定要配置别名的包,当指定后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写
mapper中的
package
标签是用于指定dao接口所在的包,当指定了之后就不需要再写mapper已经resource或者class属性了
- 配置别名后,domain中的实体类都可以用简写,不区分大小写
1
2
3<typeAliases>
<package name="com.nogi.domain"/>
</typeAliases>
mappers(映射器)
<mapper resource=" " />
- 使用相对于类路径的资源如:
<mapper resource="com/nogi/dao/UserDao.xml" />
- 使用相对于类路径的资源如:
<mapper class=" " />
- 使用 mapper 接口类路径如:
<mapper class="com.nogi.dao.UserDao"/>
- 注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
- 使用 mapper 接口类路径如:
<package name=""/>
- 注册指定包下的所有 mapper 接口如:
<package name="com.nogi.mybatis.mapper"/>
- 注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
- 注册指定包下的所有 mapper 接口如:
1 | <!--写法不同效果相同--> |
Mybatis连接池
- mybatis连接池提供了3种方式的配置
- 配置的位置:主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式
- type属性的取值:
- POOLED :采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
- UNPOOLED :采用传统的获取连接的方式,虽然也实现javax.sql.DataSource接口,但是并没用使用池的思想
- JNDI :采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样的。如果不是web或者maven的war工程,是不能使用,tomcat采用dbcp连接池
Mybatis事务控制
Mybatis中的事务提交:SqlSession对象的
commit()
方法Mybatis中的事务回滚:SqlSession对象的
rollback()
方法设置自动提交:SqlSessionFactory对象的
openSession(boolean autocommit)
Mybatis基于XML配置的动态SQL语句
if标签
- 原理:sql语句的拼接
- test属性:判断条件
1 | <!--根据条件查询--> |
where标签
- 简化上面 where 1=1 的条件拼装,我们可以采用
<where>
标签来简化开发
1 | <select id="findByProp" resultMap="userMap" parameterType="USER"> |
foreach标签
<foreach>
标签用于遍历集合,它的属性:- collection:代表要遍历的集合元素,注意编写时不要写#{}
- open:代表语句的开始部分
- close:代表结束部分
- item:代表遍历集合的每个元素,生成的变量名
- sperator:代表分隔符
SQL语句:
SELECT 字段 FROM 表名 WHERE id IN (?)
1 | <!--传入多个 id 查询用户信息,根据一组条件集合查询--> |
Mybatis多表操作
ResultMap
1 | <!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性--> |
- 一对多
1 | <mapper namespace="com.asuka.dao.UserDao"> |
JNDI
JNDI:Java Naming and Directory Interface。是SUN公司推出的一套规范,属于JavaEE技术之一。目的是模仿windows系统中的注册表。
在服务器中注册数据源:
- 在webapp目录下创建META-INF文件夹
- 创建context.xml文件
- 修改SqlMapConfig中的数据源
1 |
|
Mybatis延迟加载
- 概念:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
- 优点:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快
- 缺点:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
- 多对一、一对一:直接加载
- 多对多、一对多:延迟加载
使用步骤
在SqlMapConfig配置文件中加入
<settings>标签
1
2
3
4
5
6<settings>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认为false-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--任一方法的调用都会加载该对象的所有延迟加载属性,3.4以后默认为false-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>将原始的关联查询代码改为单表查询
- 更改关联查询的resultMap内的映射association标签加入懒加载需要的查询条件和sql语句statementId原始resultMap映射
Mybatis缓存
一级缓存
- 指的是Mybatis中SqlSession对象的缓存。
- 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块Map结构的区域。当我们在此查询同样的数据,mybatis会优先去数据库查询是否存在。
- 一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
二级缓存
二级缓存指的是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession对象共享
使用步骤:
第一步:在 SqlMapConfig.xml 文件开启二级缓存:
<setting name="cacheEnabled" value="true"/>
第二步:配置相关的 Mapper 映射文件:
<cache/>
第三步:配置 statement 上面的 useCache 属性:
<select id="findAll" resultMap="userMapAccount" useCache="true">
注意:
将 UserDao.xml 映射文件中的
<select>
标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。
当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。
Mybatis注解开发
- 常用注解
- @Insert:实现新增
- @Update:实现更新
- @Delete:实现删除
- @Select:实现查询
- @Result:实现结果集封装
- @Results:可以与@Result 一起使用,封装多个结果集
- @ResultMap:实现引用@Results 定义的封装
- @One:实现一对一结果集封装
- @Many:实现一对多结果集封装
- @SelectProvider: 实现动态 SQL 映射
- @CacheNamespace:实现注解二级缓存的使用
@Results 注解
- 代替的是标签
<resultMap>
- 该注解中可以使用单个@Result 注解,也可以使用@Result 集合@Results({@Result(),@Result()})或@Results(@Result())
- 代替的是标签
@Resutl 注解
- 代替了
<id>
标签和<result>
标签@Result 中 属性介绍:id 是否是主键字段column 数据库的列名property 需要装配的属性名one 需要使用的@One 注解(@Result(one=@One)()))many 需要使用的@Many 注解(@Result(many=@many)()))
- 代替了
@One 注解(一对一)
- 代替了
<assocation>
标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。 - @One 注解属性介绍:
select
:指定用来多表查询的 sqlmapperfetchType
: 会覆盖全局的配置参数 lazyLoadingEnabled。
- 使用格式:
@Result(column=" ",property="",one=@One(select=""))
- 代替了
@Many 注解(多对一)
- 代替了
<Collection>
标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。 - 注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType一般为 ArrayList)但是注解中可以不定义;
- 使用格式:
@Result(property="",column="",many=@Many(select=""))
- 代替了