目录
1、MyBatis的生命周期
1、SqlSessionFactoryBuilder
2、SqlSessionFactory
3、SqlSession
4、Mapper
2、MyBatis实例
1、配置log4j.properties日志
2、构建POJO对象
3、构建Mapper映射器
4、配置Mapper映射文件
5、配置mybatisConfig.xml全局基础文件
6、构建SqlSessionFactoryUtils工具类
7、测试
生命周期是组件的重要问题,尤其是在多线程的情况中,比如互联网应用、Socket请求等,而MyBatis也常用于多线程的环境中,错误使用会造成严重的多线程并发问题,因此,我们需要掌握MyBatis组件的生命周期。而MyBatis的生命周期就是每一个对象应该存活的时间,比如一些对象一次用完后就要关闭,使它们被JVM销毁,以免继续占用资源。
SqlSessionFactoryBuilder的作用在于创建SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder就失去作用,所以它只能存在于创建SqlSessionFactory的方法中,而不要让其长期存在。
SqlSessionFactory可以被认为是一个数据库连接池,它的作用是创建SqlSession接口对象。因为MyBatis的本质就是Java对数据库的操作,所以SqlSessionFactory的生命周期存在于整个MyBatis的应用中,所以一旦创建了SqlSessionFactory,就要长期保存它,直至不在使用MyBatis应用,所以可以认为SqlSessionFactory的生命周期就等同与MyBatis的应用周期。
由于SqlSessionFactory是对一个数据库的连接池,所以它占据着数据库的连接资源。如果创建多个SqlSessionFactory,那么就存在多个数据库连接池,不利于对数据库资源的控制,也将导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免出现这种情况。因此在一般的应用中,我们往往希望SqlSessionFactory作为一个单例,让它在应用中被共享。
如果说SqlSessionFactory相当于数据库连接池,那么SqlSession就相当于一个数据库连接(Connection对象),我们可以在一个事务中执行多条SQL,然后通过它的commit、rollback等方法,提交或回滚事务。所以SqlSession应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给SqlSessionFactory,否则数据库资源将被耗光,系统瘫痪,所以使用try...catch...finally语句来保证SqlSession正确关闭。
Mapper是一个接口,它由SqlSession所创建,所以它的最大生命周期至多和SqlSession保持一致,当SqlSession关闭时,Mapper的数据库连接资源也会消失,所以Mapper的生命周期应该小于等于SqlSession的生命周期。Mapper代表的是一个请求中的业务处理,所以它应该在一个请求中,一旦处理完了相关业务,就应该废弃它。
Mybatis组件生命周期如图所示:
这里我们做一个简单的实例,主要处理角色表的增、删、改、查,涉及的文件及其作用如下表 :
文件 | 作用 |
SysRoleMapper.java | Mapper映射器接口 |
SysRoleMapper.xml | Mapper映射器XML配置文件,描述映射关系、SQL等内容。【MyBatis核心文件】 |
SysRole.java | POJO对象 |
SqlSessionFactoryUtils.java | 会话工厂工具类,用于创建SqlSessionFactory和获取SqlSession对象 |
log4j.properties | 日志配置文件 |
mybatisConfig.xml | MyBatis的基础配置文件 |
SysRoleMapperConfigTest.java | 测试程序 |
#全局配置
#优先级:ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF
log4j.rootLogger=DEBUG, stdout
#设置为DEBUG级别,便于把详细的日志记录打印出来,便于调试。
#在生产中,可以设置为INFO级别
log4j.logger.org.mybatis.=DEBUG
#控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
我们设置为DEBUG级别,以能够把最详细的日志打印出来,便于调试,在生产中可设置为INFO级别。
构建一个POJO对象,最终查询会映射到它上面,或将其保存到数据库中
public class SysRole {
private Integer id;
private String name;
private String description;
private Date createTime;
private Date updateTime;
/*省略setter和getter方法*/
}
采用XML方式构建映射器,它包含一个Mapper接口和一个XML映射文件。这里要实现增、删、改、查。所以定义一个接口
SysRoleMapper.Java
/**
* 映射器接口
*/
public interface SysRoleMapper {
/*[1.1]新增一条角色------->自定义主键*/
public int insertRole(SysRole sysRole);
/*[1.2]新增一条角色------->自增主键*/
public int insertRole2(SysRole sysRole);
/*[2]删除一条角色*/
public int deleteRole(Integer id);
/*[3]修改一条角色*/
public int updateRole(SysRole sysRole);
/*[4]查询一条角色*/
public SysRole getRole(Integer id);
/*[5]通过角色名获取整个角色列表*/
public List findRoles(String roleName);
}
其中,insertRole表示插入一个SysRole对象;deleteRole表示删除;updateRole是修改一个SysRole对象;getRole获取一个SysRole对象;findRoles则是通过角色名称获取一个角色对象列表。我们要用一个XML文件来描述这些功能。
select LAST_INSERT_ID()
insert into sys_role(name,description,createTime,updateTime)
values (#{name}, #{description}, #{createTime},#{updateTime})
insert into sys_role(name,description)
values (#{name}, #{description})
delete from sys_role where id = #{id}
update sys_role set name = #{name},description = #{description}
where id = #{id}
id,name,description
这是一些比较简单的SQL语句,inser\delete、select、update、元素代表了增、删、改、查,而它们里面的元素id则标识了对应的SQL。paramterType标出了SQL返回值类型的参数,resultType则代表结果映射成为什么类型。
有了它们,我们就可以开始创建SqlSessionFactory了。首先来完成mybatisConfig.xml全局基础配置文件。
使用mybatisConfig.xml文件,通过SqlSessionFactoryBuilder来构建SqlSessionFactory。由于SqlSessionFactory应该采用单例,代码如下:
/**
* SqlSessionFactoryUtils工具类,用于创建SqlSessionFactory和SqlSession对象
*/
public class SqlSessionFactoryUtils {
private final static Class LOCK = SqlSessionFactoryUtils.class;
// [3]定义会话工厂变量
private static SqlSessionFactory sqlSessionFactory =null;
//其他代码不能通过new的方式来创建它
private SqlSessionFactoryUtils() {
}
public static SqlSessionFactory getSqlSessionFactory(){
//锁,防止在多线程中多次实例化sqlSessionFactory对象,从而保证sqlSessionFactory的唯一性
synchronized (LOCK) {
if (sqlSessionFactory != null) {
}
//[1]mybatis全局配置文件路径
String str = "mybatisConfig.xml";
InputStream inputStream;
try {
//[2]把全局配置文件放入流中
inputStream = Resources.getResourceAsStream(str);
//[4]通过SqlSessionFactoryBuilder构造器的build方法创建sqlSessionFactory会话工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//关闭流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
return null;
}
return sqlSessionFactory;
}
}
//[5]通过会话工厂调用openSession方法,来创建sqlSession会话对象
public static SqlSession openSqlSession(){
if (sqlSessionFactory == null) {
getSqlSessionFactory();
}
return sqlSessionFactory.openSession();
}
}
构造方法中,加入private关键字,使得其他代码不能通过new的方式来创建它。而加入synchronized关键字加锁,主要是为了防止在多线程中多次实例化SqlSessionFactory对象,从而保证SqlSessionFactory的唯一性。而openSqlSession方法的作用则是创建SqlSession对象。
public class SysRoleMapperConfigTest{
private static Logger log = Logger.getLogger(SysRoleMapperConfigTest.class);
/*=====================================插入一条角色记录=====================================*/
@Test
public void insertRole1() throws Exception{
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtils.openSqlSession();
SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
SysRole sysRole = new SysRole();
// sysRole.setId(23);
sysRole.setName("root2");
sysRole.setDescription("root2");
sysRole.setCreateTime(new Date());
sysRole.setUpdateTime(new Date());
int i = roleMapper.insertRole(sysRole);
log.info("==>插入角色记录的结果:" + i);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
@Test
public void insertRole2()throws Exception{
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtils.openSqlSession();
SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
SysRole sysRole = new SysRole();
sysRole.setName("root1000");
sysRole.setDescription("root1000");
int i = roleMapper.insertRole2(sysRole);
log.info("========>>>>" + i);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
/*=====================================删除一条角色记录=====================================*/
@Test
public void deleteRole(){
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtils.openSqlSession();
SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
int i = roleMapper.deleteRole(20);
log.info("==>删除角色记录的结果:" + i);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
/*=====================================修改一条角色记录=====================================*/
@Test
public void updateRole() throws Exception{
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtils.openSqlSession();
SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
SysRole sysRole = new SysRole();
sysRole.setId(22);
sysRole.setName("westone");
sysRole.setDescription("westone");
int i = roleMapper.updateRole(sysRole);
log.info("==>修改角色记录的结果:" + i);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
/*=====================================查询一条角色记录=====================================*/
@Test
public void getRoleById() throws Exception{
SqlSession sqlSession = null;
try {
//获取会话对象
sqlSession = SqlSessionFactoryUtils.openSqlSession();
//加载Mapper接口类
SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
SysRole sysRole = roleMapper.getRole(1);
log.info("==>根据ID查询的结果:" + sysRole.getName());
//提交事务
sqlSession.commit();
} catch (Exception e) {
//操作出现错误时,就回滚
sqlSession.rollback();
}finally{
if (sqlSession != null) {
sqlSession.close();
}
}
}
}