01Mybatis初探

首先给大家推荐几个网页:

http://www.mybatis.cn/

http://blog.csdn.net/isea533/article/category/2092001

http://www.mybatis.org/mybatis-3/zh/index.html

http://www.mybatis.org/spring/zh/index.html

http://www.mybatis.tk/

#什么是Mybatis?

简单介绍Mybatis

Mybatis的**前身**是iBatis,是Apache的一个开源项目,2010年这个项目从Apache迁移搭配Google改名为Mybatis之后将版本升级到3.x,文档http://www.mybatis.org/mybatis-3/,从3.2版本之后迁移到GitHubhttps://github.com/mybatis/mybatis-3,目前最新稳定版本为3.2.8

MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of

parameters and retrieval of results. MyBatis can use simple XML or Annotations for configuration

and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records.

Mybatis是一个类似于Hibernate的**ORM持久化框架**,支持**普通SQL查询**,**存储过程**以及**高级映射**,Mybatis通过使用**简单的XML**或**注解**用于**配置**和**原始映射**,MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索,将**接口和POJO对象**映射成数据中的的记录。

由于Mybatis是直接基于JDBC做了简单的映射包装,所以从**性能角度**来看;**JDBC>Mybatis>Hibemate**orm

##工具的基本思想
无论是用过的hibernate,mybatis,你都可以法相他们有一个共同点:

  1. 从配置文件(通常是XML配置文件中)得到 sessionfactory.
  2. 由sessionfactory 产生 session
  3. 在session 中完成对数据的增删改查和事务提交等.
  4. 在用完之后关闭session 。

  5. 在java 对象和 数据库之间有做mapping 的配置文件,也通常是xml 文件。

Mybatis是支持定制化sql、存储过程以及高级映射的优秀的持久层框架,其主要完成2件事情:

1.封装JDBC操作

2.利用反射打通Java类与SQL语句之间的相互转换

##特点

1.支持自定义sql。存储过程、及高级映射

2.实现自动对sql的参数设置

3.实现自动对结果集进行解析和封装

4.通过xml或者注解进行配置和映射,大大减少了代码量

5.数据源的连接信息通过配置文件进行配置

~~Mybatis的主要设计目的就是让我们执行sql语句对输入输出的数据管理更加方便,所以方便地写出sql和方便地获取sql的执行结果才是MyBatis核心竞争力。

##Mybatis整体架构

Mybatis配置

MybatisConfig.xml

Mapper1.xml Mapper2.xml …

SqlSessionFactory

sqlSession

Executor(执行器)

SQL输入参数 —>Mapped Statement —–>输出结果

HashMap HashMap

String、Integer String、Integer

POJO POJO

###1.配置文件

**全局配置文件**:Mybatis-config.xml        **作用**:配置数据源(配置数据库连接信息),引入映射文件

**映射文件**:XxxMapping.xml,            **作用**:配置sql语句、参数、结果集封装类型等

###2.SQLSessionFactory

作用:获取SqlSession



通过    newSqlSessionFactoryBuilder().build(inputStream)来构建,inputSream:读取配置文件的IO流

###3.SqlSession

作用:执行CRUD操作

它是线程不安全的,因此最佳的使用范围是请求是请求或方法范围

4.Executor

执行器,SqlSession通过调用它来完成具体的CRUD

它是一个接口,提供了两种实现:缓存的实现、数据库的实现

5.Mapped Statement

在映射文件里面的配置,包含3部分内容

具体sql,sql执行所需的参数类型,sql执行结果的封装类型

参数类型和结果集封装类型包括三种:HashMap,基本数据类型,pojo

Mybatis的主要成员

  • Configuration:MyBatis所有的配置信息都保存在Configuration对象中,配置文件的大部分配置都会存储到该类中
  • SqlSession: 作为MyBatis工作的主要顶层API,表示和数据交互时的会话,完成必要的数据库增删改查的功能
  • Executor:Mybatis执行器,时MyBatis调度的核心,负责sql语句的生成和查询缓存的维护
  • StatementHandler:封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数等
  • ParamHandler:负责对用户传递的参数转换成JDBC Statement所对应的数据类型
  • ResultSetHanler:负责JDBC返回的ResultSet结果集对象转换成List类型的集合
  • TypeHandler:负责java数据类型和jdbc数据类型(也可以数时数据列表类型)之间的映射和转换
  • MappedStatement:维护一条<select|update|delete|insert>节点的封装
  • SqlSource:负责根据用户传递的parameterObject,动态的生成SQL语句,将信息封装到BoundSql对象,并返回表示动态生成的sql语句以及相应的参数信息

#Mybatis快速入门

添加依赖—–>编写实体类—->编写全局配置文件——->编写配置文件——>编写映射文件—->编写测试类

添加依赖

通过Maven在pom.xml中添加Mybatis的依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
<scope>runtime</scope>
</dependency>

<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>

<!--slf4j 日志文件-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j2</artifactId>
<version>1.6.4</version>
</dependency>

##编写POJO类

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
package com.qiezicy.pojo;

import java.util.Date;

public class Order {
private Integer id;

private Integer userId;

private String number;

private Date createtime;

private String note;

private User user;

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public Integer getId() {
return id;
}

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

public Integer getUserId() {
return userId;
}

public void setUserId(Integer userId) {
this.userId = userId;
}

public String getNumber() {
return number;
}

public void setNumber(String number) {
this.number = number == null ? null : number.trim();
}

public Date getCreatetime() {
return createtime;
}

public void setCreatetime(Date createtime) {
this.createtime = createtime;
}

public String getNote() {
return note;
}

public void setNote(String note) {
this.note = note == null ? null : note.trim();
}

@Override
public String toString() {
return "Order [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime
+ ", note=" + note + "]";
}

}

编写全局配置文件

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
<?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>
<!--包扫描-->
<package name="com.qiezicy.pojo"/>
</typeAliases>

<!--environments 标签:配置多个数据库环境
default属性:默认指定使用那个环境,一般是引用某个环境的id
-->
<environments default="development">

<!--environment 标签:配置单个环境
id属性:标识该环境的唯一标识,不能重复,一般是用来被引用的
-->
<environment id="development">
<!--
transactionManager标签:事务管理
type属性:jdbc标识使用jdbc的事务管理,一般不修改
-->
<transactionManager type="JDBC"/>
<!--
dataSource标签:配置数据源
type:使用POOLED,表示使用连接池技术
-->
<dataSource type="POOLED">
<!--配置连接数据源-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="111"/>
</dataSource>
</environment>
</environments>

<mappers>
<mapper resource="urder.xml"/>
</mappers>

</configuration>

这个全局配置文件是mybatis用来建立sessionFactory用的,里面主要包含了数据库连接相关的东西,还有java类对应的别名

这里的dataSource 的类型设置为POOLED

  1. UNPOOLED:不使用连接池的数据源
  2. POOLED:使用连接池的数据源
  3. JNDI:使用JNDI实现的数据源

编写映射文件

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
<?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标签:用来配置各种sql语句以及其他更高级配置
namespace属性:名称空间,需要设置唯一 不能重复 一般用来区分不同的映射文件的
-->
<mapper namespace="user">
<!--
select标签:是用来专门写查询的sql语句的声明(statement)
id属性:表示sql的唯一表示,一般是用来被引用的
resultType属性:结果集类型,将数据封装到那个类型中
parameterType属性:传递参数类型
-->
<select id="getUserById" parameterType="int" resultType="user">
SELECT `id`,
`user_name`,
`password`,
`name`,
`age`,
`sex`,
`birthday`,
`created`,
`updated`

FROM
`tb_user`
WHERE id = #{id2}
</select>
</mapper>

编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test() throws IOException {
// 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
// 读取mybatis配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过输入流创建 sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = ssfb.build(inputStream);
// 打开回话,包含了访问数据库的所有api
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行查询
User User = sqlSession.selectOne("user.getUserById", 1);

System.out.println("User = " + User);

sqlSession.close();
}

总结

1.配置全局配置文件

**注意:**全局配置文件讲究严格的顺序,MyBatis的配置文件包含了影响MyBatis行为的设置和属性信息,文档的顶层结构如下:

**1.configuration配置**

    properties属性

    setting设置

    typeAliases类型命名

    typeHandlers类型处理器

    objectFactory对象工厂

    plugins插件

    environments环境

        environment环境变量

            transacManager事务管理器

            dataSource数据源

    databasidProvider数据库厂商标识

    mappers映射器

**2.settings设置**
设置参数 描述 有效值 默认值
cacheEnabled 该配置影响的所有映射器中配置的缓存的全局开关 true\ false true
lazyLoadingEnabled 延迟加载的全局开关,当开启时,所有关联对象都会延迟加载,特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态 true\ false false
aggressiveLazyLoading 当启用时,带有延迟加载属性的对象的加载与否完全取决于对任意延迟属性的调用;反之,没种属性将会按需加载 true\ false true
mapUnderscoreToCameCase 是否开启自动命名规则(camel case)映射,即从经典数据库列名A_COLUM到经典的java属性名aColum的类似的映射 true\ false false

开启驼峰匹配:完成经典的数据库命名到java属性的映射

经典数据库命名:如果多个单词之间,通常使用下划线进行连接。

java中命名:第二个单词首字母大写。

驼峰匹配:相当于去掉数据中的名字的下划线,和java进行匹配

查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name,POJO中的属性名字是userName,两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名

解决方案1:在sql语句中使用别名

解决方案2:参考驼峰匹配 — mybatis-config.xml

**3.typeAliases**

**4.typeHandlers(类处理器)**

**5.plugins(插件又称拦截器)**

**6.environments(环境)**

全局配置的作用:

  • 设置环境(事务,数据源)

  • 管理映射文件XxxMapper.xml

  • Mapper标签:用来配置不同的statement

    • resource:加载resource
      • class:class扫描器:1.映射文件与接口同一目录下 2.映射文件的名必须与接口文件名相同
        • package包扫描器要求:1.映射文件与接口在同一目录下2.映射文件名必须和接口文件的名称保持一致
  • typeAliases:自定义别名

    之前咱们在映射文件中用到java类型时,都是使用类的全路径,书写起来非常麻烦

    解决方案:

    类型别名是为 Java 类型命名的一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。(官方文档)

  • typeAlias:
  • package:别名扫描器,默认别名是类的全称。不区分大小写。

2.配置映射文件

  • NameSpace属性:用来配置名称空间,来区分不同的映射文件的statement

  • cache:给定命名空间的缓存配置

  • cache-ref:其他命名空间缓存配置的引用
  • resultMap:是最复杂也是最强大的元素,用来描述如何从数据库结果集中加载对象
  • sql:可被其他语句引用的可重用的语句块
  • insert:映射插入语句
    • id:插入语句的唯一标识
    • parametType:插入语句的参数类型,使用动态代理之后,需要和mapper接口中的参数类型一致,可以省略
    • useGeneratedKeys:开启主键自增回显,将自增长的主键值回显到形参中(即封装到User对象中)
    • keyColumn:数据库中主键的字段名称
    • keyProperty:pojo中主键对应的属性
  • update:映射更新语句
    • id:插入语句的唯一标识
    • parametType:插入语句的参数类型,使用动态代理之后,需要和mapper接口中的参数类型一致,可以省略
  • delete:映射删除语句
    • id:插入语句的唯一标识
    • parametType:插入语句的参数类型,使用动态代理之后,需要和mapper接口中的参数类型一致,可以省略
  • select:映射查询语句
    • id:该statement的唯一标识,一般用来被引用
    • resultType:结果集类型
    • parameterType:参数类型,可以省略,一般都省略
1
2
<mapper namespace="user">
<select id="getUserById" parameterType="int" resultType="com.qiezicy.pojo.User">
  • parameterType传入参数

    每一个crud标签都有一个这样的属性,statement通过它指定接收的参数类型

    • 接收参数的方式有两种

      1.#{}预编译

    单个参数时,#{}中的参数与参数名无关的。

    多个参数时,#{}和${}与参数名(@Param)有关。

    1
    2
    3
    4
    5
    6
    User login(@Param("userName")String userName, @Param("password")String password);

    // 映射文件中
    <select id = "login" resultType="User">
    select * from tab_user where user_name = #{userName} and password = #{password}
    </select>
    >将传入的数据都当成一个字符串,会自动对传入的数据加一个双引号。如:order by #{user_id},如果传入的是111,那么解析成sql的时候是order by “111”
    

    2.${}非预编译

    如果你要动态传入的字段名是表名,并且sql执行是预编译的,这显然是不允许的,所以你必须改成非预编译的,也就是这样:

    在一个参数的情况下必须,参数名称必须为value:即${value}

    但是这并不是一种稳妥的解决方案,推荐使用@Param注解指定参数名.

1
2
3
4
5
List<User> queryUserListByTableName(@Param("tableName") String tablepName);
// 映射文件中
<select id = "queryUserListByTableName" resultType="User">
select * from ${tableName}
</select>

$将传入的数据直接显示在生成的sql中,如:order by ${user_id},如果传入的值是111,那么解析sql的时候是order by 111

**注意:**

{}这样的很大成都防止sql注入

${}方式无法防止sql注入

$方式一般用于传入数据库对象,例如传入表名

一般能用#就别用$

MyBatis排序时使用order by 动态参数时需要注意,用$而不是#

  • 参数类型有三种

    1).基本数据类型

    2).HashMap(使用方式和POJO类似)

    3).pojo自定义包装类型

3.使用HashMap传递参数


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// UserMapper接口中的方法
User loginMap(Map<String,String> map);
// UserMapper配置文件
// 将map的key作为参数名称来传递参数
<select id="loginMap" resultType="User">
select * from tab_user where user_name = #{userName} and password = #{password}
<select>
// 测试用例
public void testLoginMap(){
Map<String, String> map = new HashMap<>();
map.put("userName", "zhangsan");
map.put("password", "111");
User user = userMapper.loginMap(map);
}

4.使用pojo传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 接口中的方法
User loginUser(User user);

// 映射文件
// 通过参数名称来调用User中的getting方法来获取参数
<select id="loginUser" resultType="user">
select * from tab_user where user_name = #{userName} and password = #{password}
</select>

// 测试
public void testLoginUser(){
User user = new User();
user.setUserName = ("zhangsan");
user.setPassword = ("123456");
User queryUser = userMapper.loginUser(user);
}

5.总结

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
在Mybatis的mapper中,参数传递有2种方式,一种是#{}和另一种${},两者有着很大的区别;

—_—)#{}实现的是sql语句的预处理参数,之后执行sql中用?号代替,使用时不需要关注数据类型,Mybatis自动实现数据类型转换,并且防止sql注入

—_—)${}实现是sql语句的直接拼接,不做数据类型转换,需要自行判断数据类型。不能防止sql注入

有些清空必须使用${},在分表存储的情况下,我们从那张表查询是不确定的,也就是说sql语句不能写死,表名是动态生成的,查询条件是固定的,这样:select * from ${tableName} where id = #{id}

总结:
#{}占位符用于参数传递
${}用于sql拼接
#{}:
1、 是预编译
2、 编译成占位符
3、 可以防止sql注入
4、 自动判断数据类型
5、 一个参数时,可以使用任意参数名称进行接收

${}:
1、 非预编译
2、 sql的直接拼接
3、 不能防止sql注入
4、 需要判断数据类型,如果是字符串,需要手动添加引号。
5、 一个参数时,参数名称必须是value,才能接收参数。

当填写表名的时候可以用${}其他的情况#{}

6.resultMap

resultMap是Mybatis中最重要最强大的元素,使用resultMap可以解决两大问题:

  • pojo属性名和表结构字段名不一致的问题
  查询数据库的时候,查不到userName的信息,原因:数据库的字段名是user_name,而pojo中的属性名字是userName两端不一致,造成mybatis无法填充对应字段的信息。修改方法:在sql中使用别名

解决方案:1,在sql中使用别名

解决方案:2.参考驼峰匹配    -----mybatis-config.xml的时候

​
1
2
3
4
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

**解决方案3:resultMap自定义映射**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
resultMap标签的作用:自定义结果集,自行设置结果集的封装方式
id属性:resultMap标签的唯一标识,不能重复,一般被用来被引用
type属性:结果集的封装类型
autoMapping属性:操作单表时,不配置默认为true,如果pojo对象中的属性名和表中字段名称相同,则自动映射

例子:
<resultMap id="userResultMap" type="User" autoMapping="true">
// 配置主键映射关系
<id column="id" property="id"/>
// 配置用户名映射关系
<result column="user_name" property="userName"/>
</resultMap>


notes:
上述引用:注意到 主键需要 通过id子标签配置,表字段和属性名不一致的普通字段需要通过result子标签配置,那么,字段和属性名称都匹配的字段要不要配置呢?这取决于resultMap中的autoMapping属性的值 :
为true时:resultMap中的没有配置的字段会自动对应,如果不配置,默认为true
为false时:只针对resultMap中已经配置的字段做映射

完成高级查询,比如一对一、一对多、多对多(在后面会介绍)

7、sql片段

sql标签可以定义一个sql片段,在需要使用该sql片段的地方,通过include标签来使用。在有的时候为了提高sql语句的性能,就不能使用select * 来进行查询,把需要显示的每一个字段都声明出来,如果这是查询语句比较多的情况下,每次都手动编写比较麻烦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<sql id="commonsql">
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
</sql>

<select id="queryUserListLikeUserName" resultType="user">
select <include refid="commonsql"></include> from tb_user where like "%"#{username}"%"
</select>

还可以将sql片段都定义在一个专门存放sql片段的映射文件中,然后由其他映射文件引用它即可。

在resource目录下可以新增一个CommonSQL.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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="CommonSQL">

<sql id="commonSql">
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
</sql>
</mapper>

在使用sql片段时一定要先在全局配置文件中引用

最后在需要使用该sql片段的地方通过include标签的refid属性引用该sql片段:

##Mapper动态代理方式

需要遵循以下规范:

1.映射文件中的命名空间(名称空间)与Mapper接口的全路径一致:其中namespace是命名空间,用于隔离sql

1
<mapper namespace="com.qiezicy.mapper.UserMapper">

2.映射文件中的statement的id与Mapper接口的方法名保持一致

1
2
3
4
5
6
7
8
9
映射文件
<select id="getOrderList" resultMap="order">
</select>
接口
/**
* 获取订单列表
* @return
*/
List<Order> getOrderList();

3.映射文件中statement的resultType必须和mapper接口中的方法返回类型一致(及时不采用动态代理,也要一致)如上:获得是List只需要填写类名order就行,因为配置过别名,并不区分大小写,类名既别名。

4.映射文件中的statement的parameterType必须和mapper接口方法的参数类型一致(不一定,该参数可省略)

动态sql

Mybatis的一个强大的特效之一通常是它的动态sql能力,提供了OGNL表达式动态生成sq功能。动态sql有:

if

choose,when,otherwise

where set

foreach

常用的OGNL表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.	e1 or e2
2. e1 and e2
3. e1 == e2,e1 eq e2
4. e1 != e2,e1 neq e2
5. e1 lt e2:小于
6. e1 lte e2:小于等于,其他gt(大于),gte(大于等于)
7. e1 in e2
8. e1 not in e2
9. e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
10. !e,not e:非,求反
11. e.method(args)调用对象方法
12. e.property对象属性值
13. e1[ e2 ]按索引取值,List,数组和Map
14. @class@method(args)调用类的静态方法
15. @class@field调用类的静态字段值

if标签:判断语句,用于进行逻辑判断的

test属性:用于编写表达式,支持ognl

choose标签:条件选择

​ when子标签:编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行。

​ test属性:编写ognl表达式

​ otherwise子标签:当所有条件都不满足时,才会执行该条件

where标签:可以自动将动态sql中多出来的一个and或者or去除

set标签:

​ 可以自动添加一个set关键字,并且会将动态sql最好多余的逗号去除

foreach标签:遍历集合或者数组

​ collection属性:接收的集合或者数组参数

​ item属性:集合或者数组参数中的每一个元素

​ separator属性:标签分隔符

​ open属性:以什么开始

​ close属性:以什么结束

Mybatis缓存

一级缓存

执行相同的sql语句和参数,Mybatis不再重新执行sql,而是从缓存中返回

在Mybatis中,一级缓存默认是开启的:作用域:在同一sqlSession下

但是我们可以手动清除一级缓存 使用:sqlSession.clearCashe()

>一级缓存的问题

在执行update,insert,delete的时候,即使操作的不是和一级缓存中的同一条记录,都会清空一级缓存

二级缓存

mybatis的二级缓存的作用域:mapper范围的(映射文件级别,多个sqlSession可以共享二级缓存数据)

​ 1.同一个mapper的namespace在同一个namespace中查询sql可以从缓存中命中

​ 2、夸sqlSession,不同的sqlSession可以从二级缓存中命中

场景:在用户通过id查询一个商品信息,执行完查询方法之后会将sqlSession释放掉,当再次发送请求查询同一个商品信息时会再次获取一个新的sqlSession来执行查询,那么此时如果配置了二级缓存的话,就可以直接从二级缓存中获取信息,而不用再次去数据库查询了。

如何开启二级缓存:

​ 1.在映射文件中,添加标签

​ 2.在全局配置文件中,设置CacheEnabled参数,默认已经开启

notes:由于缓存数据是在sqlSession调用close方式时,放入缓存的因此在测试二级缓存时必须先将第一个sqlSession关闭,二级缓存的对象必须序列化,例如User对象必须实现Serializable接口

二级缓存的问题

二级缓存在执行update、insert、delete的时候,也同样会清空二级缓存中的内容

关闭二级缓存

​ 1,不要在映射文件中开启就行

​ 2.设置全局的全局配置文件mybatis-config.xml中去关闭二级缓存

1
2
3
4
5
6
<settings>
<!-- 行为参数,name:参数名,value:参数值,默认为false,true:开启驼峰匹配,即从经典的数据库列名到经典的java属性名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 关闭二级缓存,默认是开启,false:关闭 -->
<setting name="cacheEnabled" value="false"/>
</settings>

#Mybatis高级查询

一对一查询

注意:一旦涉及到嵌套映射,一定要设置手动设置为自动映射。不管是Order的自动映射还是User的自动映射都需要手动设置为true。

需求:

​ 根据订单编号20140921003查询出订单的信息,并查询出下订单的用户的信息

实现:

1.编写接口

1
2
3
4
5
6
7
8
public interface OrderMapper {

/**
* 通过订单号查询订单和用户
* @param orderNumber
* @return
*/
Order queryOrderAndUserByOrderNumber(@Param("orderNumber")String orderNumber);

2.创建pojo的Order类

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

/**
* 订单表
*
*/
public class Order {

private Integer id;

private Long userId;

private String orderNumber;

private User user;

@Override
public String toString() {
return "Order{" +
"id=" + id +
", userId=" + userId +
", orderNumber='" + orderNumber + '\'' +
", user=" + user +
'}';
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public Integer getId() {
return id;
}

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

public Long getUserId() {
return userId;
}

public void setUserId(Long userId) {
this.userId = userId;
}

public String getOrderNumber() {
return orderNumber;
}

public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
}

3.编写OrderMapper.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
<?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.qiezicy.mapper.OrderMapper">


<!--配置自定义结果集 resultMap -->
<!--
id 属性:唯一标识符
type属性:映射java中那个pojo实体类型
autoMapping属性:设置为true自动映射,一旦涉及到嵌套映射,一定要设置手动设置为自动映射。不管是Order的自动映射还是User的自动映射都需要手动设置为true。
-->
<resultMap id="orderAndUserResultMap" type="order" autoMapping="true">


<!--配置 order 的主键映射-->
<id column="id" property="id" />
<!--
除了主键的其他键的映射通过result标签
其中:
column 属性 是 数据库中的列名
property 属性 是 pojo类中的 变量名
-->
<result column="user_id" property="userId"/>
<result column="order_number" property="orderNumber"/>




<!--
association:标签 用于 一对一 映射
property属性:类中的关联属性的名称
javaType属性:属性对应的类型
autoMapping属性:多表查询时,必须设置为true User 对象和tb_user表中的属性和字段才会进行自动映射
-->
<association property="user" javaType="user" autoMapping="true">
<id column="user_id" property="id"/>
<result column="user_name" property="userName"/>
</association>
</resultMap>






<!--通过订单号查询订单和用户-->
<select id="queryOrderAndUserByOrderNumber" resultMap="orderAndUserResultMap">
SELECT * FROM tb_order
INNER JOIN tb_user u ON tb_order.user_id = u.id
WHERE order_number = #{orderNumber};
</select>





</mapper>

4.全局配置文件管理映射文件

1
<mapper resource="OrderMapper.xml"/>

​ 可以通过测试类 进行测试

嵌套查询其实就是嵌套查询,而在使用嵌套查询的时候,那么普通结果集就无法满足我们的查询要求了,因此要使用到ResultMap标签进行自定义结果集的映射配置,并且需要手动配置autoMapping为true。

一对多查询

需求:

​ 通过订单编号20140921001查询订单,并查询出单人信息以及查询出订单详情

订单:订单详情 = 1:n(体现在pojo 对象中,就是在Order对象中添加OrderDetail对象的适合)

实现

1.修改Order

​ 在Order类添加List 属性,并添加get。set方法和toString方法

2.编写接口方法

1
2
3
4
5
6
/**
* 通过订单编号查询订单、用户和订单详情
* @param orderNumber:订单编号
* @return
*/
Order queryOrderAndUserAndOrderdetailByOrderNumber(@Param("orderNumber") String orderNumber);

3.编写statement

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

<!--一对多自定义结果集-->
<resultMap id="orderAndUserAndOrderdetailsResultMap" type="Order" autoMapping="true">
<id column="id" property="id"></id>
<!--一对一-->
<association property="user" javaType="User" autoMapping="true">
<id column="uid" property="id"></id>
</association>
<!-- 一对多
使用 collection 标签
property属性:类中 管理的 属性的 变量名
javaType属性:对应java 中的 类型
ofType属性 :List中每个项 对应的 类型
-->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
<id column="detail_id" property="id"></id>
</collection>

</resultMap>

<!--一对多查询-->
<select id="queryOrderAndUserAndOrderdetailByOrderNumber" resultMap="orderAndUserAndOrderdetailsResultMap">
SELECT
*,u.id as uid,od.id as detail_id
FROM
tb_order o
INNER JOIN tb_user u ON o.user_id = u.id
INNER JOIN tb_orderdetail od on od.order_id = o.id
WHERE
o.order_number = #{orderNumber}
</select>

多对多查询

需求:

​ 通过订单号20140921001查询订单,查询出 下订单人的信息 订单详情 以及商品数据

订单:订单详情 = 1:n(体现在pojo对象中就是在Order对象中添加OrderDetail对象的集合)

订单详情:商品 = 1:1(体现在pojo对象中就是在OrderDetail对象中添加Item对象)

sql:通过订单号20140921001查询订单,查询出 下订单人的信息 并查询出订单详情中的商品数据

实现:

1.修改Order

​ 在Order中添加用户配置一对一关系,在Order中添加List orderdetails配置一对多关系

​ 并在Orderdetail中添加Item配置一对一关系

​ 都需要添加get,set方法,并重写toString方法

2.编写接口方法

1
2
3
4
5
6
/**
* 通过订单编号查询订单、用户和订单详情以及商品
* @param orderNumber
* @return
*/
Order queryOrderAndUserAndOrderdetailAndItemByOrderNumber(@Param("orderNumber") String orderNumber);

​ 3.编写statement

​ OrderMapper配置(通过collection标签中嵌套使用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
<resultMap id="orderAndUserAndOrderdetailsAndItemResultMap" type="Order" autoMapping="true">

<id column="id" property="id"></id>


<!--一对一 配置 User -->
<association property="user" javaType="User" autoMapping="true">
<id column="uid" property="id"></id>
</association>



<!--一对多 配置 Orderdetails-->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">

<id column="detail_id" property="id"></id>




<!--订单详情和商品的一对一的关系-->
<association property="item" javaType="Item" autoMapping="true">
<id column="iid" property="id"></id>
</association>


</collection>

</resultMap>

<!--多对多查询-->
<select id="queryOrderAndUserAndOrderdetailAndItemByOrderNumber" resultMap="orderAndUserAndOrderdetailsAndItemResultMap">
SELECT
*,u.id as uid,od.id as detail_id,i.id as iid
FROM
tb_order o
INNER JOIN tb_user u ON o.user_id = u.id
INNER JOIN tb_orderdetail od on od.order_id = o.id
INNER JOIN tb_item i on i.id = od.item_id
WHERE
o.order_number = #{orderNumber}
</select>

resultMap继承

如果说有resultMap中的数据其他地方也要用到,那么可以使用继承的方法来复用即可

1
<resultMap id="orderAndUserAndOrderdetailsAndItemResultMap" type="Order" autoMapping="true" extends="另一个resultMap的id">

总结

resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射

resultMap:

​ type:结果集对应的数据类型

​ id:唯一表示,被引用的时候,进行指定

​ autoMapping:开启自动映射

​ extends:继承

子标签:

​ association :配置一对一映射

​ property 定义对象的属性名

​ JavaType 属性的类型

​ autoMapping 开启自动映射

​ collection:配置多对多映射

​ property:定义对象的属性名

​ javaType:集合的类型

​ ofType:集合中的元素类型

​ autoMapping :开启自动映射

延迟加载

​ 使Order的查询和User或者OrderDetail的查询分开。只有当我们访问Order对象的User或者OrderDetail属性时,才去执行User或者OrderDetail的查询。

1.编写接口方法

1
2
3
4
5
6
/**
* 延迟加载
* @param orderNumber
* @return
*/
Order queryOrderUserLazy(@Param("orderNumber")String orderNumber);

2.编写statement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<!--通过Order延迟加载User-->
<resultMap id="orderUserLazyResultMap" type="Order">
<!--
select属性:调用指定sql语句来执行延迟加载
column属性:延迟加载的sql语句中所需的参数
-->
<association property="user" javaType="User" select="queryUserByIdOfOrder" column="{id=user_id}"></association>

</resultMap>

<!--通过订单编号查询订单-->
<select id="queryOrderUserLazy" resultMap="orderUserLazyResultMap">
select * from tb_order where order_number = #{orderNumber}
</select>

<select id="queryUserByIdOfOrder" resultType="User">
select * from tb_user where id = #{id}
</select>

开启延迟加载开关

1
<setting name="lazyLoadingEnabled" value="true"></setting>

#特殊符号替换

1
2
3
4
5
6
&lt;		<		小于
&gt; > 大于
&amp; & 和and
&apos; ' 单引号
&quot; " 双引号
严格地讲,在xml中仅有字符"<"&" 是非法的。省略号,引号和大于号是合法的

还可以使用<![CDATA[ < ]]>

CDATA 内部的所有东西都会被解析器忽略,因此只要将字符实体编写在其中,就不需要进行转义。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2016 - 2018 Francis的个人博客 All Rights Reserved.

UV : | PV :