0%

Mybatis基础使用

1 MyBatis简介

1.1 MyBaties

MyBaties 是一个优秀的持久层框架。它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花精力处理例如注册驱动、创建connection、创建statemen手动设置参数、结果集检索等jdbc繁杂的过程代码。

MyBaties 通过 xml 或注解的方式将要执行的各种 statement(statement、prepareStatement、CallableStatement)配置起来。并通过 java 对象和 statement 中的 SQL 进行映射生成最终执行的 SQL 语句,最后由 MyBatis 框架执行 SQL 并将节果映射成 java 对象并返回。

使用ORM思想实现对象关系映射。也就是实体类中的属性和数据库表的字段名保持一致。

1.2 MyBatis 的框架核心

  1. MyBatis 配置文件,包括 MyBatis 全局配置文件和 MyBatis 映射文件,其中全局配置文件配置了数据源、事务等信息,映射文件配置了SQL执行相关的信息。
  2. MyBatis 通过读取配置文件信息(全局配置文件和映射文件),构造出SqlSessionFactory,即会话工厂。
  3. 通过 SqlSessionFactory,可以创建 SqlSession 即会话。Mybatis 是通过 SqlSession 来操作数据库的。
  4. SqlSession 本身并不能直接操作数据库,它是通过底层的 Executor 执行器接口来操作数据库的。Executor 接口有两个实现类,一个是普通执行器,一个是缓存执行器(默认)
  5. Executor 执行器要处理的 SQL 信息是封装到一个底层对象 MappedStatement 中。该对象包括:SQL语句、输入参数映射信息、输出就过集映射信息。曲中输入参数和输出节果的映射类型包括 Java的简单类型、HashMap集合对象、POJO对象类型

2 MyBatis 开始

2.1 开发步骤

  1. 创建maven工程并导入目标。
  2. 创建实体类和dao接口。
  3. 创建MyBatis的主配置文件 SqlMapConfig.xml。
  4. 创建映射配置文件 IUserDao.xml。

创建POJO类

1
2
3
4
5
6
7
8
public class User implements Serializable{
private int id;
private String username;
private String sex;
private Date birthday;
private String address;
// 以及属性的getter和setter
}

创建dao层

1
2
3
4
5
6
7
package cn.Retur0.dao;
import cn.Retur0.domain.User;
import java.util.List;

public interface IUserDao {
List<User> findAll();
}

创建全局配置文件SqlMapConfig.xml

在src下,创建 SqlMapConfig.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
25
26
27
<?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>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源/连接池-->
<dataSource type="POOLED">
<!--配置连接数据库的4个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mbdemo"/><!--?serverTimezone=UTC-->
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>

<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="cn/Retur0/dao/IUserDao.xml"/>
</mappers>
</configuration>

为单个dao创建配置文件IUserDao.xml

1
2
3
4
5
6
7
8
9
10
<?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="cn.Retur0.dao.IUserDao">
<!--配置查询所有-->
<select id="findAll" resultType="cn.Retur0.domain.User">
select * from user
</select>
</mapper>

2.2 注意事项

  1. IUserDao.xml 也可以起名为 IUserMapper.xml。
  2. 映射文件必须与接口文件目录结构相同。
  3. 映射配置文件的 mapper 标签 namesapce 属性的取值必须是 dao 接口的全限定类名。
  4. 映射配置文件的操作配置(select 标签),id属性的取值为 dao 中的方法名。

遵从2 3 4 点之后,在开发中就不需要写 dao 的实现类了。

2.3 运行

test.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
33
34
35
36
37
package cn.Retur0;

import cn.Retur0.dao.IUserDao;
import cn.Retur0.domain.User;
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 java.io.InputStream;
import java.util.List;

public class test {
public static void main(String[] args) throws Exception{
// 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

// 创建SqlSessionFactory工厂
SqlSessionFactoryBuilder bulider = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = bulider.build(in);

// 使用工厂创建SqlSession对象
SqlSession session = factory.openSession();

// 使用SqlSession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);

// 使用代理对象执行方法
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
// 释放资源
session.close();
in.close();
}
}

2.4 使用注解

在 dao 接口的查找方法上添加@Select注解,删除映射配置文件,并在主配置文件中改mapper为:

1
<mapper class="cn.Retur0.dao.IUserDao"/>

3 使用 MyBatis 的步骤解析

读取配置文件

1
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

此时传入的是路径。由于开发和部署环境的路径会变化,这里路径不写相对路径和绝对路径。常用的方法是:

  1. 类加载器。它只能读取类路径配置文件。
  2. 使用 ServletContext 对象的 getRealPath() 方法。

创建SqlSessionFactory工厂

1
2
SqlSessionFactoryBuilder bulider = new SqlSessionFactoryBuilder(); // 构建者
SqlSessionFactory factory = bulider.build(in);

创建工厂 mybatis 使用了构建者模式。构建者是对创建工厂类细节的封装,只需要给 builder 创建工厂类需要的信息(在 SqlMapConfig.xml 中),builder就可以给出工厂类对象。

使用工厂创建SqlSession对象

1
SqlSession session = factory.openSession();

这里使用了工厂模式。使用工厂模式的优势:解耦,降低类之间的依赖。

使用SqlSession创建Dao接口的代理对象

1
userDao = session.getMapper(IUserDao.class);

创建 Dao 实现类使用了代理模式,可以在不修改源码的基础上对方法增强。

使用代理对象执行方法

1
2
3
4
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}

释放资源

1
2
session.close();
in.close();

执行 findAll() 的分析

整个查询过程,我们需要的提供的信息有:

  1. 连接信息。

  2. 映射信息:

    • SQL语句

    • 封装结果的实体类全限定类名。

      把这连个信息组合起来定义成一个对象,就是Mapper。

创建代理对象的分析

Spring中的Proxy。

4 Mybatis 中的 CRUD 操作

IUserDao.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
package cn.Retur0.dao;
import cn.Retur0.domain.User;
import java.util.List;

public interface IUserDao {
// 查询所有用户
List<User> findAll();

// 保存用户
void saveUser(User user);

// 更新用户
void updateUser(User user);

// 删除用户
void deleteUser(int id);

// 根据id查询用户
User findById(int id);

// 根据名称模糊查询用户
List<User> findByName(String username);

// 查询总记录条数
int findTotal();
}

IUserDao.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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?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="cn.Retur0.dao.IUserDao">
<!--配置查询所有-->
<select id="findAll" resultType="cn.Retur0.domain.User">
select * from user
</select>

<!--配置保存用户-->
<insert id="saveUser" parameterType="cn.Retur0.domain.User">
<!--这行语句可以让user插入后的id存到user对象中,不存的话user对象的id是0-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday});
</insert>

<!--配置更新用户-->
<update id="updateUser" parameterType="cn.Retur0.domain.User">
update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id}
</update>

<!--配置删除用户-->
<delete id="deleteUser" parameterType="int">
delete from user where id=#{uid};
</delete>

<!--根据id查询用户-->
<select id="findById" parameterType="int" resultType="cn.Retur0.domain.User">
select * from user where id=#{uid};
</select>

<!--根据名称模糊查询用户-->
<select id="findByName" parameterType="String" resultType="cn.Retur0.domain.User">
select * from user where username like #{name}
</select>

<!--查询总记录条数-->
<select id="findTotal" resultType="int">
select count(id) from user
</select>
</mapper>

test.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
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package cn.Retur0;

import cn.Retur0.dao.IUserDao;
import cn.Retur0.domain.User;
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 org.junit.Test;

import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class test {
private InputStream in;
private SqlSession session;
private IUserDao userDao;

public void init() throws Exception{
// 读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");

// 创建SqlSessionFactory工厂
SqlSessionFactoryBuilder bulider = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = bulider.build(in);

// 使用工厂创建SqlSession对象
session = factory.openSession();

// 使用SqlSession创建Dao接口的代理对象
userDao = session.getMapper(IUserDao.class);
}

public void destory() throws Exception{
// 提交事务
session.commit();
// 关闭资源
in.close();
session.close();
}

@Test
public void t_findAll() throws Exception{
init();

// 使用代理对象执行方法
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
// 释放资源
destory();
}

@Test
public void t_saveUser() throws Exception{
User user = new User();
user.setUsername("saveuser");
user.setAddress(("1-1-1"));
user.setSex("男");
user.setBirthday(new Date());

init();

// 使用代理对象执行方法
userDao.saveUser(user);
System.out.println(user.getId());

// 释放资源
destory();
}

@Test
public void t_updateUser() throws Exception{
User user = new User();
user.setId(4);
user.setUsername("updateuser");
user.setAddress(("2-1-1"));
user.setSex("女");
user.setBirthday(new Date());

init();

// 使用代理对象执行方法
userDao.updateUser(user);

// 释放资源
destory();
}

@Test
public void t_deleteUser() throws Exception{
init();

// 使用代理对象执行方法
userDao.deleteUser(6);

// 释放资源
destory();
}

@Test
public void t_findById() throws Exception{
init();

// 使用代理对象执行方法
User u = userDao.findById(1);
System.out.println(u.getUsername());

// 释放资源
destory();
}

@Test
public void t_findByName() throws Exception{
init();

// 使用代理对象执行方法
List<User> users = userDao.findByName("name%");
for(User user : users){
System.out.println(user.getUsername());
}

// 释放资源
destory();
}

@Test
public void t_findTotal() throws Exception{
init();

// 使用代理对象执行方法
int tol = userDao.findTotal();
System.out.println(tol);

// 释放资源
destory();
}
}

4.1 parameterType

  • 传递简单类型。

  • 传递 pojo 对象。

    MyBatis 使用 ognl 表达式解析对象字段的值,#{} 或者 ${} 括号中的只为 pojo 的属性名称。

  • 传递pojo 包装对象。

    开发中通过 pojo 传递查询条件,查询条件是综合的查询条件,不仅包括用户查询条件还包括其他的查询条件(比如将用户购买商品信息也作为查询条件),这是也可以使用包装对象传递输入参数。pojo 类中包含 pojo。

4.2 resultMap

当数据库中字段和 pojo 中属性名不相同时,我们需要在映射文件 IUserDao.xml 中添加resultMap 标签。

1
2
3
4
5
6
7
8
9
<resultMap id="userMap" type="cn.Retur0.domain.User">
<!--主键对应的字段-->
<id property="userId" column="id"></id>
<!--非主键对应的字段-->
<result property="userName" cloumn="username"></result>
<result property="userAddress" cloumn="address"></result>
<result property="userSex" cloumn="sex"></result>
<result property="userBirthday" cloumn="birthday"></result>
</resultMap>

这个标签中的信息是属性名与列名的对应关系。

之后再配置 SQL 语句,标签中就不写 resultType 属性了,写 resultMap 属性,值为上面配置的 resultMap 标签的 id 属性值。这样,Mybatis 便知道实体类的属性名该如何与数据库中的类名对应。