MyBatis-用听得懂的话带你深入
文章内容输出来源:拉勾教育Java高薪训练营
Mybatis加载过程
-
SpringBoot启动时就开始加载数据库配置文件并初始化SqlSessionFactory 并交给Spring,如下简单实现:
String resource = "mybatis/conf/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//从 XML 中构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取session
SqlSession session = sqlSessionFactory.openSession();
try {
//获取Mapper
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(1L);
System.out.println(blog);
} finally {
session.close();
}
- 上个步骤实例化了SqlSessionFactory 已经初始化好了数据库配置信息 现在加入MyBatis依赖信息
-
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
在进入Mybatis之前,简单介绍下Mybatis:MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。也就是说底层封装的是JDBC.Mybatis为我们做了大部分的工作。
-
使用注解 譬如:@Select @Update @Insert @Delete 这些注解又是怎么实现的呢?
-
由上图就可以看到当我们使用某一个注解时,就会去执行对应的方法,其实在项目启动的时候注解下的方法,方法名称,入参,返回参数都已经绑定到该class。当执行一个数据库操作时,用我们的sql然后与参数进行拼接,最终根据不同的注解执行不同的逻辑。
Mybatis是如何处理事务的
解析:当一个请求过来,会从数据库连接池中获取一个数据库连接,这个数据库连接与当前线程进行绑定,如果当前线程出现异常,则调用rollback() 进行回滚事务,否则提交事务,最后关闭当前数据库连接
MyBatis的事务管理分为两种形式:
- 使用JDBC的事务管理机制:即利用java.sql.Connection对象完成对事务的提交(commit())、回滚(rollback())、关闭(close())等。
- 使用MANAGED的事务管理机制:这种机制MyBatis自身不会去实现事务管理,而是让程序的容器如(JBOSS,Weblogic)来实现对事务的管理。
-
一级缓存:
(1) MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的
(2) 同一个 SqlSession 对象, 在参数和 SQL 完全一样的情况,多次查询时会使用缓存。
二级缓存:
(1) 二级缓存是基于 mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,是Application级别的缓存。
(2) 二级缓存有全局和Mapper中的两种开关配置,默认是关闭的。
(3) 二级缓存要求返回的模型类POJO是序列化的。
MyBatis高级应用
MyBatis真正强大的是在与它的xml映射语句,接下来由浅入深进行介绍
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
#{id}
#{id} :这就告诉 MyBatis 创建一个预处理语句参数,通过 JDBC,这样的一个参数在 SQL 中会由一个"?"来标识,并被传递到一个新的预处理语句中,就像这样
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
MyBatis常用的属性以及含义:
- Select Attributes
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
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 中的一个,默认值为 unset (依赖驱动)。 |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。 |
resultSets | 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。 |
-
insert, update 和 delete 不同的属性得到不同结果
属性 | 描述 |
---|---|
id | 命名空间中的唯一标识符,可被用来代表这条语句。 |
parameterType | 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
flushCache | 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。 |
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 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
动态sql
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
解释:这是一个简单的条件判断
foreach
解释:resultType:入参 list 作为一个集合
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
-
MyBatis一对一关系映射 associaction 一个订单对应一个用户
-
<!-- 这是resultMap --> <resultMap type="com.jy.entity.Orders" id="ordersAndUser"> <id property="id" column="id"/> <result property="user_id" column="user_id"/> <!-- 这是映射 --> <association property="user" javaType="com.jy.entity.User"> <id property="id" column="id" /> <result property="username" column="username"/> <result property="password" column="password"/> <result property="realname" column="realname"/> </association> </resultMap>
解释:property 对应的是我们实体类字段名称,column 是数据库字段,如果该字段有别名 则这个column就是别名
-
MyBatis一对多关系映射 collection 一个用户可以有多个订单
-
<resultMap type="com.jy.entity.User" id="UserAndOrders"> <id property="id" column="uid"/> <result property="username" column="username"/> <result property="password" column="password"/> <result property="realname" column="realname"/> <collection property="orders" ofType="com.jy.entity.Orders"> <id property="id" column="id"/> <result property="user_id" column="user_id"/> </collection> </resultMap>
解释:上面代码容易出错的地方是:一对多数据映射失败。因为有两个id,所以两个column对应的id 不能相同,要区别开,看上图就是区别开的 uid 和 collecction下的 id
总结:上面作为课程要点记录,也是我们工作中常用到的,这里简单的分析了MyBatis底层封装映射,事务处理,动态sql,清晰且简单,用你看的懂得话讲给你听。欢迎留言,不当之处给与指正,谢谢。