入门-基础使用
安装
要使用 MyBatis
, 只需将mybatis-x.x.x.jar文件置于类路径(classpath
)中即可。
如果使用 Maven
来构建项目,则需将下面的依赖代码置于pom.xml
文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
把相应的字段复制进项目的pom.xml
文件等待maven自动下载即可。
从 XML 中构建 SqlSessionFactory
每个基于MyBatis
的应用都是以一个SqlSessionFactory
的实例为核心的。SqlSessionFactory
的实例可以通过SqlSessionFactoryBuilder
获得。而 SqlSessionFactoryBuilder
则可以从 XML
配置文件或一个预先配置的Configuration
实例来构建出 SqlSessionFactory
实例。
从 XML 文件中构建 SqlSessionFactory
的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream
)实例,比如用文件路径字符串或file:// URL
构造的输入流。MyBatis
包含一个名叫 Resources
的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
tips:这里的
Resource
传入的是路径,不是现在的包名。
<?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>
这里面的参数我们在后面会详细的说明,这里简单的说明一下,Configuration
之间的配置文件的主体,我们在这个里面可以设置很多标签。现在只有environments
标签和mappers
标签。
environments
标签指的是我们mybatis
运行的环境,我们可以配置多个环境,但是mybatis
在启动后只能使用其中的一个环境.environment
元素体中包含了事务管理和连接池的配置。
mapper
是Mapper.xml
对应的路径。我们在写完第一个Mapper
后学会配置。包含了一组映射器(mapper),这些映射器的XML
映射文件包含了 SQL
代码和映射定义信息。
当然,还有很多可以在XML
文件中配置的选项,上面的示例仅罗列了最关键的部分。
注意 XML 头部的声明,它用来验证XML
文档的正确性
(略-非主线)不使用 XML 构建 SqlSessionFactory
如果你更愿意直接从 Java 代码而不是 XML 文件中创建配置,或者想要创建你自己的配置建造器,MyBatis 也提供了完整的配置类,提供了所有与 XML 文件等价的配置项。
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);
注意该例中,configuration
添加了一个映射器类(mapper class
)。映射器类是 Java
类,它们包含 SQL
映射注解从而避免依赖 XML
文件。不过,由于Java
注解的一些限制以及某些 MyBatis
映射的复杂性,要使用大多数高级映射(比如:嵌套联合映射),仍然需要使用XML
配置。有鉴于此,如果存在一个同名 XML 配置文件,MyBatis
会自动查找并加载它(在这个例子中,基于类路径和BlogMapper.class
的类名,会加载 BlogMapper.xml
)。具体细节稍后讨论。
从 SqlSessionFactory 中获取 SqlSession
既然有了 SqlSessionFactory
,顾名思义,我们可以从中获得 SqlSession
的实例。SqlSession
提供了在数据库执行SQL
命令所需的所有方法。你可以通过SqlSession
实例来直接执行已映射的SQL
语句。例如:
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
诚然,这种方式能够正常工作,对使用旧版本 MyBatis
的用户来说也比较熟悉。但现在有了一种更简洁的方式——使用和指定语句的参数
和返回值相匹配
的接口(比如 BlogMapper.class
),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
例如:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}
现在我们来探究一下这段代码究竟做了些什么。
探究已映射的 SQL 语句
现在你可能很想知道 SqlSession
和 Mapper
到底具体执行了些什么操作,但 SQL
语句映射是个相当广泛的话题,可能会占去文档的大部分篇幅。 但为了让你能够了解个大概,这里会给出几个例子。
在上面提到的例子中,一个语句既可以通过XML
定义,也可以通过注解
定义。我们先看看XML
定义语句的方式,事实上MyBatis
提供的所有特性都可以利用基于 XML
的映射语言来实现,这使得 MyBatis
在过去的数年间得以流行。
如果你用过旧版本的 MyBatis
,你应该对这个概念比较熟悉。 但相比于之前的版本,新版本改进了许多 XML
的配置,后面我们会提到这些改进。这里给出一个基于XML
映射语句的示例,它应该可以满足上个示例中SqlSession
的调用。
<?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>
为了这个简单的例子,我们似乎写了不少配置,但其实并不多。在一个 XML
映射文件中,可以定义无数个映射语句,这样一来,XML 头部和文档类型声明部分就显得微不足道了。文档的其它部分很直白,容易理解。
它在命名空间 org.mybatis.example.BlogMapper
中定义了一个名为 selectBlog
的映射语句,这样你就可以用全限定名 org.mybatis.example.BlogMapper.selectBlog
来调用映射语句了,就像上面例子中那样:
tips
:namespace
指的是本mapper.xml
映射的接口类.
tips
:id
指的是本mapper.xml
映射的接口类定义的方法
tips
:resultType
指的是本mapper.xml
映射的接口类的方法的返回类型.
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
你可能会注意到,这种方式和用全限定名调用 Java
对象的方法类似。这样,该命名就可以直接映射到在命名空间中同名的映射器类,并将已映射的select
语句匹配到对应名称、参数和返回类型的方法。因此你就可以像上面那样,不费吹灰之力地在对应的映射器接口调用方法,就像下面这样:
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
第二种方法有很多优势,所以我们建议使用第二种通过反射加载的方法. 首先它不依赖于字符串字面值,会更安全一点;其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择到映射好的 SQL
语句。
对于像 BlogMapper
这样的映射器类来说,还有另一种方法来完成语句映射。 它们映射的语句可以不用 XML
来配置,而可以使用Java
注解来配置。比如,上面的XML
示例可以被替换成如下的配置:
package org.mybatis.example;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java
注解不仅力不从心,还会让你本就复杂的 SQL
语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用XML
来映射语句。
选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和XML
的语句映射方式间自由移植和切换。
作用域(Scope)和生命周期
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory
,就不再需要它了。 因此 SqlSessionFactoryBuilder
实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder
来创建多个 SqlSessionFactory
实例,但最好还是不要一直保留着它,以保证所有的 XML
解析资源可以被释放给更重要的事情。
建议使用static
代码块初始化sqLSessionBuilder
, 因为但他试用后我们一般不在使用了.可以释放他了
SqlSessionFactory
SqlSessionFactory
一旦被创建就应该在应用的运行期间一直存在
,没有任何理由丢弃它或重新创建另一个实例。 使用SqlSessionFactory
的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory
被视为一种代码“坏习惯”。因此SqlSessionFactory
的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式
或者静态单例模式
。
单例模式
单例模式(Singleton Pattern)是Java
中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一
的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类
的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
**意图:**保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 避免对资源的多重占用(比如写文件操作)。
缺点: 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
创建类
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
使用类
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();
}
}
实现方法
- 懒汉式,线程不安全
- 懒汉式,线程安全
- 饿汉式
- 双检锁/双重校验锁(DCL,即 double-checked locking)
- 登记式/静态内部类
- 枚举
经验之谈:一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。
SqlSession
每个线程
都应该有它自己的SqlSession
实例。SqlSession
的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
。
绝对不能将 SqlSession
实例的引用放在一个类
的静态域
,甚至一个类的实例变量
也不行。 也绝不能将 SqlSession
实例的引用放在任何类型的托管作用域中,比如 Servlet
框架中的 HttpSession
。
如果你现在正在使用一种 Web
框架,考虑将SqlSession
放在一个和HTTP
请求相似的作用域中。
换句话说,每次收到 HTTP
请求,就可以打开一个SqlSession
,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到finally
块中。 下面的示例就是一个确保 SqlSession
关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
//TODO
}finally{
session.close();
}
在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。
映射器实例
映射器是一些绑定映射语句的接口。映射器接口的实例是从SqlSession
中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的SqlSession
相同。
但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。
映射器实例并不需要被显式地
关闭。尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession
的资源会让你忙不过来。
因此,最好将映射器放在方法作用域内。就像下面的例子一样:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的应用逻辑代码
}
XML配置
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
tips
: 配置文件的标签是有顺序的, 请不要随意放置,否则会报错.
属性(properties)
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java
属性文件中配置这些属性,也可以在 properties
元素的子元素中设置。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
这个例子中的 username
和 password
将会由 properties
元素中设置的相应值来替换。driver
和url
属性将会由config.properties
文件中对应的值来替换。这样就为配置提供了诸多灵活选择。
也可以在 SqlSessionFactoryBuilder.build()
方法中传入属性值。例如:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
或者是
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
如果一个属性在不只一个地方进行了配置,那么,MyBatis
将按照下面的顺序来加载:
- 首先读取在
properties
元素体内指定的属性。 - 然后根据
properties
元素中的resource
属性读取类路径下属性文件,或根据url
属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。 - 最后读取作为
方法参数传递
的属性,并覆盖之前读取过的同名属性
。
因此,通过方法参数传递的属性具有最高优先级,resource/url
属性中指定的配置文件次之,最低优先级的则是properties
元素中指定的属性。
从 MyBatis 3.4.2 开始,你可以为占位符指定一个默认值。例如:
<dataSource type="POOLED">
<!-- ... -->
<property name="username" value="${username:ut_user}"/>
<!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
</dataSource>
这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。例如:
<!--name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true" -->
<properties resource="org/mybatis/example/config.properties">
<!-- ... -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 -->
</properties>
提示 如果你在属性名中使用了 ":"
字符(如:db:username
),或者在 SQL 映射中使用了 OGNL
表达式的三元运算符(如: ${tableName != null ? tableName : 'global_constants'}
),就需要设置特定的属性来修改分隔属性名和默认值的字符。例如:
<properties resource="org/mybatis/example/config.properties">
<!-- ... -->
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- 修改默认值的分隔符 -->
</properties>
<dataSource type="POOLED">
<!-- ... -->
<property name="username" value="${db:username?:ut_user}"/>
</dataSource>
设置(settings)
我们这里直说几个重要的设置属性, 其余的青岛官方文档参考.
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
logImpl
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J |
这是mybatis
的日志系统,内置的为STDOUT_LOGGING
,如果你想或使用这里其他的日志系统, 请自行配置jar
包,或者导入对应的maven
代码.
类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
当这样配置时,Blog
可以用在任何使用 domain.blog.Blog
的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;若有注解,则别名为其注解值。见下面的例子:
@Alias("author") public class Author { ... }
写法
一个配置完整的 settings 元素的示例如下:
<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>
映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:///
形式的 URL),或类名和包名等。例如:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
这些配置会告诉 MyBatis
去哪里找映射文件,剩下的细节就应该是每个 SQL
映射文件了,也就是接下来我们要讨论的。