3.MyaBtis
3.1持久层概念和MyBatis的特点
持久层
持久层可以将业务数据存储到磁盘,具备长期存储能力,只要磁盘不损坏(大部分的重要数据都会有相关的备份机制),在断电或者其他情况下,重新开启系统仍然可以读取这些数据。一般执行持久任务的都是数据库系统,持久层可以使用巨大的磁盘空间,也比较廉价,它的缺点就是比较慢。当然慢是针对内存而言的,在一般的系统中运行是不存在问题的,比如内部管理系统,但是在互联网的秒杀场景下,每秒都需要执行成千上万次数据操作,慢是不能承受的,极有可能导致宕机,在这样的场景下考虑使用Redis (NoSQL)。
3.2MyBatis特点
- 不屏蔽 SQL 意味着可以更为精确地定位 SQL 语句,可以对其进行优化和改造,这有利于互联网系统性能的提高,符合互联网需要性能优化的特点。
- 提供强大、灵活的映射机制 ,方便 Java 开发者使用 。提供动态 SQL 的功能,允许根据不同条件组装 SQL ,这个功能远比其他工具或者 Java 编码的可读性和可维护性高得多,满足各种应用系统的同时也满足了需求经常变化的互联网应用的要求。
- 提供了使用 Mapper的接口编程,只要一个接口和一个XML 就能创建映射器,进一步简化我们的工作,使得很多框架API在MyBatis 中消失,开发者能更集中于业务逻辑。
3.3MyBatis核心组件
- SqlSessionFactoryBuilder(构造器):它会根据配置或者代码来生成SqlSessionFactory,采用的是分步构建的Builder模式。
- SqlSessionFactory(工厂接口):生成SqlSession,使用的是工厂模式。
- SqlSession(会话):: 一个既可以发送 SQL 执行返回结果,也可以获取Mapper的接口。在现有的技术中,一 般我们会让其在业务逻辑代码中“消失”,而使用的是MyBatis提供的 SQLMapper 接口编程技术,它能提高代码的可读性和可维护性。
- SQL Mapper(映射器):MyBatis新设计存在的组件,由一个Java接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则。负责发送SQL去执行并返回结果。
3.4SqlSessionFactory(工厂接口)
使用MyBatis首先是使用配置或代码去生产SqlSessionFactory,MyBatis提供了构造器SqlSessionFactoryBuiIder。它提供了一个类org.apache.ibatis.session.Configuration作为引导,采用的是Builder模式。具体的分步则是在Configuration类里面完成的,MyBatis中,既可以通过读取配置的XML文件的形式生成SqlSessionFactory,也可以通过Java代码的形式去生成SqlSessionFactory 。推荐采用XML的形式,因为代码的方式在需要修改的时候会比较麻烦。
当配置了XML或者提供代码后,MyBatis会读取配置文件,通过Configuration类对象构建整个MyBatis的上下文。SqlSessionFactory是一个接口,在 MyBatis 中它存在两个实现类SqlSessionManager和DefaultSqlSessionFactory一般而言,具体是由DefaultSqlSessionFactory去实现的,而SqlSessionManager使用在多线程的环境中,它的具体实现依靠 DefaultSqlS essionFactory。
3.4.1使用XML构建SqlSessionFactory
在MyBatis中的XML分为两类,一类是基础配置文件,通常只有一个,主要是配置一些最基本的上下文参数和运行环境;另一类是映射文件,它可以配置映射关系、SQL、参数等信息。
- 基础配置文件
- <typeAlias>元素定义了一个别名role它代表着com.leam.ssm.chapter3.pojo.Role这个类。这样定义后,在MyBatis上下文中就可以使用别名去代替全限定名了。
- <environment>元素的定义,这里描述的是数据库。它里面的<transactionManager>元素是配置事务管理器,这里采用的是MyBatis的JDBC管理器方式。然后采用元素配置数据库,其中属性 type="POOLED"代表采用MyBatis内部提供的连接池方式,最后定义一些关于JDBC的属性信息。
SqlSessionFactory SqlSessionFactory=null;
String resource="mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource) ;
SqlSessionFactory
=new SqlSessionFactoryBuilder().build(inputStream) ;
} catch (IOException e) {
e.printStackTrace() ;
}
首先读取mybatis-config.xml,然后通过SqlSessionFactoryBuilder的Builder方法去创建SqlSessi onFactory。整个过程比较简单,而里面的步骤还是较烦琐的,只是 MyBatis 采用了Builder模式为开发者隐藏了这些细节。这样SqlSessionFactory 就被创建出来了。采用 XML 创建的形式,信息在配置文件中 ,有利于我日后的维护和修改, 避免了新编译代码,因此推荐这种方式。
3.4.2使用代码构建SqlSessionFactory
3.5SqlSession
在MyBatis中,SqlSession是其核心在MyBatis中有两个实现类,DefaultSqlSession和SqlSessionManager。DefaultSqlSession是单线程使用的,而 SqlSessionManager 在多线程环境下使用。SqlSession的作用 JDBC 中的 Connection 对象,代表着一个连接 资源的启用。具体而言,它的作用3个:
- 获取Mapper接口
- 发送SQL给数据库
- 控制数据库事务
SqlSession sqlSession = SqlSessionFactory.openSession() ;
SqlSession 只是一个门面接口,它有很多方法,可以直接发送 SQL 。它就好像一家软件公司的商务人员,是一个门面,而实际干活的是软件工程师。在MyBatis中,真正干活的是Executor,我们会在底层看到它。 SqlSession 控制数据库事务的方法
SqlSession sqlSession =null;
try {
//打开 SqlSession 会话
sqlSession =SqlSessionFactory.openSession() ;
//some code...
sqlSession. commit() ; //提交事务
} catch(Exception ex) {
sqlSession.rollback()://回滚事务
} finally {
//在 finally 语句中确保资源被顺利关闭
if (sqlSession ! =null) {
sqlSession.close();
}
}
这里使用 commit 方法提交事务,或者使用 rollback 方法回滚事务。因为它代表着数据库的连接资源,使用后要及时关闭它,如果不关闭,那么数据库的连接资源就会很快被耗费光,整个系统就会陷入瘫痪状态,所以用finally语句保证其顺利关闭。
3.6映射器
映射器是 MyBatis 中最重要、最复杂的组件,它由一个**接口和对应的 XML 文件(或注解)**组成。它可以配置以下内容:
- 描述映射规则
- 提供SQL语句,并且可以配置SQL参数类型、返回类型、缓存刷新等信息。
- 配置缓存
- 提供动态SQL
package com.learn.ssm.chapter3.pojo;
public class Role {
private Long id;
private String roleName;
private String note;
/**setter and getter**/
}
映射器主要作用就是将 SQL 查询到的结果映射为 POJO,或者将 POJO 的数据插入到数据库中 并定义一些关于缓存等的重要内容。 MyBatis运用了动态代理技术使得接口能运行起来 入门阶段只要懂得MyBatis会为这个接口生成一个代理对象,代理对象会去处理相关的逻辑即可。
3.6.1用XML实现映射器
用XML 定义映射器分为两个部分:接口和 XML。先定义一个映射器接口。
package com.learn.ssm.chapter3.mapper;
public interface RoleMapper {
public Role getRole(Long id);
}
在用 XML 方式 SqISession 配置文件中有这样一段代码:
<mapper resource="com/learn/ssm/chapter3/mapper/RoleMapper. xml” />
它的作用就是引入 XML 。用 XML 方式 建映射器:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC ”-//mybatis.org//DTD Mapper 3.0//EN”
"http://mybatis.org/dtd/rnybatis-3-mapper.dtd”>
<mapper narnespace="corn.learn.ssm.chapter3.mapper.RoleMapper>
<select id="getRole" parameterType="long” resultType="role">
select id,role_name as roleName , note from t_role where id= #{id}
</select>
</mapper>
- <mapper>元素中的属性 namespace 所对应的是一个接口的全限定名,于是MyBatis上下文就可以通过它找到对应的接口。
- 元素表明这是一条查询语旬,而属性id标识了这条SQL ,属性parameterType="long"说明传递给 SQL 的是 long型的参数,而resultType="role"表示返回的是role 类型的返回值。而role 是之前配置文件 mybatis-config.xml配置的别名,指代的是 com leam .ssm.chapter3.pojo.Role
- 这条SQL中的#{id}表示传递进去的参数。
MyBatis在默认情况下提供自动映射,只要SQL返回的列名能和 POJO 对应起来即可。这里 SQL 返回的列名 id和note 是可以和之前定义的POJO的属性对应起来的,而表里的列role_name通过SQL别名的改写,使其roleName,也是和POJO对应起来的,所以此时MyBatis就可以把SQL 查询的结果通过自动映射的功能映射成为 POJO。
3.6.2用注解实现映射器
import com.learn.ssm.chapter3.pojo.Role ;
public interface RoleMapper2 {
@Select ("select id, role_name as roleName , note from t_role where id=#{id }")
public Role getRole(Long id);
}
这完全等同于 XML 方式创建映射器。 会觉得使用注解 方式 XML 方式要简单得多。如果它和 XML 方式同时定义时 XML 方式将覆盖掉注解方式,所 MyBatis方推荐使用的是 XML 方式,因此本书 XML 方式为主讨论MyBatis 应用。
- XML与注解配置映射器优缺点比较
- XML对复杂SQL和动态SQL支持更好
- XML支持SQL语句的相互引入
3.6.3SqlSession发送SQL
Role role = (Role )sqlSession.selectOne ("com.learn.ssm.chapter3.mapper.RoleMapper.getRole",1L);
selectOne方法表示使用查询并且只返回一个对像,而参数则是一个String对象和一个Object对象。这里是一个long参数,long参数是它的主键。
3.6.3用Mapper发送SQL
SqlSession还可以获取Mapper接口,通过Mapper接口发送SQL
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class) ;
Role role=roleMapper .getRole(lL) ;
通过SqlSession的getMapper方法来获取一个Mapper接口,就可以调用它的方法了。因为XML文件或者接口注解定义的SQL都可以通过"类的全限定名+方法名"查找,所MyBatis会启用对应的SQL进行运行,并返回结果。
3.6.5对比两种发送SQL方式
推荐采用获取SqlSession获取Mapper的方式
- 使用Mapper接口编程可以消除SqlSession带来的功能性代码,提高可读性,而SqlSession发送SQL,需要一个SQL id去匹配SQL,比较晦涩难懂。使用Mapper接口,类似roleMapper.getRole(lL)则是完全面向对象的语言,更能体现业务的逻辑。
- 使用 Mapper.getRole(1L)方式,IDE会提示错误和校验,而使用
SqlSession.selectOne(“getRole”, lL) 语法,只有在运行中才能知道是否会产生错误。
3.7生命周期
生命周期是组件的重要问题,尤其是在多线程的环境中,比如互联网应用、Socket请求等,而MyBatis也常用于多线程的环境中,错误使用会造成严重的多线程并发问题,为了正确编写MyBatis的应用程序,我们需要掌握MyBatis组件的生命周期。 生命周期就是每一个对象应该存活的时间,比如一些对象一次用完后就要关闭,使它们被Java虚拟机(JVM)销毁,以避免继续占用资源,所以我们会根据每一个组件的作用去确定其生命周期。
3.7.1SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 作用在于创建SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder就失去了作用,所以它只能存在于创建SqlSessionFactory的方法中,而不要让其长期存在。
3.7.2SqlSessionFactory
SqlSessionFactory可以被认为是一个数据库连接池,它的作用是创建SqlSession接口对象。因为MyBatis本质就是Java数据库的操作,所以SqlSessionFactory的生命周期存在于整个MyBatis 应用之中。 由于SqlSessionFactory是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个SqlSessionFactory那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因此在一般的应用中往往希望SqlSessionFactory作为一个单例,让它在应用中被共享。
3.7.3SqlSession
SqlSession就相当于一个数据库连接(Connection对象) ,你可以在一个事务里面执行多条SQL,然后通过它的commit、rollback等方法 ,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用try… catch … . finally …语句来保证其正确关闭。
3.7.4Mapper
Mapper是一个接口,它由 SqlSession所创建,所以它的最大生命周期至多和 SqlSession保持一致,尽管它很好用,但是由于 SqlSession的关闭,它的数据库连接资源也会消失,所以它的生命周期应该小于等于 SqlSession的生命周期。Mapper代表的是一个请求中的业务处理,所以它应该在一个请求中,一旦处理完了相关的业务,就应该废弃它。
|