MyBatis之xml 配置版
1 MyBatis 介绍
MyBatis 是一款标准的 ORM 框架,被广泛的应用于各企业开发中。MyBatis 支持普通的 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索封装。MaBatis 可以使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
对象关系映射(Object Relational Mapping,ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM 是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
1.1 优缺点分析
优点:
- SQL 被统一提取出来,便于统一管理和优化
- SQL 和代码解耦,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰、更易维护、更易单元测试
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射
- 提供对象关系映射标签,支持对象关系组件维护
- 灵活书写动态 SQL,支持各种条件来动态生成不同的 SQL
缺点:
- 编写 SQL 语句时工作量很大,尤其是字段多、关联表多时,更是如此
- SQL 语句依赖于数据库,导致数据库移植性差
1.2 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 的实例构建出。
1.3 Mybatis工作流程
MyBatis 的工作流程如下:
- 首先加载 Mapper 配置的 SQL 映射文件,或者是注解的相关 SQL 内容。
- 创建会话工厂,MyBatis 通过读取配置文件的信息来构造出会话工厂(SqlSessionFactory)。
- 创建会话。根据会话工厂,MyBatis 就可以通过它来创建会话对象(SqlSession),会话对象是一个接口,该接口中包含了对数据库操作的增、删、改、查方法。
- 创建执行器。因为会话对象本身不能直接操作数据库,所以它使用了一个叫做数据库执行器(Executor)的接口来帮它执行操作。
- 封装 SQL 对象。在这一步,执行器将待处理的 SQL 信息封装到一个对象中(MappedStatement),该对象包括 SQL 语句、输入参数映射信息(Java 简单类型、HashMap 或 POJO)和输出结果映射信息(Java 简单类型、HashMap 或 POJO)。
- 操作数据库。拥有了执行器和 SQL 信息封装对象就使用它们访问数据库了,最后再返回操作结果,结束流程。
2 MyBatis 引入与配置
2.1 pom.xml依赖
首先在pom文件中添加依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.2 application.yml配置
application.yml 添加相关配置:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=true
username: root
password: root
mybatis:
type-aliases-package: com.mutest.model
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mappers/*.xml
这些配置都是什么含义呢?
- spring.datasource*:数据源配置,包括驱动、连接地址、用户名和密码等
- mybatis.config-location:配置 mybatis-config.xml 路径,mybatis-config.xml 中配置 MyBatis 基础属性;
- mybatis.mapper-locations:配置 Mapper 对应的 XML 文件路径;
- mybatis.type-aliases-package:配置项目中实体类包路径;
Spring Boot 启动时数据源会自动注入到 SqlSessionFactory 中,使用 SqlSessionFactory 构建 SqlSessionFactory,再自动注入到 Mapper 中,最后我们直接使用 Mapper 即可。
2.3 启动类配置
在启动类中添加 @MapperScan 注解,value定义路径,Spring Boot 启动的时候会自动加载该路径下的 Mapper。
@SpringBootApplication
@MapperScan(value = "com.mutest.dao")
public class MutestApplication {
public static void main(String[] args) {
SpringApplication.run(MutestApplication.class, args);
}
}
或者不使用@MapperScan 注解,而是直接在 Mapper 类上面添加注解 @Mapper,出于方便考虑,建议使用前者。
3 mappers文件编写
3.1 数据准备
数据库数据:
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`user_name` varchar(32) DEFAULT NULL COMMENT '用户名',
`password` varchar(32) DEFAULT NULL COMMENT '密码',
`age` int DEFAULT NULL,
`sex` enum('MALE','FEMALE'),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
创建表对应的实体类:
import lombok.Data;
import com.example.demo.enums.UserSexEnum;
/**
* @author guozhengMu
* @version 1.0
* @date 2019/8/23 17:47
* @description user表对应的实体类
* @modify
*/
@Data
public class User {
private Long id;
private String userName;
private String password;
private int age;
private UserSexEnum sex;
}
sex的枚举类:
public enum UserSexEnum {
MALE, FEMALE
}
实体类的数据类型要和数据库字段一一对应:
- Long 对应 bigint
- String 对应 varchar
- int 对应 int
3.2 mybatis-config.xml
在resources目录下新建mybatis文件夹,新建mybatis-config.xml配置文件(路径和文件名和yml配置文件配置的信息保持一致)。该文件主要配置常用的 typeAliases,设置类型别名,为 Java 类型设置一个短的名字。它只和 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>
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer"/>
<typeAlias alias="Long" type="java.lang.Long"/>
<typeAlias alias="HashMap" type="java.util.HashMap"/>
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/>
<typeAlias alias="ArrayList" type="java.util.ArrayList"/>
<typeAlias alias="LinkedList" type="java.util.LinkedList"/>
</typeAliases>
</configuration>
3.3 mapper编写与调用
3.3.1 查询示例
1、指明对应文件的 Mapper 类地址:
<mapper namespace="com.example.demo.dao.UserMapper">
2、配置表结构和类的对应关系:
<resultMap id="UserMap" type="com.example.demo.model.User">
<result column="user_name" property="userName"/>
<result column="sex" property="sex" javaType="com.example.demo.enums.UserSexEnum"/>
</resultMap>
3、编写具体的sql语句
<select id="getUserList" resultMap="UserMap">
SELECT *
FROM test.users
</select>
4、编写 Dao 层的代码
public interface UserMapper {
List<User> getUserList();
}
注意:这里的方法名需要和 XML 配置中的 id 属性一致,不然会找不到方法去对应执行的 SQL。
5、controller层代码
@RestController
@RequestMapping(value = "/test")
public class TestController {
@Autowired
private UserMapper userMapper;
@GetMapping("/mybatis")
public List<User> test() {
return userMapper.getUserList();
}
}
在Idea中,这块的注解经常会报“could not autowire”,这是 Idea 的误报。可以选择降低 Autowired 检测的级别,不要提示就好。
在 File – Settings – Editor – Inspections 选项中使用搜索功能找到 Autowiring for Bean Class,将 Severity 的级别由之前的 error 改成 warning 即可。
启动项目,浏览器输入:http://localhost:8096/test/mybatis,返回结果:
[{"id":1,"userName":"xiaohong","password":"123456","age":30,"sex":"MALE"}]
3.3.2 复用xml
MyBatis XML 有一个特点是可以复用 XML,比如我们公用的一些 XML 片段可以提取出来,在其他 SQL 中去引用。例如:
<sql id="Base_Column_List" >
id, userName, passWord, user_sex, nick_name
</sql>
<select id="getUserList" resultMap="UserMap" >
SELECT
<include refid="Base_Column_List" />
FROM test.users
</select>
3.3.3 动态sql
当我们修改数据库数据时,可能对一行数据的部分字段进行修改,但具体修改哪些字段并不能事先确定,这时候就需要用到动态sql语句了,加入if标签进行一个非空判断:
<update id="updateUser" parameterType="com.neo.model.User" >
UPDATE users
SET
<if test="userName != null">userName = #{userName},</if>
<if test="password != null">passWord = #{passWord},</if>
1 = 1
WHERE
id = #{id}
</update>
3.3.4 完整mapper
附上包含增删改查的完整mapper文件,包含了增删改查:
<?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="com.example.demo.dao.UserMapper">
<resultMap id="UserMap" type="com.example.demo.model.User">
<result column="user_name" property="userName"/>
<result column="sex" property="sex" javaType="com.example.demo.enums.UserSexEnum"/>
</resultMap>
<sql id="Base_Column_List">
id, userName, password, age, sex
</sql>
<select id="getUserList" resultMap="UserMap">
SELECT
<include refid="Base_Column_List"/>
FROM test.users
</select>
<select id="getUser" parameterType="Long" resultMap="UserMap">
SELECT
<include refid="Base_Column_List"/>
FROM users
WHERE id = #{id}
</select>
<insert id="addUser" parameterType="com.example.demo.model.User">
INSERT INTO
test.users
(user_name, password, age, sex)
VALUES
(#{userName}, #{password}, #{age}, #{userSex})
</insert>
<update id="updateUser" parameterType="com.example.demo.model.User">
UPDATE
test.users
SET
<if test="userName != null">user_name = #{userName},</if>
<if test="passWord != null">password = #{passWord},</if>
age = #{age}
WHERE
id = #{id}
</update>
<delete id="deleteUser" parameterType="Long">
DELETE FROM
test.users
WHERE
id = #{id}
</delete>
</mapper>
与之相对应的dao层代码:
public interface UserMapper {
List<User> getUserList();
User getUser(Long id);
void addUser(User user);
void updateUser(User user);
void deleteUser(Long id);
}