一、MyBatis连接池及事务控制
1、连接池
在实际开发中,都会使用连接池,因为它可以减少获取连接缩消耗的时间。所谓连接池,就是存储数据库连接的容器。连接池中存储一定数量的数据库连接,当线程需要使用到连接时,就从连接池中获取数据库连接,线程使用完数据库连接会还回连接池中。
连接池:其实就是一个集合对象,该集合必须是线程安全的;而且线程必须实现对列,满足 ” 先进先出 “ 的特性。
2、MyBatis中的连接池
三种配置方式
配置位置:MyBatis主配置文件中,<dataSource>
元素;type属性的值表示采用何种连接池;
POOLED:采用传统的
javax.sql.DataSource
接口规范中的连接池,MyBatis中有该规范的实现;UNPOOLED:采用传统获取数据库连接的方式,虽然实现了
javax.sql.DataSource
接口,但没有连接池的思想——即需要时才创建数据库连接;-
JNDI:采用服务器提供的 JNDI 技术实现,来获取 DataSource 对象,不同服务器的拿到的 DataSource 对象是不同的;
注意:不是 web 或 maven 工程,是不能使用的。Tomcat 服务器中,采用连接池是 dbcp 连接池。
3、事务
什么是事务?
事物的四大特性ACID
不考虑隔离性会产生的3个问题
解决方法:四种隔离级别
MyBatis 中的事务控制默认没有开启自动提交,可以使用 SqlSessionFactory.opensession(boolean autoCommit)
方法创建设置是否开启自动提交事务的 Sqlsession
对象。但有一点需要注意的是:开启了自动提交事务,MyBatis是对一个数据库操作(并非多个)进行事务提交。没有开启自动提交事务时,是使用 SqlSession
对象的 commit 方法 和 rollback 方法进行事务的提交的回滚。
二、动态SQL语句
1、参数传递
-
单参数——定义传入参数的类型,可以是基本类型、引用类型、映射;
- 直接使用映射的 key 值:#{key},该 key 值可以不与方法的参数名匹配;
-
多参数——需要在接口类中的抽象方法的参数前使用注解
@Param("")
指定参数名;- 使用 #{索引}、#{param+数字} 指定参数;
- 在SQL语句中,使用 #{参数名} 、或 #{param+数字} 指定参数;
<!--接口方法:User findUser(String username, String password); --> <select id="findUser" resultType="*.user">
select * from user where username=#{0} and password=#{1}
</select><!--User findUser(@Param("username") String username, @Param("password") String password);--> <select id="findUser" resultType="user">
select * from user where username=#{username} and password=#{password} </select>
2、MyBatis标签
-
if 标签:用于进行条件判断, test 属性用于指定判断条件。为了拼接条件, 在 SQL 语句后强行添加 1=1 的恒成立条件
<select id="sel" resultType="user">
select * from user where 1=1
<if test="username != null and username != ''">
and username=#{username}
</if>
<if test="password != null and password != ''">
and password=#{password}
</if>
</select> -
choose(when otherwise)标签:这是一套标签, 功能类似于 switch...case...
<select id="sel" resultType="user">
select * from user
<where>
<choose>
<when test="username != null and username != ''">
and username = #{username}
</when>
<when test="password != null and password != ''">
and password = #{password}
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select> -
where/set标签
-
用于管理 where 子句,有如下功能:
- 如果没有条件, 不会生成 where 关键字;
- 如果有条件, 会自动添加 where 关键字;
- 如果第一个条件中有 and 关键字, 去除之;
<select id="sel" resultType="user">
select * from user
<where>
<if test="username != null and username != ''">
and username=#{username}
</if>
<if test="password != null and password != ''">
and password=#{password}
</if>
</where>
</select> -
用于维护 update 语句中的 set 子句,功能如下:
- 满足条件时, 会自动添加 set 关键字;
- 会去除 set 子句中多余的逗号;
- 不满足条件时, 不会生成 set 关键字;
<update id="updUser" parameterType="user">
update user
<set>
id=#{id},
<!-- 防止所有条件不成立时的语法错误 -->
<if test="username != null and username != ''">
username=#{username},
</if>
<if test="password != null and password != ''">
password=#{password},
</if>
</set>
where id=#{id}
</update>
-
-
trim() 标签:用于在前后添加或删除一些内容
- prefix:在前面添加内容;
- prefixOverrides:从前面去除内容;
- suffix:向后面添加内容;
- suffixOverrides:从后面去除内容;
<update id="updUser" parameterType="user">
update user
<!-- prefix: 前缀, 表示向前面添加内容
prefixOverrides: 从前面删除内容
suffix: 后缀, 表示向后面添加内容
suffixOverrides: 从后面删除内容 -->
<trim prefix="set" prefixOverrides="user" suffix="hahah" suffixOverrides=",">
username=#{username},
</trim>
where id=#{id}
</update> -
bind标签:用于对数据进行再加工。比如,模糊查询中对数据的拼接
<select id="sel" resultType="user">
select * from user
<where>
<if test="username!=null and username!=''">
<bind name="username" value="'%' + username + '%'"/>
and username like #{username}
</if>
</where>
</select> -
foreach标签:用于在 SQL 语句中遍历集合参数, 在 in 查询中使用
- collection: 待遍历的集合;
- open: 设置开始符号;
- item: 迭代变量;
- separator: 项目分隔符;
- close: 设置结束符号;
<select id="selIn" parameterType="list" resultType="user">
select * from user
<where>
<foreach collection="list" open="id in (" separator="," close=")" item="item">
#{item}
</foreach>
</where>
</select> -
sql/include标签:
<sql>
用于提取 SQL 语句,<include>
用于引用 SQL 语句<sql id="mySql">
select * from user
</sql> <select id="selIn" parameterType="list" resultType="user">
<include refid="mySql"/>
from t_user where id in
<foreach collection="list" open="(" separator="," close=")" item="item">
#{item}
</foreach>
</select>
三、MyBatis的延迟加载
问题:在一对多中,当我们有一个用户,它有100个账户。1)当查询用户时,要不要把关联的账户查出来?2)当查询账户时,要不要把关联的用户查出来?
一对多的关系中,Java 中是使用集合来存储多的一方,如果,仅仅只想查询用户信息,并不关心其账户的信息,这时,如果还将该用户下的所有账户查询出来,不仅影响效率,也占用内存。
什么是延时加载(懒加载)?——在真正使用数据时才发起查询,不使用时不查询,也就是:按需查询。
什么是立即加载?——不管用不用,只要一调用方法,就发起查询。
何时使用延时加载,何时使用立即加载?——一对多、多对多:通常采用延时加载;多对一,一对一:通常采用立即加载;
延时加载实现
在 mapping 配置文件中,配置 resultMap
元素,在需要使用延迟加载的对象使用 <association>
元素或 collection
元素关联;并通过以下属性,引用查询语句和传递查询条件,MyBatis会将查询结果封装到延迟加载的对象:
- select:查询语句;
- column:查询语句的条件;
但这还没有启用懒加载方式。需要启用懒加载方式,在主配置文件 <settings>
元素中配置以下 setting 子元素:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
- lazyLoadingEnabled:全局启用或禁用延迟加载。当禁用时, 所有关联对象都会即时加载。 默认值:(true|false)
- aggressiveLazyLoading:当启用时, 有延迟加载属性的对象在被调用时将会完全加载任意属性。否则, 每种属性将会按需要加载。 默认值:(true|false)
在没有启用延迟加载时,MyBatis会加载所有查询数据;启用延迟加载时,当延迟加载的对象被调用,才会加载;
四、MyBatis缓存机制
什么是缓冲?——存储在内存中的临时数据;
为什么使用缓冲?——减少和数据库的交互次数,提高执行效率;
什么数据能使用缓存,什么数据不能?
- 适用于缓存:经常查询且不常改变、数据的正确与否对最终结果影响不大;
- 不适用缓存:经常改变的数据、数据的正确与否对最终结果影响很大;比如:商品的库存,银行的汇率,股市的牌价等;
一级缓存和二级缓存
-
一级缓存
指的是 MyBatis 中 SqlSession 对象的缓存。
当执行查询之后,查询结果会同时存入到 SqlSession 提供的一个 Map 区域中;当再次查询相同的数据时,MyBatis 会先去 Map 区域中查找,没有找到在执行查询操作。当 SqlSession 对象消失或清空缓存操作时,一级缓存也就消失了。
如何保证数据库数据和一级缓存数据保持一致(数据同步)?
如果 sqlSession 执行 commit 操作(执行插入、更新、删除),会清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
-
二级缓存
指的是 MyBatis 中 SqlSessionFactory 对象的缓存,有同一个 SqlSessionFactory 对象创建并共享缓存。
二级缓存的使用步骤
-
让 MyBatis 支持二级缓存(在主配置文件中配置);
<settings>
<setting name="cacheEnabled" value="true"/>
</settings> -
让当前映射文件支持二级缓存(在 mapper 配置文件配置);
<cache/>
-
让当前操作支持二级缓存(select 元素中配置)
<select id="findAll"resultMap="user" useCache="true">
select * from class
</select>
二级缓存中存放的内容是数据,而不是对象。
-
五、注解开发
当使用注解开发时,相同包名下不能有 mapper 配置文件,会发生配置文件解析冲突错误。
1、增删改查
@Select()
@Delete()
@Update()
@Insert()
2、POJO类属性名与列名不一致
-
定义属性名与列名映射
@Results(id="UserMap",value={
@Result(id=true,column="",property=""),
@Result(column="",property=""),
@Result(column="",property=""),
}) -
引用映射,引用
@Results
中的 ID 值@ResultMap(value={"UserMap"})
3、多表查询
-
一对一
@Results(id="UserMap",value={
@Result(id=true,column="",property=""),
@Result(column="",property=""),
@Result(column="",property=""),
@Result(column="作为查询的列名",property="属性名",
one=@One(
select="全限定查询方法"
fetchType=FetchType.EAGER
))
})使用
@One
注解定义查询方法,根据 column 中的属性作为查询条件,返回一条数据;- select:查询语句(包名.类名.方法名);
- fetchType:加载方式;FetchType对象的
LAZY(延迟加载)、EAGER(立即加载)、DEFAULT
;
-
一对多
@Results(id="UserMap",value={
@Result(id=true,column="",property=""),
@Result(column="",property=""),
@Result(column="",property=""),
@Result(column="作为查询的列名",property="属性名",
many=@Many(
select="全限定查询方法"
fetchType=FetchType.LAZY
))
})
4、开启二级缓存
-
在主配置文件中配置开启二级缓存;
<settings>
<setting name="cacheEnabled" value="true"/>
</settings> 在接口类级别上使用
@CacheNamespace(blocking= true)
;