MyBatis简介

MyBatis是一个持久化框架(ORM),它原本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。相对于同样实现持久化功能的Hibernate 而言,它更轻量级、学习成本更低、可控性更高。尤其是改名为MyBatis后,框架整体做了较大的改进,渐渐成为能与Hibernate比肩的持久化框架。

与Hibernate不同MyBatis的核心思想不是“对象关系映射”,而是“对象查询映射”。程序员需要自己编写SQL语句来实现Java对象与数据库的转换,因此在编码量上较Hibernate大,但在性能、可控性和易用性上却比Hibernate更好。

ORM(Object Relational Mapping):对象关系映射
编写程序的时候,以面向对象的方式处理数据,保存数据的时候,却以关系型数据库的方式存储。
持久化的操作:将对象保存到关系型数据库中 ,将关系型数据库中的数据读取出来以对象的形式封装

学习文档

相对与其它框架,MyBatis的使用相对比较简单,MyBatis框架和工具可以在google code上下载:https://code.google.com/p/mybatis

MyBatis官方配有中文翻译的使用文档:http://mybatis.github.io/mybatis-3/zh/index.html

后续学习MyBatis-Plushttps://mp.baomidou.com/guide

书籍下载:https://pengyirui.lanzoui.com/iF6c1wcdl7a

导包

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
  <dependencies>   
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>

<build>
<plugins>
<!--指定编译的jdk版本-->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<!--在idea中要求将mapper文件存放在resources目录,【如果是】其他目录则需要手动设置编译路径-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
</project>

创建MyBatis主配置文件(配置SqlSession工厂)

src目录下创建名为mybatis-config.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
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
<?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>
<!--读取jdbc.properties属性文件
properties:用来读取属性文件
参数:
resource:是从resources目录下找指定名称的jdbc.properties文件进行读取
url:是从绝对路径中获取属性文件D:/course\19.MyBatis\04_project\mybatisall\mybatis001_student\src\main\resources
-->
<properties resource="jdbc.properties"></properties>

<!-- 简化实体类别名 -->
<typeAliases>
<typeAlias type = "io.peng.demo.pojo.Users" alias="users"/>
</typeAliases>

<!--创建环境变量(指明数据库的连接信息,指明事务管理器)
environments:总的环境变量的配置,其default属性指定多套配置中使用哪套配置,根据id名称来选择.
-->
<environments default="development">

<!--在公司开发时使用的配置
environment:具体连接信息的配置.
属性:
id:为当前的配置起个名称,供environments的default属性使用.
-->
<environment id="development">
<!--
transactionManager:指定MyBatis框架的事务处理方式
属性:
type:指定事务提交的方式
参数:
JDBC:程序员自己手工提交事务,每个增删改后要手工commit();
MANAGED:由容器负责事务的提交.例如:spring容器
-->
<transactionManager type="JDBC"></transactionManager>

<!--
dataSource:进行数据源的配置,配置驱动的名称,url,访问数据库的用户名和密码
属性:
type:数据源的类型
参数:
JNDI:Java命名目录接口,在服务器端维护数据库连接池
POOLED:使用数据库连接池,由MyBatis提供的数据库连接池.并且提供最优配置,无须程序员手工配置.
UNPOOLED:不使用数据库连接池
-->

<dataSource type="POOLED">
<!--进行数据库连接池的配置
private String driver;
private String url;
private String username;
private String password;
-->
<property name="driver" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>

<!--&lt;!&ndash;在家开发时使用的配置&ndash;&gt;-->
<!--<environment id="home">-->
<!--<transactionManager type=""></transactionManager>-->
<!--<dataSource type=""></dataSource>-->
<!--</environment>-->

<!--&lt;!&ndash;上线时使用的配置&ndash;&gt;-->
<!--<environment id="online">-->
<!--<transactionManager type=""></transactionManager>-->
<!--<dataSource type=""></dataSource>-->
<!--</environment>-->

</environments>

<!--注册mapper.xml文件
mappers:用来注册所有的mapper.xml文件
-->
<mappers>
<!--
mapper:单独一个一个的完成mapper.xml文件的注册

属性:
resource:是指从当前工程的resources目录下找指定名称的mapper.xml文件
url:同properties用法,使用绝对路径定位文件
class:使用动态代理的方式注册mapper.xml文件
-->
<mapper resource="StudentMapper.xml"></mapper>
</mappers>

</configuration>

创建实体类及实体映射文件(Mapper/Dao)

例子:
创建数据实体类Category.java和实体映射文件Category.xml

每添加一个实体配置文件,就应该在主配置文件(mybatis-config.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="com.powernode.dao.CategoryDao">

<select id="getAll" resultType="com.powernode.model.Category">
select * from category
</select>
</mapper>

测试,创建MyBatis会话(SqlSession)并执行查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void getAll(){
try {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory =new SqlSessionFactoryBuilder().build(inputStream);

SqlSession sqlSession=factory.openSession();

List<Category> list=sqlSession.selectList("getAll");
for (Category category : list) {
System.out.println(category.getName());
}

}catch (Exception ex){
System.out.println(ex.getMessage());
}
}

上述代码中:

SqlSessionFactoryBuilder:用于构建SqlSessionFactory,此后这个类就不需要存在了。因此SqlSessionFactoryBuilder实例的最佳范围是方法范围 (也就是本地方法变量)。

SqlSessionFactory:一旦被创建,应该在你的应用执行期间都存在。没有理由来处理或重 新创建它。使用SqlSessionFactory的最佳实践是在应用运行期间不要重复创建多次,因此 SqlSessionFactory 的最佳范围是应用范围,最简单的就是使用单例模式或者静态单例模式。

SqlSession:每个线程都应该有它自己的SqlSession实例。SqlSession 的实例不能被共享,它是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将 SqlSession 实例的引用放在一个类的静态字段甚至是实例字段中。如果你在使用Web框架,则要考虑把SqlSession 放在一个和HTTP请求对象相似的范围内(Open Session In View)。关闭 Session 很重要,你应该确保使 用 finally 块来关闭它。

配置日志功能

mybatis.xml 文件加入日志配置,可以在控制台输出执行的 sql 语句和参数

1
2
3
<settings>  
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>

log4j配置

导入log4j的包

修改配置

1
2
3
<settings>  
<setting name="logImpl" value="LOG4J" />
</settings>

resources目录下添加log4j.properties文件

1
2
3
4
5
6
7
8
9
log4j.rootLogger=debug, stdout,logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.logger.java.sql.ResultSet=INFO
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File =d:/mylog.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %l %F %p %m%n

封装工具类MyBatisUtil类

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
public class MyBatisUtil {
// 定义 SqlSessionFactory
private static SqlSessionFactory factory = null;
static {
// 使用 静态块 创建一次 SqlSessionFactory

try {
String config = "mybatis-config.xml";
// 读取配置文件
InputStream in = Resources.getResourceAsStream(config);
// 创建 SqlSessionFactory 对象
factory = new SqlSessionFactoryBuilder().build(in);
} catch (Exception e) {
factory = null;
e.printStackTrace();
}
}
/* 获取 SqlSession 对象 */
public static SqlSession getSqlSession() {
SqlSession session = null;
if (factory != null) {
session = factory.openSession();
}
return session;
}
}

工具类+接口

在之前的数据库查询中,使用映射语句com.mycinema.model.Category.getAll指定要执行的SQL,这种做法有些弱点:缺乏编译检查。为此,MyBatis还提供了映射器的执行方法。

getMapper 获取代理对象 只需调用 SqlSession 的 getMapper()方法,即可获取指定接口的实现类对象。该方法的参数为指定 Dao 接口类的 class 值。

1
2
SqlSession session = factory.openSession(); 
CategoryDao categoryDao= session.getMapper(CategoryDao.class);

使用Mapper接口实现数据访问

编写数据访问的接口

1
2
3
public interface CategoryDao {  
List<Category> getAll();
}

编写数据访问类的映射文件(在目录:resource/mapper/CategoryDao.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="com.powernode.dao.CategoryDao">

<select id="getAll" resultType="com.powernode.model.Category">
select * from category
</select>
</mapper>

注意:其中方法CategoryDao中的(getAll)的签名与配置的select元素相同。

此后,我们就可以通过CategoryDao接口实现前面的查询了:

1
2
3
4
5
6
SqlSession sqlSession=factory.openSession();
CategoryDao categoryDao= sqlSession.getMapper(CategoryDao.class);
List<Category> list=categoryDao.getAll();
for (Category category : list) {
System.out.println(category.getName());
}

这种调用方式是强类型的,更为对象化,不容易出错,但是需要编写额外的Mapper接口。

动态代理

动态代理开发规范

MyBatis框架使用动态代理的方式来进行数据库的访问.
Mapper接口的开发相当于是过去的Dao接口的开发。由MyBatis框架根据接口定义创建动态代理对象,代理对象的方法体同Dao接口实现类的方法。在设计时要遵守以下规范.

  1. Mapper接口与Mapper.xml文件在同一个目录下(未必。。)
  2. Mapper接口的完全限定名与Mapper.xml文件中的namespace的值相同。
  3. Mapper接口方法名称与Mapper.xml中的标签的statement 的ID完全相同。
  4. Mapper接口方法的输入参数类型与Mapper.xml的每个sql的parameterType的类型相同
  5. Mapper接口方法的输出参数与Mapper.xml的每个sql的resultType的类型相同。
  6. Mapper文件中的namespace的值是接口的完全限定名称.
  7. 在SqlMapConfig.xml文件中注册时,使用class属性=接口的完全限定名.

一句话:UsersMapper.java接口和UsersMapper.xml文件必须在同一个目录下,且必须同名。
在UsersMapper.xml文件中添加namespace属性为接口的完全路径名。

#{}和${}

#{}是对非字符串拼接的参数的占位符,如果入参是简单数据类型,#{}里可以任意写,但是如果入参是对象类型,则#{}里必须是对象的成员变量的名称,可以有效防止sql注入。

${}主要是针对字符串拼接替换,如果入参是基本数据类型,${}里必须是value,但是如果入参是对象类型,则${}里必须是对象的成员变量的名称。可以替换列名和表名,存在sql注入风险,尽量少用。

返回主键标签

在完成插入操作后,将生成的主键信息通过实体类对象返回,在进行后继关联插入操作时,不用再次访问数据库

方式一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<insert id="insertUser" parameterType="com.oracle.demo.pojo.Users">
<!--
Order:指定生成返回主键的时机,
AFTER:先插入再返回主键
BEFORE: 先生成再完成插入
keyProperty: 生成的主键放入到对象的哪个属性中
resultType: 返回的主键的类型
-->

<selectKey order="AFTER" keyProperty="uid" resultType="int">
select LAST_INSERT_ID()
</selectKey>

<!-- 先 生成随机字符串,再返回
<selectKey order="BEFORE" keyProperty="uid" resultType="string">
select uuid()
</selectKey>
-->

动态sql

<sql>标签

当多种类型的查询语句的查询字段或者查询条件相同时,可以将其定义为常量,方便调用。

1
2
3
<sql id="columns">
id,username,birthday,sex,address
</sql>

<include>标签

以为select为例调用<sql>标签

1
2
3
4
5
6
7
<select id="getByCondiition" parameterType="users" resultType="users">

select
<include refid="columns" />
from users

</select>

<if>标签

test:判断条件,如果test为true则执行

1
2
3
<if test="userName != null">
and username like '%${userName}%'
</if>

<where>标签 + <if>标签

一般开发复杂业务的查询条件时,如果有多个查询条件,通常会使用<where>标签来进行控制。 标签可以自动的将第一个条件前面的逻辑运算符 (or ,and) 去掉,正如代码中写的,id 查询条件前面是有“and”关键字的,但是在打印出来的 SQL 中却没有,这就是 的作用。

1
2
3
4
5
6
7
8
9
<select id="getByCondiition" parameterType="users" resultType="users">
select <include refid="columns"></include>
from users
<where>
<if test="userName != null">
and username like '%${userName}%'
</if>
</where>
</select>

<set>标签

常用于更新
使用 if+set 标签修改后,在进行表单更新的操作中,哪个字段中有值才去更新,如果某项为 null 则不进行更新,而是保持数据库原值。切记:至少更新一列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<update id="updateSet" parameterType="users" >
update users
<set>
<if test="userName != null">
username=#{userName},
</if>
<if test="birthday != null">
birthday = #{birthday},
</if>
<if test="sex != null">
sex = #{sex},
</if>
<if test="address != null">
address = #{address}
</if>
</set>
where id = #{id}
</update>

<foreach>标签

<foreach>主要用来进行集合或数组的遍历,主要有以下参数:
collection:
collection 属性的值有三个分别是 list、array、map 三种,分别对应的参数类型为:List、数组、map 集合。

item :循环体中的具体对象。支持属性的点路径访问,如item.age,item.info.details,在list和数组中是其中的对象,在map中是value。

index:在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
open :表示该语句以什么开始
close :表示该语句以什么结束
separator :表示元素之间的分隔符,例如在in()的时候

循环遍历参数集合

1
2
3
4
5
6
7
8
<select id="getByIds" resultType="users">
select <include refid="columns"></include>
from users
where id in
<foreach collection="list" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>

批量删除

1
2
3
4
5
6
<delete id="deleteBatch" >
delete from users where id in
<foreach collection="array" item="id" close=")" open="(" separator=",">
#{id}
</foreach>
</delete>

批量增加

1
2
3
4
5
6
<insert id="insertBatch" >
insert into users(username,birthday,sex,address) values
<foreach collection="list" separator="," item="u">
(#{u.userName},#{u.birthday},#{u.sex},#{u.address})
</foreach>
</insert>

批量更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--    有选择的批量更新,至少更新一列-->
<update id="updateSet" >
<foreach collection="list" item="u" separator=";">
update users
<set>
<if test="u.userName != null">
username=#{u.userName},
</if>
<if test="u.birthday != null">
birthday = #{u.birthday},
</if>
<if test="u.sex != null">
sex = #{u.sex},
</if>
<if test="u.address != null">
address = #{u.address}
</if>
</set>
where id = #{u.id}
</foreach>
</update>

注意:要使用批量更新,必须在url中添加&allowMultiQueries=true,允许多行操作。

入参也能循环

1
2
//新增用户的角色
void addUserRoles(@Param("userId")int userId, @Param("roleIds") int[] roleIds);
1
2
3
4
5
6
7
<insert id="addUserRoles">
insert into userRole(userId,roleId)
values
<foreach collection="roleIds" separator="," item="rid">
(#{userId}, #{rid})
</foreach>
</insert>

指定参数位置

可以不使用对象的属性名进行参数值绑定,使用下标值。 mybatis-3.3 版本和之前的版本使用#{0},#{1}方式, 从 mybatis3.4 开始使用#{arg0}方式。

UsersMapper.java接口中:

1
2
//查询指定日期范围内的用户信息,实体类的成员变量无法包含条件了,所以散给条件
List<Users> getByBirthday(Date begin, Date end);

UsersMapper.xml文件中:

1
2
3
<select id="getByBirthday" resultType="users">
select * from users where birthday between #{arg0} and #{arg1}
</select>

@Param指定参数名称

UsersMapper.java接口中:

1
2
3
4
//切换列名进行模糊查询
//@Param("columnName"):这里定义的columnName的名称是要在xml文件中的${引用定义的名称}
List<Users> getByColunm(@Param("columnName") String columnName,
@Param("columnValue") String columnValue);

UsersMapper.xml文件中:

1
2
3
4
5
<select id="getByColunm" resultType="users">
select <include refid="columns"></include>
from users
where ${columnName} =#{columnValue}
</select>

入参是map

入参是map,是因为当传递的数据有多个,不适合使用指定下标或指定名称的方式来进行传参,又加上参数不一定与对象的成员变量一致,考虑使用map集合来进行传递。
map使用的是键值对的方式.当在sql语句中使用的时候#{键名},${键名},用的是键的名称。

UsersMapper.java接口中

1
2
//入参是map
List<Users> getByMap(Map<String,Date> map);

UsersMapper.xml文件中:

1
2
3
4
5
<select id="getByMap" parameterType="map" resultType="users">
select <include refid="columns"></include>
from users
where birthday between #{zarbegin} and #{zarend}
</select>

测试类中

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testGetByMap()throws Exception{
Date begin = new SimpleDateFormat("yyyy-MM-dd").parse("1996-01-01");
Date end = new SimpleDateFormat("yyyy-MM-dd").parse("1998-12-31");
Map<String,Date> map = new HashMap<>();
//放入map集合中的数据是键值对
map.put("zarbegin",begin);
map.put("zarend",end);
List<Users> list = mapper.getByMap(map);
list.forEach(u-> System.out.println(u));
}

返回值是map

返回值是map的适用场景,如果的数据不能使用对象来进行封装,可能查询的数据来自多张表中的某些列,这种情况下,使用map,但是map的返回方式破坏了对象的封装,返回来的数据是一个一个单独的数据, 之间不相关.map使用表中的列名或别名做为键名进行返回数据.

map封装返回值是一行

UsersMapper.java接口中:

1
2
//返回值是一个值,是map类型,根据主键查用户对象
Map<String,Object> getReturnMapOne(int id);

UsersMapper.xml文件中:

1
2
3
4
5
<select id="getReturnMapOne" resultType="map" parameterType="int">
select id myid,username myusername,sex mysex,address myaddress,birthday mybirthday
from users
where id=#{id}
</select>

测试类中:

1
2
3
4
5
6
@Test
public void testGetReturnMapOne(){
Map<String,Object> map = mapper.getReturnMapOne(7);
System.out.println(map);
System.out.println(map.get("username"));
}

返回值是map多行

UsersMapper.java接口中:

1
2
//使用map封装返回多个map的集合--->List<Map<String,Object>>
List<Map<String,Object>> getReturnMap();

UsersMapper.xml文件中:

1
2
3
4
<select id="getReturnMap" resultType="map" >
select <include refid="columns"></include>
from users
</select>

测试类中:

1
2
3
4
5
@Test
public void testGetReturnMap(){
List<Map<String,Object>> list = mapper.getReturnMap();
list.forEach(map-> System.out.println(map));
}

列名与类中成员变量名称不一致

解决方案一:
使用列的别名,别名与类中的成员变量名一样,即可完成注入。

解决方案二:
使用<resultMap>标签进行映射。

结果集映射

如果对象和表之间有更复杂的差异,比如Java对象中内嵌其它对象属性(多对一或一对多),就需要在MyBatis的实体配置文件中使用resultMap元素描述映射细节。

多对一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Movie {
private int id;
private String title;
private String movieCode;
private Category category;
private String director;
private Date dateReleased;
……
}
public class Category {
private int id;
private String name;
……
}

这种情况下,若仅仅配置返回resultTypeMovie类型的查询,则无法返回外键对象Category的数据:

1
2
3
<select id="fetchById" parameterType="int" resultType="com.powernode.model.Movie">
select * from Movie where id = #{id}
</select>

这时,可以使用<select>元素中resultMap属性替代resultType属性,配置对象的映射。resultMap元素是MyBatis中最重要最强大的元素,让你远离90%的需要从结果集中取出数据的JDBC代码,通过配置实现Java对象属性和查询结果字段的所有映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<resultMap type="Movie" id="movieResultMap1">
<id column="id" property="id"/>
<result column="title" property="title"/>
<result column="movieCode" property="movieCode"/>
<result column="director" property="director"/>
<result column="dateReleased" property="dateReleased"/>

<!-- 多对一 -->
<association property="category" column="categoryId" javaType="Category"
select="io.peng.model.Category.fetchById">
</association>
</resultMap>

<select id="fetchById " parameterType="int" resultMap="movieResultMap1">
select * from Movie where id = #{id}
</select>

以下是resultMap中重要子元素的解析:

子元素 作用
id 一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能。
result 注入到字段或 JavaBean 属性的普通结果
association 一个复杂的类型关联;许多结果将包成这种类型嵌入结果映射
collection 复杂类型的集嵌入结果映射
column sql语句上的 (select语句)
property java对象实体上的

在这里,association元素实现了Movie和Category对象的多对一关系。
其中,select属性指定了另一个映射文件Category.xml中通过Id获取Category对象的查询。

在实际应用中,这种通过select属性获取关联对象的方式实际上产生了两条SQL语句,
第一条SQL查询Movie对象本身,第二条SQL通过外键categoryId查询Category对象。若某Movie查询返回N条Move记

如果使用SQL连接(Join)的方式,只需要一次查询即可,MyBatis中完全可以使用后者完成Movie数据的加载。
<select>元素中配置一个连接查询SQL语句,并在<resultMap><association>中进一步描述Category的映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<resultMap type="Movie" id="movieResultMap2">
<id column="mid" property="id"/>
<result column="title" property="title"/>
<result column="movieCode" property="movieCode"/>
<result column="director" property="director"/>
<result column="dateReleased" property="dateReleased"/>
<association property="category" column="categoryId" javaType="Category">
<id column="cid" property="id" />
<result column="name" property="name" />
</association>
</resultMap>

<select id="fetchById " parameterType="int" resultMap="movieResultMap2">
select m.*, c.*, m.id as mid, c.id as cid from Movie m inner join Category c on
m.categoryId=c.id where m.id=#{id}
</select>

值得注意的是,MyBatis依靠查询结果集中的字段名来加载对象属性,
若两个连接的表字段同名(如id)时,需要用声明别名来完成映射(column=mid)。

一对多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<select id="selectAll" resultMap="UserType">
SELECT u.id uid, u.username,u.`Password`,u.status,
r.id rid, r.name
FROM USER u INNER JOIN userrole ur
ON u.`Id`=ur.`UserId`
INNER JOIN role r
ON r.`Id`=ur.`RoleId`
</select>

<resultMap id="UserType" type="User">
<id property="id" column="uid"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="status" column="status"/>
<!--一对多集合映射-->
<collection property="roles" javaType="Role">
<id property="id" column="rid"/>
<result property="name" column="name"/>
</collection>
</resultMap>

缓存

mybaits提供一级缓存和二级缓存。默认开启一级缓存。

  • 机制
    在进行数据库访问时,首先去访问缓存,如果缓存中有要访问的数据,则直接返回客户端,如果没有则去访问数据库,在库中得到数据后,先在缓存放一份,再返回客户端。

  • 一级缓存
    第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。如果sqlSession去执行commit操作(执行插入、更新、删除),则清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
    第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。如果没有重复第一次查询操作。

  • 二级缓存
    mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存,并且要让实体类实现serializable接口。

逆向工程插件

作用:快速生成 与表对照的实体类与XXXmapper.xml文件
逆向工厂跑第二次时要删除干净第一次生成的文件(XXXXXmapper.xml默认追加)

在maven文件的<build>标签下添加插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<build>
<finalName>provider</finalName>
<plugins>
<!--mybatis代码自动生成插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<!--配置文件的位置-->
<configurationFile>GeneratorMapper.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>

<!-- 定义版本用的,可以不添加 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>

</plugins>
</build>

此文件与maven文件放在同级目录下
根据实际情况修改

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

<!-- 指定连接数据库的JDBC驱动包所在位置,指定到你本机的完整路径 -->
<classPathEntry location="C:\Empolder\mvn-repository\mysql\mysql-connector-java\5.1.6\mysql-connector-java-5.1.6.jar"/>

<!-- 配置table表信息内容体,targetRuntime指定采用MyBatis3的版本 -->
<context id="tables" targetRuntime="MyBatis3">

<!-- 抑制生成注释,由于生成的注释都是英文的,可以不让它生成 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>

<!-- 配置数据库连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mycinema"
userId="root"
password="123456">
</jdbcConnection>

<!-- 生成model类,targetPackage指定model类的包名, targetProject指定生成的model放在eclipse的哪个工程下面-->
<javaModelGenerator targetPackage="io.peng.mycinema.pojo"
targetProject="src\main\java">
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="false" />
</javaModelGenerator>

<!-- 生成MyBatis的Mapper.xml文件,targetPackage指定mapper.xml文件的包名, targetProject指定生成的mapper.xml放在eclipse的哪个工程下面 -->
<sqlMapGenerator targetPackage="/" targetProject="src\main\resources\mapper">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>

<!-- 生成MyBatis的Mapper接口类文件,targetPackage指定Mapper接口类的包名, targetProject指定生成的Mapper接口放在eclipse的哪个工程下面 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="io.peng.mycinema.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="false" />

</javaClientGenerator>

<!-- 数据库表名及对应的Java模型类名 -->
<table tableName="category" domainObjectName="Category"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>

<table tableName="movie" domainObjectName="Movie"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>

</context>

</generatorConfiguration>

过滤器

设置编码用

新建类,实现Filter接口