MyBatis的简单使用

传统JDBC

在JSP/Servlet时代,进行数据持久化的方式采用JDBC,这是一种比较原生的数据库连接方式,我深深记得,在本科的Android课程设计中,还使用JDBC来连接数据库,JDBC使用起来还算方便,引入jar包后,立马就可以编写代码了,但它也有很多缺点:

  1. 大量的模版代码
  2. MySQL语句硬编码,直接写在了Java代码中(需要更改SQL语句时,整个项目都得重新编译打包)
  3. 每次操作数据库,都要建立连接,操作数据,然后断开连接,开销很大

JDBC Template

解决了大量的模版代码问题,将模版代码进行了封装,使得后期维护代码更加方便了,但因为还是基于JDBC,所以仍然没有解决SQL语句硬编码和性能的问题.

MyBatis时代

MyBatis采用XML或注解的方式进行显式配置,XML可以解决JDBC的SQL语句硬编码的问题. 并且MyBatis支持生命时数据缓存,可以将数据库中获得的数据存储在高速缓存中,当下次再进行查询时,就可以直接从缓存中获取,这样也在一定程度上优化了JDBC的性能问题.MyBatis已经成为Web开发中ORM的经典.

动态代理

通过接口调用相应的方法,不需要实现接口,也不需要在实现类中使用SqlSession间接调用.动态代理的方式被广泛使用,因此我主要记录一下这个方式,在此之后还诞生了更为方便的MyBatis Generator,但掌握了较为基础的动态代理方式,学习MyBatis Generator也自然不在话下.

以下记录动态代理的使用:

项目构建

使用Spring Initializr构建一个Spring Boot项目,并添加以下依赖:
4xT6BT.png

添加以下文件夹:
4x7TRs.png

运行以下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
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for country
-- ----------------------------
DROP TABLE IF EXISTS `country`;
CREATE TABLE `country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`countryname` varchar(255) DEFAULT NULL,
`countrycode` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of country
-- ----------------------------
BEGIN;
INSERT INTO `country` VALUES (1, '中国', 'CN');
INSERT INTO `country` VALUES (2, '美国', 'US');
INSERT INTO `country` VALUES (3, '俄罗斯', 'RU');
INSERT INTO `country` VALUES (4, '英国', 'GB');
INSERT INTO `country` VALUES (5, '法国', 'FR');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

编辑application.yml配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
# 标注待解析的mapper的xml文件位置
mapper-locations: classpath:mapper/*.xml
# 标注实体类位置
type-aliases-package: com.example.country.entity

代码编写

实体类:

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
32
33
34
35
36
37
38
39
40
41
42
package com.example.country.entity;


public class Country {
private Long id;
private String countryname;
private String countrycode;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getCountryname() {
return countryname;
}

public void setCountryname(String countryname) {
this.countryname = countryname;
}

public String getCountrycode() {
return countrycode;
}

public void setCountrycode(String countrycode) {
this.countrycode = countrycode;
}

@Override
public String toString() {
return "Country{" +
"id=" + id +
", countryname='" + countryname + '\'' +
", countrycode='" + countrycode + '\'' +
'}';
}
}

Dao层:

1
2
3
4
5
6
7
8
9
package com.example.country.dao;

import com.example.country.entity.Country;
import java.util.List;

public interface CountryMapper {
List<Country> getCountryList();

}

Dao层对应的XML文件(放在resources/mapper文件夹中)

1
2
3
4
5
6
7
8
9
10
11
<?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.country.dao.CountryMapper">
<!--查询全部信息-->
<select id="getCountryList" resultType="country">
select * from country
</select>
</mapper>

可以发现select语句的id属性值与Mapper接口中的方法名是一致的,它们就是靠这种方式将接口方法与XML中的SQL语句关联到一起的;resultType属性值指定的是查询结果的返回值类型,这里返回的是一个List,属性值country表明这个List中的元素都为country bean

Service接口:

1
2
3
4
5
6
7
8
9
package com.example.country.service;

import com.example.country.entity.Country;
import java.util.List;

public interface CountryService {
List<Country> getCountryList();

}

Service接口实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.country.service.impl;


import com.example.country.dao.CountryMapper;
import com.example.country.entity.Country;
import com.example.country.service.CountryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CountryServiceImpl implements CountryService {

@Autowired
private CountryMapper countryMapper;

@Override
public List<Country> getCountryList() {
return countryMapper.getCountryList();
}
}

Controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.country.controller;


import com.example.country.entity.Country;
import com.example.country.service.CountryService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping(path = "/country",produces = "application/json")
public class CountryController {
@Resource
private CountryService countryService;

@GetMapping("/getCountryList")
public List<Country> getCountryList(){
return countryService.getCountryList();
}
}

这样就可以在浏览器中访问到该资源了:

4xO48e.png

注解方式

注解方式的使用更为简单,只需要在Dao的接口方法中添加相应的注解,就免去了配置XML的麻烦,但我并不推荐,因为这种方式也把SQL语句硬编码在了代码中,比较适合个人开发.以下为示例:

1
2
3
4
5
6
7
8
9
10
11
package com.example.country.dao;

import com.example.country.entity.Country;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CountryMapper {
@Select("select * from country")
List<Country> getCountryList();
}

添加注解后就不需要Mapper.xml文件了,不得不说确实方便.