SqlSession介绍和使用
SqlSession是一个接口类,它类似于你们公司前台的美女客服,它扮演着门面的作用,而真正干活的是Executor接口,你可以认为它是公司的工程师。假设我是客户找你们公司干活,我只需要告诉前台的美女客服(SqlSession)我要什么信息(参数),要做什么东西,过段时间,她会将结果给我。在这个过程中,作为用户的我所关心的是:
1)要给美女客服(SqlSession)什么信息(功能和参数)。
2)美女客服会返回什么结果(Result)。
而我不关心工程师(Executor)是怎么为我工作的,只要前台告诉工程师(Executor),工程师就知道如何为我工作,这个步骤对我而言是个黑箱操作。 在MyBatis中SqlSession接口的实现类有两个,分别是DefaultSqlSession和SqlSession Manager。这里我们暂时不深入讨论Executor接口及其涉及的其他类,只关心SqlSession的用法就好。
我们构建了SqlSessionFactory,然后生成MyBatis的门面接口SqlSession。SqlSession接口类似于一个JDBC中的Connection接口对象,我们需要保证每次用完正常关闭它,所以正确的做法是把关闭SqlSession接口的代码写在finally语句中保证每次都会关闭SqlSession,让连接资源归还给数据库。如果我们不及时关闭资源,数据库的连接将很快被耗尽,系统很快因为数据库资源的匮乏而瘫痪。
SqlSession是一个会话,相当于JDBC的一个Connection对象,它的生命周期应该是在请求数据库处理事务的过程中。它是一个线程不安全的对象,在涉及多线程的时候我们需要特别的当心,操作数据库需要注意其隔离级别,数据库锁等高级特性。此外,每次创建的SqlSession都必须及时关闭它,它长期存在就会使数据库连接池的活动资源减少,对系统性能的影响很大。正如前面的代码一样,我们往往通过finally语句块保证我们正确的关闭SqlSession。它存活于一个应用的请求和操作,可以执行多条SQL,保证事务的一致性。
让我们看看实现的伪代码,如代码所示。
标准SqlSession使用方法 :
SqlSession sqlSession = null;//定义SqlSession
try
{
//打开sqlsession会话
sqlSession = sqlSessionFactory.openSession();
sqlSession.commit();
}
catch(Exception ex){
System.err.println(ex.getMessage());
sqlSession.rollback();
}
finally{
if(sqlSession != null)
sqlSession.close();
}
这样SqlSession就被我们创建出来了,在finally语句中我们保证了它的合理关闭,让连接资源归还给数据库连接池,以便后续使用。
SqlSession的用途主要有两种。
(1)获取映射器,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果。
(2)直接通过命名信息去执行SQL返回结果,这是iBatis版本留下的方式。在SqlSession层我们可以通过update、insert、select、delete等方法,带上SQL的id来操作在XML中配置好的SQL,从而完成我们的工作,与此同时它也支持事务,通过commit、rollback方法提交或者回滚事务。 关于这两种用途我们会在映射器里面进行讨论,这两种用途的优劣我们也会进行研讨。
Mapper映射器
Mapper是一个接口,而没有任何实现类,它的作用是发送SQL,然后返回我们需要的结果,或者执行SQL从而修改数据库的数据,因此它应该在一个SqlSession事务方法之内,是一个方法级别的东西。它就如同JDBC中的一条SQL语句的执行,它最大的范围和SqlSession是相同的。尽管我们想一直保存着Mapper,但是你会发现它很难控制,所以尽量在一个SqlSession事务的方法中使用它们,然后废弃掉。
映射器是由Java接口和XML文件(或注解)共同组成的,它的作用如下。
● 定义参数类型。
● 描述缓存。
● 描述SQL语句。
● 定义查询结果和POJO的映射关系。
一个映射器的实现方式有两种,一种是通过XML文件方式实现,如在mybatis-config.xml文件中写一个XML文件,它是用来生成Mapper的。
mybatis-config.xml:(一个基础的mybatis配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<DOCTYPE configuration PUBLIC "-//mybatis. org//DTD Config 3.0//EN"
"http: //mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc. Driver"/>
<property name="url" value="jdbc: mysql: //localhost: 3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="learn"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com\learn\mybatis\chapter1 \pojo\role.xml"/> <!--这里是自己的xml路径-->
</mappers>
</configuration>
其次是一个简单的映射文件:
role.xml:
<?xml version="1.0" encoding="UTF-8"?>
<DOCTYPE configuration PUBLIC "-//mybatis. org//DTD Config 3.0//EN"
"http: //mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learn.mybatis.chapter1.mapper.RoleMapper">
<select id="getRole" parameterType = "long" resultType="com.learn.mybatis.chapter1.pojo.Role">
select id,role_name as roleName, note from t_role where id = #{id}
</select>
</mapper>
再者,我们还需要一个接口,注意仅仅是接口,而无需实现类
public interface RoleMapper{
public Role getRole(Long id)
}
这里我们给出了SQL,但是并没有给出映射规则,因为这里我们使用的SQL列名和POJO的属性名保持一致,这个时候MyBatis会自动提供映射规则,所以省去了这部分的配置工作。
描述一下上面的role.xml文件做了什么:
● 这个文件是我们在配置文件mybatis-config.xml中配置了的,所以MyBatis会读取这个配置文件,生成映射器。
● 定义了一个命名空间为com.learn.chapter2.mapper.RoleMapper的SQL Mapper,这个命名空间和我们定义的接口的全限定名是一致的。
● 用一个select元素定义了一个查询SQL,id为getRole,和我们接口方法是一致的,而parameterType则表示我们传递给这条SQL的是一个java.lang.Long型参数,而resultType则定义我们需要返回的数据类型,这里为role,而role是我们之前注册com.learn.chapter2.po.Role的别名。
#{id}为这条SQL的参数。而SQL列的别名和POJO的属性名称保持一致。那么MyBatis就会把这条语句查询的结果自动映射到我们需要的POJO属性上,这就是自动映射。我们可以用SqlSession来获取这个Mapper:
//用sqlSession来获取Mapper
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMappler.getRole(1L);//执行查询方法
System.out.println(role.getRoleName());//打印角色名称
另外一种就是通过代码方式实现,在Configuration里面注册Mapper接口,但是更建议使用xml文件配置方式,原因:
● Java注解是受限的,功能较少,而MyBatis的Mapper内容相当多,而且相当复杂,功能很强大,使用XML文件方式可以带来更为灵活的空间,显示出MyBatis功能的强大和灵活。
● 如果你的SQL很复杂,条件很多,尤其是存在动态SQL的时候,写在Java文件里面可读性较差,增加了维护的成本。
如果上面的xml配置改为注解的方式:
public interface RoleMapper2{
@Select(value = "select id,role_name as roleName.note from t_role where id = #{id}")
public Role getRole(Long id)
}
我们使用了@Select注解,注入了和XML一样的select元素,这样MyBatis就会读取这条SQL,并将参数id传递进SQL。同样使用了别名,这样MyBatis会为我们自动映射,得到Role对象。
同时我们需要加入
configuration.addMapper(RoleMapper2.class);
注册这个接口为映射器。
一些其他疑惑:
我们使用的仅仅是Java接口和一个XML文件(或者注解)去实现Mapper,Java接口不是实现类,对于Java语言不熟悉的读者肯定会十分疑惑,一个没有实现类的接口怎么能够运行呢?其实它需要运用到Java语言的动态代理去实现,而实现Java语言的动态代理的方式有多种。这里我们还是集中于它的用法,所以可以这样理解:我们会在MyBatis上下文中描述这个接口,而MyBatis会为这个接口生成代理类对象,代理对象会根据“接口全路径+方法名”去匹配,找到对应的XML文件(或注解)去完成它所需要的任务,返回我们需要的结果。
总结
SqlSessionFactory一般是一个全局单例,对应一个数据库连接池,用它来创建SqlSession
SqlSession相当于一个JDBC的Connection对象,在一次请求事务会话后,我们会将它关闭。它可以为我们获得Mapper
Mapper相当于执行一条条SQL,由于它们难以控制,当SqlSession销毁的时候也会销毁它