MyBatis 第一课
Ⅰ第一个MyBatis程序
我们先建立一个数据库 mybatis
,建立一张表tbl_employee
。
方便起见,我们先填充一条记录。
然后我们建立一个和这张表映射的实体类Employee
,完成其 Setter 方法和 toString
方法。
注意,我这里的 firstName
属性和数据库中的字段名就不一样了。
我们先用普通的方法,不用Maven做。
首先要导入几个 jar 包,第一个就是下载的MyBatis中的jar包,然后我们还需要一个MySQL的jar包,为了结果的清晰,我们再加一个日志包 log4j。
log4j 需要配置一个 xml 文件,命名就是 log4j.xml
,不可以更改。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true"
xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
接着我们需要创建一个 MyBatis 的配置文件,我命名为 mybatis.config.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>
我们需要将数据库对应的配置修改一下。
改成自己的数据库配置
对这里不理解的同学可以去看我写的 JdbcTemplate 。
在这个模板的下面,映射了一个文件 BlogMapper.xml
,显然我们也没有这个文件。这个我们后面再看。
接下来我们再建一个测试类,写一个测试方法。
现在我们需要在代码中创建 SqlSessionFactory
。
我们可以直接把官方文档中的代码复制上来。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
第一行的 resource 我们需要改成自己创建的配置文件的路径,
接下来按照文档的步骤我们需要获取 SqlSession
。
获取的语句是 openSession()
。SqlSession
能直接执行已经映射的SQL语句。
然后可以看到文档下面有一个方法 selectOne()
。我们来看一下这个方法。
第一个参数 statement 是 sql 的唯一标识,第二个参数 parameter 是执行 sql 要用的参数。
显然我们连 sql 语句都没有,这时候我们来看文档中说的 SqlSession
实例能执行已经映射的sql语句,再往下翻官方文档,可以看到一个 xml 文件配置的模板,这个显然就是 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>
其中,namespace
是名称空间,我们可以随便起个名字,改成自己的。
而 id 属性就是唯一标识,显然这个块就是查询的。
resultType 属性是说查询的返回值类型,由于我们要查的是 Employee 类,所以可以直接将这个类的全路径写上去。
中间的查询语句我们就照着模板的格式写就可以了。
这里记得要把模板中的 Blog 改成自己要查询的表名。
可以看到后面有一个 #{id}
,它的意思就是从传过来的参数中取得 id 值,就类似于我们在 JDBC 中写的 ?
。
现在再来看之前的 selectOne()
方法,第一个参数是 sql 语句的唯一标识,也就是说我们需要把 id 写上去,但是这样还是有可能会和别人重复,那怎么办呢?这就是我们上面写的 namespace 派上用场的时候了。命名空间是你自己写的,命名空间加上 id ,和别人重复的概率就很小了,所以在写唯一标识符的时候,尽量用这个写法。
比如我们要查询 1 号员工。
在使用完成之后,我们需要把 SqlSession
关掉,所以我们可以直接写到 finally 中。
或者直接换成更简洁的写法:
现在还差一步,就是修改一开始的配置文件,我们说还没有写的那个xml文件就是上面的 sql 映射文件。
可以看到测试结果如下:
其他的属性都和我们在数据中写入的一样,只有 firstName
由于和数据库中的名称不同所以输出的是 null
。
有一个方法是写 sql 语句的时候对查询的字段使用别名。
select id, first_name firstName, gender, email from tbl_employee where id = #{id}
这样一条数据的查询我们就通过 MyBatis 完成了。
最后我们再总结一下这个流程:
-
根据xml配置文件,创建一个SqlSessionFactory对象,xml文件中需要配置数据源一些运行环境信息
-
写sql映射文件,这个文件中配置了每一个sql以及sql的封装规则等
-
将sql映射文件注册在全局配置文件中
-
写代码:
4.1 根据全局配置文件得到SqlSessionFactory对象4.2 使用SqlSession工厂,获取SqlSession对象,一个SqlSession代表和数据库的一次会话,利用它完成数据的增删改查,用完需要将其关闭
4.3 使用sql的唯一标识来告诉MyBatis执行哪个sql, sql都是保存在sql映射文件中的
Ⅱ 接口式编程的实现
首先我们先定义一个接口,它只有一个方法,就是根据 id 返回 Employee
对象。
这个接口我们并不需要实现类,因为我们从数据库中查询,也是根据 id 然后返回一个 Employee
对象,所以我们可以把接口和映射文件动态绑定。
要怎么绑定呢?上面我们的 namespace 是自己随便写的,现在就不能随便写了,要写这个接口的全类名。
接着,我们需要把 sql 的 id 改成接口的方法名。
然后我们来写一下测试代码。
前面的步骤是一样的,首先我们要获取 SqlSessionFactory
对象,然后再使用SqlSessionFactory
对象获取 SqlSession
对象。
接下来就不是直接使用selectOne()
方法获取 Employee 对象了,而是通过接口,获得接口的实现类,再调用接口类的方法,由于接口和 sql 映射文件是动态绑定的,所以我们直接调用接口的方法就可以获取数据库中对应的对象了。
最后我们需要将 SqlSession
对象关闭。
测试成功
可以看到我们并没有为接口创建实现类,但是却正常调用了接口的方法,这是怎么实现的呢?
我们可以在这里输出一下 employeeMapper
的类型。
可以看到,employeeMapper
是一个代理对象。在这里 MyBatis 会为接口创建一个代理对象,由代理对象执行增删改查工作。
以上就是接口式编程的实现。
关于 SqlSession
还有一点需要注意,就是它不是线程安全的,所以不可以直接将其作为成员变量。只能在使用的时候获取新的对象,并且使用完必须关闭。
Ⅲ 全局配置文件
我们现在来看一个文件,在这个文件中我们配置了数据库的一些信息还有sql映射文件。
MyBatis 配置文件包含了影响 MyBatis 行为很深的设置(settings)和属性(properties)信息,我们可以先看一下官方文档的结构。
接下来我们就来详细看一下 MyBatis 的配置文件。
1. properties
MyBatis 可以使用 properties
标签来引入外部 properties 配置文件的内容。比如我们之前配置的数据源的信息,就可以写到 properties 配置文件中。
我们可以看到 properties
标签有两个属性,一个是 resource
,一个是 url
。
resource
用来引入类路径下的资源, url
用来引入网络路径或者磁盘路径下的资源。
我们在src目录下建立一个 properties文件,然后将数据库的配置信息写入。
由于这个文件是在类路径下的,所以我们用 resource
引入。
如果这个文件在某个包下,我们在前面需要将包名加上,比如下面这样
那么在下面的配置中,我们就不需要再写具体的信息了,而是 ${}
,括号里写 properties 配置文件中的属性名。
测试仍然是成功的。
2. settings
settings 标签,被官方文档标明了极为重要,这个标签配置的是 MyBatis 的运行时行为。
我以一个属性作为例子,就是下图中的最后一条,是否开启自动驼峰命名规则映射。
一般Java中我们标准的命名就是单驼峰命名,比如在上面的case中,我们数据库的一个字段名是 first_name
。
但是在实体类中我们的命名是驼峰式的,是firstName
。
所以我们在查询的时候需要有一个设置别名的操作,这样才能把实体类和数据库的字段对应起来然后对这个属性进行注入。
那我们现在开启一下settings 的属性,看一下结果。
可以看到即使没有使用别名,MyBatis 也成功地找到了数据库字段和实体类的这个映射。
3. typeAliases
typeAliases 是一个起别名的标签,我们看一下官方文档。
简单来说就是把类全路径起一个短点的名字,以便在之后的引用中写得简单点。
就比如我们在sql映射文件中写的返回类型,就是实体类的类全路径。
如果之后还要写很多个模块,意味着要写很多遍这么长的名字,所以我们可以直接使用 typeAliases 标签给它起个别名。
这里我们不写别名,默认的别名就是类名的小写,也就是 employee
。
当然我们也可以自己起个名字。
结果依旧是正确的。
那么这里还有一个问题,我们只有一个实体类 Employee
,所以起个别名比较方便,写一行就好了,那么如果我们有很多很多个实体类怎么办呢?一个一个起别名还是很麻烦。
那么我们就可以批量的起别名。
package 标签会将 name 属性中写的包下的所有类(包括子包中的所有类)设置别名为其类名的小写。这个是没法自己再设置独特的名字了,整个包是统一设置的。
这里其实还会有一个问题,就是当子包中也有一个和父包相同的类时,它们的别名就会被设置成一样的,这样 MyBatis 在查询的时候就会报错。
所以我们可以使用 @Alias
注解,为某个类单独设置别名。
这里需要知道,别名是不区分大小写的。
MyBatis 对Java中的基本类型也起了别名,引用类型的都还是全小写,七个基本类型是下划线加本身,所以我们在自己定义别名的时候,要注意避开这几个别名,防止出现问题。
4. environments
在配置文件中,environments 标签是我们从官方文档复制来的,它是基本的元素,用来配置环境。
那么,除了我们一开始配置的数据库信息以外,我们还可以再配置多个环境。
其中 id 表示的是当前环境的唯一标识,我们一开始配置的环境 id 是 development
,。意为开发,就好比是我们开发人员使用的这个数据库。那么测试人员可能使用的是不同的数据库,所以可以再配置一个测试的数据库。
大家可以看到我圈起来的,在 environments
标签中有一个属性 default
,意为默认环境,当我们需要切换数据库环境为测试数据库时,可以直接将这里改为 test,不需要再做其他的配置了。
每个 environment
标签,除了要写自己的 id 以外,还必须写两个标签的内容,一个是 transactionManager
,就是事务管理器;另一个是数据源 dataSource
。
在官方文档中我们可以看到,事务管理器的 type
属性有两种取值,一个是 JDBC,一个是 MANAGED。
我们在 MyBatis 源码中可以找一个类 Configuration
,在这个类中有两行代码:
JDBC 和 MANAGED 都是被注册的别名,它们实际上对应的类分别是 JdbcTransactionFactory
和 ManagedTransactionFactory
。
这里其实也可以自己定义一个事务管理器,只需要像JdbcTransactionFactory
一样,继承 TransactionFactory
接口,然后在 type 中就可以写自己定义的事务管理器的全类名。
接着我们再看一下 dataSource 标签的 type 属性。
它一共有三种取值:UNPOOLED、POOLED 和 JNDI。
UNPOOLED 意为不使用连接池技术,那么每次对数据库进行增添改查都要建立一个新的连接,POOLED 意为不使用连接池技术,那么肯定是 POOLED 更好一些。
这三个同样是被注册的别名,
我们点进 PooledDataSourceFactory
的源码,可以看到它继承了 UnpooledDataSourceFactory
类,因此我们同样可以自定义 dataSource 的 type,只需要自己写一个数据源类,然后将类全路径写到 type 中即可。
这部分我们最后都会和 Spring 整合起来使用,这里只做一个简单的了解即可。
5. databaseIdProvider
官方文档中这句话的意思是 MyBatis 可以根据不同的厂商执行不同的 SQL 语句。这个标签就是对 MyBatis 一致性的一个支持。
databaseIdProvider 有一个属性 type,我们写的是 DB_VENDOR
,在 Configuration
类中,它同样的被注册的一个别名,所代表的类型是 VendorDatabaseIdProvider
类。
它的作用就是会得到不同数据库厂商的标识,比如MySQL 的标识就是 MySQL,Oracle 就是 Oracle 。
我们可以为不同的数据库厂商起一个别名。
在写 SQL 语句的时候,我们可以通过这些别名,表示需要 MyBatis 执行的 SQL 环境。
同样的 databaseId
我们可以写 oracle,表示这个是 oracle 的语句,写 sqlServer 表示这是 SQL Server 的语句。
6. mappers
在前面我们写好了一个 SQL 映射文件,然后需要将其通过 mappers 标签注册到全局配置文件里。
mapper 有三个属性:resource,class,url
resource
属性是引入类路径下的 SQL 映射文件;
url
属性是引入网络路径或者磁盘路径下的 SQL 映射文件;
resource 和 url 都是注册配置文件 ,而 class
属性注册的是接口。
那么 class 属性要如何用呢?
第一种方法:
我们还是需要一个 sql 映射文件,并且需要一个接口,接口名和 sql 映射文件必须同名且放在同一个包下。
我们在前面分别写过配置文件和接口。
现在需要将配置文件放到 dao 目录下,然后将接口注册。
测试成功。
第二种方法:
我们可以不写 SQL 映射文件,所有的 SQL 都利用注解写在接口上。
方法还是相同的方法,只不过我们直接把查询语句写到方法上。和 Select
相对的还有 Insert
、Delet
注解,对应着对数据库的不同操作。
接着我们将其注册到全局配置文件中。
测试结果如下,是成功的。
看上去使用注解非常简单,不用写那么麻烦的 sql 映射文件。但是 sql 映射文件在维护起来的时候是很方便的,不用到处去找源码修改。
所以这里的建议是,比较重要的、复杂的Dao接口我们写SQL映射文件;不重要、简单的Dao接口为了开发快速可以使用注解。
除了 mapper 以外,mappers 还有一个标签 package
,它可以实现批量注册。
批量注册的时候,注册第二种方式的注解是很方便的,但是要是第一种方式的接口,就需要将 sql 映射文件加到批量注册的包下了,和接口放在同一个目录。
配置文件的标签大概就说得差不多了,有一点我们需要注意,就是标签的顺序是不可以换的,就是我们一开始发的结构图。
有的标签可以不写,但是这些标签的顺序要保持这样,从上到下,如果交换顺序的话 MyBatis 就会报错。
比如我要把 settings 放到 properties 上面,就会报错,顺序不匹配。