MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了google code,并且改名为 MyBatis 。2013 年 11 月迁移到 Github。
iBATIS 一词来源于“internet”和“abatis”的组合,是一个基于 Java 的持久层框架。iBATIS 提供的持久层框架包括 SQL Maps 和 Data Access Objects(DAOs)。
MyBatis 是支持普通 SQL 查询、存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和Java 的 POJOs(Plain Ordinary Java Objects,普通的 Java 对象)映射成数据库中的记录。
持久层可以将业务数据存储到磁盘,具备长期存储能力,只要硬盘不损坏,在断电或其他情况下,重启系统仍然可以读取出这些数据。一般执行持久任务的都是数据库系统,持久层可以使用巨大的磁盘空间,也比较廉价,但缺点是比较慢。当然慢是针对内存而言,在一般的系统中运行是没有问题的,但如果在互联网秒杀场景下,每秒都要执行成千上万次数据操作,慢是不能承受的,可能导致系统宕机,在这样的场景下考虑使用Redis(NoSQL)来处理。
Java互联网应用可以通过MyBatis框架来访问数据库,如下图所示。
而使用MyBatis的原因,是其具备以下几点优势:
MyBatis的核心组件有以下四个:
使用MyBatis首先是用配置或者代码去生产SqlSessionFactory,MyBatis提供了构造器SqlSessionFactoryBuilder。一般采用读取配置XML文件的形式生成SqlSessionFactory,读取后通过Configuration类对象构建整个MyBatis的上下文。
SqlSessionFactory是一个接口,在MyBatis中它存在两个实现类:SqlSessionManager和DefaultSqlSessionFactory。一般而言,具体是由DefaultSqlSessionFactory去实现的,而SqlSessionManager使用在多线程的环境中,它的具体实现依靠DefaultSqlSessionFactory。
每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为中心的,而SqlSessionFactory的唯一作用就是生产MyBatis的核心接口对象SqlSession,所以说他的责任是唯一的,这样,我们就用单例模式去处理它。
在MyBatis中,SqlSession是其核心接口。在MyBatis中有两个实现类,DefaultSqlSession(单线程)和SqlSessionManager(多线程)。SqlSession的作用类似一个JDBC中的Connection对象,代表着一个资源的启用,具体作用有三个:
SqlSession只是一个门面接口,实际进行这些功能操作的是Executor。
由于SqlSession的获取Mapper接口和发送SQL的功能都要先实现映射器的功能,而映射器接口本身也能实现发送SQL功能,所以一般使用映射器(Mapper)直接发送SQL。
映射器是MyBatis中最重要、复杂的组件,由一个接口和对应的XML文件(或注解)组成,它可以配置如下内容:
映射器的主要作用就是将SQL查询到的结果映射为一个POJO,或者将POJO的数据插入到数据库中,并定义一些关于缓存的重要内容。
以后面的实例中getRole这条语句为例,此处的selectOne方法表示使用查询并且只返回一个对象,而参数则是一个String对象和一个Object对象。这里是一个Long参数,是它的主键。
Role role=sqlSession.selectOne("com.ssm.mapper.RoleMapper.getRole",2L);
String对象是由一个命名空间家SQL id组合而成的,它完全定位了一条SQL,使MyBatis能够找到对应的SQL。如果在MyBatis中只有一个id为getRole的SQL,那么也可以简写为:
Role role=sqlSession.selectOne("getRole",2L);
SqlSession还可以通过获取Mapper接口,通过其来发送SQL。
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(2L);//指定用户id来查找
通过SqlSession的getMapper方法来获取一个Mapper接口,就可以调用它的方法了。
两种SQL发送方式的比较:
1.使用Mapper接口编程可以消除SqlSession带来的功能性代码,提高可读性,类似roleMapper.getRole(2L)是完全面向对象的语言,更能体现业务的逻辑。
2.使用SqlSession发送SQL,需要一个SQL id去匹配SQL,使用sqlSession.selectOne("getRole",2L)语句,IDE不会提示错误和校验,只有在运行中才能知道是否会产生错误。
现在,Mapper接口编程是主流,建议采用这种方式发送SQL。
生命周期是每一个对象应该存活的时间,比如一些对象一次用完后就要关闭,使他们被JVM销毁,以避免继续占用资源。
生命周期是组件的重要问题,尤其是在多线程的环境中,比如互联网应用、Socket请求等,而MyBatis也常用于多线程的环境中,错误使用会造成严重的多线程并发问题,因此我们需要掌握MyBatis中组件的生命周期。
在实际应用中应该根据每一个组件的作用去确定去生命周期。
它的作用是创建SqlSessionFactory,然后就不再有其它作用了,所以它应该只存在于创建SqlSessionFactory的方法中。
它可以视为一个数据库连接池,作用就是创建SqlSession接口对象。因为MyBatis的本质就是Java对数据库的操作,所以SqlSessionFactory的生命周期存在于整个MyBatis的应用之中,因此它的生命周期等于MyBatis的应用周期。
如果说SqlSessionFactory相当于数据库连接池,那么SqlSession则相当于一个数据库连接(Connection对象)。我们可以在一个事务里面执行若干条SQL,然后通过commit、roolback等方法进行事务的提交和回滚,因此,它存活在一个业务请求中,处理完这个请求后就应该关闭这条连接,把它归还给SQLSessionFactory,否则数据库资源会被耗尽。
Mapper是一个接口,由SqlSession所创建,它代表的是一个请求中的业务处理,所以它应该存活在一个请求中,一旦处理完了相关的也业务,就应该关闭它。
我们在idea上建立一个maven项目,进行基本的MyBatis配置,实现与数据库的连接,完成增删改查基本操作。项目构成如图。
public class Role implements Serializable {
private Long id;
private String roleName;
private String note;
/*
getter and setter
**/
}
interface:
public interface RoleMapper {
public int insertRole(Role role);
public int deleteRole(Role role);
public int updateRole(Role role);
public Role getRole(Long id);
public List findRolds(String roleName);
}
xml文件:
insert into t_role(role_name,note) values (#{roleName},#{note})
delete from t_role where id= #{id}
update t_role set role_name = #{roleName}, note = #{note} where id= #{id}
public class SqlSessionFactoryUtils {
private final static Class LOCK = SqlSessionFactoryUtils.class;
private static SqlSessionFactory sqlSessionFactory = null;
private SqlSessionFactoryUtils() {}
public static SqlSessionFactory getSqlSessionFactory() {
synchronized (LOCK) {
if (sqlSessionFactory != null) {
return sqlSessionFactory;
}
String resource = "mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return sqlSessionFactory;
}
}
//代码生成SqlSessionFactory
public static SqlSessionFactory getSqlSessionFactory2() {
synchronized (LOCK) {
//数据库连接池信息
PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql://127.0.0.1/ssm");
dataSource.setDefaultAutoCommit(false);
//采用MyBatis的JDBC事务方式
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
//创建Configuration对象
Configuration configuration = new Configuration(environment);
//注册一个MyBatis上下文别名
configuration.getTypeAliasRegistry().registerAlias("role", Role.class);
//加入一个映射器
configuration.addMapper(RoleMapper.class);
//注解形式
//configuration.addMapper(RoleMapper2.class);
//使用SqlSessionFactoryBuilder构建SqlSessionFactory
sqlSessionFactory =
new SqlSessionFactoryBuilder().build(configuration);
return sqlSessionFactory;
}
}
public static SqlSession openSqlSession() {
if (sqlSessionFactory == null) {
getSqlSessionFactory();
}
return sqlSessionFactory.openSession();
}
}
public class Test {
public static void main(String[] args) {
testRoleMapper();
}
private static void testRoleMapper() {
Logger log = LoggerFactory.getLogger(Test.class);
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtils.openSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(2L);//指定用户id来查找
System.out.println("-----取出的用户:"+role.getRoleName()+"------");
log.info(role.getRoleName());
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
3.5 mybatis-config
运行结果:
test中进行了查询id为2的用户名的操作,成功取出了Bill。