《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

时间:2023-03-09 19:37:07
《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

1. 简介

Java程序都是通过JDBC连接数据库,但是只定义了接口规范,具体的实现交给各个数据库厂商去实现,因为每个数据库都有其特殊性。所以JDBC是一种桥接模式

这里为什么说JDBC是一种桥接模式呢?为例避免JDBC的缺陷出现了ORM模型,比如Hibernate,提供的是一种全表映射模型。因为Hibernate的一些缺陷,比如:

  • Hibernate屏蔽了SQL,只能全表映射,更新时需要发送所有的字段
  • 无法根据不同的条件组装不同的SQL
  • 对多表关联和复杂SQL查询支持较差,需要自己写SQL,返回后,需要自己将数据组装为POJO
  • 不能有效地支持存储过程
  • 虽然有HQL,但是性能较差。

因此出现了MyBatis,一个半自动的框架。之所以称之为半自动,是因为它需要手动提供POJO、SQL和映射关系,而全表映射的Hibernate只需要提供POJO和映射关系即可,因为Hibernate无需编写SQL,所以开发效率优于MyBatis。

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

2. MyBatis入门

MyBatis的核心组件:

  • SqlSessionFactoryBuilder(构造器):它会根据配置信息或者代码来生成SqlSessionFactory(工厂接口)
  • SqlSessionFactory:依靠工厂来生成SqlSession
  • SqlSession:是一个既可以发送SQL去执行并返回结果,也可以获取Mapper的接口
  • SQL Mapper:它是MyBatis新设计的组件,是由一个java接口和XML文件(或注解)构成的,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返回结果。

2.1 构建SqlSessionFactory

每个MyBatis的应用都是以SqlSessionFactory的实例为中心,可以通过SqlSessionFactoryBuilder获得,注意SqlSessionFactory是一个工厂接口而不是实现类,其任务是创建SqlSession。SqlSession类似于一个JDBC的Connection对象。MyBatis提供了两种模式去创建SqlSessionFactory:一种是XML配置的方式,另一种是代码方式。要尽量的使用配置文件,这样一方面可以避免硬编码,一方面方便日后配置人员修改。

org.apache.ibatis.session.Configuration在MyBatis中将以一个Configuration类对象的形式存在,存在于整个MyBatis应用的生命期中,以便重复读取和运用。我们可以一次性解析一次配置的XML保存到Configuration对象中。在MyBatis中提供了两个SqlSessionFactory的实现类,DefaultSqlSessionFactory和SqlSessionManager。目前使用的是DefaultSqlSessionFactory。

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

2.1.1 使用XML方式构建

这里以一个简易的XML为例,分别配置了:

  • 包含数据库连接实例的数据源
  • 决定事务范围和控制方式的事务管理器
  • 映射器
<?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>
<!--定义别名 -->
<typeAliases>
<typeAlias alias="role" type="com.learn.chapter2.po.Role"/>
</typeAliases>
<!--定义数据库信息,默认使用development数据库构建环境-->
<environments default="development">
<environment id="development">
<!--采用jdbc事务管理 -->
<transactionManager type="JDBC">
<property name="autoCommit" value="false"/>
</transactionManager>
<!--配置数据库链接信息 -->
<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="root" />
</dataSource>
</environment>
</environments>
<!--定义映射器 -->
<mappers>
<mapper resource="com/learn/chapter2/mapper/roleMapper.xml"/>
</mappers>
</configuration>

然后用代码实现创建SqlSessionFactory:

String resource="mybatis-config.xml";
InputStream inputStream=null;
inputStream=Resources.getResourceAsStream(resource);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);

这里创建了一个XML文件输入流,用SqlSessionFactoryBuilder读取XML的信息 来创建SqlSessionFactory的对象。MyBatis的解析程序会将mybatis-config.xml文件配置信息解析到Configuration类对象里面,然后利用SqlSessionFactoryBuilder读取这个对象,为我们创建SqlSessionFactory。

2.1.2 使用代码方式构建

不推荐这种方式,因为修改环境的时候,要重新编译代码,不利于维护。

     //构建数据库连接池
PooledDataSource dataSource=new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
dataSource.setUsername("root");
dataSource.setPassword("root");
//构建数据库事务方式
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);
//使用SqlSessionFactoryBuilder构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(configuration);
return sqlSessionFactory;

采用代码方式一般是需要加入自己特性的时候才会用到,例如数据源配置的信息要求是加密的时候,我们需要把它转化出来。大部分情况下不建议用这个方式来创建SqlSessionFactory

2.1.2 创建SqlSession

在MyBatis中SqlSession接口的实现类有两个,分别是DefaultSqlSession和SqlSessionManager。我们构建了SqlSessionFactory,然后生成门面接口SqlSession。SqlSession接口类似于一个JDBC中的Connection接口对象,我们需要保证每次用完正确关闭它。

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

SqlSession的用途主要是获取映射器以及直接通过命名信息去执行SQL返回结果。

2.1.3 映射器

由Java接口和XML文件(或注解)共同组成,作用如下:

  • 定义参数类型
  • 描述缓存
  • 描述SQL语句
  • 定义查询结果和POJO的映射关系

使用XML文件配置方式实现Mapper,第一步给出Java接口:

public interface RoleMapper {
public Role getRole(Long id);
public int deleteRole(Long id);
public int insertRole(Role role);
}

第二步给出一个映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learn.chapter2.mapper.RoleMapper">
<select id="getRole" resultType="role" parameterType="long">
select id,role_name as roleName,note from t_role where id=#{id}
</select>
</mapper>
  • MyBatis会读取这个配置文件,生成映射器
  • 定义一个命名空间为com.learn.chapter2.mapper.RoleMapper的SQL Mapper,这个命名空间和我们定义的接口的全限定名要一致
  • 用一个select元素定义一个查询SQL,id是getRole,和我们接口中的方法名是一致的
  • #{id}为这条SQL的参数。而SQL列的别名和POJO的属性名称保持一致。那么MyBatis就会把这条语句查询的结果自动映射到我们需要的POJO属性上

现在可以使用SqlSession来获取这个Mapper:

RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);
Role role=roleMapper.getRole(1L);

2.1.4 Java注解方式实现Mapper

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

不推荐注解的方式,如果系统简单的话,使用注解方式也不失为一个好办法。

注册这个接口为映射器:

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

2.1.4 一些疑问

这要注意的是在MyBatis中保留这iBatis,通过命名空间+SQL id 的方式发送SQL并返回数据的形式,而不需要获取映射器:

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

2.2 生命周期

SqlSessionFactoryBuilder:只存在于方法的局部,作用是生成SessionFactory对象。

SqlSessionFactory:存在于MyBatis应用的整个生命周期中,唯一责任是创建SqlSession,要采用单例模式。

SqlSession:一个会话,相当于JDBC的一个Connection对象,他的生命周期应该是在请求数据库处理事务的过程中。它是一个线程不安全的对象。

Mapper:一个接口,没有任何实现类,作用是发送SQL。因此它应该在一个SqlSession事务方法之内,是一个方法级别的东西。

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

2.3 实例

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

《深入浅出MyBatis技术原理与实战》——1.简介,2.入门

代码清单2-11:log4j.properties

log4j.rootLogger=DEBUG, stdout
log4j.logger.org=BEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: m%n

其中参数的解释见博文:http://www.cnblogs.com/alipayhutu/archive/2012/06/21/2558249.html

其次,构建SessionFactory,我们需要配置文件,代码:

<?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>
<!--定义别名 -->
<typeAliases>
<typeAlias alias="role" type="com.learn.chapter2.po.Role"/>
</typeAliases>
<!--定义数据库信息,默认使用development数据库构建环境-->
<environments default="development">
<environment id="development">
<!--采用jdbc事务管理 -->
<transactionManager type="JDBC">
<property name="autoCommit" value="false"/>
</transactionManager>
<!--配置数据库链接信息 -->
<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="root" />
</dataSource>
</environment>
</environments>
<!--定义映射器 -->
<mappers>
<mapper resource="com/learn/chapter2/mapper/roleMapper.xml"/>
</mappers>
</configuration>

构建SqlSessionFactory

package com.learn.chapter2.util;

import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger; import org.apache.commons.logging.Log;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class SqlSessionFactoryUtil {
//SqlSessionFactory对象
private static SqlSessionFactory sqlSessionFactory=null;
//类线程锁
private static final Class Class_LOCK=SqlSessionFactoryUtil.class;
/**
* 私有化构造参数
*/
private SqlSessionFactoryUtil(){} /**
* 构建SqlSessionFactory
*/
public static SqlSessionFactory initSqlSessionFactory(){
String resource="mybatis-config.xml";
InputStream inputStream=null;
try{
inputStream=Resources.getResourceAsStream(resource);
}catch (Exception e) {
Logger.getLogger(SqlSessionFactoryUtil.class.getName()).log(Level.SEVERE, null,e);
}
synchronized (Class_LOCK) {
if(sqlSessionFactory==null){
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
}
}
return sqlSessionFactory;
}
/**
* 打开SqlSession
*/
public static SqlSession openSqlSession(){
if(sqlSessionFactory==null){
initSqlSessionFactory();
}
return sqlSessionFactory.openSession();
}
}

上面要注意的部分:

1. 类线程锁:private static final Class Class_LOCK=SqlSessionFactoryUtil.class;

实现一个POJO类——Role.java

package com.learn.chapter2.po;

public class Role {
private Long id;
private String roleName;
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}

还需要一个映射器的描述:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learn.chapter2.mapper.RoleMapper">
<select id="getRole" resultType="role" parameterType="long">
select id,role_name as roleName,note from t_role where id=#{id}
</select>
<insert id="insertRole" parameterType="role">
insert into t_role(role_name,note) values (#{roleName},#{note})
</insert>
<delete id="deleteRole" parameterType="long">
delete from t_role where id=#{id}
</delete>
</mapper>

定义相应的接口:

package com.learn.chapter2.mapper;

import com.learn.chapter2.po.Role;

public interface RoleMapper {
public Role getRole(Long id);
public int deleteRole(Long id);
public int insertRole(Role role);
}

使用一个main方法来测试:

package com.learn.chapter2.main;

import org.apache.ibatis.session.SqlSession;

import com.learn.chapter2.mapper.RoleMapper;
import com.learn.chapter2.po.Role;
import com.learn.chapter2.util.SqlSessionFactoryUtil; public class Chapter2Main {
public static void main(String[] args){
SqlSession sqlSession=null;
try{
sqlSession=SqlSessionFactoryUtil.openSqlSession();
RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);
Role role=new Role();
role.setRoleName("testName");
role.setNote("testRole");
roleMapper.insertRole(role);
roleMapper.deleteRole(1L);
sqlSession.commit();
}catch (Exception e) {
System.err.println(e.getMessage());
sqlSession.rollback();
}finally{
if(sqlSession!=null){
sqlSession.close();
}
}
}
}