原创-转载请注明 http://tramp.cincout.cn/2017/06/22/mybatis-2017-06-22-mybatis-quickstart/
Mybatis 概述
凡是使用过JavaEE
的开发者,我相信对 ORM
不太陌生。很多开发者一上手就想要整合Spring+Mybatis
,有点舍本逐末。因此,我将从Mybatis自身开始,直到学习Mybatis
与Spring
以及Spring Boot
的整合。本文主要介绍Mybatis
的使用场景,以及在只引入Mybatis
相关的依赖时,如何快速地启动Mybatis
。并对Mybatis
的核心类进行简要的介绍。
Mybatis 的两种启动方式
Mybatis 基于XML配置的启动
pom.xml 引入基本的依赖
首先,新建最基本的Maven 项目,在pom.xml
中引入如下依赖:
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
上述依赖中仅仅添加了Mybatis 的核心以及Junit 和MySQL 的驱动程序。
创建数据库
CREATE TABLE Customer
(
id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
name VARCHAR(100),
phoneNumber VARCHAR(100)
);
CREATE UNIQUE INDEX Customer_name_uindex ON Customer (name);
CREATE UNIQUE INDEX Customer_phoneNumber_uindex ON Customer (phoneNumber);
构建mybatis-config.xml 文件
mybatis-config.xml 是Mybatis 的核心配置文件。包括数据库连接、环境、全局设置、映射等核心配置。
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties">
</properties>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
<typeAliases>
<typeAlias type="cn.cincout.tech.black.hole.mybatis.domain.Customer" alias="Customer"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<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/CustomerMapper.xml"/>
</mappers>
</configuration>
jdbc.properties 配置
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root
核心的领域对象为Customer
:
public class Customer {
private Long id;
private String name;
private String phoneNumber;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
'}';
}
}
mapper 映射文件:
<?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="cn.cincout.tech.black.hole.mybatis.inf.mapper.CustomerMapper">
<select id="selectCustomer" resultType="Customer">
select * from Customer where id = #{id}
</select>
</mapper>
在此处有两点需要我们额外的注意:
-
namespace 对我们定义的映射器进行了命名空间的隔离,并不要求我们真的在
cn.cincout.tech.black.hole.mybatis.inf.mapper
路径下拥有对应的接口 - 当我们希望通过
SqlSession.getMapper()
直接采用接口进行DAO
操作时,我们可以定义与命名空间完全对应的接口以及方法映射。
启动 Mybatis
public class MybatisStarterXml {
public static void main(String[] args) {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
SqlSessionFactory sessionFactory = null;
SqlSession sqlSession = null;
try {
inputStream = Resources.getResourceAsStream(resource);
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sessionFactory.openSession();
// (1)
Customer customer = sqlSession.selectOne("cn.cincout.tech.black.hole.mybatis.inf.mapper.CustomerMapper.selectCustomer", 1);
System.out.println(customer);
// (2)
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
System.out.println(customerMapper.selectCustomer(1));
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 在
(1)
处,我们直接访问了命名空间定义的select
查询方法,结果正确 - 在
(2)
处,我们定义了与CustomerMapper.xml
配置的namespace
对应的接口,通过获取接口的代理来对数据库进行访问。
运行结果如下:
Customer{id=1, name='zhang', phoneNumber='18511929810'}
Customer{id=1, name='zhang', phoneNumber='18511929810'}
Mybatis 基于Java配置的启动
这里所说的Mybatis
基于Java 的配置,并不是指Mybatis 的注解映射。注解映射在后文会有深入的介绍。个人比较偏好于直接Java 类来配置。此处的Java 配置是指mybatis-confg.xml
的内容通过Java直接配置:
DataSource dataSource = new org.apache.ibatis.datasource.pooled.PooledDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(CustomerMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
上面的配置近似文章开头的xml 配置。
MyBatis 核心类及生命周期
理解Mybatis
核心类的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。
对象生命周期和依赖注入框架 依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器(mapper)并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。后文的MyBatis-Spring 整合会对该内容进行介绍。
由上述的快速启动案例可以总结Mybatis
的核心类如下:
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理作用域中,比如 Servlet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。映射器实例(Mapper Instances)
映射器是创建用来绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,映射器实例的最大作用域是和 SqlSession 相同的,因为它们都是从 SqlSession 里被请求的。尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可废弃。
源代码
本工程源代码可以从github
获取。源代码
总结
本文主要实现了Mybatis
的快速启动。对理解Mybatis 在Java 开发中的位置有一定的帮助。Mybatis 并不需要一定与Spring 绑定才能工作。任何想通过Java 访问关系型数据局的场景都可以使用。