为什么是Mybatis

首先使用JDBC操作数据库存在以下的问题

  • 首先是硬编码问题:数据库连接,账号,密码和SQL语句等直接写在代码中,每次修改都需要修改源代码并且重新编译,非常不方便。
  • 然后是数据库操作繁琐:使用JDBC操作数据库非常繁琐:设置SQL语句变量,执行SQL语句,处理返回集;而Mybatis只需要两行代码就可以完成这些操作。

操作Mybatis

导入MyBatis

首先我们需要在项目中导入关于MyBatis的依赖,在这里我们使用Maven将MyBatis导入项目中:

1
2
3
4
5
6
7
8
<!-- 在pom.xml写入如下坐标依赖 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>

编写MyBatis的配置文件

每一个基于MyBAtis的应用都是以一个SqlSessionFactory的实例为核心的。实例SqlSessionFactory可以通过SqlSessionFactoryBuilder获得,而SqlSessionFactoryBuilder则会通过XML配置文件获取数据库配置信息创建实例SqlSessionFactory

为了获取该配置文件,MyBatis中实现了一个Resources工具类,用于获取配置文件的输入流:

1
2
3
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

配置文件

XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。后面会再探讨 XML 配置文件的详细内容,这里先给出一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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>
<!-- SQL语句映射文件 -->
<mapper resource="DepartMentMapper.xml"/>
</mappers>
</configuration>

通过上面的代码,我们将在如下介绍一下配置文件的一些属性:

  • environments:MyBatis可以配置以适应不同的环境(既可以配置多个environment,并且通过default铁环不同的environment)
  • typeAliases:为类设置别名,方便编码,可以使用包扫描简化。

SQL映射文件

在项目中我们可以通过xml文件来编写SQL映射文件,其具体内容一般如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace: 名称空间
-->
<mapper namespace="test">
<select id="selectAll" resultType="com.takune.pojo.Department">
select * from department;
</select>
</mapper>

正式编码

接下来我们就可以编写对应的DAO类来存储查询的信息;然后利用Mybatis实现数据库的查询操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) throws IOException {
// 1. 加载Mybatis核心配置文件,获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 该参数为mybatis配置文件的路径

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 2. 获取sqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();

// 3. 执行SQL语句
List<Department> departmentList = sqlSession.selectList("test.selectAll");
//该方法的参数命名格式为namespace.id,返回的数据类型为List<resultType>

System.out.println(departmentList);
// 4. 释放资源

sqlSession.close();
}

Mapper代理

上面我们可以看到,在执行SQL语句的第三步依旧存在依靠字符串的硬编码问题,因此为了解决这个硬编码的问题同时提高编码效率,我们可以使用Mapper代理的方法,步骤如下:

  • 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放在同一目录下
  • 设置SQL映射文件的namespace属性为Mapper接口全限定名;
  • 在Mapper接口中定义与SQL映射文件中的id相同的方法,并且保持参数类型,个数和返回值一致
  • 最后编码执行,调用接口中的方法即可执行SQL语句。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws IOException {
// 1. 加载Mybatis核心配置文件,获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 2. 获取sqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();

// 3. 获取Mapper代理,执行SQL语句
DepartmentMapper departmentMapper = sqlSession.getMapper(DepartmentMapper.class);
List<Department> departmentList = departmentMapper.selectAll();
System.out.println(departmentList);

// 4. 释放资源

sqlSession.close();
}

Mapper代理中的注意事项

在使用Mapper代理的过程中,我们总会遇到这样那样的问题,以下将介绍一些常见的注意事项:

数据库字段与实体类变量名不一致

在实际开发中,不同的领域,不同的团队中的变量有着不同的命名风格,比如说驼峰命名等,这样经常会导致一个问题:由于数据库字段和实体类对应的变量名不一样,导致MyBatis自动封装数据的功能失效,因此为了解决这个问题,我们将采用resultMap来解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 	<!-- 修改前 -->
<select id="selectAll" resultType="brand">
select * from db.tb_brand;
</select>
<!-- 修改后 -->

<!--
数据库字段和实体类的名字不一样,则不能自动封装数据
* 取别名
* resultMap:
* 定义resultMap标签,并且使用resultMap替换resultType属性
* id: 完成主键字段的映射
* result:完成一般字段的映射
* column:数据库中的列名
* property:实体类中的成员变量名
-->
<resultMap id="brandResultMap" type="brand">
<result column="brand_name" property="brandName" />
<result column="company_name" property="companyName" />
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select * from db.tb_brand;
</select>

带变量的SQL语句

在实际开发过程中,我们写SQL语句时经常会有获取用户输入的条件进行查询的需求,因此我们需要解决这样的的问题:

1
2
3
4
5
6
7
8
9
<!--
参数占位符:
* #{var}: 会将其替换为?防止sql注入,其中var与接口中传入的参数变量名一致
* ${var}: 拼写sql字符串,所以存在sql注入问题,如果表名或者列名不确定,我们就会使用这个占位符,其中var与接口中传入的参数变量名一致
* 特殊字符:比如说'<'会在xml中识别为标签开始符,可以使用转义符或者CDATA解决
-->
<select id="selectById" resultMap="brandResultMap">
select * from db.tb_brand where id = #{id};
</select>

带多个变量的SQL语句

在实际开发中我们经常会遇到多条件查询的业务情况,在使用Mapper代理的前提下设置多个参数有如下三种方法:

  1. 散装参数:需要使用@Param("SQL中的占位符名称")
  2. 实体类封装参数:只需要保证SQL中占位符名称与实体类属性名称对应上即可设置成功;
  3. map集合:保证SQL中占位符名称与map集合的键名称对上,即可设置成功。

动态SQL

在项目开发过程中,经常会出现动态条件查询的问题,假如说为了每一种可能的条件写一条SQL语句是非常繁琐的,因此为了解决这个问题,MyBatis通过一些特定的标签实现了动态SQL的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- 多条件动态查询 -->
<select id="selectByMul" resultMap="brandResultMap">
select * from db.tb_brand
<where>
<if test="brandName != null and brandName != ''">
and brand_name like #{brandName}
</if>
<if test="companyName != null and companyName != ''">
and company_name like #{companyName}
</if>
<if test="status != null">
and status = #{status}
</if>
</where>
</select>
<!-- 单条件动态查询 -->
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>