一、 Mybatis 简述
MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由
apache software foundation 迁移到了 google code, 并且改名为 MyBatis 。
2013 年 11 月迁移到 Github。
iBATIS 一词来源于"internet" 和"abatis" 的组合, 是一个基于 Java
的持久层框架。 iBATIS 提供的持久层框架包括 SQL Maps 和 Data Access
Objects(DAO)
MyBatis 是一个支持普通 SQL 查询, 存储过程和高级映射的优秀持久层
框架。 MyBatis 消除了几乎所有的 JDBC
代码和参数的手工设置以及对结果集的检索封装。 MyBatis 可以使用简单的 XML
或注解用于配置和原始映射, 将接口和 Java 的 POJO(Plain Old Java
Objects, 普通的 Java 对象) 映射成数据库中的记录。
mybatis 不是一个完全的 orm 框架, Mybatis 需要程序员自己写 sql, 但
是也存在映射(输入参数映射, 输出结果映射) , 学习门槛 mybatis 比
hibernate 低; 同时灵活性高, 特别适用于业务模型易变的项目, 使用范围
广。
简单概括:更加简化 jdbc 代码,简化持久层,sql 语句从代码中分离,利用反
射,将表中数据与 java bean 属性一一映射 即 ORM(Object Relational
Mapping 对象关系映射).
使用范围:
在日常的开发项目中, 如中小型项目, 例如 ERP,需求与关系模型相对固
定建议使用 Hibernate, 对于需求不固定的项目, 比如: 互联网项目,
建议使用 mybatis, 因为需要经常灵活去编写 sql 语句。 总之, mybatis
成为当下必须学习掌握的一个持久层框架。
二、 MyBatis 功能架构图
从上面我们可以看到 mybatis 的功能架构分为三层:
API 接口层:提供给外部使用的接口 API,开发人员通过这些本地 API
来操纵
数据库。接口层接收到调用请求就会调用数据处理层来完成具体的数据处理。
数据处理层:负责具体的 SQL 查找、SQL 解析、SQL
执行和执行结果映射处
理等。它主要的目的是根据调用的请求完成一次数据库操作。
基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和
缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据
处理层提供最基础的支撑。
三、 MyBatis 框架体系结构
1、 Configuration-mybatis 配置
1)、 与 spring 一样,可以通过配置文件或注解的形式进行配置;
2)、 SqlMapConfig.xml, 此文件作为 mybatis 的全局配置文件, 配置了
mybatis 的运行环境等信息;
3)、 mapper 文件即 sql 映射文件, 文件中配置了操作数据库的 sql 语句。
此文件需要在 SqlMapConfig.xml 中加载。
4)、 有了配置文件后, 通过 mybatis 环境等配置信息构造
SqlSessionFactory即会话工厂
5)、 由会话工厂创建 sqlSession 即会话, 操作数据库需要通过 sqlSession
进行
6)、 sqlSession 使用 Executor(数据库操作执行器口)操作数据库, 同
Executor 具体实现类实现指定 dao 层数据访问操作。
2、 Mapped Statement
框架底层封装对象(sql 语句、 输入参数、 输出结果类型) , 它包装了
mybatis 配置信息及 sql 映射信息等, mapper 文件(即 Mapper.xml)中一个
sql对应一个 Mapped Statement 对象, sql 的 id 即是 Mapped statement 的
id。
3、 Sql 的输入映射参数
基本和简单类型、 HashMap、 自定义 POJO 等。 输入参数映射就是 jdbc 编
程中对 preparedStatement 设置参数, Executor 通过 Mapped Statement
在执行 sql 前将输入的 java 对象映射至 sql 中。
4、 Sql 的输出映射参数
基本和简单类型、 HashMap、 自定义 POJO。 Statement 对 sql 执行输出
结果进行定义, 输出结果映射过程相当于 jdbc 编程中对结果的解析处理过程,
Executor 通过 Mapped Statement 在执行 sql 后将输出结果映射至 java
对象中。
这里使用 Maven 工程, 对应的 mybatis jar 包下载通过 maven 仓库统一
管理下载。
四、 Mybatis 环境搭建
1.新建 Maven 项目
新建 maven 项目 ,pom 文件添加依赖 jar
junit
junit
4.12
test
org.mybatis
mybatis
3.4.1
mysql
mysql-connector-java
5.1.39
log4j
log4j
1.2.16
2.log4j 日志添加
在 src/main/resources 资源包下添加 log4j 日志输出
properties 文件, 便于查看日志输出信息
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
3.resources 目录下主配置文件添加
新建 mybatis.xml 文件, 并加入配置信息如下(数据库名mybatis,表 user)
4.映射文件添加, 新建 UserMapper.xml 配置文件
5.1修改 pom 配置,设置资源文件夹路径(使java类下的xml包含进配置)
src/main/resources
src/main/java
**/*.properties
**/*.xml
**/*.tld
false
6.新建user类
package com.shsxt.po;
/**
* Created by xlf on 2019/7/8.
*/
public class User {
private Integer id;
private String userName;
private String userPwd;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", userPwd='" + userPwd + '\'' +
'}';
}
}
7.创建单元测试MybaitsTest
package com.shsxt;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import com.shsxt.po.User;
import java.io.IOException;
import java.io.InputStream;
/**
* test mybatis环境搭建
*/
public class MyBatisTest {
@Test
public void test01() throws IOException {
/**
a)、读取mybatis的配置文件
b)、加载配置文件创建SqlSessionFactory
c)、根据SqlSessionFactory创建SqlSession
d)、通过sqlSession操作数据库
e)、处理结果
f)、关闭session
*/
InputStream is= Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession session = build.openSession();
User user = session.selectOne("com.shsxt.mapper.UserMapper.queryUserById", 6);
System.out.println(user);
session.close();
}
@Test
public void test02() throws IOException {
InputStream is= Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is, "test");
SqlSession session = build.openSession();
User user = session.selectOne("com.shsxt.mapper.UserMapper.queryUserById", 6);
System.out.println(user);
session.close();
}
}
五、 Mybatis 配置文件解释
1. properties
这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配
置,亦可通过 properties 元素的子元素来传递。例如:
其中的属性就可以在整个配置文件中使用来替换需要动态配置的属性值。比如:
如何配?
在 config.xml 文件中
并修改原有数据源连接相关配置如下:
即可完成。
2. settings(了解)
这是 MyBatis 修改操作运行过程细节的重要的步骤。下方这个表格描述了这些设
置项、含义和默认值。一般我们用默认即可(详细解释见官网文档)
对应 xml 配置如下(开发中一般采用默认配置即可):
3.typeAliases
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义
仅在于用来减少类完全限定名的冗余。例如:
Configuration 标签下添加(properties标签之后,environment之前加入)
修改 UserMapper.xml 文件
也可以指定一个包名(大家最喜欢的方式),MyBatis
会在包名下面搜索需要的 Java
Bean,(包扫描自动取别名)比如:
每一个在包 com.shsxt.mybatis.po 中的 Java
Bean,在没有注解的情况下,会使用
Bean 的首字母小写的非限定类名来作为它的别名。 比如
com.shsxt.mybatis.po.User 的别名为 user;
若有注解,则别名为其注解值。 注解名@Alias(value="user")
同样 mybatis 已经为我们内建了相应的类型别名,
它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理。
4.typeHandlers 类型处理器
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是
从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成
Java
类型。下表描述了一些默认的类型处理器。
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。
具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承
一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以
选择性地将它映射到一个 JDBC 类型。
5. 对象工厂(objectFactory)
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂
(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。默认情
况下,我们不需要配置,mybatis 会调用默认实现的
objectFactory。从这个类的外部
看,这个类的主要作用就是根据一个类的类型得到该类的一个实体对象,比如,我们
给他一个 User 的 type,他将会给我们一个 Tiger 的实体对象,我们给他一个
java.lang.List 对象,他将会给我们一个 List 的实体对象。**类似于 spring
工厂实例化bean
**6. plugins 插件
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,
MyBatis 允许使用插件来拦截的方法调用包括:
• Executor (sql 执 行 时 , update, query, flushStatements, commit,
rollback,
getTransaction, close, isClosed)
• ParameterHandler (参数的处理, getParameterObject, setParameters)
• ResultSetHandler (结果集的处理, handleResultSets,
handleOutputParameters)
• StatementHandler (申明语句的处理, prepare, parameterize, batch,
update,query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看
MyBatis
的发行包中的源代码。
假设你想做的不仅仅是监控方法的调用,那么你应该很好的了
解正在重写的方法的行为。
因为如果在试图修改或重写已有方法的行为的时候,你很
可能在破坏 MyBatis 的核心模块。
这些都是更低层的类和方法,所以使用插件的时候
要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor
接口,并指定了想要拦截的方法签名即可。
总配置添加
插件 demo:
@Intercepts({
@Signature(
type=Executor.class,
/**
* 拦截所有方法
*/
method="query",
/**
* 参数定义
*/
args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
)
})
public class ExamplePlugin implements Interceptor {
/**
* 每个插件必须实现以下三个方法
*/
/**
* Object intercept(Invocation invocation)是实现拦截逻辑的地方,
* 内部要通过 invocation.proceed()显式地推进责任链前进,也就是调用下一个拦
截器拦截目标方法。
*/
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("intercept");
return invocation.proceed();
} /
**
* Object plugin(Object target) 就是用当前这个拦截器生成对目标 target 的代
理,
* 实际是通过 Plugin.wrap(target,this) 来完成的,把目标 target 和拦截器 this
传给了包装函数。
*/
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器
的 Properties 节点里。
*/
public void setProperties(Properties properties) {
System.out.println(properties.get("hello"));
}
}
7. 配置环境(environments) (熟悉 配多个数据源)
MyBatis 可以配置成适应多种环境, 这种机制有助于将 SQL 映射应用于多种
数据库之中, 现实情况下有多种理由需要这么做。 例如, 开发、
测试和生产环境需要有不同的配置; 或者共享相同 Schema 的多个生产数据库,
想使用相同的 SQL 映射。 许多类似的用例。
不过要记住: 尽管可以配置多个环境, 每个 SqlSessionFactory
实例只能选
择其一。
所以, 如果你想连接两个数据库, 就需要创建两个 SqlSessionFactory 实
例, 每个数据库对应一个。 而如果是三个数据库, 就需要三个实例,
依此类推, 记起来很简单.
• 每个数据库对应一个 SqlSessionFactory 实例
7.1配置mybatis.xml
7.2 db.properties添加另一个数据库连接
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis
username=root
password=123456
driver2=com.mysql.jdbc.Driver
url2=jdbc:mysql://127.0.0.1:3306/mybatis_test
username2=root
password2=123456
7.3测试 sqlSessionFactory
第一个test对应查询mybatis数据库,第二个test对应mybatis_test数据库
*/
public class MyBatisTest {
@Test
public void test01() throws IOException {
/**
a)、读取mybatis的配置文件
b)、加载配置文件创建SqlSessionFactory
c)、根据SqlSessionFactory创建SqlSession
d)、通过sqlSession操作数据库
e)、处理结果
f)、关闭session
*/
InputStream is= Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession session = build.openSession();
User user = session.selectOne("com.shsxt.mapper.UserMapper.queryUserById", 6);
System.out.println(user);
session.close();
}
@Test
public void test02() throws IOException {
InputStream is= Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is, "test");
SqlSession session = build.openSession();
User user = session.selectOne("com.shsxt.mapper.UserMapper.queryUserById", 6);
System.out.println(user);
session.close();
}
}
8. 事务管理器(transactionManager) (了解)
在 MyBatis 中有两种类型的事务管理器(也就
type="[JDBC|MANAGED]") :
• JDBC -- 这个配置就是直接使用了 JDBC 的提交和回滚设置, 它依赖于
从数据源得到的连接来管理事务范围。
• MANAGED -- 这个配置几乎没做什么。 它从来不提交或回滚一个连接,
而是让容器来管理事务的整个生命周期。 默认情况下它会关闭连接, 然而
一些容器并不希望这样, 因此需要将 closeConnection 属性设置为 false 来
阻止它默认的关闭行为。 例如:
如果你正在使用 Spring + MyBatis, 则没有必要配置事务管理器, 因为
Spring 模块会使用自带的管理器来覆盖前面的配置。
9.dataSource 数据源(了解)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
数据源类型有三种:UNPOOLED,POOLED,JNDI
9.1UNPOOLED
这个数据源的实现只是每次被请求时打开和关闭连接。虽然有一点慢,它对在及时
可用连接方面没有性能要求的简单应用程序是一个很好的选择。
不同的数据库在这方
面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理
想的。
• driver -- 这是 JDBC 驱动的 Java 类的完全限定名(并不是 JDBC
驱动中可能
包含的数据源类)。
• url -- 这是数据库的 JDBC URL 地址。
• username -- 登录数据库的用户名。
• password -- 登录数据库的密码。
• defaultTransactionIsolationLevel -- 默认的连接事务隔离级别。
作为可选项,你也可以传递属性给数据库驱动。要这样做,属性的前缀为"driver.",例
如:
• driver.encoding=UTF8
这将通过 DriverManager.getConnection(url,driverProperties)方法传递值
为 UTF8 的 encoding 属性给数据库驱动。
**9.2POOLED
**这种数据源的实现利用"池"的概念将 JDBC
连接对象组织起来,避免了创建新的连
接实例时所必需的初始化和认证时间。 这是一种使得并发 Web
应用快速响应请求的
流行处理方式。(一般选用这种)
• poolMaximumActiveConnections -- 在任意时间可以存在的活动(也就是正
在使用)连接数量,默认值:10
• poolMaximumIdleConnections -- 任意时间可能存在的空闲连接数。
• poolMaximumCheckoutTime -- 在 被 强 制 返 回 之 前 , 池 中 连 接 被
检 出
(checked out)时间,默认值:20000 毫秒(即 20 秒)
• poolTimeToWait --
这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000
毫秒(即 20 秒)。
• poolPingQuery -- 发送到数据库的侦测查询,用来检验连接是否处在正常工
作秩序中并准备接受请求。默认是"NO PING QUERY SET" ,这会导致多数数
据库驱动失败时带有一个恰当的错误消息。
• poolPingEnabled -- 是否启用侦测查询。若开启,也必须使用一个可执行的
SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认
值:false。
• poolPingConnectionsNotUsedFor -- 配置 poolPingQuery 的使用频度。这
可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认
值:0(即所有连接每一时刻都被侦测 --- 当然仅当 poolPingEnabled 为
true 时适用)。
•
**9.3JNDI
**这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以
集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
• initial_context -- 这个属性用来在 InitialContext 中寻找上下文(即,
initialContext.lookup(initial_context)) 。 这是个可选属性, 如果忽略,
那么
data_source 属性将会直接从 InitialContext 中寻找。
• data_source -- 这是引用数据源实例位置的上下文的路径。 提供了
initial_context 配置时会在其返回的上下文中进行查找,
没有提供时则直接在
InitialContext 中查找。和其他数据源配置类似,
可以通过添加前缀"env."直接把属性传递给初始上下文。 比如:
• env.encoding=UTF8
这就会在初始上下文(InitialContext) 实例化时往它的构造方法传递值
为 UTF8 的 encoding 属性。
10.mappers 映射器(四种配置) (熟悉)
这里是告诉 mybatis 去哪寻找映射 SQL 的语句。可以使用类路径中的资源引
用,或者使用字符,输入确切的 URL 引用。
包扫描条件:
1.同时存在mapper.xml和mapper.java
2.必须在同一个包里面
这些配置会告诉了 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL
映射文
件。
六、 封装 Dao
新建接口 UserDao 以及对应实现类 UserDaoImpl
6.1接口定义:
public interface UserMapper {
public User queryUserById(int id);
}
6.2实现类
public class UserDaoImpl implements UserMapper {
private SqlSessionFactory build;
public UserDaoImpl(SqlSessionFactory build) {
this.build = build;
}
@Override
public User queryUserById(Integer id){
SqlSession session = build.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(id);
session.close();
return user;
}
6.3测试test
public class UserDaoImplTest {
SqlSessionFactory build;
UserDaoImpl userDao;
@Before
public void init() throws IOException {
InputStream is= Resources.getResourceAsStream("mybatis.xml");
build = new SqlSessionFactoryBuilder().build(is);
userDao = new UserDaoImpl(build);
}
@Test
public void queryUserById() throws Exception {
User user = userDao.queryUserById(6);
System.out.println(user);
}