Mybatis 文档篇 1:开始使用
1 什么是 MyBatis?
MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings.
是什么:持久层框架,支持定制化SQL、存储过程、高级映射;
MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results.
优势:避免 JDBC 代码、手动设置参数、获取结果集 ;
MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records.
怎么做:使用简单 XML 或注解来配置或映射原生信息,将Map 接口、Java POJO对象映射成数据库中的记录。
2 如何使用?
2.1 导入 MyBatis:
- 下载 jar/source 包方式:mybatis-3
- maven 依赖方式:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
2.2 构建 SqlSessionFactory
Every MyBatis application centers around an instance of SqlSessionFactory.
重要性:每个 Mybatis 应用都是以一个 SqlSessionFactory 实例为中心的。
A SqlSessionFactory instance can be acquired by using the SqlSessionFactoryBuilder.
父母何人:SqlSessionFactory 实例可以通过 SqlSessionFactoryBuilder 获得。
SqlSessionFactoryBuilder can build a SqlSessionFactory instance from an XML configuration file, or from a custom prepared instance of the Configuration class.
怎么来:SqlSessionFactoryBuilder 可以通过两种方式构建 SqlSessionFactory 实例:XML 配置文件、 预先定制的 Configuration 的实例。
2.2.1 通过 XML 配置文件构建
It is recommended that you use a classpath resource for this configuration, but you could use any InputStream instance, including one created from a literal file path or a file:// URL.
建议使用类路径下的资源文件进行配置,但也可以使用任意 InputStream 实例,包括:字符串形式、file:// 的 URL 形式的文件路径。
MyBatis includes a utility class, called Resources, that contains a number of methods that make it simpler to load resources from the classpath and other locations.
Resources 工具类:Mybatis 提供 Resources 工具类,该工具类包含很多方法用来简化从类路径或者其他路径加载资源文件。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
The configuration XML file contains settings for the core of the MyBatis system, including a DataSource for acquiring database Connection instances, as well as a TransactionManager for determining how transactions should be scoped and controlled.
功能:XML 配置文件包含 Mybatis 的核心配置,包括:提供数据库连接实例的数据源(DataSource)、决定事务作用域和控制的事务管理器(TransactionManager)。
一个简单的 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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
2.2.2 通过 Java 类配置
If you prefer to directly build the configuration from Java, rather than XML, or create your own configuration builder, MyBatis provides a complete Configuration class that provides all of the same configuration options as the XML file.
XML 功能:如果你更喜欢直接从 Java 类中构建配置而不是通过 XML,或者构建你自己的配置构建器,Mybatis 提供一个完整的 Configuration 类,该类包含和 XML 文件相同的配置项。
//获取 DataSource 可用:DataSource dataSource = new PooledDataSource(driver, url, username, password);
//官方文档中示例如下
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
Notice in this case the configuration is adding a mapper class. Mapper classes are Java classes that contain SQL Mapping Annotations that avoid the need for XML.
Mapper 类:这个例子中 configuration 添加了一个映射器类。映射器类是包含 SQL 映射注解从而避免依赖 XML 的 Java 类。
However, due to some limitations of Java Annotations and the complexity of some MyBatis mappings, XML mapping is still required for the most advanced mappings (e.g. Nested Join Mapping).
XML 重要性:然而,由于 Java 注解的一些限制和 Mybatis 映射的复杂性,XML 映射在某些高级映射中依然是需要的(如嵌套 Join 映射)。
For this reason, MyBatis will automatically look for and load a peer XML file if it exists (in this case, BlogMapper.xml would be loaded based on the classpath and name of BlogMapper.class)
因此,如果存在匹配的 XML 文件,Mybatis 会自动寻找并加载它(此例中,BlogMapper.xml 将基于类路径和 BlogMapper.class 的类名被加载)。
2.3 构建 SqlSession
The SqlSession contains absolutely every method needed to execute SQL commands against the database.
功能:SqlSession 包含了面向数据库执行 SQL 命令所需的所有方法。
获取 SqlSession 的方式:
SqlSession session = sqlSessionFactory.openSession(true);
注意:执行更改数据库的操作都需要传入autoCommit 参数值为 true,即自动提交。默认为 false。
可查看源码 DefaultSqlSessionFactory.java:
//...
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
//...
2.3.1 直接使用 SqlSession 实例
直接使用 SqlSession 示例来映射 SQL 语句。
SqlSession session = sqlSessionFactory.openSession();
try {
Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
session.close();
}
2.3.2 使用 Mapper 类
Using an interface (e.g. BlogMapper.class) that properly describes the parameter and return value for a given statement
是什么:使用一个接口(如 BlogMapper.class),该接口描述了给定语句的参数和返回值。
you can now execute cleaner and more type safe code, without error prone string literals and casting.
优势:运行更简洁明晰、类型安全的代码,不用担心易错的字符串字面值和强制类型转换。
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
} finally {
session.close();
}
2.4 映射 SQL 语句
<?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="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
It defines a name for the mapped statement “selectBlog”, in the namespace “org.mybatis.example.BlogMapper.
在命名空间 “org.mybatis.example.BlogMapper” 中定义了一个名为 “selectBlog” 的映射语句。
2.4.1 直接调用
通过指定完全限定名 “org.mybatis.example.BlogMapper.selectBlog” 来调用这条语句:
Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
2.4.2 通过 Mapper 调用
This name can be directly mapped to a Mapper class of the same name as the namespace, with a method that matches the name, parameter, and return type as the mapped select statement.
这个命名可以直接映射到命名空间中同名的 Mapper 类,并将已映射的 select 语句中的命名、参数和返回类型匹配成方法。
This allows you to very simply call the method against the Mapper interface:
这将允许你非常简单地通过你上面看到的 Mapper 接口调用方法。
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
First, it doesn't depend on a string literal, so it's much safer.
优势一:它不依赖一个字符串字面值,所以它更安全。
Second, if your IDE has code completion, you can leverage that when navigating your mapped SQL statements.
优势二:如果你的 IDE 支持代码补全,就可以在已映射的 SQL 语句基础上利用这个功能。
2.4.3 通过 Java 注解调用
package org.mybatis.example;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
The annotations are a lot cleaner for simple statements, however, Java Annotations are both limited and messier for more complicated statements.
优缺点:注解方式对于简单语句来说更加简洁,但是,Java 注解对于更加复杂的语句有局限且混乱。
2.4.4 总结
Therefore, if you have to do anything complicated, you're better off with XML mapped statements.
最佳选择:如果你必须做一些复杂的操作,最好使用 XML 来映射语句。
It will be up to you and your project team to determine which is right for you, and how important it is to you that your mapped statements be defined in a consistent way. That said, you're never locked into a single approach. You can very easily migrate Annotation based Mapped Statements to XML and vice versa.
总结:使用哪种方式以及对映射语句定义的一致性要求等等这些将取决于你和你的项目团队。换句话说,你没有被拘泥于一种方式,你可以轻松地在基于注解和 XML 语句映射方式之间自由移植和切换。
附:命名解决方案:
命名解析:
-
Fully qualified names (e.g. “com.mypackage.MyMapper.selectAllThings”) are looked up directly and used if found.
完全限定名:用于直接查找和使用,例如:(e.g. “com.mypackage.MyMapper.selectAllThings”)。 -
Short names (e.g. “selectAllThings”) can be used to reference any unambiguous entry. However if there are two or more (e.g. “com.foo.selectAllThings and com.bar.selectAllThings”), then you will receive an error reporting that the short name is ambiguous and therefore must be fully qualified.
短名称(如 “selectAllThings”) :可以作为全局唯一的引用。若包含两个或两个以上的同名(如 “com.foo.selectAllThings 和 com.bar.selectAllThings”),你将收到一份短名称不唯一的错误报告,这种情况必须使用完全限定名。
3 作用域和生命周期
Dependency Injection frameworks can create thread safe, transactional SqlSessions and mappers and inject them directly into your beans so you can just forget about their lifecycle.
不用关注生命周期的场景:依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和 mapper,并将他们直接注入到 bean里,因此你可以忽略它们的生命周期。
3.1 SqlSessionFactoryBuilder
This class can be instantiated, used and thrown away.There is no need to keep it around once you've created your SqlSessionFactory. Therefore the best scope for instances of SqlSessionFactoryBuilder is method scope (i.e. a local method variable).
这个类可以被初始化、使用和丢弃。一旦你创建了 SqlSessionFactory,SqlSessionFactoryBuilder 就没有必要再保留。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(即方法局部变量)。
You can reuse the SqlSessionFactoryBuilder to build multiple SqlSessionFactory instances, but it's still best not to keep it around to ensure that all of the XML parsing resources are freed up for more important things.
你可以重用 SqlSessionFactoryBuilder 去创建多个 SqlSessionFactory 实例,但最好不要一直保留它,以便所有的 XML解析资源能被释放给更重要的事。
3.2 SqlSessionFactory
Once created, the SqlSessionFactory should exist for the duration of your application execution. There should be little or no reason to ever dispose of it or recreate it. It's a best practice to not rebuild the SqlSessionFactory multiple times in an application run. Doing so should be considered a “bad smell”.
SqlSessionFactory 一经创建,就应当在整个应用运行期间存在。没有什么理由可以对它进行清除或重建。应用运行期间的一个最佳实践是不要多次创建 SqlSessionFactory。多次重建被视为“坏味道” 。
Therefore the best scope of SqlSessionFactory is application scope. This can be achieved a number of ways. The simplest is to use a Singleton pattern or Static Singleton pattern.
因此 SqlSessionFactory 最好的作用域是应用作用域。有多种方法可以实现它,最简单的方式就是使用单例模式或者静态单例模式。
3.3 SqlSession
Each thread should have its own instance of SqlSession.Instances of SqlSession are not to be shared and are not thread safe.Therefore the best scope is request or method scope.
每个线程应当持有它自己的 SqlSession 实例。SqlSession 实例是不被共享且不是线程安全的。因此最好的作用域是请求或方法作用域。
Never keep references to a SqlSession instance in a static field or even an instance field of a class. Never keep references to a SqlSession in any sort of managed scope, such as HttpSession of the Servlet framework.
绝对不能将 SqlSession 实例的引用放在类的静态域甚至当做一个类的实例变量。绝对不能将 SqlSession 实例的引用放在任何类型的管理作用域,比如 Servlet 框架中的 HTTPSession。
If you're using a web framework of any sort, consider the SqlSession to follow a similar scope to that of an HTTP request. In other words, upon receiving an HTTP request, you can open a SqlSession, then upon returning the response, you can close it.
如果你使用的是任何一种类型的 web 框架,考虑将 SqlSession 放置于一个和 Http 请求相似的作用域中。换句话说,每次收到一个 HTTP请求,就可以打开一个 SqlSession,每次返回一个响应,就可以关闭这个 SqlSession 。
Closing the session is very important. You should always ensure that it's closed within a finally block.
重要:关闭一个会话是非常重要的。你应当始终确保它在一个 finally 块中被关闭。
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
3.4 Mapper 接口
Mappers are interfaces that you create to bind to your mapped statements.Instances of the mapper interfaces are acquired from the SqlSession. As such, technically the broadest scope of any mapper instance is the same as the SqlSession from which they were requested.
映射器是用来绑定映射的语句而创建的接口。映射接口的实例从 SqlSession 中获取。从技术上来讲,映射器实例的最大作用域与请求它的 SqlSession 的作用域相同。
However, the best scope for mapper instances is method scope. That is, they should be requested within the method that they are used, and then be discarded.
但是,映射器实例的最佳作用域是方法作用域。也就是说,它们应当在使用其的方法中被请求,随即销毁。
They do not need to be closed explicitly. While it's not a problem to keep them around throughout a request, similar to the SqlSession, you might find that managing too many resources at this level will quickly get out of hand.
映射器实例不必被显式地关闭。尽管让它们像 SqlSession 一样存在于整个请求期间不是什么问题,但你可能会发现在难以这一级别管理太多的资源。
Keep it simple, keep Mappers in the method scope.
所以为了保持简单,将映射器放在方法作用域就好。
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// do work
} finally {
session.close();
}
最后
说明:MyBatis 官网提供了简体中文的翻译,但个人觉得较为生硬,甚至有些地方逻辑不通,于是自己一个个重新敲着翻译的(都不知道哪里来的自信...),有些地方同官网翻译有出入,有些倔强地保留了自己的,有的实在别扭则保留了官网的,这些都会在实践中一一更正。鉴于个人英文能力有限,文章中保留了官方文档原英文介绍(个别地方加以调整修剪),希望有缘看到这里的朋友们能够有自己的理解,不会被我可能错误或不合理的翻译带跑偏(〃'▽'〃),欢迎指正!
当前版本:mybatis-3.5.0
官网文档:MyBatis
官网翻译:MyBatis 简体中文
项目实践:MyBatis Learn