Mybatis 基于 Web 应用的使用

简介

引用 MyBatis 官网的介绍:

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

对于不同的人有不同的喜好,有些喜欢使用 xml 配置文件,有些则喜欢基于注解的方式,这个因人而异,使用者可以根据实际情况和个人喜好选择。

MyBatis 是一个简单易学的 ORM 框架,适合初学者入门 Java ORM 框架。ORM 是 Object Relation Mapping 的缩写(中文翻译:对象关系映射)。ORM 的作用是在关系型数据库和业务实体对象(POJO)之间作一个映射,我们在进行具体的业务操作的时候,就不需要再去和复杂的 SQL 语句打交道,只需简单的操作对象的属性和方法。

ORM 的优点:

  • 剔除拼接 SQL 语句带来的麻烦以及易出错的问题。
  • 剔除传统 JDBC 代码调用中重复性的代码,使数据库的配置更加简单化。
  • 剔除 JDBC 返回的结果转换成领域模型的 Java 对象的麻烦,直接返回映射为 Java 对象的结果。

开发环境

使用语言:Java(jdk1.8,下载地址:JavaSE Development Kit 8
IDE:Eclipse(Neon,下载地址:Eclipse Neon
项目构建工具:Maven
数据库:Mysql(下载地址:MySQL Community Server

主要类的认识(引用自官网)

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在,以保证所有的 XML 解析资源开放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理作用域中,比如 Serlvet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。

使用 Maven 搭建 Web 项目

这里参考之前的文章:Eclipse 使用 Maven 构建 Web 项目

项目结构

搭建完成的项目目录如下图所示:
mybatis

在 pom.xml 中添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- mybatis 包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

数据库准备

schema.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) DEFAULT NULL,
`address` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `book` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`title` VARCHAR(50) NOT NULL,
`userid` INT(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

data.sql

1
2
3
4
5
6
INSERT INTO `user` (`name`, `address`) VALUES ('Leo', 'GuangDong HuiZhou');
INSERT INTO `user` (`name`, `address`) VALUES ('Dan', 'GuangDong ShanTou');
INSERT INTO `book` (`title`, `userid`) VALUES ('C++', '1');
INSERT INTO `book` (`title`, `userid`) VALUES ('Java', '1');
INSERT INTO `book` (`title`, `userid`) VALUES ('Python', '1');

POJO 类

User.java

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
package com.lake.model;
public class User {
private int id;
private String name;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

MyBatis 的配置文件

resources 目录下建立 mybatis.xml 文件,其中定义了 Java 类所对应的别名、数据源和 mapper 文件的位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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="User" type="com.lake.model.User" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/lake/mapper/UserMapper.xml" />
</mappers>
</configuration>

其它的 XML 映射配置文件可参见官网

定义 Mapper 接口

定义 Mapper 的 Java 接口,用来访问数据库的接口,其中包括数据库操作的增删查改:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.lake.mapper;
import java.util.List;
import com.lake.model.User;
public interface UserMapper {
public User selectUserByID(int id);
public List<User> selectUsersByName(String name);
public void addUser(User user);
public void updateUser(User user);
public void deleteUser(int id);
}

使用 XML 定义 Mapper

根据已实现的 Mapper 接口,我们需要通过 XML 来给出 Mapper 的实现,通常将 xxxMapper.xml(UserMapper) 文件放在 resources 目录下对应于 Mapper 接口的 com/lake/mapper/ 目录中。

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
<?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.lake.mapper.UserMapper">
<select id="selectUserByID" parameterType="int" resultType="User">
select * from `user` where id = #{id}
</select>
<!-- 为了返回list 类型而定义的returnMap -->
<resultMap type="User" id="resultListUser">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="address" property="address" />
</resultMap>
<!-- 返回list 的select 语句,注意 resultMap 的值是指向前面定义好的 -->
<select id="selectUsersByName" parameterType="string" resultMap="resultListUser">
select * from user where name like #{name}
</select>
<!-- useGeneratedKeys为true时,表示自动获取数据库生成的主键值;keyProperty="id"表示将获取的主键值主要到User的属性id中 -->
<insert id="addUser" parameterType="User" useGeneratedKeys="true"
keyProperty="id">
insert into user (name, address) values (#{name}, #{address})
</insert>
<update id="updateUser" parameterType="User">
update user set name=#{name}, address=#{address} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>

Mapper XML 文件更加详细的配置信息,参考官网的 Mapper XML 文件版块

关联数据

一个用户可以拥有多本书,所以是一对多的关系。

创建 Book.java 类:

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
package com.lake.model;
public class Book {
private int id;
private String title;
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

定义 Book 的 mapper 接口

1
2
3
4
5
6
7
8
package com.lake.mapper;
import java.util.List;
import com.lake.model.Book;
public interface BookMapper {
public List<Book> getUserBooks(int id);
}

实现 Mapper 接口的 XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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.lake.mapper.BookMapper">
<!-- User 联合 Book 进行查询方法之一的配置 (多对一的方式) -->
<resultMap id="resultUserBookList" type="Book">
<id property="id" column="bid" />
<result property="title" column="title" />
<association property="user" javaType="User">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="address" column="address" />
</association>
</resultMap>
<select id="getUserBooks" parameterType="int" resultMap="resultUserBookList">
select
user.id, user.name, user.address, book.id, book.title from user, book
where book.userid=#{id} and user.id=book.userid
</select>
</mapper>

<association> 的更多用法具体可到官网的自动映射模块查询。

接口测试

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package com.lake.test;
import java.io.Reader;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.lake.mapper.UserMapper;
import com.lake.model.User;
public class Test {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("mybatis.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static SqlSessionFactory getSession() {
return sqlSessionFactory;
}
/**
* 获取用户列表
*/
public void getUserList(String name) {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> users = userMapper.selectUsersByName(name);
for (User user : users) {
System.out.println(user.getId() + "--" + user.getName() + "--" + user.getAddress());
}
} finally {
session.close();
}
}
/**
* 增加用户信息,且必须提交事务,否则不会写入到数据库.
*/
public void addUser() {
User user = new User();
user.setName("Lake");
user.setAddress("GuangDong HuiZhou");
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.addUser(user);
session.commit();
} finally {
session.close();
}
}
/**
* 更新用户信息
*/
public void updateUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.selectUserByID(1);
user.setAddress("GuangDong ChaoZhou");
userMapper.updateUser(user);
session.commit();
} finally {
session.close();
}
}
/**
* 删除用户信息,删除一定要 commit.
*/
public void deleteUser(int id) {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.deleteUser(id);
session.commit();
} finally {
session.close();
}
}
/**
* 返回关联的 Book 列表
*/
public void getUserBooks(int userid) {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper bookMapper = session.getMapper(BookMapper.class);
List<Book> books = bookMapper.getUserBooks(userid);
System.out.println(books.size());
for (Book book : books) {
System.out.println(book.getTitle() + " --- " + book.getUser().getName()
+ " --- " + book.getUser().getAddress());
}
} finally {
session.close();
}
}
public static void main(String[] args) {
Test testUser = new Test();
testUser.addUser();
testUser.getUserList("%");
testUser.updateUser();
testUser.getUserList("%");
testUser.deleteUser(1);
testUser.getUserList("%");
}
}

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
1--Leo--GuangDong HuiZhou
2--Dan--GuangDong ShanTou
3--Lake--GuangDong HuiZhou
1--Leo--GuangDong ChaoZhou
2--Dan--GuangDong ShanTou
3--Lake--GuangDong HuiZhou
2--Dan--GuangDong ShanTou
3--Lake--GuangDong HuiZhou
C++ --- Leo --- GuangDong ChaoZhou
Java --- Leo --- GuangDong ChaoZhou
Python --- Leo --- GuangDong ChaoZhou

热评文章