导览
- 前言
- Q:什么是JPA
- 1. 简介
- 2. 原理
- Q:JPA持久化框架—Mybatis
- 1. 内部组成与关系
- 2. 各组件作用域和生命周期
- 3. 动态SQL
- 3.1 if语句
- 3.2 choose-when-otherwise
- 4. mapper映射XML
- 4.1 select
- 4.2 insert
- 5. $与#的区别
- 5.1 #{...}
- 5.2 ${...}
- 结语
- 精彩回顾
前言
我们在做应用开发的时候,需要依赖持久层提供数据支撑,比如你要连接MySQL、Oracle、Sqlserver、redis、Mongo等各类DB。为节约资源并提高效率,Java提供了一套持久化API,即JPA。
本文先从这个JPA说起,咱们边走边聊~
Q:什么是JPA
1. 简介
Java 持久化 API (Java Persistence API)
是一个Java应用程序接口规范,描述了使用Java SE和Java EE的应用中的关系数据的管理规范。
持久化的含义包含以下3个:
- API 本身,定义在 javax.persistence 包内;
- Java持久化查询语言 (JPQL);
- 对象/关系 元数据;
在引入EJB 3.0规范之前,通常企业级Java开发人员使用由持久化框架(例如Hibernate)或数据访问对象(DAO)提供的轻量级持久化对象来代替实体bean(EJB的一种)。 这是因为在以前的EJB规范中,实体bean需要太多复杂的代码和繁重的资源占用,并且由于bean和DAO对象或持久化框架之间的源代码中的互连和依赖性,它们只能在Java EE应用程序服务器中使用。 因此,最初在第三方持久性框架中提供的许多功能都被合并到Java Persistence API
中,并且从2006年开始,像Hibernate(版本3.2)和TopLink Essentials这样的项目已经实现Java Persistence API规范。
2. 原理
下图是关于JPA的运行机制和原理示意。通过应用程序与JPA进行交互,实现数据的持久化。
再放大观察,我们可以更清楚的了解JPA
到底干了些什么。
Q:JPA持久化框架—Mybatis
MyBatis
是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的繁杂操作。
MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和Java的POJO为数据库中的记录。
1. 内部组成与关系
这张图说明了Mybatis3各组成部分的交互关系,博主按箭头编号展开分解:
Step1:创建SqlSessionFactoryBuilder;——> 代表(1)
Step2:通过mybatis配置文件生成SqlSessionFactory;——> 代表(2)和(3)
Step3:客户端请求应用程序;——> 代表(4)
Step4:通过SqlSessionFactory初初始化SqlSession并返回到应用程序;——> 代表(5)、(6)、(7)
Step5:应用程序调用Mapper接口;——> 代表(8)
Step6:Mapper接口实现对象调用SqlSession执行SQL;——> 代表(9)
Step7:SqlSession通过配置文件获取sql并执行;——> 代表(10)
2. 各组件作用域和生命周期
- (1)SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃。一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder来创建多个SqlSessionFactory实例,但是最好还是不要让其一直存在,以保证所有的 XML 解析资源可以被释放。 - (2)SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”
。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。 - (3)SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种Web框架,要考虑将 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。 换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应立即关闭它。这个关闭操作是很重要的,应该放到 finally 块中以确保每次都能执行关闭。
下面的示例就是一个确保 SqlSession 关闭的标准模式:
Sqlsession session = sqlsessionFactory.openSession();
try {//TODO 你的应用逻辑代码}finally {session.close();
}
- (4)映射器实例(Mapper)
映射器(Mapper)是一些由你创建的、绑定你映射的语句(SQL)的接口。Mapper接口的实例是从SqlSession中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。 也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。 并不需要显式地关闭映射器实例,尽管在整个请求作用域保持映射器实例也不会有什么问题,但是你很快会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。 为了避免这种复杂性,最好把映射器放在方法作用域内。
下面的示例就展示了这个实践:
Sqlsession session =sqlSessionFactory.openSession();
try {BlogHapper mapper =session.getMapper(BlogMapper.class);//TODO 你的应用逻辑代码}finally {session.close();
}
3. 动态SQL
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。而Mybatis提供了该能力的支持,下面做一个简单的举例。
3.1 if语句
在where语句中添加if语句,提供条件筛选。
<select id="findActivePager" resultType="Pager">SELECT * FRON papers MHERE state='1'<if test="title !=null">AND title like #{title}</if><if test="author !=null and author.name !=null">AND author_name like #{author.name}
</if>
</select>
3.2 choose-when-otherwise
choose类似java中的switch语句一样,写法如下:
<select id="findActivePager" resultType="Pager">
SELECT * FRON papers MHERE state='1'<choose><when test="title !=null"></when>AND title like #{title}<when test="author !=null and author.name !=null">AND author_name like #{author.name}
</when><otherwise>and 1=1 </otherwise></choose>
</select>
4. mapper映射XML
MyBatis 为 SQL 而构建,SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出),具体如下:
元素名称 | 用途说明 |
---|---|
cache | 对给定命名空间的缓存配置。 |
cache-ref | 对其他命名空间缓存配置的引用。 |
resultMap | 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。 |
parameterMap | 参数映射(已被废弃 ) |
sql | 可被其他语句引用的可重用语句块。 |
insert | 映射插入语句 |
update | 映射更新语句 |
delete | 映射删除语句 |
select | 映射查询语句 |
4.1 select
<select id="selectPerson" parameterType="int" resultType="hashmap">SELECT * FROM user WHERE ID = #{id}
</select>
- id
在命名空间中唯一的标识符,可以被用来引用这条语句。 - parameterType
将会传入这条语句的参数类的别名(通常是一个PO路径)。 - resultMap
外部resultMap的命名引用。可以使用 resultMap 或 resultType,但不能同时使用。 - resultType
从这条语句中返回的期望类型的类的别名(通常是一个DO路径)。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。
4.2 insert
insert示例如下:
<insert id="insert" parameterType="com.test.User">INSERT INTO userVALUES(#{id}, #{name}, #{sex}, #{email} )
</insert>
5. $与#的区别
5.1 #{…}
#{…}将被解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,表现为一个占位符。示例如下:
select * from user where id= #{id}
编译后的SQL为
select * from user where id= ?
5.2 ${…}
而${… } 仅仅为一个纯碎的 string 替换,不占位。
select * from user where id= #{id}
编译后的SQL为
select * from user where id= 1
因此,能使用 #{… } 的地方就用 #{ …}。表名作为变量时,必须使用 ${ …}。
结语
本文对JPA及其实践进行了讲解。并且以Mybatis作为案例以增进各位盆友对JPA的理解和学习。Mybatis作为JPA实践的优秀框架,已被用在各种微服务应用中并受广大程序员的喜爱。通过此文相信各位能够对它有更深刻的理解和学习,并期望能够在未来的学习或面试中,可以助你一臂之力。
走过的、路过的盆友们,点点赞,收收藏,并加以指导,以备不时之需哈~
精彩回顾
一文读懂事务及Spring事务的管理(面试经)_下篇
一文读懂事务及Spring事务的管理(面试经)_上篇
一文读懂Spring Security的工作原理和机制(面试经)
一文读懂Spring AOP的工作原理和机制(面试经)
一文读懂Spring IoC的工作原理和机制(面试经)
一文读懂SpringMVC的工作原理
Springboot中基于X509完成SSL检验的原理与实践
基于springboot+enum配置化实践