如何优雅地使用 MyBatis XML 配置版

1    第3-2课:如何优雅地使用 MyBatis XML 配置版

MyBatis 是现现在最流行的 ORM 框架之一,咱们先来了解一下什么是 ORM 框架。前端

1.1     ORM 框架

对象关系映射(Object Relational Mapping,ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM 是经过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。java

为何须要 ORM?mysql

当你开发一个应用程序的时候(不使用 O/R Mapping),可能会写很多数据访问层代码,用来从数据库保存、删除、读取对象信息等;在 DAL 中写了不少的方法来读取对象数据、改变状态对象等任务,而这些代码写起来老是重复的。针对这些问题 ORM 提供了解决方案,简化了将程序中的对象持久化到关系数据库中的操做。git

ORM 框架的本质是简化编程中操做数据库的编码,在 Java 领域发展到如今基本上就剩两家最为流行,一个是宣称能够不用写一句 SQL 的 Hibernate,一个是以动态 SQL 见长的 MyBatis,二者各有特色。在企业级系统开发中能够根据需求灵活使用,会发现一个有趣的现象:传统企业大都喜欢使用 Hibernate,而互联网行业一般使用 MyBatis。github

1.2     MyBatis 介绍

MyBatis 是一款标准的 ORM 框架,被普遍的应用于各企业开发中。MyBatis 最先是 Apache 的一个开源项目 iBatis,2010 年这个项目由 Apache Software Foundation 迁移到了 Google Code,而且更名为 MyBatis,2013 年 11 月又迁移到 Github。从 MyBatis 的迁移史,也能够看出源码托管平台的发展史,GitHub 目前已经成为世界上最大的开源软件托管平台,建议你们多多关注这个全球最大的同性社交网站。spring

MyBatis 支持普通的 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎全部的 JDBC 代码和参数的手工设置以及对结果集的检索封装。MaBatis 可使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。sql

做为一款使用普遍的开源软件,它的特色有哪些呢?数据库

优势编程

  • SQL 被统一提取出来,便于统一管理和优化
  • SQL 和代码解耦,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰、更易维护、更易单元测试
  • 提供映射标签,支持对象与数据库的 ORM 字段关系映射
  • 提供对象关系映射标签,支持对象关系组件维护
  • 灵活书写动态 SQL,支持各类条件来动态生成不一样的 SQL

缺点mybatis

  • 编写 SQL 语句时工做量很大,尤为是字段多、关联表多时,更是如此
  • SQL 语句依赖于数据库,致使数据库移植性差

1.3     MyBatis 几个重要的概念

Mapper 配置可使用基于 XML 的 Mapper 配置文件来实现,也可使用基于 Java 注解的 MyBatis 注解来实现,甚至能够直接使用 MyBatis 提供的 API 来实现。

Mapper 接口是指自行定义的一个数据操做接口,相似于一般所说的 DAO 接口。早期的 Mapper 接口须要自定义去实现,如今 MyBatis 会自动为 Mapper 接口建立动态代理对象。Mapper 接口的方法一般与 Mapper 配置文件中的 select、insert、update、delete 等 XML 结点存在一一对应关系。

Executor,MyBatis 中全部的 Mapper 语句的执行都是经过 Executor 进行的,Executor 是 MyBatis 的一个核心接口。

SqlSession,是 MyBatis 的关键对象,是执行持久化操做的独享,相似于 JDBC 中的 Connection,SqlSession 对象彻底包含以数据库为背景的全部执行 SQL 操做的方法,它的底层封装了 JDBC 链接,能够用 SqlSession 实例来直接执行被映射的 SQL 语句。

SqlSessionFactory,是 MyBatis 的关键对象,它是单个数据库映射关系通过编译后的内存镜像。SqlSessionFactory 对象的实例能够经过 SqlSessionFactoryBuilder 对象类得到,而 SqlSessionFactoryBuilder 则能够从 XML 配置文件或一个预先定制的 Configuration 的实例构建出。

MyBatis 的工做流程以下:

 

  • 首先加载 Mapper 配置的 SQL 映射文件,或者是注解的相关 SQL 内容。
  • 建立会话工厂,MyBatis 经过读取配置文件的信息来构造出会话工厂(SqlSessionFactory)。 
  • 建立会话。根据会话工厂,MyBatis 就能够经过它来建立会话对象(SqlSession),会话对象是一个接口,该接口中包含了对数据库操做的增、删、改、查方法。 
  • 建立执行器。由于会话对象自己不能直接操做数据库,因此它使用了一个叫作数据库执行器(Executor)的接口来帮它执行操做。
  • 封装 SQL 对象。在这一步,执行器将待处理的 SQL 信息封装到一个对象中(MappedStatement),该对象包括 SQL 语句、输入参数映射信息(Java 简单类型、HashMap 或 POJO)和输出结果映射信息(Java 简单类型、HashMap 或 POJO)。
  • 操做数据库。拥有了执行器和 SQL 信息封装对象就使用它们访问数据库了,最后再返回操做结果,结束流程。

在咱们具体的使用过程当中,就是按照上述的流程来执行。

1.4     什么是 MyBatis-Spring-Boot-Starter

mybatis-spring-boot-starter 是 MyBatis 帮助咱们快速集成 Spring Boot 提供的一个组件包,使用这个组件能够作到如下几点:

  • 构建独立的应用
  • 几乎能够零配置
  • 须要不多的 XML 配置

mybatis-spring-boot-starter 依赖于 MyBatis-Spring 和 Spring Boot,最新版 1.3.2 须要 MyBatis-Spring 1.3 以上,Spring Boot 版本 1.5 以上。

注意 mybatis-spring-boot-starter 是 MyBatis 官方开发的 Starter,而不是 Spring Boot 官方开发的启动包,实际上是 MyBatis 看 Spring Boot 市场使用度很是高,所以主动开发出 Starter 包进行集成,但这一集成确实解决了不少问题,使用起来比之前简单不少。mybatis-spring-boot-starter 主要提供了两种解决方案,一种是简化后的 XML 配置版,一种是使用注解解决一切问题。

MyBatis 之前只有 XML 配置这种使用的形式,到了后来注解使用特别普遍,MyBatis 也顺应潮流提供了注解的支持,从这里能够看出 MyBatis 一直都跟随着主流技术的变化来完善本身。接下来给你们介绍一下如何使用 XML 版本。

XML 版本保持映射文件的方式,最新版的使用主要体如今不须要实现 Dao 的实现层,系统会自动根据方法名在映射文件中找到对应的 SQL。

1.5     初始化脚本

为了方便项目演示,须要在 test 仓库建立 users 表,脚本以下:

DROPTABLEIFEXISTS`users`;
CREATETABLE`users`(
`id`bigint20NOTNULLCOMMENT'主键id' ()AUTO_INCREMENT,
`userName`varchar32DEFAULTNULLCOMMENT'用户名' (),
`passWord`varchar32DEFAULTNULLCOMMENT'密码' (),
`user_sex`varchar32DEFAULTNULL (),
`nick_name`varchar32DEFAULTNULL (),
KEY`id`  PRIMARY()
ENGINEInnoDB1DEFAULTCHARSET)=AUTO_INCREMENT==utf8;

1.5.1   关键依赖包

固然任何模式都须要首先引入 mybatis-spring-boot-starter 的 pom 文件,如今最新版本是 1.3.2。

<dependency>
<groupId></groupId>   org.mybatis.spring.boot
<artifactId></artifactId>   mybatis-spring-boot-starter
<version></version>   1.3.2
</dependency>

1.5.2   application 配置

application.properties 添加相关配置:

mybatis.config-location=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
mybatis.type-aliases-package=com.neo.model
 
testtruetruespring.datasource.url=jdbc:mysql://localhost:3306/?serverTimezone=UTC&useUnicode=&characterEncoding=utf-8&useSSL=
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

其中:

  • mybatis.config-location,配置 mybatis-config.xml 路径,mybatis-config.xml 中配置 MyBatis 基础属性;
  • mybatis.mapper-locations,配置 Mapper 对应的 XML 文件路径;
  • mybatis.type-aliases-package,配置项目中实体类包路径;
  • spring.datasource.*,数据源配置。

Spring Boot 启动时数据源会自动注入到 SqlSessionFactory 中,使用 SqlSessionFactory 构建 SqlSessionFactory,再自动注入到 Mapper 中,最后咱们直接使用 Mapper 便可。 

1.5.3   启动类

在启动类中添加对 Mapper 包扫描 @MapperScan,Spring Boot 启动的时候会自动加载包路径下的 Mapper。

@SpringBootApplication
@MapperScan"com.neo.mapper"()
publicclass Application {
 
public static void main(String[] args)    {
        SpringApplication.run(Application.class, args);
    }
}

或者直接在 Mapper 类上面添加注解 @Mapper,建议使用上面那种,否则每一个 mapper 加个注解也挺麻烦的。

1.6     示例演示

1.6.1   MyBatis 公共属性

mybatis-config.xml 主要配置经常使用的 typeAliases,设置类型别名,为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减小类彻底限定名的冗余。

<configuration>
type    <Aliases>
typealias"Integer"type"java.lang.Integer"        <Alias==/>
typealias"Long"type"java.lang.Long"        <Alias==/>
typealias"HashMap"type"java.util.HashMap"        <Alias==/>
typealias"LinkedHashMap"type"java.util.LinkedHashMap"        <Alias==/>
typealias"ArrayList"type"java.util.ArrayList"        <Alias==/>
typealias"LinkedList"type"java.util.LinkedList"        <Alias==/>
type    </Aliases>
</configuration>

这样咱们在使用 Mapper.xml 的时候,须要引入能够直接这样写:

"Integer"resultType=
//或者
"Long"parameterType=

1.6.2   添加 User 的映射文件

第一步,指明对应文件的 Mapper 类地址:

namespace"com.neo.mapper.UserMapper"<mapper=>

第二部,配置表结构和类的对应关系:

<resultMap id="BaseResultMap" type="com.neo.model.User" >
<id column="id" property="id" jdbcType="BIGINT" />   
<result column="userName" property="userName" jdbcType="VARCHAR" />   
<result column="passWord" property="passWord" jdbcType="VARCHAR" />   
<result column="user_sex" property="userSex" javaType="com.neo.enums.UserSexEnum"/>   
<result column="nick_name" property="nickName" jdbcType="VARCHAR" />   
</resultMap>

这里为了更好的贴近工做状况,将类的两个字段和数据库字段设置为不一致,其中一个使用了枚举。使用枚举有一个很是大的优势,插入此属性的数据会自动进行校验,若是不是枚举的内容会报错。

第三步,写具体的 SQL 语句,好比这样:

select"getAll""BaseResultMap"<id=resultMap=  >
   SELECT
   *
   FROM users
select</>

MyBatis XML 有一个特色是能够复用 XML,好比咱们公用的一些 XML 片断能够提取出来,在其余 SQL 中去引用。例如:

<sql id="Base_Column_List" >
    id, userName, passWord, user_sex, nick_name
</sql>
 
<select id="getAll" resultMap="BaseResultMap"  >
   SELECT
<include refid="Base_Column_List" />   
   FROM users
</select> 

这个例子就是,上面定义了须要查询的表字段,下面 SQL 使用 include 引入,避免了写太多重复的配置内容。

下面是经常使用的增、删、改、查的例子:

select"getOne""Long""BaseResultMap"<id=parameterType=resultMap=>
    SELECT
"Base_Column_List"   <include refid=/>
   FROM users
#{id}   WHERE id =
select</>
 
"insert""com.neo.model.User"<insert id=parameterType=>
INSERT INTO   
           users
           (userName,passWord,user_sex) 
       VALUES
           (#{userName}, #{passWord}, #{userSex})
</insert>
 
<update id"update""com.neo.model.User"=parameterType=>
   UPDATE
           users
   SET
if"userName != null"#{userName},</if>       <test=>userName =
if"passWord != null"#{passWord},</if>       <test=>passWord =
#{nickName}       nick_name =
   WHERE
#{id}           id =
</update>
 
"delete""Long"<delete id=parameterType=>
   DELETE FROM
            users
   WHERE
#{id}            id =
</delete>

上面 update 的 SQL 使用了 if 标签,能够根据不一样的条件生产动态 SQL,这就是 MyBatis 最大的特色。

1.6.2.1      编写 Dao 层的代码

publicinterfaceUserMapper{
 
List<UserEntity> getAll()   ;
 
UserEntity getOne(Long id)   ;
 
void insert(UserEntity user)   ;
 
void update(UserEntity user)   ;
 
void delete(Long id)   ;
}

注意:这里的方法名须要和 XML 配置中的 id 属性一致,否则会找不到方法去对应执行的 SQL。

1.6.2.2      测试使用

按照 Spring 一向使用形式,直接将对应的 Mapper 注入便可。

Resource@
private UserMapper userMapper;

若是使用的是 Idea,这块的注解常常会报“could not autowire”,Eclipse 却没有问题,其实代码是正确的,这是 Idea 的误报。能够选择下降 Autowired 检测的级别,不要提示就好。

在 File | Settings | Editor | Inspections 选项中使用搜索功能找到 Autowiring for Bean Class,将 Severity 的级别由以前的 error 改为 warning 便可。

 

接下来直接使用 userMapper 进行数据库操做便可。

Test@
public void testUser()  {
//增长   
new"aa""a123456"    userMapper.insert(User(,, UserSexEnum.MAN));
//删除   
intdelete2l   count=userMapper.();
1l    User user = userMapper.getOne();
"smile"    user.setNickName();
//修改   
    userMapper.update(user);
//查询   
    List<User> users = userMapper.getAll();
}

在示例代码中,写了两份的使用示例,一个是 Test,一个在 Controller 层,方便你们下载查看。

1.7     分页查询

多条件分页查询是实际工做中最常使用的功能之一,MyBatis 特别擅长处理这类的问题。在实际工做中,会对分页进行简单的封装,方便前端使用。另外在 Web 开发规范使用中,Web 层的参数会以 param 为后缀的对象进行传参,以 result 结尾的实体类封装返回的数据。

下面给你们以 User 多条件分页查询为例进行讲解。

先定义一个分页的基础类:

publicclassPageParam{
privateint//起始行   beginLine;      
private3   Integer pageSize =;
private0// 当前页   Integer currentPage=;       
//getter setter省略   
public int getBeginLine()    {
return//自动计算起始行       pageSize*currentPage;
    }
}

默认每页 3 条记录,能够根据前端传参进行修改。

user 的查询条件参数类继承分页基础类:

publicclass UserParam extends PageParam{
private   String userName;
private   String userSex;
//getter setter省略   
}

接下来配置具体的 SQL,先将查询条件提取出来。

"Base_Where_List"<sql id=>
iftest"userName != null  and userName != ''"    <=>
#{userName}        and userName =
if    </>
iftest"userSex != null and userSex != ''"    <=>
#{userSex}        and user_sex =
if    </>
</sql>

从对象 UserParam 中获取分页信息和查询条件,最后进行组合。

select"getList""BaseResultMap""com.neo.param.UserParam"<id=resultMap=parameterType=>
select   
"Base_Column_List"    <include refid=/>
from   users
where11   =
"Base_Where_List"    <include refid=/>
by    orderid desc
#{beginLine} , #{pageSize}    limit
select</>

前端须要展现总共的页码,所以须要统计出查询结果的总数。

select"getCount""Integer""com.neo.param.UserParam"<id=resultType=parameterType=>
select   
    count(1)
    from users
    where 11=
"Base_Where_List"    <include refid=/>
select</>

Mapper 中定义的两个方法和配置文件相互对应。

publicinterfaceUserMapper{
List<UserEntity> getList(UserParam userParam)   ;
int getCount(UserParam userParam)   ;
}

具体使用:

Test@
public void testPage() {
new    UserParam userParam=UserParam();
"WOMAN"    userParam.setUserSex();
1    userParam.setCurrentPage();
    List<UserEntity> users=userMapper.getList(userParam);
long   count=userMapper.getCount(userParam);
new    Page page =Page(userParam,count,users);
out    System..println(page);
}

在实际使用中,只须要传入 CurrentPage 参数便可,默认 0 就是第一页,传 1 就是第二页的内容,最后将结果封装为 Page 返回给前端。

publicclass Page<E> implements Serializable {
privateint0//当前页数   currentPage =;
privatelong//总页数   totalPage;      
privatelong//总记录数   totalNumber;   
private//数据集   List<E> list;       
}

Page 将分页信息和数据信息进行封装,方便前端显示第几页、总条数和数据,这样分页功能就完成了。

1.8     多数据源处理

接下来为你们介绍如何使用 MyBatis 配置多数据源使用。

1.8.1   配置文件

首先咱们须要配置两个不一样的数据源:

mybatis.config-location=classpath:mybatis/mybatis-config.xml
 
testtruetruespring.datasource.one.jdbc-url=jdbc:mysql://localhost:3306/1?serverTimezone=UTC&useUnicode=&characterEncoding=utf-8&useSSL=
spring.datasource.one.username=root
spring.datasource.one.password=root
spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
 
testtruetruespring.datasource.two.jdbc-url=jdbc:mysql://localhost:3306/2?serverTimezone=UTC&useUnicode=&characterEncoding=utf-8&useSSL=
spring.datasource.two.username=root
spring.datasource.two.password=root
spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver

注意,须要提早在 test1 和 test2 库中建立好 User 表结构。

第一个数据源以 spring.datasource.one.* 为前缀链接数据库 test1,第二个数据源以 spring.datasource.two.* 为前缀链接数据库 test2。

同时须要将上述的 UserMapper.xml 文件复制两份到 resources/mybatis/mapper/one 和 resources/mybatis/mapper/two 目录下各一份。

1.8.2   数据源配置

为两个数据源建立不一样的 Mapper 包路径,将之前的 UserMapper 复制到包 com.neo.mapper.one 和 com.neo.mapper.two 路径下,而且分别重命名为:User1Mapper、User2Mapper。

配置第一个数据源,新建 DataSource1Config。

首先加载配置的数据源:

@Bean"oneDataSource"(name =)
@ConfigurationProperties"spring.datasource.one"(prefix =)
@Primary
public DataSource testDataSource() {
return   DataSourceBuilder.create().build();
}

注意,在多数据源中只能指定一个 @Primary 做为默认的数据源使用。

根据建立的数据源,构建对应的 SqlSessionFactory。

@Bean"oneSqlSessionFactory"(name =)
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("oneDataSource") DataSource dataSource) throws Exception {
new    SqlSessionFactoryBean bean =SqlSessionFactoryBean();
    bean.setDataSource(dataSource);
new"classpath:mybatis/mapper/one/*.xml"    bean.setMapperLocations(PathMatchingResourcePatternResolver().getResources());
return   bean.getObject();
}

代码中须要指明须要加载的 Mapper xml 文件。

同时将数据源添加到事务中。

@Bean"oneTransactionManager"(name =)
@Primary
public DataSourceTransactionManager testTransactionManager(@Qualifier("oneDataSource") DataSource dataSource) {
returnnew   DataSourceTransactionManager(dataSource);
}

接下来将上面建立的 SqlSessionFactory 注入,建立咱们在 Mapper 中须要使用的 SqlSessionTemplate。

@Bean"oneSqlSessionTemplate"(name =)
@Primary
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("oneSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
returnnew   SqlSessionTemplate(sqlSessionFactory);
}

最后将上面建立的 SqlSessionTemplate 注入到对应的 Mapper 包路径下,这样这个包下面的 Mapper 都会使用第一个数据源来进行数据库操做。

@Configuration
@MapperScan"com.neo.mapper.one""oneSqlSessionTemplate"(basePackages =, sqlSessionTemplateRef  =)
publicclass OneDataSourceConfig {
...
}
  • basePackages 指明 Mapper 地址。
  • sqlSessionTemplateRef 指定 Mapper 路径下注入的 sqlSessionTemplate。

1.8.3   第二个数据源配置

DataSource2Config 的配置和上面相似,方法上须要去掉 @Primary 注解,替换对应的数据源和 Mapper 路径便可。下面是 DataSource2Config 完整示例:

@Configuration
@MapperScan"com.neo.mapper.two""twoSqlSessionTemplate"(basePackages =, sqlSessionTemplateRef  =)
publicclass DataSource2Config {
 
@Bean"twoDataSource"   (name =)
@ConfigurationProperties"spring.datasource.two"   (prefix =)
public DataSource testDataSource()    {
return       DataSourceBuilder.create().build();
    }
 
@Bean"twoSqlSessionFactory"   (name =)
public SqlSessionFactory testSqlSessionFactory(@Qualifier("twoDataSource") DataSource dataSource) throws Exception    {
new        SqlSessionFactoryBean bean =SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
new"classpath:mybatis/mapper/two/*.xml"        bean.setMapperLocations(PathMatchingResourcePatternResolver().getResources());
return       bean.getObject();
    }
 
@Bean"twoTransactionManager"   (name =)
public DataSourceTransactionManager testTransactionManager(@Qualifier("twoDataSource") DataSource dataSource)    {
returnnew       DataSourceTransactionManager(dataSource);
    }
 
@Bean"twoSqlSessionTemplate"   (name =)
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("twoSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception    {
returnnew       SqlSessionTemplate(sqlSessionFactory);
    }
 
}

从上面的步骤咱们能够总结出来,建立多数据源的过程就是:首先建立 DataSource,注入到 SqlSessionFactory 中,再建立事务,将 SqlSessionFactory 注入到建立的 SqlSessionTemplate 中,最后将 SqlSessionTemplate 注入到对应的 Mapper 包路径下。其中须要指定分库的 Mapper 包路径。

注意,在多数据源的状况下,咱们不须要在启动类添加:@MapperScan("com.xxx.mapper") 的注解。

这样 MyBatis 多数据源的配置就完成了,若是有更多的数据源请参考第二个数据源的配置便可。

1.8.4   测试

配置好多数据源以后,在项目中想使用哪一个数据源就把对应数据源注入到类中使用便可。

@RunWith(SpringRunner.class)
@SpringBootTest
publicclass UserMapperTest {
@Autowired   
private   User1Mapper user1Mapper;
@Autowired   
private   User2Mapper user2Mapper;
 
@Test   
public void testInsert() throws Exception    {
new"aa111""a123456"        user1Mapper.insert(User(,, UserSexEnum.MAN));
new"bb111""b123456"        user1Mapper.insert(User(,, UserSexEnum.WOMAN));
new"cc222""b123456"        user2Mapper.insert(User(,, UserSexEnum.MAN));
    }
}

上面的测试类中注入了两个不一样的 Mapper,对应了不一样的数据源。在第一个数据源中插入了两条数据,第二个数据源中插入了一条信息,运行测试方法后查看数据库1有两条数据,数据库2有一条数据,证实多数据源测试成功。

1.9     总结

这节课介绍了 ORM 框架 和 MyBatis 框架相关概念介绍,以用户数据为例演示了 MyBatis 的增、删、改、查,以及分页查询、多数据源处理等常见场景。经过上面的示例能够发现 MyBatis 将执行 SQL 和代码作了隔离,保证代码处理和 SQL 的相对独立,层级划分比较清晰,MyBatis 对动态 SQL 支持很是友好,能够在 XML 文件中复用代码高效编写动态 SQL。

点击这里下载源码

相关文章
相关标签/搜索