Mybatis: 入门

简介

Mybatis框架的核心组成部分:mybatis-config.xml、SqlSession、Executor、StatementHandler、ResultSetHandler。

原理

JDBC数据库操作

1
2
3
4
5
6
7
8
9
10
11
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test";
Connection con = DriverManager.getConnection(url, "username","password");
String sql = "SELECT * FROM table_name WHERE id = ?";
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, 10);
ResultSet rs = ps.executeQuery();
while(rs.next()){
System.out.println("id:" + rs.getInt(1));
}
con.close();

流程: 建立连接(数据源配置信息)->传递sql(sql语句)->传递参数(sql参数)->sql执行->处理结果(映射关系)->关闭连接。

ORM框架的本质是抽取共性、封装逻辑

括号中代表变化的部分。

  • 数据源配置信息:配置文件,在启动时从配置文件中读取并建立数据源对象。
  • Sql语句:配置文件(代码),在启动时抽取解析,以的形式存储。
  • Sql参数:java bean对象,在执行前从对象中抽取。
  • 映射关系:配置文件(代码),根据映射关系读取结果集并创建java bean返回。

参考:深入理解Mybatis工作原理

配置文件

Mybatis的全局配置文件 mybatis-config.xml,配置的内容有:

  • properties(属性)
    先解析其子节点<property>属性,再解析resource属性,后者会覆盖前者。
  • settings(全局配置参数)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • environments(环境集合属性对象)
    • environment(环境子属性对象)
      • transactionManager(事务管理)
      • dataSource(数据源)
  • mappers(映射器)

具体参见:Mybatis3

SqlSession

Mybatis的程序入口是SqlSessionFactoryBuilder,它的作用是通过mybatis-config.xml配置文件创建Configuration对象,然后通过build()方法创建SqlSessionFactory对象,而SqlSessionFactory主要功能是创建SqlSession对象。

SqlSession对象的主要功能是完成一次数据库的访问和结果的映射,不是线程安全的。SqlSession对数据库的操作都是通过Executor来完成的。

SqlSession对象的getMapper()方法,Mybatis会根据传入的接口类型和对应的XML配置文件生成一个代理对象(Mapper对象)。通过这个Mapper对象就可以访问SqlSession对象,然后执行相应的sql操作。

Executor

Executor是在调用SqlSessionFactoryopenSession()过程中被创建的,默认创建的类型是SimpleExecutorExecutor对象的主要功能是调用StatementHandler访问数据库。

DefaultSessionFactory
1
2
3
4
5
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
...
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
}

Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
// 默认为true
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

StatementHandler

StatementHandler是真正访问数据库的地方,在进行sql操作后,会调用ResultSetHandler处理查询结果。

在解析sql语句时,会先调用ParameterHandlersetParameters()方法设置参数,对于每个参数再调用相应的TypeHandlersetParameter()去设置。

ResultSetHandler在对查询结果进行处理时,也会调用TypeHandler对相应类型的属性进行处理。

实例

PO

Actor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package model;
import java.util.Date;
public class Actor {
private Short actorId;
private String firstName;
private String lastName;
private Date lastUpdate;
public Short getActorId() {
return actorId;
}
public void setActorId(Short actorId) {
this.actorId = actorId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName == null ? null : firstName.trim();
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName == null ? null : lastName.trim();
}
public Date getLastUpdate() {
return lastUpdate;
}
public void setLastUpdate(Date lastUpdate) {
this.lastUpdate = lastUpdate;
}
@Override
public String toString() {
return "Actor{" +
"actorId=" + actorId +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", lastUpdate=" + lastUpdate +
'}';
}
}

映射文件

ActorMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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="dao.ActorMapper">
<resultMap id="BaseResultMap" type="model.Actor">
<id column="actor_id" jdbcType="SMALLINT" property="actorId" />
<result column="first_name" jdbcType="VARCHAR" property="firstName" />
<result column="last_name" jdbcType="VARCHAR" property="lastName" />
<result column="last_update" jdbcType="TIMESTAMP" property="lastUpdate" />
</resultMap>
<sql id="Base_Column_List">
actor_id, first_name, last_name, last_update
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Short" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from actor
where actor_id = #{actorId,jdbcType=SMALLINT}
</select>
</mapper>

dao接口

Mybatis会根据接口实现相应的代理对象。

ActorMapper.java
1
2
3
4
5
6
7
8
package dao;
import model.Actor;
public interface ActorMapper {
Actor selectByPrimaryKey(Short actorId);
}

配置文件

mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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>
<properties resource="config.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/ActorMapper.xml"/>
</mappers>
</configuration>

测试

1
2
3
4
5
6
7
8
9
String resource = "mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = factory.openSession();
ActorMapper mapper = session.getMapper(ActorMapper.class);
Actor actor = mapper.selectByPrimaryKey((short) 1);
System.out.println(actor);
session.close();

Mybatis和Hibernate

Hibernate:标准的ORM框架,自动生成sql语句,对sql语句进行优化、修改较困难。

Mybatis:专注sql本身,程序员自己维护sql语句,sql修改优化比较方便。

Wiki: Unlike ORM frameworks, MyBatis does not map Java objects to database tables but Java methods to SQL statements.