1.初识MyBatis框架

1.1.框架(Framework)

框架就是一套完整的解决方案,用户在使用框架时只需要按照框架的约定进行使用即可。框架会自动完成对应的功能。

1.2.Jdbc

1.2.1.概述

Jdbc(Java DataBase Connector的缩写)称为Java数据库连接对象,Jdbc是Java和数据库之间连接的桥梁。通过Jdbc可以使用Java来操作数据库中的数据。

1.2.2.缺点

开发过程中需要将Sql语句以硬编码的方式写入到Java代码中,如果Sql语句发生改变,则需要对整个Java代码进行重新编译。

  • Sql语句以硬编码的方式进行编写,不便于系统的维护和修改
  • 需要手动编写代码实现数据的封装和处理

1.2.3.解决

将Sql语句定义在配置文件中,Java代码通过动态读取配置文件的方式来加载Sql语句。通过配置文件编写Sql语句后,即使Sql语句发生了改变,Java代码也不需要重新进行编译。

  • 解决了Java中sql语句硬编码的问题
  • Java的参数以映射方式动态与Sql语句参数进行映射
  • ResultSet结果集可以自动映射为Java实体类

1.3.MyBatis

  • 2001:iBatis=Internet aBatis
  • 2010:iBatis==》MyBatis(Google托管)

1.3.1.MyBatis是什么

  • MyBatis是一个持久层框架
  • MyBatis是一个Orm框架(Object Relational Mapping:对象关系映射,Orm是一种程序设计技术,主要用于面向对象编程中不同编程语言中不同类型之间的转换)
  • MyBatis提供了SqlMap和Data Access Objects(Daos)

1.3.2.数据库访问技术的发展历程

Jdbc==>DBUtil==>MyBatis==>Hibernate(已死)==>Jpa

  • Jdbc:原生态的,需要开发人员手写Sql语句及数据封装的代码
  • DBUtil:对Jdbc进行了一定的封装,但需要开发人员手写Sql语句,工具类通过反射技术实现了简单的数据绑定
  • MyBatis:需要开发人员通过配置文件或注解的方式实现Sql语句(半Orm框架),框架实现了数据的绑定和参数映射。
  • Hibernate:需要开发人员编写配置文件,框架自动实现数据的绑定和处理
  • Jpa:简单且功能强大的框架

1.3.3.MyBatis的本质

MyBatis其实就是一个功能晚上的高级版DBUtil工具类。

1.3.4.MyBatis的目的

MyBatis的愿景就是希望开发人员把更多经历放在Sql语句和业务上,通过MyBatis提供的映射方式自动化将结果集中的数据映射当Java对象上。

1.3.5.第一个MyBatis示例

1)创建步骤

  • 创建项目

  • 添加依赖

  • 编写核心配置文件

  • 编写Pojo类

  • 编写映射文件(Mapper文件)

  • 在核心配置文件中引入映射文件(Mapper文件)

  • 编写代码使用映射
    2)示例

  • 创建项目

    打开系统菜单:File==>New==>Project


    选择项目类型:一般Java项目
    从模版创建项目:无
    编辑项目信息:项目名、保存位置、模块名等

  • 添加依赖(.jar文件,也称为jar包)

  • Mybatis-3.4.4.jar:MyBatis的依赖

  • Mysql-connector-java-8.0.17.jar:MySql数据库的依赖

    在项目下添加名为lib的目录:项目(鼠标右键)==>New==>Directory

    复制依赖到lib目录下:

  • 复制:Ctrl+c

  • 粘贴:Ctrl+v

    将依赖添加到当前项目中:依赖文件(鼠标右键)==>菜单

  • 编写核心配置文件

    在src目录下添加名为mybatis-config.xml的配置文件:src(鼠标右键)==》New==》File

    编写核心配置文件内容:参照官方帮助手册

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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 数据库驱动:告诉编程语言如何来连接数据库-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!-- 数据库连接字符串:说明了数据库服务器的地址、端口号及数据库名称等信息-->
<property name="url" value="jdbc:mysql://localhost:3306/bank_db?serverTimezone=UTC"/>
<!-- 用户名:连接数据库时需要使用的用户名 -->
<property name="username" value="root"/>
<!-- 密码:连接数据库时与用户名匹配的密码-->
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>

</mappers>
</configuration>
  • 编写Pojo类

    创建名为edu.shifan.pojo的包:src(鼠标右键)==>New==>Package
    包的命名规则:域名倒序,如:xja.com,则包名为:com.xja

    在pojo包下创建名为的User类:包名(鼠标右键)==>New==>Java Class

    示例代码

    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
    /**
    * Pojo类(实体类)
    * 作用:用于实现数据的封装及传递
    * 创建要求:一个数据库中的表对应于一个(或多个)Pojo类
    * 命名规范:
    * 实体类的名称与数据库表的名称相一致
    * 多采用表名的单数形式进行命名
    * 如果表名有前缀,则去掉前缀。如表名为sys_user,则实体类名为User
    * 四要素:
    * a、公共的类(类用public关键字进行修饰)
    * b、私有成员字段
    * 字段的数量、类型及名称与数据库表中的字段相一致
    * MySql Java
    * int int/Integer
    * varchar String
    * bit(1) boolean
    * c、无参构造器
    * d、get/set访问器
    */
    public class User {
    //b、私有成员字段
    private int userId;
    private String userName;
    private String card_no;
    private String pwd;
    private int blance;

    //c、无参构造
    //快捷键:alt + Insert
    public User() {
    }

    //d、get/set访问器
    public int getUserId() {
    return userId;
    }

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

    … …
    }
  • 编写映射文件(Mapper文件)

    在pojo包下创建映射文件:包名(鼠标右键)==>New==>File
    映射文件的命名规则:实体类名+Mapper.xml

    编辑映射文件的内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?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">

    <!-- 映射文件:用于定义Sql语句代码片段-->
    <!-- 参考帮助手册:入门==》探究已映射的 SQL 语句-->
    <mapper namespace="edu.shifan.pojo.UserMapper">
    <!-- 查询所有的用户数据-->
    <select id="findAll" resultType="edu.shifan.pojo.User">
    select * from users
    </select>
    </mapper>
  • 在核心配置文件中引入映射文件(Mapper文件)
    在MyBatis的核心配置文件中引入Mapper映射文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <configuration>
    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <!-- 数据库驱动:告诉编程语言如何来连接数据库-->
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    <!-- 数据库连接字符串:说明了数据库服务器的地址、端口号及数据库名称等信息-->
    <property name="url" value="jdbc:mysql://localhost:3306/bank_db?serverTimezone=UTC"/>
    <!-- 用户名:连接数据库时需要使用的用户名 -->
    <property name="username" value="root"/>
    <!-- 密码:连接数据库时与用户名匹配的密码-->
    <property name="password" value="123456"/>
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <!-- 在mappers标签中引入映射文件:告诉MyBatis映射文件的位置-->
    <mapper resource="edu/shifan/pojo/UserMapper.xml" />
    </mappers>
    </configuration>
  • 编写代码使用映射
    在src目录下创建名为edu.shifan.demo的包:src(鼠标右键)==>New==>package
    在demo包下创建名为Demo的类。示例代码如下:

    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
    public class Demo {
    /**
    * main()函数
    * 快捷键:
    * psvm + 回车 //生成main()方法
    * sout + 回车 //生成输出语句
    *
    * @param args
    */
    public static void main(String[] args) {
    //以流方式加载配置文件
    InputStream is = Demo.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
    //获取工厂构建器对象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    //通过构建器对象获取工厂对象
    SqlSessionFactory factory = builder.build(is);
    //通过工厂对象获取SqlSession对象
    SqlSession session = factory.openSession();
    //通过session对象执行映射
    //如果类型显示为红色则表示未导包,通过快捷键alt+Enter导包
    List<User> list = session.selectList("edu.shifan.pojo.UserMapper.findAll");
    //关闭session对象
    session.close();

    //通过输出语句输出查询结果
    //System.out.println(list);
    for(User u : list)
    System.out.println(u);
    }
    }

2.MyBatis实现CRUD

2.1.常用Sql语句

【Select语句】

  • 1、R–Read:从数据库中表中读取数据
    1
    select *|字段列表 from 表名 [where条件] [order by 字段名 asc(默认值,升序排序) | desc(降序排序)]
  • 使用*查询所有用户信息
  • *表示所有字段,实际工作中不推荐使用,可能会导致索引失效
    1
    select * from users
  • 使用字段列表进行查询(推荐方式)
  • 缺点:Sql语句相对比较复杂
  • 优点:使得查询更加灵活(可以根据需要返回字段列表)
    1
    select user_id,user_name,card_no,pwd,blance from users
  • 使用where语句实现数据的查询
    1
    2
    select user_id,user_name,card_no,pwd,blance from users
    where blance>500 and blance <5000
  • 使用order by子句实现数据的查询
    1
    2
    select user_id,user_name,card_no,pwd,blance from users
    order by blance desc

    【Insert语句】

  • 2、C–Create:将数据写入到数据库表中
    1
    insert into 表名 [(字段列表)] values(值列表)
  • 带有字段列表的插入语句
  • 注意:语句中字段的个数和值的个数必须相匹配
    1
    2
    insert into users (user_id,user_name,card_no,pwd,blance)
    values(6,'Tom','5542145','123',200)
  • 带有部分字段列表的插入语句
  • 注意:插入数据时必须为非空字段提供值,否则执行报错
    1
    2
    insert into users (user_id,user_name,blance)	
    values(8,'Jerry',300)
  • 缺省字段列表的插入语句
    1
    insert into users values(9,'Rose','5542147','123',200)
  • 缺省字段列表的方式对部分字段进行数据插入
  • 注意:缺省字段列表的插入语句默认将为所有字段插入数据(不支持部分插入)
  • 下面的语句无法正常执行
    1
    insert into users values(8,'Jerry',300)

    【Update语句】

  • 3、U–Update:更新数据库表中的数据
    1
    update 表名 set 字段名1=值1 [,字段名2=值2,......] [where条件]
  • 带有where条件的更新语句
    1
    2
    update users set card_no='63340921',pwd='789'
    where user_id=8

    【Delete语句】

  • 4、D–Delete:从数据库表中删除数据
    1
    delete from 表名 [where条件]
  • 带有where条件的删除语句
    1
    2
    3
    delete from users 
    where user_id>4
    注意:如果在update或delete语句中省略了where子句,则语句会对表中所有数据生效。

2.2.MyBatis实现CRUD

2.2.1.创建项目

2.2.2.添加依赖

2.2.3.添加MyBatis核心配置文件

2.3.创建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
/**
* 实体类
* 命名规则
* 组成类名的各单词首字母大写。
* 字段命名规则
* 如果字段名由一个单词组成,则单词的各字母全部小写
* 如果字段名由多个单词组成,则第一个单词全部小写,其后各单词首字母大写。如:userName
*/
public class User {
//私有成员字段
private int userId;
private String userName;
private String cardNo;
private String pwd;
private int blance;

//无参构造函数
public User() {
}

//get/set访问器
public int getUserId() {
return userId;
}

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

……
}

2.4.编写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
<mapper namespace="edu.shifan.pojo.UserMapper">
<!-- 新增数据到数据库中 -->
<insert id="add">
insert into users
values(10,'Tom','53343261','123',2000)
</insert>

<!-- 按Id修改数据 -->
<update id="updateById">
update users set user_name='Jerry',pwd='456',blance=10000
where user_id=10
</update>

<!-- 按Id删除数据 -->
<delete id="delById">
delete from users
where user_id=10
</delete>

<!-- 查询所有用户数据 -->
<select id="findAll" resultType="edu.shifan.pojo.User">
select * from users
</select>
</mapper>

2.5.引入Mapper映射

1
2
3
4
<mappers>
<!-- 引入映射文件 -->
<mapper resource="edu/shifan/pojo/UserMapper.xml" />
</mappers>

2.6.使用映射

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
public class Demo {
public static void main(String[] args) {
//新增数据到Users表中
//add();

//更新users表中数据
//update();

//按Id删除用户信息
del();

//查询所有用户列表
findList();
}

/**
* 新增用户信息到Users表中
*/
private static void add() {
//以流方式读取配置文件
InputStream is = Demo.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//获取SqlSession工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//获取SqlSession对象
SqlSession session = factory.openSession(true);

//使用映射
//count:受影响行数
int count = session.insert("edu.shifan.pojo.UserMapper.add");

//关闭Session对象
session.close();

//处理执行结果
if(count>0)
System.out.println("数据添加成功");
else
System.out.println("数据添加失败");
}

/**
* 更新用户信息到Users表中
*/
private static void update() {
//以流方式读取配置文件
InputStream is = Demo.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//获取SqlSession工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//获取SqlSession对象
SqlSession session = factory.openSession(true);

//使用映射
//count:受影响行数
int count = session.update("edu.shifan.pojo.UserMapper.updateById");

//关闭Session对象
session.close();

//处理执行结果
if(count>0)
System.out.println("数据修改成功");
else
System.out.println("数据修改失败");
}

/**
* 更新用户信息到Users表中
*/
private static void del() {
//以流方式读取配置文件
InputStream is = Demo.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//获取SqlSession工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//获取SqlSession对象
SqlSession session = factory.openSession(true);

//使用映射
//count:受影响行数
int count = session.delete("edu.shifan.pojo.UserMapper.delById");

//关闭Session对象
session.close();

//处理执行结果
if(count>0)
System.out.println("数据删除成功");
else
System.out.println("数据删除失败");
}

/**
* 查询所有用户列表
*/
private static void findList() {
//以流方式读取配置文件
InputStream is = Demo.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//获取SqlSession工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//获取SqlSession对象
SqlSession session = factory.openSession();

//使用映射
List<User> list = session.selectList("edu.shifan.pojo.UserMapper.findAll");

//关闭Session对象
session.close();

//处理查询结果
for(User u : list)
System.out.println(u);
}
}

3.MyBatis配置文件

3.1.核心配置文件

3.1.1.概述

Mybatis-config.xml配置文件(核心配置文件)是一个全局的配置文件。

3.1.2.配置项

  • Properties:加载外部资源文件
  • Settings:全局参数配置
  • typeAliases:类型别名
  • typeHandler:类型处理器
  • plugins:插件
  • environments:运行环境对象集合
  • environment:运行环境对象
  • mappers:映射器

    注意:配置项有先后顺序的要求

.2.常用配置

3.2.1.Properties

1)作用
引入外部配置文件
2)示例

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
【Properties配置文件】
# 配置文件
# 常见配置文件:.ini、.xml、.properties
# 格式:key=value
# 注意:配置文件中的所有内容都是字符串类型的
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bank_db?serverTimezone=UTC
user=root
pwd=123456
【核心配置文件】
<!--
xml文件
文档声明

文档内容(configuration部分,configuration称为根节点)
注意:xml文件中根节点只能有一个
-->
<configuration>
<!-- properties:用于引入外部配置文件,配置在environments的前面-->
<properties resource="db.properties"/>

<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>

<mappers>
</mappers>
</configuration>

3)说明
Mybatis核心配置文件中的properties标签的加载是有顺序的,首先加载properties标签中定义的属性,然后在去读取resources标签中resource属性引入的外部文件。读取过程后面的内容会覆盖前面的内容。
4)优势
使得mybatis的环境配置与数据库的配置分离,便于数据库连接信息的修改。
5)注意事项

  • Properties标签需要定义在environments的前面
  • 注意区分大小写

3.2.2.environments

1)概述
MyBatis中通过environments可以配置数据库的连接信息。
2)配置

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
<!--
xml文件
文档声明

文档内容(configuration部分,configuration称为根节点)
注意:xml文件中根节点只能有一个
-->
<configuration>
<!-- properties:用于引入外部配置文件,配置在environments的前面-->
<properties resource="db.properties"/>
<!--
运行环境对象集合(数据库环境配置)
default属性:指定当前使用的数据源环境
-->
<environments default="dev">
<!-- 数据源环境-->
<!-- 数据源环境1:开发时使用的数据库环境-->
<environment id="dev">
<!--
配置事务管理的方式:
type的取值:
JDBC:使用JDBC的事务提交,事务管理器来源于数据源
MANAGED:使用容器来实现事务管理,如:spring(容器)
-->
<transactionManager type="JDBC"/>
<!--
dataSource:配置数据源对象
type的取值:
UNPOOLED:使用简单数据源(不使用连接池),每次请求都会打开和关闭一个连接对象
POOLED:使用连接池对象,效率相对较高
JNDI:在外部配置数据源
-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>

<!-- 数据源环境2:测试时使用的数据库环境-->
<environment id="test">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="jdbc:mysql://192.168.1.103/bank_db?serverTimezone=UTC"/>
<property name="username" value="${user}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>

<mappers>
</mappers>
</configuration>

4.封装Util工具类

4.1.项目搭建

4.1.1.创建项目

4.1.2.添加依赖

4.1.3.编写配置文件

1)创建资源目录
项目名(鼠标右键)==>New==>Directory==>命名为resources
2)将目录标记为资源目录
Resources(鼠标右键)==>Mark Directory as==>Resource Root
3)创建配置文件

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
【db.properties】
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bank_db?serverTimezone=UTC
username=root
password=123456
【mybatis-config.xml】
<configuration>
<!-- 引入外部资源文件-->
<properties resource="db.properties" />

<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>

</mappers>
</configuration>

4.2.封装Util工具类

4.2.1.对象的范围

1)SqlSessionFacBuilder

  • 作用
    提供用于构建SqlSessionFa的方法。
  • 范围
    最佳范围是方法范围(方法的局部变量)。

    2)SqlSessionFactory

  • 作用
    提供了用于构建SqlSession的方法。
  • 范围
    应用程序范围内(整个应用程序运行期间仅有一个对象)。

    3)SqlSession

  • 作用
    提供用于调用映射的相关方法。
  • 范围
    每个线程或请求都应该有自己的Session示例,所以最佳范围是请求或方法范围。

4.2.2.封装Util工具类

1)步骤

  • 创建名为util的包
  • 创建名为SessionUtil的类
  • 编写代码

    2)示例

    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
    /**
    * Util工具类
    * 类中提供了操作Session对象的方法
    */
    public class SessionUtil {
    //使用static修饰的成员称为静态成员(静态成员在类中只有一个实例)
    //private:私有的,被private修饰的成员不会被其他类所访问(封装的思想)
    private static SqlSessionFactory factory = null;
    //ThreadLocal会为每一个线程提供一块独立的内存空间用于存储数据(对象)
    //ThreadLocal相当于万德隆楼下的储物柜(每个用户相当于一个线程,每个用户都有自己独立的存储空间)
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();

    /**
    * 静态代码块
    * 在类加载后被执行(只被执行1次)
    * 通过静态代码块对factory对象进行实例化
    */
    static {
    //以流方式读取配置文件
    InputStream is = SessionUtil.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
    //通过SqlSessionFactoryBuilder来构建SqlSessionFactory对象
    factory = new SqlSessionFactoryBuilder().build(is);
    }

    /**
    * 返回一个SqlSession对象
    * @return
    */
    public static SqlSession getSession(){
    //从存储空间中读取对象的session对象
    SqlSession session = threadLocal.get();

    //判断session对象是否有效
    if(session==null){
    //通过工厂创建对象
    session = factory.openSession(true);
    //将session对象添加到存储空间中进行存储(一遍下一次使用)
    threadLocal.set(session);
    }
    return session;
    }

    /**
    * 返回一个SqlSession对象
    * @return
    */
    public static SqlSession getSession(boolean state){
    //从存储空间中读取对象的session对象
    SqlSession session = threadLocal.get();

    //判断session对象是否有效
    if(session==null){
    //通过工厂创建对象
    session = factory.openSession(state);
    //将session对象添加到存储空间中进行存储(一遍下一次使用)
    threadLocal.set(session);
    }
    return session;
    }

    /**
    * 关闭session对象
    */
    public static void close(){
    //从存储空间中读取对象的session对象
    SqlSession session = threadLocal.get();

    //判断session的状态
    if(session!=null)
    //关闭session
    session.close();

    //从存储空间中移除对象
    threadLocal.remove();
    }

    }

4.3.测试工具类

4.3.1.需求

查询所有用户信息列表。

4.3.2.实现

1)编写pojo类

  • 实现步骤
    • 创建pojo包
    • 创建实体类
    • 编写实体类代码
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    public class User {
    //私有成员字段
    private int userId;
    private String userName;
    private String cardNo;
    private String pwd;
    private int blance;
    }

    2)编写Mapper映射

  • 实现步骤
    • 在resources目录下创建名为mappers的子目录
    • 在mappers子目录下创建Mapper映射文件
  • 示例
    1
    2
    3
    4
    5
    6
    <mapper namespace="edu.shifan.mapper.UserMapper">
    <!-- 查询所有用户信息 -->
    <select id="findAll" resultType="edu.shifan.pojo.User">
    select * from users
    </select>
    </mapper>

    3)注册映射

    1
    2
    3
    <mappers>
    <mapper resource="mappers/UserMapper.xml" />
    </mappers>

    4)编写测试代码

    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
    public class TestUtil {
    public static void main(String[] args) {
    //以前的写法
    /*
    //1、以流方式读取配置文件
    InputStream is = TestUtil.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
    //2、获取SqlSessionFactory对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
    //3、获取Session对象
    SqlSession session = factory.openSession();
    */
    SqlSession session = SessionUtil.getSession(false);
    //4、使用映射
    List<User> list = session.selectList("edu.shifan.mapper.UserMapper.findAll");

    //5、关闭连接
    //session.close();
    SessionUtil.close();

    //处理查询结果
    //System.out.println(list);
    for(User u : list)
    System.out.println(u);
    }
    }

5.列名与字段名不一致

5.1.问题

在进行查询时,如果查询结果中列的名称与Java实体类中字段名称不一致,将导致实体类中字段的值绑定失败。

5.2.解决方案

5.2.1.字段别名

1)实现方式
在查询语句中使用字段别名方式进行查询。
2)示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<mapper namespace="edu.shifan.mapper.UserMapper">
<!-- 查询所有用户信息列表 -->
<!-- 1、使用通配符方式进行查询 -->
<!--
resultType:配置代码段执行结果的返回值的类型
-->
<select id="findAll1" resultType="edu.shifan.pojo.User">
select * from users
</select>

<!-- 2、查询语句字段别名方式进行查询-->
<select id="findAll2" resultType="edu.shifan.pojo.User">
select user_id userId, user_name username, card_no cardno,pwd,blance from users
</select>
</mapper>

3)缺点

  • 代码复用性较差
  • Sql语句存在一定的冗余

5.2.2.ResultMap描述映射关系

1)实现方式
通过resultMap对查询结果中的列名与Java实体类的字段名进行对应关系描述。
2)示例

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
<mapper namespace="edu.shifan.mapper.UserMapper">
<!-- 查询所有用户信息列表 -->
<!-- 1、使用通配符方式进行查询 -->
<!--
resultType:配置代码段执行结果的返回值的类型
-->
<select id="findAll1" resultType="edu.shifan.pojo.User">
select * from users
</select>

<!-- 2、查询语句字段别名方式进行查询-->
<select id="findAll2" resultType="edu.shifan.pojo.User">
select user_id userId, user_name username, card_no cardno,pwd,blance from users
</select>

<!-- 3、通过ResultMap描述对应关系-->
<select id="findAll3" resultMap="userMap">
select * from users
</select>

<!--
resultMap:结果映射关系图,可以将查询结果中的字段与Java实体类中的字段按照对应关系进行描述
id:结果映射关系图的唯一标识,其他代码段通过id对其进行调用
type:配置需要进行映射的实体类(如果没有配置类型别名,则需要使用类全名=包名+类名)

如果表中字段名与实体类的属性名相一致,可以不进行强制的对应关系描述
建议:全部进行描述
-->
<resultMap id="userMap" type="edu.shifan.pojo.User">
<!--
id:用于描述主键列的映射关系
column:进行映射的数据表中字段的名称
property:进行映射的Java实体类的字段名称
-->
<id column="user_id" property="userId" />
<!--
result:用于描述非主键列的映射关系
column:进行映射的数据表中字段的名称
property:进行映射的Java实体类的字段名称
-->
<result column="user_name" property="userName" />
<result column="card_no" property="cardNo" />
</resultMap>
</mapper>

3)优点

  • 代码复用性较高
  • 降低了Sql语句的冗余

5.2.3.启用mapUnderscoreToCamelCase

1)实现方式
在MyBatis的核心配置文件中通过在settings节中配置mapUnders coreToCamelCase项来开启自动匹配功能。
2)示例

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 引入外部配置文件 -->
<properties resource="db.properties" />

<!-- 通过setttings全局配置进行实现-->
<settings>
<!--
mapUnderscoreToCamelCase:开启从数据库经典列名到Java实体类字段经典列名的自动转换
true:开启转换功能
false:关闭转换功能
-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

3)优点

  • 简单方便

6.面向接口开发

6.1.常见异常

6.1.1.错误1

1)错误描述

2)错误原因

  • 配置文件所属目录不是资源或src目录
  • 引入的配置文件不存在或文件名错误

    3)解决方案

  • 检查目录是否正确
  • 检查文件路径是否正确
  • 检查文件名是否正确

6.1.2.错误2

1)错误描述
Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: The setting mapUnderscoreToCamelCase is not known. Make sure you spelled it correctly (case sensitive).
2)错误原因

  • 配置文件中的属性名存在错误

    3)解决方案

  • 检查配置文件中的属性名是否正确

6.1.3.错误3

1)错误描述
Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for edu.shifan.mappers.UserMapper.findAll
2)错误原因

  • 映射文件没有在MyBatis核心配置文件中注册
  • 测试代码中的映射与Mapper映射文件中的不一致

    3)解决方案

  • 检查映射文件是否进行注册
  • 检查测试代码中与映射文件中配置是否一致

6.1.4.错误4

1)错误描述
Cause: java.sql.SQLException: Error setting driver on UnpooledDataSource. Cause: java.lang.ClassNotFoundException: Cannot find class: ${driver}
2)错误原因

  • 配置文件中的key不存在
  • 驱动类错误

    3)解决方案

  • 检查数据库的配置文件中key与读取的key值是否一致
  • 检查驱动类是否正确

6.1.5.错误5

1)错误描述
Cause: java.sql.SQLException: No suitable driver found for jdbc.mysql://localhost:3306/band_db?serveTimezone=UTC
2)错误原因

  • 数据库配置文件中的连接字符串存在错误

    3)解决方案

  • 检查url连接字符串是否正确

6.1.6.错误6

1)错误描述
Cause: java.sql.SQLException: Access denied for user ‘root‘@’localhost’ (using password: YES)
2)错误原因

  • 数据库访问密码错误

    3)解决方案

  • 检查数据库访问密码

6.1.7.错误7

1)错误描述
Cause: java.sql.SQLSyntaxErrorException: Unknown database ‘band_db’
2)错误原因

  • 数据库连接字符串中的数据库名称错误

    3)解决方案

  • 检查数据库名称是否正确

6.1.8.错误8

1)错误描述
Cause: java.sql.SQLException: The server time zone value ‘�й���׼ʱ��’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
2)错误原因

  • 数据库连接字符串中的参数名有错误

    3)解决方案

  • 检查参数名并改正

6.1.9.错误9

1)错误描述
Cause: java.sql.SQLSyntaxErrorException: Table ‘bank_db.user’ doesn’t exist
2)错误原因

  • 映射文件中Sql语句中使用到的数据表名称不存在
    3)解决方案
  • 检查数据表名

6.2.面向接口开发

6.2.1.面向接口开发的原则

  • Mapper映射文件的namespace必须和mapper接口的类全名相一致
  • Mapper映射接口中的方法名和Mpper映射文件中statement的id必须保持一致
  • Mapper映射接口中方法的参数类型必须和Mapper映射文件中sta tement的parameterType类型保持一致
  • Mapper映射接口中方法的返回值类型必须和Mapper映射文件中re sultType的类型保持一致
  • Mapper映射接口和Mapper映射文件必须在同一目录(包)下,且具有相同的名称

6.2.2.实现步骤

  • 编写Mappe映射接口和Mapper映射文件
  • 在MyBatis核心配置文件中注册映射接口或映射文件
  • 在测试代码中使用映射接口

6.2.3.实现

1)创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 实体类
* 作用:封装数据
*/
public class User {
//私有成员字段
private int userId;
private String userName;
private String cardNo;
private String pwd;
private int blance;
}

2)创建映射接口

  • 创建名为edu.shifan.dao的包
  • 创建映射接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * 映射接口
    * 接口:接口是一种约束,接口中只能提供方法声明(不能有具体的实现)
    * 方法四要素:返回值类型、方法名、参数列表、方法体
    */
    public interface UserDao {
    /**
    * 将用户信息添加到数据库中
    * @param user:封装了用户的信息
    * @return:受影响行数
    */
    int add(User user);
    }
    3)创建映射文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!--
    namespace与映射接口的类全名(包名+类名)相一致
    接口名(鼠标右键)==》copy==》copy reference
    -->
    <mapper namespace="edu.shifan.dao.UserDao">
    <!-- 新增用户信息-->
    <!--
    parameterType必须和接口中方法的参数类型保持一致
    在insert、update及delete中resultType默认类型为int,可以缺省
    -->
    <insert id="add" parameterType="edu.shifan.pojo.User" >
    <!-- #{}表示读取指定参数的值-->
    insert into users values(#{userId},#{userName},#{cardNo},#{pwd},#{blance})
    </insert>
    </mapper>
    4)注册映射接口
    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
    <configuration>
    <!-- 引入外部配置文件-->
    <property resource = "db.properties" />

    <!-- 开启自动匹配设置 -->
    <settings>
    <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>

    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <!--
    注册映射接口
    注意:需要使用class属性
    -->
    <mapper class="edu.shifan.dao.UserDao" />
    </mappers>
    </configuration>
    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
    27
    /**
    * 新增用户信息
    */
    private static void add() {
    //通过对象U封装数据
    User u = new User();
    u.setUserId(9);
    u.setUserName("Mike");
    u.setCardNo("622020013");
    u.setPwd("123456");
    u.setBlance(9000);

    //1、获取Session对象
    SqlSession session = SessionUtil.getSession();
    //2、获取映射对象
    UserDao dao = session.getMapper(UserDao.class);
    //3、调用到接口对象的方法
    int count = dao.add(u);
    //4、关闭Session对象
    SessionUtil.close();

    //5、处理执行结果
    if(count>0)
    System.out.println("信息添加成功");
    else
    System.out.println("信息添加失败");
    }

7.动态SQL语句

7.1.多条件查询

7.1.1.需求

按姓名、性别及所属年级等多个字段组合查询学生信息。

7.1.2.SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1、未提供任何条件字段值
select * from sys_students

# 2、提供了姓名字段值
select * from sys_students where stu_name like '%亮%'

# 3、提供了性别字段值
select * from sys_students where gender=true

# 4、提供了所属年级ID字段值
select * from sys_students where grade_id=3

# 5、提供了姓名、性别字段值
select * from sys_students where stu_name like '%亮%' and gender=true

7.1.3.条件组合

姓名	性别	所属年级

1 × × ×
2 √ × ×
3 × √ ×
4 × × √
5 √ √ ×
6 √ × √
7 √ √ √

7.2.实现方式

7.2.1.传统方式

在Java中通过if语句+SQL的辅助条件(1=1)进行实现。

7.2.2.MyBatis方式

MyBatis中提供了标签的方式实现动态SQL语句。

7.3.动态SQL语句

所谓动态SQL语句是指程序代码根据条件的不同动态生成SQL语句。

7.4.If标签

7.4.1.需求

根据姓名、性别及所属年级等条件组合实现数据的查询。

7.4.2.实现

1)编写Pojo类

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 学生实体类
* 基本类型 === 包装类型
* int == Integer
* boolean == Boolean
* long == Long
*/
public class Student {
private String stuName;
private Boolean gender;
private Integer gradeId;
}

2)编写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
【Mapper接口】
public interface StudentMapper {
/**
* 按照条件查询学生信息
* 条件:姓名、性别、所属年级ID
*/
List<Student> findByExp(Student student);

}
【Mapper映射】
<mapper namespace="edu.shifan.mapper.StudentMapper">
<!-- if标签使用示例 -->
<select id="findByExp" parameterType="edu.shifan.pojo.Student" resultType="edu.shifan.pojo.Student">
select * from sys_students where 1=1

<if test="stuName!=null and stuName!=''">
and stu_name like "%"#{stuName}"%"
</if>

<if test="gender!=null">
and gender=#{gender}
</if>

<if test="gradeId!=null and gradeId!=0">
and grade_id=#{gradeId}
</if>
</select>
</mapper>

3)测试映射

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
/**
* 测试if标签
*/
private static void testIf() {
//1、封装参数对象
Student stu = new Student();
stu.setStuName("J");
stu.setGender(false);

System.out.println(stu);

//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//b、获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list = mapper.findByExp(stu);
//d、关闭Session
SessionUtil.close();

//3、处理查询结果
for(Student s : list)
System.out.println(s);
}

4)优点
实现了动态SQL语句,提高了语句的灵活性。
5)缺点
为了保证语句的完整性,需要借助于辅助条件(1=1)进行实现。

7.5.Where标签

7.5.1.实现

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
1)编写Mapper映射
public interface StudentMapper {
/**
* if标签示例
* 按照条件查询学生信息
* 条件:姓名、性别、所属年级ID
*/
List<Student> findByExp(Student student);

/**
* where标签示例
*/
List<Student> findByExp2(Student student);

}
<mapper namespace="edu.shifan.mapper.StudentMapper">
<!-- if标签使用示例 -->
<select id="findByExp" parameterType="edu.shifan.pojo.Student" resultType="edu.shifan.pojo.Student">
select * from sys_students where 1=1

<if test="stuName!=null and stuName!=''">
and stu_name like "%"#{stuName}"%"
</if>

<if test="gender!=null">
and gender=#{gender}
</if>

<if test="gradeId!=null and gradeId!=0">
and grade_id=#{gradeId}
</if>
</select>

<!-- where标签使用示例-->
<select id="findByExp2" parameterType="edu.shifan.pojo.Student" resultType="edu.shifan.pojo.Student">
select * from sys_students
<where>
<if test="stuName!=null and stuName!=''">
and stu_name like "%"#{stuName}"%"
</if>

<if test="gender!=null">
and gender=#{gender}
</if>

<if test="gradeId!=null and gradeId!=0">
and grade_id=#{gradeId}
</if>
</where>
</select>
</mapper>

2)测试查询

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
/**
* 测试where标签
*/
private static void testWhere() {
//1、封装参数对象
Student stu = new Student();
stu.setStuName("J");
stu.setGender(false);

System.out.println(stu);

//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//b、获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list = mapper.findByExp2(stu);
//d、关闭Session
SessionUtil.close();

//3、处理查询结果
for(Student s : list)
System.out.println(s);
}

3)优点
实现了动态SQL语句,提高了语句的灵活性。
4)If与where

  • Where标签只能用于生成动态的多条件where语句;if标签除了动态生成多条件where语句外还能实现其他的动态处理
  • Where标签实现动态SQL语句需要借助于if标签;而if标签则不一定借助于where标签进行实现

7.6.Foreach标签

7.6.1.需求

按ID(不连续的多个ID)查询学生信息

7.6.2.实现

1)编写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
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
public interface StudentMapper {
/**
* if标签示例
* 按照条件查询学生信息
* 条件:姓名、性别、所属年级ID
*/
List<Student> findByExp(Student student);

/**
* where标签示例
*/
List<Student> findByExp2(Student student);

/**
* foreach标签示例
*/
List<Student> findByExp3(List<Integer> array);

}
<mapper namespace="edu.shifan.mapper.StudentMapper">
<sql id="findStatement">select * from sys_students</sql>
<!-- if标签使用示例 -->
<select id="findByExp" parameterType="edu.shifan.pojo.Student" resultType="edu.shifan.pojo.Student">
<include refid="findStatement" /> where 1=1

<if test="stuName!=null and stuName!=''">
and stu_name like "%"#{stuName}"%"
</if>

<if test="gender!=null">
and gender=#{gender}
</if>

<if test="gradeId!=null and gradeId!=0">
and grade_id=#{gradeId}
</if>
</select>

<!-- where标签使用示例-->
<select id="findByExp2" parameterType="edu.shifan.pojo.Student" resultType="edu.shifan.pojo.Student">
<include refid="findStatement" />

<where>
<if test="stuName!=null and stuName!=''">
and stu_name like "%"#{stuName}"%"
</if>

<if test="gender!=null">
and gender=#{gender}
</if>

<if test="gradeId!=null and gradeId!=0">
and grade_id=#{gradeId}
</if>
</where>
</select>

<!-- foreach标签使用示例-->
<select id="findByExp3" parameterType="java.util.List" resultType="edu.shifan.pojo.Student">
<include refid="findStatement" />

<where>
<foreach collection="list" item="id" open="stu_no in(" close=")" separator=",">
#{id}
</foreach>
</where>
</select>
</mapper>

2)测试

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
/**
* 测试forEach标签
*/
private static void testForeach() {
List<Integer> array = new ArrayList<>();
array.add(1);
array.add(2);
array.add(41);
array.add(42);
array.add(50);

//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//b、获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list = mapper.findByExp3(array);
//d、关闭Session
SessionUtil.close();

//3、处理查询结果
for(Student s : list)
System.out.println(s);
}

3)说明
多参数查询的本质就是一个in子查询

7.7.Choose标签

7.7.1.需求

使用姓名、性别或所属年级之一作为查询条件进行查询。

7.7.2.实现

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
1)编写Mapper映射
public interface StudentMapper {
/**
* if标签示例
* 按照条件查询学生信息
* 条件:姓名、性别、所属年级ID
*/
List<Student> findByExp(Student student);

/**
* where标签示例
*/
List<Student> findByExp2(Student student);

/**
* foreach标签示例
*/
List<Student> findByExp3(List<Integer> array);

/**
* choose标签示例
*/
List<Student> findByExp4(Student student);

}

<!-- choose标签使用示例-->
<select id="findByExp4" parameterType="edu.shifan.pojo.Student" resultType="edu.shifan.pojo.Student">
<include refid="findStatement" />

<where>
<!-- 相当于Java语言中的switch语句-->
<choose>
<!-- 相当于Java语言中的case语句-->
<when test="stuName!=null and stuName!=''">
and stu_name like "%"#{stuName}"%"
</when>

<when test="gender!=null">
and gender=#{gender}
</when>

<when test="gradeId!=null and gradeId!=0">
and grade_id=#{gradeId}
</when>

<!-- 相当于Java语言中的default语句-->
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select>

2)测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 测试choose标签
*/
private static void testChoose() {
Student student = new Student();
student.setGradeId(3);
student.setStuName("T");

//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//b、获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list = mapper.findByExp4(student);
//d、关闭Session
SessionUtil.close();

//3、处理查询结果
for(Student s : list)
System.out.println(s);
}

3)说明
Choose标签相当于Java中的switch语句,只能匹配其中一个语句段。

8.多表连接查询

8.1.表间关系

8.1.1.一对一

1)数据间关系

左侧表中1条数据对应于右侧表中0~1条数据,这种对应关系就是一对一关系。
2)表结构

表名:公民信息表(sys_person)
字段名 类型 特征 说明
Person_Id Int(4) PK、自增 记录ID
Name Varchar(20) 公民姓名
Loc Varchar(50) 所在地

表名:身份证信息表(sys_IdCard)
字段名 类型 特征 说明
Card_No Int(18) PK 身份证号码
Create_Date Date 发证时间
P_Id Int(4) 所属公民ID

8.1.2.一对多

1)数据间关系

左侧表中一条数据对应于右侧表中0~N表数据,这种数据对应关系就是一对多关系。
2)表结构

表名:公民信息表(sys_person)
字段名 类型 特征 说明
Person_Id Int(4) PK、自增 记录ID
Name Varchar(20) 公民姓名
Loc Varchar(50) 所在地

表名:账号信息表(sys_Account)
字段名 类型 特征 说明
Card_No Int(19) PK 卡号
Blance Int 余额
P_Id Int(4) 所属储户ID

8.1.3.多对一

参考一对多

8.1.4.多对多

1)数据间关系

左右两侧表中数据都能够于对方形成0~N的对应关系,这种就称为多对多关系。
2)表结构

表名:教师信息表(sys_teacher)
字段名 类型 特征 说明
Teach_Id Int(4) PK、自增 教师编号
Teach_Name Varchar(20) 教师名称

表名:学生信息表(sys_Students)
字段名 类型 特征 说明
Stu_No Int(4) PK 学号
Stu_Name Varchar(20) 学生姓名
注意:多对多关系一般都需要借助于中间表进行实现。

8.2.多表连接查询

8.2.1.一对一

1)Sql语句

1
2
3
4
5
6
7
# a、连接查询
select *
from sys_person a
left join sys_idcard b on a.person_id = b.p_id
# b、独立查询语句
select * from sys_person
select * from sys_idcard where p_id = 1

2)编写实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 实体类
*/
public class IdCard {
private String cardNo;
private Date createDate;
private int pId;
}
/**
* 实体类
*/
public class Person {
private int personId;
private String personName;
private String location;

//引入其他类型--人拥有身份证
private IdCard card;
}

3)编写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
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 一对一映射
*/
public interface OneToOneMapper {
/**
* 查询公民完整信息
* @return
*/
List<Person> findAll();
}
<mapper namespace="edu.shifan.mapper.OneToOneMapper">
<!-- 身份证实体关系映射图 -->
<resultMap id="cardMap" type="edu.shifan.pojo.IdCard">
<id property="cardNo" column="card_No" />
<result property="createDate" column="create_Date" />
<result property="pId" column="p_Id" />
</resultMap>

<!-- 公民实体关系映射图 -->
<resultMap id="personMap" type="edu.shifan.pojo.Person">
<id property="personId" column="person_Id" />
<result property="personName" column="person_Name" />
<result property="location" column="location" />

<!-- 引入另一个查询 -->
<!--
association:表示一对一引用关系
property:当前返回值类型中引入的属性名
JavaType:属性的类型名
select:引入的查询唯一标识
column:为引入查询提供查询参数的列名
-->
<association property="card"
javaType="edu.shifan.pojo.IdCard"
select="getCard"
column="person_id" />
</resultMap>

<!-- 查询公民信息-->
<select id="findAll" resultMap="personMap">
select * from sys_person
</select>

<select id="getCard" parameterType="int" resultMap="cardMap">
select * from sys_idcard where p_id = #{id}
</select>
</mapper>

4)测试映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 测试一对一查询
*/
private static void testOneToOne() {
//1、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//2、获取Mapper对象
OneToOneMapper mapper = session.getMapper(OneToOneMapper.class);
//3、执行查询
List<Person> list = mapper.findAll();
//4、关闭Session
SessionUtil.close();

//处理查询结果
for(Person p : list)
System.out.println(p);

}

8.2.2.一对多

1)Sql语句

1
2
3
4
5
6
7
# a、连接查询
select *
from sys_person a
left join sys_account b on a.person_id = b.p_id
# b、独立查询语句
select * from sys_person
select * from sys_account where p_id = 1

2)编写实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 实体类
*/
public class Account {
private String cardNo;
private int blance;
private int pId;
}
/**
* 实体类
*/
public class Person {
private int personId;
private String personName;
private String location;

//引入其他类型--人拥有身份证
private IdCard card;

//引入其他类型--银行账户
private List<Account> accounts;
}

3)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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* 一对一映射
*/
public interface OneToMoreMapper {
/**
* 查询公民完整信息
* @return
*/
List<Person> findAll();
}

<mapper namespace="edu.shifan.mapper.OneToMoreMapper">
<!-- 银行账户实体关系映射图-->
<resultMap id="accountMap" type="edu.shifan.pojo.Account">
<id property="cardNo" column="card_No" />
<result property="blance" column="blance" />
<result property="pId" column="p_Id" />
</resultMap>

<!-- 公民实体关系映射图 -->
<resultMap id="personMap" type="edu.shifan.pojo.Person">
<id property="personId" column="person_Id" />
<result property="personName" column="person_Name" />
<result property="location" column="location" />

<!-- 引入另一个查询 -->
<!--
collection:表示一对多引用关系
property:当前返回值类型中引入的属性名
JavaType:属性的类型名(集合类型)
ofType:集合中元素的类型
select:引入的查询唯一标识
column:为引入查询提供查询参数的列名
-->
<collection property="accounts"
javaType="java.util.List"
ofType="edu.shifan.pojo.Account"
select="getAccount"
column="person_id" />
</resultMap>

<!-- 查询公民信息-->
<select id="findAll" resultMap="personMap">
select * from sys_person
</select>

<select id="getAccount" parameterType="int" resultMap="accountMap">
select * from sys_account where p_id = #{id}
</select>


</mapper>

4)测试映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 测试一对多查询
*/
private static void testOneToMore() {
//1、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//2、获取Mapper对象
OneToMoreMapper mapper = session.getMapper(OneToMoreMapper.class);
//3、执行查询
List<Person> list = mapper.findAll();
//4、关闭Session
SessionUtil.close();

//处理查询结果
for(Person p : list)
System.out.println(p);
}

9.Web应用程序

9.1.分类

  • 桌面应用程序(C/S):需要预先进行安装,然后可以直接启动并运行的应用程序。如:qq、idea、游戏。
  • Web应用程序(B/S):客户端不需要进行安装,通过浏览器器进行访问。如:淘宝、网易邮箱、其他网站等。Web应用程序需要部署在服务器上进行运行。

9.2.服务器

  • 硬件:一台可以为其他客户端提供服务器的计算机
  • 软件:web应用程序的容器,可以用于部署web应用程序。如:Tomcat、WebShare、JBoss等

9.3.Tomcat服务器

9.3.1.概述

Tomcat是一个免费开源的Web服务器。具有运行稳定,功能强大的特点。

9.3.2.目录结构

9.3.3.Tomcat服务器的启动与停止

  • 启动Tomcat服务器
  • Startup.bat:windows下的批处理文件
  • Startup.sh:Linux下的shell脚本文件
  • 停止Tomcat服务器
  • Shutdown.bat
  • Shutdown.sh

9.3.4.测试

9.4.Web项目

9.4.1.创建web项目

1)打开系统菜单
File==>New==>Project
2)选择项目类型

3)配置Jdk版本

4)集成Tomcat服务器
通过new按钮打开服务器选择窗口

选择服务器

服务器路径选择窗口

选择Tomcat服务器的目录

5)添加依赖和框架支持

6)目录结构

9.4.2.启动web应用程序

10.练习

10.1.需求

实现学生基本信息维护功能。

10.2.功能模块

  • 新增学生信息(姓名、性别、年龄、电话、家庭住址、所属年级、状态)
  • 修改学生信息
  • 删除学生信息
  • 显示学生列表
  • 多条件组合查询

10.3.库表设计

10.3.1.数据库

数据库名称:school_db

10.3.2.表

表名:年级信息表(sys_grade)
字段名 类型 特征 描述
Grade_id Int(4) PK、自增 记录ID
Grade_name Varchar(50) Not Null 年级名称
Memo Varchar(200) 备注

表名:学生信息表(sys_student)
字段名 类型 特征 描述
Stu_no Int(4) PK、自增 学号
Stu_name Varchar(20) Not Null 姓名
Gender Bit(1) 性别
Age Int(4) 年龄
Tel Varchar(15) 电话
Address Varchar(50) 家庭住址
Grade_id Int(4) FK 所属年级ID
State Int(4) 状态(1-在校;2-休学;3-退学;4-入伍;5-离校)

10.4.代码实现

10.4.1.创建项目

1)创建项目

2)添加框架依赖(如果有web目录则不需要进行操作)

3)添加子目录
在web/WEB-INF下添加子目录:

  • Classes:用于保存生成后的字节码文件(可以缺省)
  • Lib:用于保存项目的依赖
    4)设置子目录
    设置classes目录:File==》Project Stucture……
    第一步:选择操作的内容

    第二步:设置Classes

    第三步:设置lib目录

10.4.2.运行web项目

10.4.3.实现学生列表

1)编写jsp页面

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
<html>
<head>
<title>$Title$</title>
<style type="text/css">
.grid{
width:100%;
border-collapse: collapse;
}

.grid th,td{
padding: 4px 5px;
border:solid 1px cadetblue;
}
</style>
</head>
<body>
<a href="#">新增学生信息</a>
<table class="grid">
<tr>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>电话</th>
<th>所属年级</th>
<th>家庭住址</th>
<th>状态</th>
<th>&nbsp;</th>
</tr>

<tr>
<td>1</td>
<td>Tom</td>
<td>男</td>
<td>20</td>
<td>8907664</td>
<td>二年1班</td>
<td>南阳市卧龙区78号</td>
<td>在校</td>
<td>
<a href="#">修改</a>
<a href="#">删除</a>
</td>
</tr>

</table>
</body>
</html>

2)实现思路

3)创建Pojo类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 实体类
*/
public class Student {
//私有成员字段
private int stuNo;
private String stuName;
private boolean gender;
private int age;
private String tel;
private int gradeId;
private String address;
private int state;
}

4)创建Mapper映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 映射接口
*/
public interface StudentMapper {
/**
* 查询全部学生信息列表
* @return
*/
List<Student> findAll();
}

【映射文件】
<mapper namespace="edu.shifan.mapper.StudentMapper">

<!-- 查询全部学生信息列表 -->
<select id="findAll" resultType="edu.shifan.pojo.Student">
select * from sys_students
</select>
</mapper>

5)注册映射接口

1
2
3
<mappers>
<mapper class="edu.shifan.mapper.StudentMapper" />
</mappers>

6)创建Servlet类

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
/**
* Servlet
* 一个继承了HttpServlet的Java类
*
* 实现步骤
* 1、创建类并继承于HttpServlet
* 2、使用@WebServlet注解进行标注
* 3、重写父类的service方法
* 4、编写业务代码
*
*/
@WebServlet("/list")
public class ListServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//调用Mapper映射的相关方法
//获取Session对象
SqlSession session = SessionUtil.getSession(false);
//获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//调用接口方法
List<Student> list = mapper.findAll();

//System.out.println(list);
//将数据传递给Jsp页面
//将数据放到作用域中(一块公共的内存空间)
req.setAttribute("list",list);

//跳转到index.jsp页面
req.getRequestDispatcher("index.jsp").forward(req,resp);
}
}

7)Jsp绑定数据

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 引入依赖 -->
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>$Title$</title>
<style type="text/css">
.grid{
width:100%;
border-collapse: collapse;
}

.grid th,td{
padding: 4px 5px;
border:solid 1px cadetblue;
}
</style>
</head>
<body>
<a href="#">新增学生信息</a>
<table class="grid">
<tr>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>电话</th>
<th>所属年级</th>
<th>家庭住址</th>
<th>状态</th>
<th>&nbsp;</th>
</tr>

<c:forEach items="${list}" var="l">
<tr>
<td>${l.stuNo}</td>
<td>${l.stuName}</td>
<td>${l.gender}</td>
<td>${l.age}</td>
<td>${l.tel}</td>
<td>${l.gradeId}</td>
<td>${l.address}</td>
<td>${l.state}</td>
<td>
<a href="#">修改</a>
<a href="#">删除</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>

10.4.4.添加学生信息

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
1)编写Jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>新增学生信息</title>
<style type="text/css">
.grid{
width: 600px;
margin:0px auto;
border-collapse: collapse;
}

.grid th,td{
padding: 6px 7px;
border:solid 1px cadetblue;
}

.grid th{
text-align: right;
background: lightgray;
}
</style>
</head>
<body>
<table class="grid">
<caption>新增学生信息</caption>
<tr>
<th>姓名:</th>
<td>
<input type="text" name="stuName" />
</td>
</tr>
<tr>
<th>性别:</th>
<td>
<input type="radio" name="gender" value="1" />男
<input type="radio" name="gender" value="0" />女
</td>
</tr>
<tr>
<th>年龄:</th>
<td>
<input type="text" name="age" />
</td>
</tr>
<tr>
<th>联系电话:</th>
<td>
<input type="text" name="tel" />
</td>
</tr>
<tr>
<th>所属年级:</th>
<td>
<select name="gradeId">
<option value="0">---请选择---</option>
</select>
</td>
</tr>
<tr>
<th>家庭住址:</th>
<td>
<input type="text" name="address" />
</td>
</tr>
<tr>
<th></th>
<td>
<input type="submit" value="提交" />
<input type="reset" value="重置" />
</td>
</tr>
</table>
</body>
</html>

2)实现思路

3)编写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
/**
* 映射接口
*/
public interface StudentMapper {
/**
* 将学生信息添加到数据库中
* @param student
* @return
*/
int add(Student student);


/**
* 查询全部学生信息列表
* @return
*/
List<Student> findAll();
}

<mapper namespace="edu.shifan.mapper.StudentMapper">
<!-- 将学生信息写入到数据库中 -->
<insert id="add" parameterType="edu.shifan.pojo.Student">
insert into sys_students(stu_name,gender,age,tel,grade_id,address)
values(#{stuName},#{gender},#{age},#{tel},#{gradeId},#{address})
</insert>

<!-- 查询全部学生信息列表 -->
<select id="findAll" resultType="edu.shifan.pojo.Student">
select * from sys_students
</select>
</mapper>

4)编写Jsp页面

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
<body>
<!--
method:提交方式
get:通过URL地址进行参数传递
post:通过报文进行参数传递
action:请求的响应者(谁来处理请求)
-->
<form method="post" action="add">
<table class="grid">
<caption>新增学生信息</caption>
<tr>
<th>姓名:</th>
<td>
<input type="text" name="stuName" />
</td>
</tr>
<tr>
<th>性别:</th>
<td>
<input type="radio" name="gender" value="1" />男
<input type="radio" name="gender" value="0" />女
</td>
</tr>
<tr>
<th>年龄:</th>
<td>
<input type="text" name="age" />
</td>
</tr>
<tr>
<th>联系电话:</th>
<td>
<input type="text" name="tel" />
</td>
</tr>
<tr>
<th>所属年级:</th>
<td>
<select name="gradeId">
<option value="0">---请选择---</option>
</select>
</td>
</tr>
<tr>
<th>家庭住址:</th>
<td>
<input type="text" name="address" />
</td>
</tr>
<tr>
<th></th>
<td>
<input type="submit" value="提交" />
<input type="reset" value="重置" />
</td>
</tr>
</table>
</form>
</body>

5)编写Servlet

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
/**
* Servlet--响应用户的请求(将用户提交的信息写入到数据库中)
* 1、创建类并继承HttpServlet类
* 2、使用@WebServlet注解进行标注
* 3、重写Service()方法
* 4、编写业务代码
*/
@WebServlet("/add")
public class AddServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求的编码格式(解决中文乱码问题)
req.setCharacterEncoding("UTF-8");
//1、获取用户提交的数据
//用于封装用户提交的请求数据
Student stu = new Student();
//getParaemeter():用于获取用户提交的请求参数,返回结果为字符串类型数据
stu.setStuName(req.getParameter("stuName"));
stu.setGender("1".equals(req.getParameter("gender")));
stu.setAge(Integer.parseInt(req.getParameter("age")));
stu.setTel(req.getParameter("tel"));
stu.setAddress(req.getParameter("address"));
stu.setGradeId(1);
System.out.println(stu);

//2、调用Mapper的方法将数据写入到数据库
//获取SqlSession对象
SqlSession session = SessionUtil.getSession();
//获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//调用mapper的方法将数据写入到数据库中
mapper.add(stu);
//关闭Session对象
SessionUtil.close();

//3、跳转到index.jsp页面
req.getRequestDispatcher("list").forward(req,resp);
}
}

10.4.5.下拉列表绑定数据

1)实现思路

2)编写Mapper映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
【mapper接口】
public interface GradeMapper {
/**
* 查询所有年级信息
* @return
*/
List<Grade> findAll();
}
【mapper映射】
<mapper namespace="edu.shifan.mapper.GradeMapper">
<!-- 查询所有年级信息 -->
<select id="findAll" resultType="edu.shifan.pojo.Grade">
SELECT * FROM sys_grade ORDER BY grade_id
</select>
</mapper>

3)编写Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Servlet
*/
@WebServlet("/init")
public class InitServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、调用Mapper对象的方法获取数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//b、获取Mapper对象
GradeMapper mapper = session.getMapper(GradeMapper.class);
//c、调用方法
List<Grade> list= mapper.findAll();
//d、关闭session
SessionUtil.close();

//2、将数据传递给add.jsp页面
req.setAttribute("list",list);

//3、跳转到add.jsp页面
req.getRequestDispatcher("add.jsp").forward(req,resp);
}
}

4)修改Jsp文件

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
【index.jsp】
<a href="init">新增学生信息</a>
【add.jsp】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 引入依赖 -->
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
<title>新增学生信息</title>
<style type="text/css">
.grid{
width: 600px;
margin:0px auto;
border-collapse: collapse;
}

.grid th,td{
padding: 6px 7px;
border:solid 1px cadetblue;
}

.grid th{
text-align: right;
background: lightgray;
}
</style>
</head>
<body>
<!--
method:提交方式
get:通过URL地址进行参数传递
post:通过报文进行参数传递
action:请求的响应者(谁来处理请求)
-->
<form method="post" action="add">
<table class="grid">
<caption>新增学生信息</caption>
<tr>
<th>姓名:</th>
<td>
<input type="text" name="stuName" />
</td>
</tr>
<tr>
<th>性别:</th>
<td>
<input type="radio" name="gender" value="1" />男
<input type="radio" name="gender" value="0" />女
</td>
</tr>
<tr>
<th>年龄:</th>
<td>
<input type="text" name="age" />
</td>
</tr>
<tr>
<th>联系电话:</th>
<td>
<input type="text" name="tel" />
</td>
</tr>
<tr>
<th>所属年级:</th>
<td>
<select name="gradeId">
<option value="0">---请选择---</option>

<c:forEach items="${list}" var="g">
<option value="${g.gradeId}">${g.gradeName}</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<th>家庭住址:</th>
<td>
<input type="text" name="address" />
</td>
</tr>
<tr>
<th></th>
<td>
<input type="submit" value="提交" />
<input type="reset" value="重置" />
</td>
</tr>
</table>
</form>
</body>
</html>

10.4.6.初始化更新数据

1)实现思路

2)编写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
35
36
37
38
39
40
41
42
43
【映射接口】
/**
* 映射接口
*/
public interface StudentMapper {
/**
* 将学生信息添加到数据库中
* @param student
* @return
*/
int add(Student student);


/**
* 查询全部学生信息列表
* @return
*/
List<Student> findAll();

/**
* 按Id查询学生信息
*/
Student findById(int id);
}

【映射文件】
<mapper namespace="edu.shifan.mapper.StudentMapper">
<!-- 将学生信息写入到数据库中 -->
<insert id="add" parameterType="edu.shifan.pojo.Student">
insert into sys_students(stu_name,gender,age,tel,grade_id,address)
values(#{stuName},#{gender},#{age},#{tel},#{gradeId},#{address})
</insert>

<!-- 查询全部学生信息列表 -->
<select id="findAll" resultType="edu.shifan.pojo.Student">
select * from sys_students
</select>

<!-- 按id查询学生信息-->
<select id="findById" parameterType="int" resultType="edu.shifan.pojo.Student">
SELECT * FROM sys_students where stu_no=#{id}
</select>
</mapper>

3)编写Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@WebServlet("/load")
public class LoadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、获取客户端传递的Id数据
int id = Integer.parseInt(req.getParameter("id"));

//2、调用mapper对象的方法查询数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//b、获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
Student student=mapper.findById(id);

//3、将数据放到作用域中(供页面读取)
req.setAttribute("data",student);

//4、跳转到update.jsp页面
req.getRequestDispatcher("update.jsp").forward(req,resp);
}
}

4)修改Jsp页面

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
【index.jsp】
<a href="load?id=${l.stuNo}">修改</a>
【update.jsp】
<body>
<!--
method:提交方式
get:通过URL地址进行参数传递
post:通过报文进行参数传递
action:请求的响应者(谁来处理请求)
-->
<form method="post" action="add">
<table class="grid">
<caption>更新学生信息</caption>
<tr>
<th>姓名:</th>
<td>
<input type="text" name="stuName" value="${data.stuName}"/>
</td>
</tr>
<tr>
<th>性别:</th>
<td>
<input type="radio" name="gender" value="1" />男
<input type="radio" name="gender" value="0" />女
</td>
</tr>
<tr>
<th>年龄:</th>
<td>
<input type="text" name="age" value="${data.age}"/>
</td>
</tr>
<tr>
<th>联系电话:</th>
<td>
<input type="text" name="tel" value="${data.tel}"/>
</td>
</tr>
<tr>
<th>所属年级:</th>
<td>
<select name="gradeId">
<option value="0">---请选择---</option>
</select>
</td>
</tr>
<tr>
<th>家庭住址:</th>
<td>
<input type="text" name="address" value="${data.address}"/>
</td>
</tr>
<tr>
<th></th>
<td>
<input type="submit" value="提交" />
<input type="reset" value="重置" />
</td>
</tr>
</table>
</form>
</body>

10.4.7.修改radio的选中状态

1
2
3
4
5
6
7
8
9
<!-- 根据学生的性别,设置radio的checked属性-->
<c:if test="${data.gender}" var="flag">
<input type="radio" name="gender" value="1" checked="checked"/>男
<input type="radio" name="gender" value="0" />女
</c:if>
<c:if test="${!flag}">
<input type="radio" name="gender" value="1" />男
<input type="radio" name="gender" value="0" checked="checked"/>女
</c:if>

10.4.8.修改下拉列表的选中状态

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
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、获取客户端传递的Id数据
int id = Integer.parseInt(req.getParameter("id"));

//2、调用mapper对象的方法查询数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//b、获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
Student student=mapper.findById(id);

//获取GradeMapper对象
GradeMapper gradeMapper = session.getMapper(GradeMapper.class);
//调用gradeMapper对象的方法
List<Grade> gradeList=gradeMapper.findAll();

//3、将数据放到作用域中(供页面读取)
req.setAttribute("data",student);
req.setAttribute("grades",gradeList);

//4、跳转到update.jsp页面
req.getRequestDispatcher("update.jsp").forward(req,resp);
}

<select name="gradeId">
<option value="0">---请选择---</option>

<!-- 绑定下拉列表的数据 -->
<c:forEach items="${grades}" var="g">
<!-- 绑定每一个下拉项时,判断年级ID与学生的年级ID是否一致。如果一致则选中-->
<c:if test="${g.gradeId==data.gradeId}" var="flag">
<option value="${g.gradeId}" selected="selected">${g.gradeName}</option>
</c:if>
<c:if test="${!flag}">
<option value="${g.gradeId}">${g.gradeName}</option>
</c:if>
</c:forEach>
</select>

10.4.9.更新表中数据

1)实现思路

2)编写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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
【Mapper接口】
/**
* 映射接口
*/
public interface StudentMapper {
/**
* 将学生信息添加到数据库中
* @param student
* @return
*/
int add(Student student);

/**
* 按ID更新学生信息
* @param student
* @return
*/
int update(Student student);

/**
* 查询全部学生信息列表
* @return
*/
List<Student> findAll();

/**
* 按Id查询学生信息
*/
Student findById(int id);
}
【Mapper映射】
<mapper namespace="edu.shifan.mapper.StudentMapper">
<!-- 将学生信息写入到数据库中 -->
<insert id="add" parameterType="edu.shifan.pojo.Student">
insert into sys_students(stu_name,gender,age,tel,grade_id,address)
values(#{stuName},#{gender},#{age},#{tel},#{gradeId},#{address})
</insert>

<!-- 按ID更新学生信息-->
<update id="update" parameterType="edu.shifan.pojo.Student">
update sys_students set stu_name=#{stuName},gender=#{gender},age=#{age},tel=#{tel},grade_id=#{gradeId},address=#{address}
where stu_no=#{stuNo}
</update>

<!-- 查询全部学生信息列表 -->
<select id="findAll" resultType="edu.shifan.pojo.Student">
select * from sys_students
</select>

<!-- 按id查询学生信息-->
<select id="findById" parameterType="int" resultType="edu.shifan.pojo.Student">
SELECT * FROM sys_students where stu_no=#{id}
</select>
</mapper>

3)编写Servlet

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
@WebServlet("/update")
public class UpdateServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求的编码格式(解决中文乱码问题)
req.setCharacterEncoding("UTF-8");
//1、获取用户提交的数据
//用于封装用户提交的请求数据
Student stu = new Student();
//getParaemeter():用于获取用户提交的请求参数,返回结果为字符串类型数据
stu.setStuName(req.getParameter("stuName"));
stu.setGender("1".equals(req.getParameter("gender")));
stu.setAge(Integer.parseInt(req.getParameter("age")));
stu.setTel(req.getParameter("tel"));
stu.setAddress(req.getParameter("address"));
stu.setGradeId(Integer.parseInt(req.getParameter("gradeId")));
stu.setStuNo(Integer.parseInt(req.getParameter("stuNo")));
System.out.println(stu);

//2、调用Mapper的方法将数据写入到数据库
//获取SqlSession对象
SqlSession session = SessionUtil.getSession();
//获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//调用mapper的方法将数据写入到数据库中
mapper.update(stu);
//关闭Session对象
SessionUtil.close();

//3、跳转到index.jsp页面
req.getRequestDispatcher("list").forward(req,resp);
}
}

4)修改Jsp页面

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
<form method="post" action="update">
<input type="hidden" name="stuNo" value="${data.stuNo}" />

<table class="grid">
<caption>更新学生信息</caption>
<tr>
<th>姓名:</th>
<td>
<input type="text" name="stuName" value="${data.stuName}"/>
</td>
</tr>
<tr>
<th>性别:</th>
<td>
<!-- 根据学生的性别,设置radio的checked属性-->
<c:if test="${data.gender}" var="flag">
<input type="radio" name="gender" value="1" checked="checked"/>男
<input type="radio" name="gender" value="0" />女
</c:if>
<c:if test="${!flag}">
<input type="radio" name="gender" value="1" />男
<input type="radio" name="gender" value="0" checked="checked"/>女
</c:if>
</td>
</tr>
<tr>
<th>年龄:</th>
<td>
<input type="text" name="age" value="${data.age}"/>
</td>
</tr>
<tr>
<th>联系电话:</th>
<td>
<input type="text" name="tel" value="${data.tel}"/>
</td>
</tr>
<tr>
<th>所属年级:</th>
<td>
<select name="gradeId">
<option value="0">---请选择---</option>

<!-- 绑定下拉列表的数据 -->
<c:forEach items="${grades}" var="g">
<!-- 绑定每一个下拉项时,判断年级ID与学生的年级ID是否一致。如果一致则选中-->
<c:if test="${g.gradeId==data.gradeId}" var="flag">
<option value="${g.gradeId}" selected="selected">${g.gradeName}</option>
</c:if>
<c:if test="${!flag}">
<option value="${g.gradeId}">${g.gradeName}</option>
</c:if>
</c:forEach>
</select>
</td>
</tr>
<tr>
<th>家庭住址:</th>
<td>
<input type="text" name="address" value="${data.address}"/>
</td>
</tr>
<tr>
<th></th>
<td>
<input type="submit" value="提交" />
<input type="reset" value="重置" />
</td>
</tr>
</table>
</form>

10.4.10.删除数据
1)实现思路

2)编写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
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
/**
* 映射接口
*/
public interface StudentMapper {
/**
* 将学生信息添加到数据库中
* @param student
* @return
*/
int add(Student student);

/**
* 按照ID删除学生信息
*/
int delById(int id);

/**
* 按ID更新学生信息
* @param student
* @return
*/
int update(Student student);

/**
* 查询全部学生信息列表
* @return
*/
List<Student> findAll();

/**
* 按Id查询学生信息
*/
Student findById(int id);
}
【Mapper映射】
<mapper namespace="edu.shifan.mapper.StudentMapper">
<!-- 将学生信息写入到数据库中 -->
<insert id="add" parameterType="edu.shifan.pojo.Student">
insert into sys_students(stu_name,gender,age,tel,grade_id,address)
values(#{stuName},#{gender},#{age},#{tel},#{gradeId},#{address})
</insert>

<!-- 按ID删除学生信息-->
<delete id="delById" parameterType="int">
delete from sys_students where stu_no=#{id}
</delete>

<!-- 按ID更新学生信息-->
<update id="update" parameterType="edu.shifan.pojo.Student">
update sys_students set stu_name=#{stuName},gender=#{gender},age=#{age},tel=#{tel},grade_id=#{gradeId},address=#{address}
where stu_no=#{stuNo}
</update>

<!-- 查询全部学生信息列表 -->
<select id="findAll" resultType="edu.shifan.pojo.Student">
select * from sys_students
</select>

<!-- 按id查询学生信息-->
<select id="findById" parameterType="int" resultType="edu.shifan.pojo.Student">
SELECT * FROM sys_students where stu_no=#{id}
</select>
</mapper>

3)编写Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@WebServlet("/del")
public class DeleteServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、获取ID参数值
int id = Integer.parseInt(req.getParameter("id"));

//2、调用mapper方法删除数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession();
//b、获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//c、调用Mapper对象的方法
mapper.delById(id);
//d、关闭session对象
SessionUtil.close();

//3、跳转到index.jsp页面
req.getRequestDispatcher("list").forward(req,resp);
}
}

4)修改Jsp页面

1
<a href="del?id=${l.stuNo}">删除</a>

10.4.11.多条件查询

1)Jsp页面

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
.box{
border: solid 1px cadetblue;
background: gainsboro;
padding: 15px;
margin-bottom: 30px;
}
【Html】
<!-- 多条件查询 -->
<form method="get" action="find">
<div class="box">
姓名:
<input type="text" name="stuName" />&nbsp;&nbsp;
性别:
<select name="gender">
<option value="-1">---请选择---</option>
<option value="1">男</option>
<option value="0">女</option>
</select>&nbsp;&nbsp;
所属年级:
<select name="gradeId">
<option value="0">---请选择---</option>
<option value="1">20级大数据1班</option>
<option value="2">20级大数据2班</option>
<option value="3">20级攻防1班</option>
<option value="4">21级土木3班</option>
</select>&nbsp;&nbsp;&nbsp;
<input type="submit" value="查询" />
<input type="reset" value="重置" />
</div>
</form>

2)修改实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 实体类
*/
public class Student {
//私有成员字段
private int stuNo;
private String stuName;
private Boolean gender; //将属性的类型修改为引用类型(包装类型)
private int age;
private String tel;
private Integer gradeId; //将属性的类型修改为引用类型(包装类型)
private String address;
private int state;
}

3)编写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
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
【映射接口】
/**
* 映射接口
*/
public interface StudentMapper {
/**
* 将学生信息添加到数据库中
* @param student
* @return
*/
int add(Student student);

/**
* 按照ID删除学生信息
*/
int delById(int id);

/**
* 按ID更新学生信息
* @param student
* @return
*/
int update(Student student);

/**
* 查询全部学生信息列表
* @return
*/
List<Student> findAll();

/**
* 实现多条件查询
* @return
*/
List<Student> findAll2(Student student);


/**
* 按Id查询学生信息
*/
Student findById(int id);
}
【映射配置】
<!-- 多条件查询 -->
<select id="findAll2" parameterType="edu.shifan.pojo.Student" resultType="edu.shifan.pojo.Student">
select * from sys_students

<!-- 拼接条件查询语句-->
<where>
<if test="stuName!=null and stuName!=''">
<!-- 数据表中列名 | 实体类属性名-->
and stu_name like "%"#{stuName}"%"
</if>

<if test="gender!=null">
and gender = #{gender}
</if>

<if test="gradeId!=null and gradeId!=0">
and grade_id=#{gradeId}
</if>
</where>
</select>

4)编写Servlet

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
/**
* Servlet
*/
@WebServlet("/find")
public class FindServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、设置编码格式
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");

//2、获取请求参数
Student student = new Student();

//判断学生姓名是否不为空
if(req.getParameter("stuName").length()>0)
student.setStuName(req.getParameter("stuName"));

//判断性别是否为默认值
if(!"-1".equals(req.getParameter("gender")))
student.setGender("1".equals(req.getParameter("gender")));

student.setGradeId(Integer.parseInt(req.getParameter("gradeId")));

//3、调用mapper对象的方法查询数据
//a、获取Session对象
SqlSession session = SessionUtil.getSession(false);
//b、获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//c、调用方法
List<Student> list = mapper.findAll2(student);
//d、关闭session
SessionUtil.close();

//4、将查询结果写入到作用域
//将数据放到作用域中(一块公共的内存空间)
req.setAttribute("list",list);

//5、跳转到index.jsp页面
req.getRequestDispatcher("index.jsp").forward(req,resp);
}
}

10.4.12.两表联查

1)修改实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 实体类
*/
public class Student {
//私有成员字段
private int stuNo;
private String stuName;
private Boolean gender; //将属性的类型修改为引用类型(包装类型)
private int age;
private String tel;
private Integer gradeId; //将属性的类型修改为引用类型(包装类型)
private String address;
private int state;

//引入其他类型(学生是主体,学生和年级是一对一关系)
private Grade grade;
}

2)编写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
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
【映射接口】
/**
* 映射接口
*/
public interface StudentMapper {
/**
* 将学生信息添加到数据库中
* @param student
* @return
*/
int add(Student student);

/**
* 按照ID删除学生信息
*/
int delById(int id);

/**
* 按ID更新学生信息
* @param student
* @return
*/
int update(Student student);

/**
* 查询全部学生信息列表
* @return
*/
List<Student> findAll();

/**
* 实现多条件查询
* @return
*/
List<Student> findAll2(Student student);

/**
* 实现两表联查
* @return
*/
List<Student> findAll3();

/**
* 按Id查询学生信息
*/
Student findById(int id);
}
【映射配置--GradeMapper】
<mapper namespace="edu.shifan.mapper.GradeMapper">
<resultMap id="gradeMap" type="edu.shifan.pojo.Grade">
<id property="gradeId" column="grade_Id" />
<result property="gradeName" column="grade_Name" />
<result property="memo" column="memo" />
</resultMap>
<!-- 查询所有年级信息 -->
<select id="findAll" resultType="edu.shifan.pojo.Grade">
SELECT * FROM sys_grade ORDER BY grade_id
</select>
</mapper>
【映射配置--StudentMapper】
<mapper namespace="edu.shifan.mapper.StudentMapper">
<resultMap id="studentMap" type="edu.shifan.pojo.Student">
<id property="stuNo" column="stu_No" />
<result property="stuName" column="stu_Name" />
<result property="gender" column="gender" />
<result property="age" column="age" />
<result property="tel" column="tel" />
<result property="gradeId" column="grade_Id" />
<result property="address" column="address" />
<result property="state" column="state" />

<!-- 引入另一个查询-->
<association property="grade"
javaType="edu.shifan.pojo.Grade"
select="getGrade"
column="grade_id" />
</resultMap>

<!-- 实现多表连接查询 -->
<select id="findAll3" resultMap="studentMap">
select * from sys_students
</select>
<!-- 查询年级信息 -->
<select id="getGrade" parameterType="int" resultMap="edu.shifan.mapper.GradeMapper.gradeMap">
SELECT * FROM sys_grade where grade_id=#{id}
</select>

<!-- 按id查询学生信息-->
<select id="findById" parameterType="int" resultType="edu.shifan.pojo.Student">
SELECT * FROM sys_students where stu_no=#{id}
</select>
</mapper>

3)修改Servlet

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
/**
* Servlet
* 一个继承了HttpServlet的Java类
*
* 实现步骤
* 1、创建类并继承于HttpServlet
* 2、使用@WebServlet注解进行标注
* 3、重写父类的service方法
* 4、编写业务代码
*
*/
@WebServlet("/list")
public class ListServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//调用Mapper映射的相关方法
//获取Session对象
SqlSession session = SessionUtil.getSession(false);
//获取Mapper对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//调用接口方法
List<Student> list = mapper.findAll3();//mapper.findAll();

//System.out.println(list);
//将数据传递给Jsp页面
//将数据放到作用域中(一块公共的内存空间)
req.setAttribute("list",list);

//跳转到index.jsp页面
req.getRequestDispatcher("index.jsp").forward(req,resp);
}
}

4)修改Jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<c:forEach items="${list}" var="l">
<tr>
<td>${l.stuNo}</td>
<td>${l.stuName}</td>
<td>${l.gender?"男":"女"}</td>
<td>${l.age}</td>
<td>${l.tel}</td>
<td>${l.grade.gradeName}</td>
<td>${l.address}</td>
<td>${l.state}</td>
<td>
<a href="del?id=${l.stuNo}">删除</a>
<a href="load?id=${l.stuNo}">修改</a>
</td>
</tr>
</c:forEach>

11.缓存

11.1.概述

为了提高相同查询语句的重复执行,MyBatis提供了查询缓存功能。在MyBatis中提供了一级缓存、二级缓存、参照缓存和自定义缓存。MyBatis的缓存是一个PurpertralCache和HashMap为基础进行实现的。

11.2.作用

为了提高程序的执行效率,避免相同查询语句的重复执行。

11.3.一级缓存

11.3.1.概述

一级缓存默认是开启的,一级缓存通常也成为Session缓存。

11.3.2.特点

1)同一个Session对象的查询语句只被执行1次

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
/**
* 1、同一个Session对象的查询语句只被执行1次
*/
private static void method01() {
//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session1 = SessionUtil.getSession(false);
SqlSession session2 = SessionUtil.getSession(false);

System.out.println(session1);
System.out.println(session2);

//b、获取Mapper对象
StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list1 = mapper1.findAll();
List<Student> list2 = mapper2.findAll();
//d、关闭Session
SessionUtil.close();

//3、处理查询结果
System.out.println(list1);
System.out.println("-----------------------------------------------------");
System.out.println(list2);
}

2)不同Session对象之间无法共享缓存

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
/**
* 2、不同Session对象之间无法共享缓存
*/
private static void method02() {
//以流方式读取MyBatis核心配置文件
InputStream is = SessionUtil.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//通过SqlSessionFactoryBuilder对象构建facotry对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();

System.out.println(session1);
System.out.println(session2);

//b、获取Mapper对象
StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list1 = mapper1.findAll();
List<Student> list2 = mapper2.findAll();

//d、关闭Session
session1.close();
session2.close();

//3、处理查询结果
System.out.println(list1);
System.out.println("-----------------------------------------------------");
System.out.println(list2);
}

3)在两个查询语句之间如果执行CUD操作将导致缓存失效

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
/**
* 3、在两个查询语句之间如果执行CUD操作将导致缓存失效
*/
private static void method03() {
//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session1 = SessionUtil.getSession(false);
SqlSession session2 = SessionUtil.getSession(false);

System.out.println(session1);
System.out.println(session2);

//b、获取Mapper对象
StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list1 = mapper1.findAll();

//执行删除操作
mapper1.delById();

List<Student> list2 = mapper2.findAll();
//d、关闭Session
SessionUtil.close();

//3、处理查询结果
System.out.println(list1);
System.out.println("-----------------------------------------------------");
System.out.println(list2);
}

4)如果在两次查询之间调用了clearCache()方法将导致缓存失效(重建)

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
/**
* 4、如果在两次查询之间调用了clearCache()方法将导致缓存失效(重建)
*/
private static void method04() {
//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session1 = SessionUtil.getSession(false);
SqlSession session2 = SessionUtil.getSession(false);

System.out.println(session1);
System.out.println(session2);

//b、获取Mapper对象
StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list1 = mapper1.findAll();

//清除缓存
session1.clearCache();

List<Student> list2 = mapper2.findAll();
//d、关闭Session
SessionUtil.close();

//3、处理查询结果
System.out.println(list1);
System.out.println("-----------------------------------------------------");
System.out.println(list2);
}

11.4.二级缓存

11.4.1.概述

一级缓存的最大范围是Session(所以也成为Session缓存)。如果缓存需要被多个Session所共享,则可以考虑使用二级缓存。二级缓存是namespace级别的缓存。

11.4.2.实现

1)核心配置文件

1
2
3
4
5
6
7
8
9
10
<!-- 开启自动映射配置-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--
开启二级缓存
true:开启二级缓存
false:关闭二级缓存
-->
<setting name="cacheEnabled" value="true" />
</settings>

2)Mapper映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--
配置二级缓存
eviction:设置缓存的回收策略
LRU —— 最少使用的优先回收
FIFO —— 先进先出
SOFT —— 软移除
WEAK —— 弱引用
flushInterval:设置缓存的刷新时间
size:配置内存资源的大小
readOnly:是否设置为只读
-->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>


<!-- 查询所有学生信息 -->
<select id="findAll" resultType="edu.shifan.pojo.Student">
select * from sys_students
</select>

3)测试

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
/**
* 测试二级缓存
* 必须等待Session关闭或执行了commit()提交事务后才能使二级缓存生效
*/
private static void method05() {
//以流方式读取MyBatis核心配置文件
InputStream is = SessionUtil.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//通过SqlSessionFactoryBuilder对象构建facotry对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//2、调用Mapper对象的方法查询数据
//a、获取Session对象
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();

System.out.println(session1);
System.out.println(session2);

//b、获取Mapper对象
StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
//c、调用mapper对象的方法
List<Student> list1 = mapper1.findAll();
//提交事务
session1.commit();

List<Student> list2 = mapper2.findAll();

//d、关闭Session
session1.close();
session2.close();

//3、处理查询结果
System.out.println(list1);
System.out.println("-----------------------------------------------------");
System.out.println(list2);
}

11.4.3.特点

  • 二建缓存只在第一个Session调用了commit() (或close()方法) 提交后才能生效
  • 执行CUD操作可能会对缓存产生影响

Mybatis

框架概述

软件框架是一种通用的、可复用的软件环境,它提供特定的功能,促进软件应用、产品和解决方案的开发工作。软件框架会包含支撑程序、编译器、代码、库、工具集以及API,它把所有这些部件汇集在一起,以支持项目或系统的开发。
软件框架可以形象地比喻成我们在盖楼房时,用梁+柱子+承重墙搭建起来的钢筋混凝土结构的建筑框架,它是整个建筑的骨架。而实现的软件功能,也就像在这个建筑框架中所要实现的不同类型、功能的房子,比如健身房、商场、酒店、饭店等。

框架的优势

1.**提高开发效率:如果采用成熟、稳健的框架,那么一些通用的基础工作,如事务处理、安全性、数据流控制等都可以交给框架处理,程序员只需要集中精力完成系统的业务逻辑设计,降低了开发难度。
2.
提高代码规范性和可维护性:当多人协同进行开发时,代码的规范性和可维护性就变得非常重要。成熟的框架都有严格的代码规范,能保证团队整体的开发风格统一。
3.
提高软件性能:**使用框架进行软件开发,可以减少程序中的冗余代码。例如,使用Spring框架开发时,通过Spring的IOC特性,可以将对象之间的依赖关系交给Spring控制,方便解耦,简化开发;使用MyBatis框架开发时,MyBatis提供了XML标签,支持动态的SQL,开发人员无需在类中编写大量的SQL语句,只需要在配置文件中进行配置即可。

当前主流框架

Spring框架
Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的,其主要优势之一就是分层架构。Spring提供了更完善的开发环境,可以为POJO(PlainOrdinary Java Object,普通Java对象)对象提供企业级的服务。
Spring MVC框架
Spring MVC是一个Web开发框架,可以将它理解为Servlet。在MVC模式中,Spring MVC作为控制器(Controller)用于实现模型与视图的数据交互,是结构最清晰的。
Spring MVC框架采用松耦合、可插拔的组件结构,具有高度可配置性,与其他的MVC框架相比,具有更强的扩展性和灵活性。
MyBatis框架
MyBatis 是Apache的一个开源项目iBatis,2010年这个项目由Apache SoftwareFoundation迁移到了Google Code,并且改名为MyBatis , 2013年11月MyBatis又被迁移到Github。
MyBatis是一个优秀的持久层框架,它可以在实体类和SQL语句之间建立映射关系,是一种半自动化的ORM (Object/Relation Mapping,即对象关系映射)实现。MyBatis封装性要低于Hibernate,但它性能优越、简单易学,在互联网应用的开发中被广泛使用。
SpringBoot框架
Spring Boot框架是Pivotal团队基于Spring 开发的全新框架,其设计初衷是为了简化Spring 的配置,使用户能够构建独立运行的程序,提高开发效率。
Spring Boot框架本身并不提供Spring 框架的核心特性及扩展功能,它只是用于快速、敏捷地开发新一代基于Spring框架的应用,同时它还集成了大量的第三方类库(如Jackson.JDBC、Redis 等),使用户只需少量配置就能完成相应功能。
Spring Could框架
Spring Cloud是一系列框架的有序集合,为开发人员构建微服务架构提供了完整的解决方案,它利用Spring Boot的开发便利性巧妙地简化了分布式系统的开发。例如,配置管理、服务发现、控制总线等操作,都可以使用Spring Boot做到一键启动和部署。可以说,Spring Cloud将Spring Boot框架进行了再封装,屏蔽掉了复杂的配置和实现原理,具有简单易懂、易部署和易维护等特点。

传统JDBC的劣势

JDBC是Java程序实现数据访问的基础,JDBC的劣势主要有以下几个方面。
(1)数据库连接创建、释放频繁会造成系统资源浪费,从而影响系统性能。
(2) SQL语句在代码中硬编码,造成代码不易维护。在实际应用的开发中,SQL变化的可能性较大。在传统JDBC编程中,SQL变动需要改变Java代码,违反了开闭原则。
(3)用PreparedStatement向占有位符号传参数存在硬编码,因为SQL语句的where条件不一定,可能多也可能少,修改SQL需要修改代码,造成系统不易维护。
(4) JDBC对结果集解析存在硬编码(查询列名),SQL变化导致解析代码变化,造成系统不易维护。

Mybatis概述

什么是Mybatis

MyBatis是一个支持普通SQL查询、存储过程以及高级映射的持久层框架,它消除了几乎所有的JDBC代码和参数的手动设置以及对结果集的检索,使用简单的XML或注解进行配置和原始映射,将接口和Java的POJO映射成数据库中的记录,使得Java开发人员可以使用面向对象的编程思想来操作数据库。

ORM框架工作原理
MyBatis框架是一个ORM (Object/Relation Mapping,即对象关系映射)框架。所谓的ORM就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述Java对象与数据库表之间的映射关系,自动将Java应用程序中的对象持久化到关系型数据库的表中。RM框架的工作原理可以通过一张图来展示。

Mybatis环境搭建

使用MyBatis框架进行数据库开发之前,需要先搭建MyBatis环境,MyBatis环境搭建主要有如下基本步骤。
(1)创建工程;



(2)引入相关依赖;

(3)数据库准备;

(4)编写数据库连接信息配置文件;

(5)编写核心配置文件和映射文件。

Mybatis入门程序

  1. 数据准备
  2. 创建POJO实体
  3. 创建映射文件UserMapper.xml
  4. 修改mybatis-config.xml配置文件
  5. 编写测试类

Mybatis工作原理

mybatis工作原理
MyBatis框架在操作数据库时,大体经过了8个步骤。下面结合MyBatis工作原理图对每一步流程进行详细讲解,具体如下。

  1. MyBatis读取核心配置文件mybatis-config.xml: mybatis-config.xml核心配置文件主要配置了MyBatis的运行环境等信息。

  2. 加载映射文件Mapper.xml: Mapper.xml文件即SQL映射文件,该文件配置了操作数据库的SQL语句,需要在mybatis-config.xml中加载才能执行。

  3. 构造会话工厂:通过MyBatis的环境等配置信息构建会话工厂SqlSessionFactory,用于创建SqlSession。

  4. 创建会话对象:由会话工厂SqlSessionFactory创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。

  5. 创建执行器:会话对象本身不能直接操作数据库,MyBatis底层定义了一个Executor接口用于操作数据库,执行器会根据SqlSession传递的参数动态的生成需要执行的SQL语句,同时负责查询缓存地维护。

  6. 封装SQL信息: SqlSession内部通过执行器Executor操作数据库,执行器将待处理的SQL信息封装到MappedStatement对象中。

  7. 操作数据库:根据动态生成的SQL操作数据库。

  8. 输出结果映射:执行SQL语句之后,通过MappedStatement对象将输出结果映射至Java对象中。

SqlSessionFactoryBuilder对象

  1. 形式一: SqlSessionFactoryBuilder构建build()方法
  • build(InputStream inputStream,String environment,Properties properties)
    上述build()方法中,参数inputStream是字节流,它封装了XML文件形式的配置信息;参数environment和参数properties为可选参数。其中,参数environment决定将要加载的环境,包括数据源和事务管理器;参数properties决定将要加载的properties文件。
  1. 形式二:SqlSessionFactoryBuilder构建build()方法
  • build(Reader reader,String environment,Properties properties)
    由上述build()方法可知,第二种形式的build()方法参数作用与第一种形式大体一致,唯一不同的是,第一种形式的build()方法使用InputStream字节流封装了XML文件形式的配置信息,而第二种形式的build()方法使用Reader字符流封装了xml文件形式的配置信息。
  1. 形式三:SqlSessionFactoryBuilder构建build()方法
  • build(Configuration config)
    通过以上代码可知,配置信息可以通过**InputStream(字节流)、Reader(字符流)Configuration(类)**三种形式提供给SqlSessionFactoryBuilder的build()方法。

以读取XML文件的方式构造SqlSessionFactory对象
通过过读取XML配置文件的方式构造SqlSessionFactory对象的关键代码如下所示。

1
2
3
4
//读取配置文件
InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
//根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSessionFactory对象

openSession(ExecutorType execType)参数值
参数execType有三个可选值:

  • ExecutorType.SIMPLE:表示为每条语句创建一条新的预处理语句。
  • ExecutorType.REUSE:表示会复用预处理语句。
  • ExecutorType.BATCH:表示会批量执行所有更新语句。

openSession(ExecutorType execType,Connection connection)参数值

  • 参数execType有三个可选值,同openSession(ExecutorType execType)的参数
  • 参数connection可提供自定义连接。

SqlSession对象


SqlSession对象的使用范围
每一个线程都应该有一个自己的SqlSession对象,并且该对象不能共享。SqlSession对象是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在类的静态字段、对象字段或任何类型的管理范围(如Servlet的HttpSession)中使用。SqlSession对象使用完之后,要及时的关闭,SqlSession对象通常放在finally块中关闭,代码如下所示。

1
2
3
4
5
6
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
//此处执行持久化操作
}finally {
sqlSession.close();
}

Mybatis配置文件的主要元素

的子元素的执行顺序
元素是整个XML配置文件的根元素,相当于MyBatis各元素的管理员。有很多子元素,MyBatis的核心配置就是通过这些子元素完成的。需要注意的是,在核心配置文件中,的子元素必须按照上图由上到下的顺序进行配置。否则MyBatis在解析XML配置文件的时候会报错。

properties元素



setting元素

元素中常见配置参数的使用方式

...

typeAliases元素



environments元素

各元素配置运行环境
MyBatis的运行环境信息包括事务管理器和数据源。在MyBatis的核心配置文件中,MyBatis通过元素定义一个运行环境。元素有两个子元素,元素和元素。元素用于配置运行环境的事务管理器;元素用于配置运行环境的数据源信息。

元素配置事务管理器
在MyBatis中,元素可以配置两种类型的事务管理器,分别是JDBC和MANAGED。

  • JDBC;此配置直接使用JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。
  • MANAGED:此配置不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下,它会关闭连接,但可以将元素的closeConnection属性设置为false来阻止它默认的关闭行为。

mappers元素

  1. 使用类路径引入
    使用类路径引入映射文件的示例代码如下所示。

  2. 使用本地文件路径引入
    使用本地文件路径引入映射文件的示例代码如下所示。

  3. 使用接口类引入
    使用接口类引入映射文件的示例代码如下所示。

  4. 使用包名引入
    使用包名引入映射文件的示例代码如下所示。

Mybatis映射文件中的常用元素


select元素

元素用来映射查询语句,它可以从数据库中查询数据并返回。使用
select * from users where id = #{id}

insert元素

元素的插入使用
元素用于映射插入语句,在执行完元素中定义的SQL语句后,会返回插入记录的数量。使用元素执行插入操作非常简单,示例代码如下:


insert into users(uid,uname,uage)values(#{uid}, #{uname},#{uage})

a.使用支持主键自动增长的数据库获取主键值
如果使用的数据库支持主键自动增长(如MySQL和SQL Server),那么可以通过keyProperty属性指定POJO类的某个属性接收主键返回值(通常会设置到id属性上),然后将useGeneratedKeys的属性值设置为true。

insert into users(uid,uname,uage)values(#{uid},#{uname},#{uage})

b.使用不支持主键自动增长的数据库获取主键值
使用MyBatis提供的元素来自定义主键。

在上述元素的属性中,order属性可以被设置为BEFORE或AFTER。如果设置为BEFORE,那么它会首先执行元素中的配置来设置主键,然后执行插入语句;如果设置为AFTER,那么它先执行插入语句,然后执行元素中的配置内容。

update元素

元素的更新使用
元素用于映射更新语句,它可以更新数据库中的数据。在执行完元素中定义的SQL语句后,会返回更新的记录数量。使用元素执行更新操作非常简单,示例代码如下:


update users set uname= #{uname},uage = #{uage} whereuid = #{uid}

delete元素

元素的删除使用
元素用于映射删除语句,在执行完元素中的SQL语句之后,会返回删除的记录数量。使用元素执行删除操作非常简单,示例代码如下所示:

<delete id=”deleteUser” parameterType=”Integert>
delete from users where uid=#{uid}

元素中,除了上述示例代码中的几个属性外,还有其他一些可以配置的属性,如flushCache、timeout等。

sql元素

元素的作用
在一个映射文件中,通常需要定义多条SQL语句,这些SQL语句的组成可能有一部分是相同的(如多条select语句中都查询相同的id、username字段),如果每一个SQL语句都重写一遍相同的部分,势必会增加代码量。针对此问题,可以在映射文件中使用MyBatis所提供的元素,将这些SQL语句中相同的组成部分抽取出来,然后在需要的地方引用。
元素的作用是定义可重用的SQL代码片段,它可以被包含在其他语句中。元素可以被静态地(在加载参数时)参数化,元素不同的属性值通过包含的对象发生变化。

实现一个根据客户id查询客户信息的SQL片段

from

uid,uname,uage

resultMap元素

元素的作用
元素表示结果映射集,是MyBatis中最重要也是功能最强大的元素。元素主要作用是定义映射规则、更新级联以及定义类型转化器等。
默认情况下,MyBatis程序在运行时会自动将查询到的数据与需要返回的对象的属性进行匹配赋值(数据表中的列名与对象的属性名称完全一致才能匹配成功并赋值)。然而实际开发时,数据表中的列和需要返回的对象的属性可能不会完全一致,这种情况下MyBatis不会自动赋值,这时就需要使用元素进行结果集映射。






多学一招:使用工具类创建SqlSession对象

在上述案例中,由于每个方法执行时都需要读取配置文件,并根据配置文件的信息构建SqlSessionFactory对象、创建SqlSession对象、释放资源,这导致了大量的重复代码。为了简化开发,我们可以将读取配置文件和释放资源的代码封装到一个工具类中,然后通过工具类创建SqlSession对象。

案例:员工管理系统

  1. 项目搭建:建一个名称为mybatisdemo的项目,项目的具体搭建过程请参考1.3节。
  2. 数据准备:在mybatis数据库中创建employee表,并在employee表中插入几条数据。
    1
    2
    3
    4
    5
    6
    7
    use mybatis;
    create table employee(
    id int primary key auto_increment,name varchar(20) not null,
    age int not null,
    position varchar(20) );
    insert into employee(id,name,age,position) values(null,"张三',20,员工‘),
    (null,'李四',18, '员工'),.(null,'王五',35,'经理');
  3. POJO类准备:在项目src/main/java目录下创建com.itheima.pojo包,在该包下创建持久化类Employee,并在类中声明id(编号).name(姓名) 、 age (年龄)和position(职位)属性,以及属性对应的getter/setter方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Employee {
    private Integer id;
    private String name;
    private Integer age;
    private String position;
    //省略getter/setter方法
    @Override
    public String toString() {
    return "Employee{" + "id=" + id + ", name=" + name +
    " , age=" + age + ", position=" + position +'}' ;
    }
    }
  4. 编写映射文件:在项目src/main/java目录下创建com.itheima.mapper包,在com.itheima.mapper包下创建映射文件EmployeeMapper.xml,该文件主要用于实现SQL语句和Java对象之间的映射,部分文件内容如下。

<select id=”findByld”parameterType=”Integer”
resultType=”com.itheima.pojo.Employee”> select * fromemployee where id = #{id}

insert into employee(id,name,age,position)values
(#{id},#{name},#{age},#{position})

  1. 修改mybatis-config.xml核心配置文件:在mybatis-config.xml映射文件的元素下添加EmployeeMapper.xml映射文件路径的配置,用于将EmployeeMapper.xml映射文件加载到程序中。


  1. 编写MyBatisUtils工具类:在项目src/main/java目录下创建com.itheima.utils包,在该包下创建MyBatisUtils工具类,该类用于封装读取配置文件信息的代码。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class MyBatisUtils 
    private static SqlSessionFactory sqlSessionFactory = null;
    static { try {
    //使用MyBatis提供的Resources类加载MyBatis的配置文件
    Reader reader = Resources.getResourceAsReader("mybatis-
    config.xml");}
    //构建SqlSessionFactory工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    } catch (Exception e) { e.printStackTrace();}
    public static SqlSession getSession){//获取SqlSession对象的静态方法
    return sqlSessionFactory.openSession();}
  2. 编写测试类
    (1)在项目src/test/java目录下创建Test包,在Test包下创建MyBatisTest测试类,用于程序测试。在MyBatisTest测试类中添加findByldTest()方法,用于根据id查询员工信息。
    (2)在MyBatisTest测试类中添加insertEmployeeTest()方法,用于插入员工信息。
    (3)在MyBatisTest测试类中添加updateEmployeeTest()方法,用于更新员工信息。
    (4))在MyBatisTest测试类中添加deleteEmployeeTest()方法,用于删除员工信息。

动态SQL中的元素

在实际项目的开发中,开发人员在使用JDBC或其他持久层框架进行开发时,经常需要根据不同的条件拼接SQL语句,拼接SQL语句时还要确保不能遗漏必要的空格、标点符号等,这种编程方式给开发人员带来了非常大的不便,而MyBatis提供的SQL语句动态组装功能,恰能很好的解决这一问题。本章将对MyBatis框架的动态SQL进行详细讲解。

if元素

元素的应用
在MyBatis中,元素是最常用的判断元素,它类似于Java中的if语句,主要用于实现某些简单的条件判断。在实际应用中,我们可能会通过某个条件查询某个数据。例如,要查找某个客户的信息,可以通过姓名或者年龄来查找客户,也可以不填写年龄直接通过姓名来查找客户,还可以都不填写而查询出所有客户,此时姓名和年龄就是非必须条件。类似于这种情况,在MyBatis中就可以通过元素来实现。

  1. 通过一个具体的案例演示单条件判断下元素的使用,案例具体实现步骤如下。
    数据库准备:在名称为mybatis的数据库中,创建一个t_customer表,并插入几条测试数据。

    1
    2
    3
    4
    5
    6
    7
    8
    USE mybatis;
    CREATE TABLE t_customer (
    id int(32)PRIMARY KEY AUTO_INCREMENT,username varchar(50),
    jobs varchar(50),
    phone varchar(16));
    INSERT INTO t_customer VALUES ('1', 'joy', 'teacher", '13733333333');
    INSERT INTO t_customer VALUES (2', 'jack', 'teacher', '13522222222');
    INSERT INTO t_customer VALUES ('3', 'tom', 'worker', '15111111111');
  2. POJO类准备:在com.itheima.pojo包下创建持久化类Customer,在类中声明id、username、jobs和phone属性,及属性对应的getter/setter方法。
    public class Customer
    private Integer id; private String username;l/主键ID、客户名称private String jobs; private String phone;/职业、电话
    /省略getter/setter方法
    @Override
    public String toString() {
    return “Customer [id=” + id + “, username=” + username + “, jobs=”+ jobs + “, phone=” + phone + “]”;}

  3. 创建映射文件:在项目com.itheima.mapper包下创建映射文件CustomerMapper.xml,在映射文件中,根据客户姓名和年龄组合条件查询客户信息,使用元素编写该组合条件的动态SQL。

<!-该xml文件中只列出了if元素的动态SQL–>
<if test=”username !=null and username != ““>
and username like concat(‘%’,#{username}, “‘%’)
<if test=”jobs !=null and jobs != ““>and jobs= #{jobs}

  1. 修改核心配置文件:在配置文件mybatis-config.xml中,引入CustomerMapper.xml映射文件,将CustomerMapper.xml映射文件加载到程序中。

  2. 创建获取SqlSession对象的工具类:本案例使用2.3.7节的MyBatisUtils类作为获取SqlSession对象的工具类。首先在项目的src/main/java目录下创建一个com.itheima.utils包,然后将2.3.7节的MyBatisUtils类复制到该包下即可。

  3. 编写测试类:在测试类MyBatisTest中,编写测试方法findCustomerByNameAndJobsTest(),该方法用于根据客户姓名和职业组合条件查询客户信息列表。

public class MyBatisTest {
@Test
public void findCustomerByNameAndJobsTest(){SqlSession session =
MyBatisUtils.getSession();Customer customer = new Customer();customer.setUsername(“jack”); customer.setJobs(“teacher”);
List customers = session.selectList(“ com.itheima.mapper”

  • “.CustomerMapper.findCustomerByNameAndJobs”,customer);for (Customer customer2 : customers) { System.out.printIn(customer2);}session.close();}
    }

choose、when、otherwise元素

otherwise>使用场景
在使用元素时,只要test属性中的表达式为true,就会执行元素中的条件语句,但是在实际应用中,有时只需要从多个选项中选择一个去执行。
例如下面的场景:“当客户名称不为空,则只根据客户名称进行客户筛选;当客户名称为空,而客户职业不为空,则只根据客户职业进行客户筛选。当客户名称和客户职业都为空,则要求查询出所有电话不为空的客户信息。”
针对上面情况,使用元素进行处理是不合适的。MyBatis提供了.元素进行处理,这三个元素往往组合在一起使用,作用相当于Java语言中的if…else if…else。
使用元素组合演示上面场景的情况。

  1. 在映射文件CustomerMapper.xml中,添加使用元素
    执行上述情况的动态SQL。

    and username like concat('%',#{username}, "%') and jobs= #{jobs} and phone is not null
  2. 在测试类MyBatisTest中,编写测试方法findCustomerByNameOrJobsTest(),该方法用于根据客户姓名或职业查询客户信息列表。

public void findCustomerByNameOrJobsTest() {sqlSession session=MyBatisUtils.getSession();
Customer customer=new Customer();
customer.setUsername(“tom”);customer.setJobs(“teacher”);
List customers=session.selectList(“ com.itheima.mapper”+ “.CustomerMapper.findCustomerByNameOrJobs” ,customer);
for (Customer customer2 : customers) {
System.out.printIn(customer2);}
session.close();
}

where元素和trim元素

使用场景
在映射文件中,编写的SQL后面加入了“where 1=1”的条件的话,既保证了where后面的条件成立,又避免了where后面第一个词是and或者or之类的关键字。
例如下面这条Mybatis拼接出的SQL语句是不正确的。
select * from t_customer where and username like concat(‘%’,?,’%’) and jobs = #{jobs}
上述SQL语句中,where后直接跟的是and,这在运行时会报SQL语法错误,针对这种情况,可以使用MyBatis提供的元素和元素进行处理。

元素

元素
元素用于删除多余的关键字,它可以直接实现元素的功能。元素包含4个属性。
属性 说明
prefix 指定给SQL语句增加的前缀
prefixOverrides 指定SQL语句中要去掉的前缀字符串
suffix 指定给SQL语句增加的后缀
suffixOverrides 指定SQL语句中要去掉的后缀字符串

元素
<select id=”findCustomerByNameAndJobs”
parameterType=”com.itheima.pojo.Customer”resultType=”com.itheima.pojo.Customer”>select * from t_customer


and username like concat(‘%’ ,#{username}, ‘%’)

and jobs= #{jobs}

更新元素

元素使用场景
在Hibernate框架中,如果想要更新某一个对象,就需要发送所有的字段给持久化对象,然而在实际应用中,大多数情况下都是更新某一个或几个字段。如果更新的每一条数据都要将其所有的属性都更新一遍,那么执行效率是非常差的。为了解决更新数据的效率问题,MyBatis提供了元素。元素主要用于更新操作,它可以在动态SQL语句前输出一个SET关键字,并将SQL语句中最后一个多余的逗号去除。元素与元素结合可以只更新需要更新的字段。

  1. 通过一个案例演示如何使用元素更新数据库的信息,案例具体步骤如下。
    在映射文件CustomerMapper.xml中,添加使用元素执行更新操作的动态SQL。

update t_customer
username=#{usernaime},
jobs=#{jobs},
phone=#{phone},
where id=#{id}

  1. 编写测试方法updateCustomerBySetTest()。
    public void updateCustomerBySetTest()
    SqlSession sqlSession = MyBatisUtils.getSession);
    Customer customer = new Customer();
    customer.setld(3);
    customer.setPhone(“13311111234”);
    int rows = sqlSession.update(“com.itheima.mapper”
  • “.CustomerMapper.updateCustomerBySet” ,customer);
    if(rows > 0) {System.out.println(“您成功修改了”+rows+”条数据!”);}else { System.out.printIn(“执行修改操作失败!!! “);
    }sqlSession.commit();sqlSession.close();

foreach元素中的属性

  1. 属性的取值
    在遍历参数时,collection>属性的值是必须指定的。不同情况下,该属性的取值也是不一样的,主要有以下三种情况。
  2. List类型
    若入参为单参数且参数类型是一个List,collection属性值为list。
  3. 数组类型
    若入参为单参数且参数类型是一个数组,collection属性值为array。
  4. Map类型
    若传入参数为多参数,就需要把参数封装为一个Map进行处理,collection属性值为Map。

foreach元素迭代数组

实现入参为数组类型的遍历
例如,要从数据表t_customer中查询出id为1、2、3的客户信息,就可以利用数组作为参数,存储id的属性值1、2、3,并通过元素迭代数组完成客户信息的批量查询操作。

元素迭代数组的实现具体如下。

  1. 在映射文件CustomerMapper.xml中,添加使用元素迭代数组执行批量查询操作的动态SQL。

    select * from t_customer where id in
    <foreach item=”id”index=”index” collection=”list”
    open=”(“ separator=”,” close=”)”>

#{id}

  1. 在测试类MyBatisTest中,编写测试方法findByListTest(),用于批量查询客户信息。
    public void findByListTest() {
    sqlSession session = MyBatisUtils.getSession(;List ids=new ArrayList();ids.add(1); ids.add(2);
    List customers = session.selectList(“com.itheima.mapper”
  • “.CustomerMapper.findByList””,ids);
    for (Customer customer : customers){System.out.println(customer);} session.close());}
  1. 执行MyBatisTest测试类的findByListTest()方法,控制台会输出结果。

foreach元素迭代Map

  1. 下面通过一个案例演示如何使用元素迭代Map集合,实现多参数入参查询操作,案例具体实现步骤如下。
    在映射文件CustomerMapper.xml中,添加使用元素迭代Map集合执行批量查询操作的动态SQL。

  2. 在测试类MyBatisTest中,编写测试方法findByMapTest(),用于批量查询客户信息。
    public void findByMapTest()
    SqlSession session = MyBatisUtils.getSession);List ids=new ArrayList();ids.add(1); ids.add(2); ids.add(3);
    Map<String,Object> conditionMap = new HashMap<String, Object>);conditionMap.put(“id” ,ids); conditionMap.put(“jobs” ,”teacher”);
    List customers = session.selectList(“com.itheima.mapper”+ “.CustomerMapper.findByMap” , conditionMap);
    for (Customer customer : customers) { System.out.println(customer);}
    session.close();

案例:学生信息查询系统

多条件查询
当用户输入的学生姓名不为空,则只根据学生姓名进行学生信息的查询;
当用户输入的学生姓名为空,而学生专业不为空,则只根据学生专业进行学生的查询;
单条件查询
查询出所有id值小于5的学生的信息;

  1. 项目搭建:创建一个名称为mybatis-demo03的项目,项目的具体搭建过程请参考1.3节。
  2. 数据准备:在名称为mybatis的数据库中,创建一个dm_student表,并插入几条测试数据。

USE mybatis;
CREATE TABLE dm_student(
id int(32)PRIMARY KEY AUTO_INCREMENT,name varchar(50),
major varchar(50),sno varchar(16) );#插入7条数据,其他省略
INSERT INTO dm_student VALUES (‘1’, 张三’,’数学’,’10001’);

  1. POJO类准备:在项目src/main/java目录下创建com.itheima.pojo包,在该包下创建持久化类Student,在类中声明id、name、major和sno属性,以及属性对应的getter/setter方法。
    public class Student{//定义变量主键id,姓名name,专业major,学号sno
    private Integer id; private String name;
    private String major;
    private String sno;/省略getter/setter方法@Override
    public String toString0 {
    return”Student” +“id=” + id + “, name= “”+ name + “”,major=” + major + “, sno=” + sno + }’;》}

  2. 创建映射文件:在项目src/main/java目录下创建com.itheima.mapper包,在该包下创建映射文件StudentMapper.xml,编写根据学生姓名和专业组合成的条件查询学生信息的动态SQL。

  3. 修改mybatis-config.xml核心配置文件:在mybatis-config.xml映射文件的元素下添加StudentMapper.xml映射文件路径的配置,用于将
    StudentMapper.xml映射文件加载到程序中。具体配置代码如下。

  4. 编写MyBatisUtils工具类:在项目src/main/java目录下创建com.itheima.utils包,在com.itheima.utils包下创建MyBatisUtils工具类,该类用于封装读取配置文件信息的代码。

  5. 编写测试方法:在测试类MyBatisTest中,编写测试方法
    findStudentByNameOrMajorTest(),该方法用于根据学生姓名或专业查询学生信息。
    public void findStudentByNameOrMajorTest() {
    SqlSession session=MyBatisUtils.getSession();Student student=new Student();
    student.setName(“张三”);
    student.setMajor(“英语”);
    List students = session.selectList(“com.itheima.mapper”

  • “.StudentMapper.findStudentByNameAndMajor”,student);for (Student student2 : students) {System.out.printIn(student2);}session.close();}
  1. 查看运行结果:执行测试类MyBatisTest的findStudentByNameOrMajorTest()方法,控制台会输出结果。

关联映射概述

  • 一对一关系
    一个数据表中的一条记录最多可以和另一个数据表中的一条记录相关。例如,现实生活中学生与校园卡就属于一对一的关系,一个学生只能拥有一张校园卡,一张校园卡只能属于一个学生。
  • 一对多关系
    主键数据表中的一条记录可以和另外一个数据表的多条记录相关。但另外一个数据表中的记录只能与主键数据表中的某一条记录相关。例如,现实中班级与学生的关系就属于一对多的关系,一个班级可以有很多学生,但一个学生只能属于一个班级。
  • 多对多关系
    一个数据表中的一条记录可以与另外一个数据表任意数量的记录相关,另外一个数据表中的一条记录也可以与本数据表中任意数量的记录相关。例如,现实中学生与教师属于多对多的关系,一名学生可以由多名教师授课,一名教师可以为多名学生授课。

一对一查询

元素
在MyBatis中,通过元素来处理一对一关联关系。元素提供了一系列属性用于维护数据表之间的关系。

元素的配置方式
元素是元素的子元素,它有两种配置方式,嵌套查询方式和嵌套结果方式,下面对这两种配置方式分别进行介绍。

  1. 嵌套查询方式
    嵌套查询是指通过执行另外一条SQL映射语句来返回预期的复杂类型。

2.嵌套结果方式
嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。

接下来就以个人和身份证之间的一对一关联关系为例,对MyBatis中一对一关联关系的处理进行详细讲解。案例具体实现步骤如下。

  • STEP 01 创建数据表:在mybatis数据库中分别创建名为tb_idcard的身份证数据表和名称为tb person的个人数据表,同时预先插入几条数据。
    USE mybatis;

#创建一个名称为tb_idcard的表CREATE TABLE tb_idcard(
id INT PRIMARY KEY AUTO_INCREMENT,CODE VARCHAR(18));
#插入2条数据
INSERT INTO tb_idcard(CODE)VALUES(152221198711020624’);
#创建一个名称为tb_person的表,同理

  • STEP 02
    持久化类IDCard类:在项目的com.itheima.pojo包下创建持久化类ldCard,用于封装身份证属性。
    public class ldCard {
    private lnteger id;
    //主键id
    private String code;
    //身份证号码
    /省略getter/setter方法}
    @Override
    public String toString0) {
    return “ldCard [id=” + id + “ , code=” + code + “]”;
    }

  • STEP 03
    持久化类Person类:在项目的com.itheima.pojo包下创建持久化类Person,用于封装个人属性。
    public class Person {
    private Integer id;
    主键id
    private String name;
    /姓名
    private Integer age;
    /年龄
    private String sex;
    /性别
    private ldCard card;
    /人员关联的证件
    /省略getter/setter方法,重写的toString()方法}

  • STEP 04
    编写ldCardMapper.xml文件:在com.itheima.mapper包中,创建身份证映射文件ldCardMapper.xml,并在映射文件中编写一对一关联映射查询的配置信息。


    <select id=”findCodeByld” parameterType=”Integer”resultType=”IdCard”>
    SELECT * from tb_idcard where id=#{id}

  • STEP 05
    编写PersonMapper.xml文件:在com.itheima.mapper包中,创建人员映射文件PersonMapper.xml,并在映射文件中编写一对一关联映射查询的配置信息。

  • STEP 06
    引入映射文件:在核心配置文件mybatis-config.xml中,引入ldCardMapper.xml和PersonMapper.xml映射文件,并为com.itheima.pojo包下的所有实体类定义别名。

  • STEP 07
    编写测试类:在测试类MyBatisTest中,编写测试方法findPersonByldTest()。
    public void findPersonByldTest() {
    l/1、通过工具类获取SqlSession对象
    SqlSession session = MyBatisUtils.getSession();//⒉.使用MyBatis嵌套查询的方式查询id为1的人的信息
    Person person = session.selectOne(“ com.itheima.mapper.”
    +”PersonMapper.findPersonByld”,1);
    l/3、输出查询结果信息
    System.out.println(person);//4、关闭SqlSession
    session.close();}

  • 多学一招:MyBatis延迟加载的配置
    在使用MyBatis嵌套查询方式进行MyBatis关联映射查询时,使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在mybatis-config.xml中的元素内进行配置。

一对多查询

元素
在MyBatis中,通过元素来处理一对多关联关系。元素的属性大部分与元素相同,但其还包含一个特殊属性一ofType.ofType属性与javaType属性对应,它用于指定实体类对象中集合类属性所包含的元素的类型。
a.嵌套查询方式

b.嵌套结果方式



接下来以用户和订单之间的一对多关联关系为例,详细讲解如何在MyBatis中处理一对多关联关系,具体步骤如下。
在名为mybatis的数据库中,创建两个数据表,分别为tb_user(用户数据表)和

  • STEP 01
    tb_orders(订单表),同时在表中预先插入几条测试数据。
    uSE mybatis;

#创建一个名称为tb_user的表CREATE TABLE tb_user (
id int(32)PRIMARY KEY AUTO_INCREMENT,username varchar(32),
address varchar(256));#插入3条数据,其他语句省略
INSERT INTO tb_user VALUES (‘1’, “小明”,’北京’);#创建一个名称为tb_orders的表,同理

  • STEP 02
    在com.itheima.pojo包中,创建持久化类Orders,并在类中定义订单id和订单编号等属性。
    public class Orders {
    private lnteger id;
    //订单id
    private String number;
    //订单编号
    /省略getter/setter方法}
    @Override
    public String toString0 {
    return “Orders [id=” + id + “, number=” + number + “]”;}

  • STEP 03
    在com.itheima.pojo包中,创建持久化类Users,并在类中定义用户编号、用户姓名、用户地址以及用户关联的订单等属性。
    public class Users {
    private Integer id;
    /用户编号
    private String username;
    //用户姓名
    private String address;
    /用户地址
    private List ordersList;//用户关联的订单/省略getter/setter方法}
    @Override
    public String toString) {
    return “User [id=” + id + “, username=” + username+ “ , address=”+ address + “ , ordersList=” + ordersList + “]”;}

  • STEP 04
    在com.itheima.mapper包中,创建用户实体映射文件UsersMapper.xml,并在文件中编写一对多关联映射查询的配置。

  • STEP 05
    在核心配置文件mybatis-config.xml中,引入UsersMapper.xml,将UsersMapper.xml映射文件加载到程序中。

  • STEP 06
    在测试类MyBatisTest中,编写测试方法findUserTest()。
    public void findUserTest() {
    1/1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();/⒉查询id为1的用户信息
    Users users = session.sglectOne(“com.itheima.mapper.”
    +”UsersMapper.findUserWithOrders”,1);
    /3.输出查询结果信息
    System.out.println(users);//4.关闭SqlSession
    session.close();}

多对多查询

下面以订单表与商品表之间的多对多关系为例,讲解如何使用MyBatis处理多对多的关系

  • STEP 01
    在名为mybatis的数据库中创建名称为tb_product的商品表和名称为tb_ordersitem 的中间表,同时在表中预先插入几条数据。
    CREATE TABLE tb_product (
    id INT(32)PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(32), price DOUBLE );

#插入1条数据,其他省略
INSERT INTO tb_product VALUES (‘1’,’Java基础入门’,’44.5’);

  • STEP 02
    在com.itheima.pojo包中,创建持久化类Product,并在类中定义商品id、商品名称、商品单价等属性,以及与订单关联的属性。
    public class Product {
    private lnteger id; private String name;
    private Double price;
    private List arders;//关联订单属性
    //省略getter/setter方法}
    @Override
    public String toString() {
    return “Product [id=” +id + “, name=” + name
  • “, price=” + price + “]”;}
  • STEP 03
    在商品持久化类中,除了需要添加订单的集合属性外,还需要在订单持久化类
    Orders.java)中增加商品集合的属性及其对应的getter/setter方法,Orders类中添加的代码如下。
    关联商品集合属性
    private List productList;
    /省略getter/setter方法,以及重写的toString方法

  • STEP 04
    在com.itheima.mapper包中,创建订单实体映射文件OrdersMapper.xml,用于编写订单信息的查询SQL语句,并在映射文件中编写多对多关联映射查询的配置信息。

  • STEP 05
    在com.itheima.mapper包中,创建商品实体映射文件ProductMapper.xml,用于编写订单与商品信息的关联查询SQL语句。

  • STEP 06
    将新创建的映射文件OrdersMapper.xml和ProductMapper.xml的文件路径配置到核心配置文件mybatis-config.xml中。
    <mapper

    resource=”com/itheima/mapper/ProductMapper.xml”/>

  • STEP 07
    在测试类MyBatisTest中,编写多对多关联查询的测试方法findOrdersTest()。
    public void findOrdersTest() {
    //1.生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();//⒉.查询id为1的订单中的商品信息
    Orders orders = session.selectOne(“ com.itheima.mapper.”

  • “OrdersMapper.findOrdersWithPorduct”,1);
    System.out.println(orders);// 3.输出查询结果信息
    session.close();/l/ 4.关闭SqlSession}

查询订单及关联商品的另一方式
除了使用嵌套查询的方式查询订单及其关联的商品信息外,还可以在OrdersMapper.xml中使用嵌套结果的方式进行查询。

一级缓存

  • MyBatis的一级缓存级别
    MyBatis的一级缓存是SqlSession级别的缓存扌如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高了数据库的查询效率。

  • 举例说明MyBatis的一级缓存级别
    例如,存在数据表tb_book,从表中多次查询id为1的图书信息,当程序第一次查询id为1的图书信息时,程序会将查询结果写入MyBatis一级缓存,当程序第二次查询id为1的图书信息时,MyBatis直接从一级缓存中读取,不再访问数据库进行查询。当程序对数据库执行了插入、更新、删除操作,MyBatis会清空一级缓存中的内容以防止程序误读。

通过一个案例来对MyBatis一级缓存的应用进行详细讲解,该案例要求根据图书id查询图书信息。案例具体步骤如下。

  • STEP 01
    在mybatis数据库中创建名为tb_book的数据表,同时预先插入几条测试数据。
    USE mybatis;

#创建一个名称为tb_book的表,并插入数据,这里只展示一条CREATE TABLE tb_book(
id INT PRIMARY KEY AUTO_INCREMENT,bookName VARCHAR(255),
price double,
author VARCHAR(40) );
INSERT INTO tb book(bookName,price,author)
VALUES(“Java基础入门”,45.0,传智播客高教产品研发部’);

  • STEP 02
    在项目的com.itheima.pojo包下创建持久化类Book,在Book类中定义图书id、图书名称.图书价格、图书作者属性,以及属性对应的getter/setter方法。
    public class Book implements Serializable {
    private Integer id;
    /主键
    private String bookName;1/图书名称
    private double price; private String author;/价格、作者/省略getter/setter方法}
    @Override
    public String toString0 return “Book{“+
    “id=” + id + “, bookName=”” + bookName +”, price=” + price + “ , author=’” + author+ Y ; }

  • STEP 03
    在com.itheima.mapper包中,创建图书映射文件BookMapper.xml,并在该文件中编写根据图书id查询图书信息的SQL语句。



    update tb_book set bookName=#{bookName},price=#{price}
    where id=#{id}

  • STEP 04
    在核心配置文件mybatis-config.xml中的标签下,引入BookMapper.xml映射文件。

  • STEP 05
    由于需要通过log4j日志组件查看一级缓存的工作状态,因此需要在pom.xml中引入log4j的相关依赖。

    log4jlog4j1.2.17
  • STEP 06
    在工程的src/main/resources目录下创建log4jproperties文件,用于配置MyBatis和控制台的。

#全局日志配置
log4j.rootLogger=DEBUG, Console#控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppenderlog4j.appender.Console.layout=org.apache.log4j.PatternLayoutlog4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c]- %m%n
#日志输出级别,只展示了一个
log4j.logger.java.sql.PreparedStatement=DEBUG

  • STEP 07
    在测试类MyBatisTest中,编写测试方法findBookByldTest1()。
    public void findBookByldTest10 {
    //1.通过工具类生成SqIlSession对象
    SqlSession session1 = MyBatisUtils.getSession();//2.使用session1查询id为1的图书的信息
    Book book1 = session.selectOne(“com.itheima.mapper.”
    +”BookMapper.findBookByld”,1);
    System.out.printIn(book1.toStringO);// 3.输出查询结果信息/再次使用session1查询id为1的图书的信息,同2、3步1/4.关闭SqlSession
    session1.close();}

二级缓存

  • 使用二级缓存的好处
    由4.5.1节的内容可知,相同的Mapper类,相同的SQL语句,如果SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次,这样也会降低数据库的查询效率。为了解决这个问题,就需要用到MyBatis的二级缓存。MyBatis的二级缓存是Mapper级别的缓存,与一级缓存相比,二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源。

  • MyBatis二级缓存的执行过程
    在MyBatis中,一个Mapper.xml文件通常称为一个Mapper,MyBatis以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据。

  • 二级缓存与一级缓存的不同点
    与MyBatis的一级缓存不同的是,MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤。

  • a.开启二级缓存的全局配置
    与使用二级缓存前,需要在MyBatis的核心配置mybatis-config.xml文件中通过元素开启二级缓存的全局配置。

  • b.开启当前Mapper的namespace下的二级缓存
    开启当前Mapper的namespace下的二级缓存,可以通过MyBatis映射文件中的元素来完成。

  • 默认状态的二级缓存可实现的功能
    (1)映射文件中所有select语句将会被缓存。
    (2)映射文件中的所有jinsert、update和delete语句都会刷新缓存。
    (3)缓存会使用LRU算法回收!
    (4)投有刷新间隔,缓存不会以任何时间顺序来刷新。
    (5)缓存会存储列表集合或对象的1024个引用。
    (6)缓存是可读/回身的缓存,这意味着对象检索不是共享的,缓存可以安全的被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

接下来通过一个案例演示MyBatis二级缓存的应用,该案例仍旧根据id查询图书信息,案例具体步骤如下。
修改映射文件BookMapper.xml,在映射文件的元素下追加编写

  • STEP 01
    素开启当前Mapper的namespace的二级缓存。
    <!—开启当前BookMapper的namespace下的二级缓仔–>

  • STEP 02
    在测试类MyBatisTest中,编写测试方法findBookByldTest1().
    public void findBookByldTest3() {
    /1.通过工具类生成两个SqlSession对象,这里只展示了一个SqlSession session1 = MyBatisUtils.getSession();
    //⒉.使用session1查询id为1的图书的信息
    Book book1 = session1.selectOne(“com.itheima.mapper.”

  • “BookMapper.findBookByld”,1);
    System.out.printIn(book1.toString());// 3.输出查询结果信息//4.关闭SqlSession1
    session1.close();}
  • STEP 03
    执行MyBatisTest测试类的findBookByldTest3()方法,控制台会输出结果。

  • 对MyBatis二级缓存的应用案例的运行结果分析
    控制台输出了执行SQL语句的日志信息以及查询结果。通过分析SQL语句日志信息可以发现,当第一个SqlSession对象session1执行查询时,Cache Hit Ratio (缓存命中率)为0,程序发送了SQL语句;当第二个SqlSession对象session2执行相同的查询时,Cache Hit Ratio为0.5,程序没有发出SQL语句,这就说明程序直接从二级缓存中获取了数据。

  • 多个SqlSession在同一个Mapper中执行
    在实际开发中,经常会遇到多个SqlSession在同一个Mapper中执行操作,例如,SqlSession1执行查询操作,SqlSession2执行插入、更新、删除操作,
    SqlSession3又执行和SqlSession1相同的查询操作。当SqlSession1执行查询操作时,程序会将查询结果写入MyBatis二级缓存,当SqlSession2对数据库执行了插入、更新、删除操作后,MyBatis会清空二级缓存中的内容,以防止程序误读

    SqlSession3执行和SqlSession1相同的查询操作时,MyBatis会重新访问数据库。

  • 多学一招: Cache Hit Ratio(缓存命中率)
    终端用户访问缓存时,如果在缓存中查找到了要被访问的数据,就叫做命中。如果缓存中没有查找到要被访问的数据,就是没有命中。当多次执行查询操作时,缓存命中次数与总的查询次数(缓存命中次数+缓存没有命中次数)的比,就叫作缓存命中率,即缓存命中率=缓存命中次数/总的查询次数。当MyBatis开启二级缓存后,第一次查询数据时,由于数据还没有进入缓存,所以需要在数据库中查询而不是在缓存中查询,此时,缓存命中率为0。第一次查询过后,MyBatis会将查询到的数据写入缓存中,当第二次再查询相同的数据时,MyBatis会直接从缓存中获取这条数据,缓存将命中,此时的缓存命中率为0.5(1/2)。当第三次查询相同的数据,则缓存命中率为0.66666 (2/3),以此类推。

案例:商品的类别

  • STEP 01 项目搭建:创建一个名称为mybatis-demo04的项目,项目的具体搭建过程请参考1.3节。
    数据库准备:在名为mybatis的数据库中,创建两个数据表,分别为product和category,同时在表中预先插入几条测试数据。
  • STEP 02

USE mybatis;
#创建一个名称为category的表CREATE TABLE category (
id int(32)PRIMARY KEY AUTO_INCREMENT,
typename varchar(40)
);
#插入2条数据
INSERT INTO category VALUES (1, ‘黑色家电’);
INSERT INTO category VALUES (2,’白色家电’);#创建一个名称为product的表,同理

  • STEP 03
    POJO类准备:(1)在com.itheima.pojo包中,创建持久化类Category,并在类中定义商品类别的相关属性和方法。
    public class Category {private Integer id;
    主键id
    private String typename;
    /类别名称
    private List productList;/商品集合省略getter/etter方法}
    @Override
    public String toString() {
    return “Category{“+”id=” + id + “ , typename=’” +
    typename +”, productList=” + productList + ‘};}

  • STEP 04
    编写映射文件:在com.itheima.mapper包中,创建商品类别实体映射文件CategoryMapper.xml,并在文件中编写一对多关联映射查询的配置

  • STEP 05
    修改mybatis-config.xml核心配置文件:在核心配置文件mybatis-config.xml中,引入CategoryMapper.xml,将CategoryMapper.xml映射文件加载到程序中。

    resource="com/itheima/mapper/CategoryMapper.xml"
  • STEP 06
    编写测试方法:在测试类MyBatisTest中,编写测试方法findCategoryTest()。
    public void findCategoryTest() {
    //1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();//⒉.查询id为2的商品类别信息
    Category category =
    session.selectOne(“com.itheima.mapper.”

  • “CategoryMapper.findCategoryWithProduct”,2);System.out.printIn(category);
    // 3.输出查询结果信息session.close();
    //4.关闭SqlSession}

@Select注解

下面通过一个案例演示@Select主解的使用,该案例要求根据员工的id查找员工信息,案例具体实现步骤如下。

  • STEP 01
    建表:在mybatis数据库中创建名为tb_worker的数据表,同时预先插入几条测试数据。
    CREATE TABLE tb_worker(
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(32),age INT, sex VARCHAR(8),worker_id lNT UNIQUE);
    INSERT INTO tb_worker(name,age,sex,worker_id)VALUES(‘张三’,32,’女’,1001);

创建类:在项目的com.itheima.pojo包下创建持久化类Worker,在Worker类中定义id、员

  • STEP 02
    工姓名、年龄、性别、工号等属性以及属性对应的getter/setter方法。
    public class Worker {
    private lnteger id; private String name; private Integer age;private String sex;
    private String worker_id;省略getter/setter方法}@Override
    public String toString() {
    return “Worker{“+ “id=” +id + “, name=” + name +
    “, age=” + age + “ , sex=” + sex + “, worker_id=” + worker_id + ‘};}

编写查询方法:在项目的src/main/java目录下创建com.itheima.dao包,并在该包下创建

  • STEP 03
    WorkerMapper接口,用于编写@Select注解映射的select查询方法。
    package com.itheima.dao;
    import com.itheima.pojo.Worker;
    import org.apache.ibatis.annotations.Select;public interface WorkerMapper {
    Select(“select * from tb_worker where id = #{id}”)Worker selectWorker(int id);
    }

加载配置文件:在核心配置文件mybatis-config.xml中的元素下引入

  • STEP 04
    WorkerMapper接口,将WorkerMapper.java接口加载到核心配置文件中。

编写测试方法:在测试类MyBatisTest中,编写测试方法findWorkerByldTest()。

  • STEP 05
    public void findWorkerByldTest() {//1.获取SqlSession对象
    SqlSession session = MyBatisUtils.getSession();
    WorkerMapper mapper = session.getMapper(WorkerMapper.class);//⒉.查询id为1的员工信息
    Worker worker = mapper.selectWorker(1);System.out.println(worker.toString());//3.关闭SqlSession
    session.close();}

@Insert注解

下面通过一个案例演示@Insert注解的使用,要求实现员工信息的插入,案例具体实现步骤如下。
添加注解:在WorkerMapper接口中添加向tb_worker数据表插入数据的方法

  • STEP 01
    insertWorker(),并在方法上添加@Insert注解。
    lnsert(“insert into tb_worker(name,age,sex,worker_id)”
  • “values(#{name},#{age},#{sex},#{worker_id})”)int insertWorker(Worker worker);

编写测试类:在测试类MyBatisTest中,编写测试方法insertWorkerTest()。

  • STEP 02
    public void insertWorkerTest() {
    //1.生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();Worker worker = new Worker();
    worker.setld(4); worker.setName(“赵六”); worker.setAge(36);worker.setSex(“女”);worker.setWorker_id(“1004”);
    WorkerMapper mapper = session.getMapper(WorkerMapper.class);/⒉插入员工信息
    int result = mapper.insertWorker(worker);/输出语句省略..
    session.commit();
    session.close(); // 3.关闭SqlSession}

@Update注解

下面通过一个案例演示@Update注解的使用,该案例要求实现员工信息的修改,案例具体实现步骤如下。

  • STEP 01
    添加注解:在WorkerMapper接口中添加更新tb_worker表中数据的方法,并在方法上添加@Update注解。
    @Update(“update tb_worker set name = #{name},age = #{age} “+ “where id = #{id}”)
    int updateWorker(Worker worker);

编写测试类:测试类MyBatisTest中,编写测试方法updateWorkerTest()。

  • STEP 02
    public void updateWorkerTest() {//1.生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();Worker worker = new Worker();
    worker.setld(4); worker.setName(“李华”); worker.setAge(28);WorkerMapper mapper = session.getMapper(WorkerMapper.class);//⒉.更新员工信息
    int result = mapper.updateWorker(worker);/输出语句省略…
    session.commit();
    session.close();// 3.关闭SqlSession}

@Delete注解

下面通过一个案例演示@Delete注解的使用,该案例要求实现员工信息的删除
案例具体实现步骤如下。
添加注解:在WorkerMapper接口中添加删除数据库中数据的方法,并在方法上添加

  • STEP 01
    @Delete注解。
    @Delete(“delete from tb_worker where id = #{id}”)int deleteWorker(int id);

编写测试类:在测试类MyBatisTest中,编写测试方法deleteWorkerTest()。

  • STEP 02
    public void deleteWorkerTest() {
    SqlSession session = MyBatisUtils.getSession();l/ 1.生成SqlSession对象WorkerMapper mapper = session.getMapper(WorkerMapper.class);/⒉删除员工信息
    int result = mapper.deleteWorker(4);if result>o{
    System.out.println(“成功删除”+result+”条数据”);}else { System.out.println(“删除数据失败”);}
    session.commit();
    session.close();// 3.关闭SqlSession
    }

@Param注解

下面通过一个案例演示@Param注解的使用,该案例要求根据员工的id和姓名查询员工信息,案例具体实现步骤如下。

  • STEP 01
    添加注解:在WorkerMapper接口中添加多条件查询的方法。
    @Select(“select * from tb_worker where id = #{param01}
    and name = #{param02}”)
    Worker selectWorkerByldAndName(@Param(“param01”) int id,
    Param(“param02”) String name);

编写测试类:在测试类MyBatisTest中,编写测试方法selectWorkerByldAndNameTest()。

  • STEP 02
    public void selectWorkerByldAndNameTest() {//1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();
    WorkerMapper mapper = session.getMapper(WorkerMapper
    .class);
    //⒉查询id为3姓名为王五的员工的信息
    Worker worker = mapperselectWorkerByldAndName(3,”王五”);System.out.println(worker.toString());
    session.close();
    }

一对一查询

接下来,以4.2节中使用的tb_idcard和tb_person数据表为例,详细讲解基于注解@One实现tb_idcard和tb_person数据表之间的一对一关联查询,具体步骤如下。

  • STEP 01 创建持久化类:本案例使用4.2节中的ldCard类和Person类作为持久类。

  • @Result注解的三个属性及含义
    (1) property属性用来指定关联属性,这里为card。
    (2) column属性用来指定关联的数据库表中的字段,这里为card_id。
    (3) one属性用来指定数据表之间属于哪种关联关系,通过@One注解表明数据表tb_idcard和tb_person之间是一对一关联关系。

引入接口:在核心配置文件mybatis-config.xml中的元素下引入

  • STEP 03
    ldCardMapper和PersonMapper接口。

编写测试类:在测试类MyBatisTest中,编写测试方法selectPersonByldTest()。

  • STEP 04
    public void selectPersonByldTest() {//1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();
    PersonMapper mapper = session.getMapper(PersonMapper.class);/⒉.查询id为1的人员的信息
    Person person = mapper.selectPersonByld(2);System.out.println(person.toString());
    session.close(); //3.关闭SqlSession}

一对多查询

  • STEP 01 创建持久化类:本案例使用4.3节中的Users类和Orders类作为持久类。

编写接口方法:(1)在项目的com.itheima.dao包下创建OrdersMapp该接口中编写selectOrdersByUserld()方法,通过user_id查询用户对应的

  • STEP 02
    public interface OrdersMapper {
    @Select(“select * from tb_orders where user_id=#{id} “)@Results({@Result(id = true,column = “id”,property = “id”),
    Result(column = “number” ,property = “number”) })List selectOrdersByUserld(int user_id);
    }

编写接口方法:(2)在项目的com.itheima.dao包下创建UsersMappe

  • STEP 02
    接口中编写selectUserByld()方法,通过id查询用户信息。
    public interface UsersMapper {
    Select(“select * from tb_user where id=#{id} “)
    @Results({@Result(id = true,column = “id”,property = “id”),
    @Result(column = “username” ,property = “username”),@Result(column = “address”,property = “address”),@Result(column = “id”,property = “ordersList”,
    many = @Many(select =
    “com.itheima.dao.OrdersMapper.selectOrdersByUserld”))})Users selectUserByld(int id);}

引入接口:在核心配置文件mybatis-config.xml中的元素

  • STEP 03
    和OrdersMapper接口。
    l心

编写测试类:在测试类MyBatisTest中,编写测试方法selectUserByldT

  • STEP 04
    public void selectUserByldTest() //1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();UsersMapper mapper =
    session.getMapper(UsersMapper.class);/⒉.查询id为1的人的信息
    Users users = mapper.selectUserByld(1);
    System.out.println(users.toString)); session.close();

多对多查询

  • 多对多关联使用中间表
    在数据库中,表与表之间的多对多关联关系通常使用一个中间表来维开以4.4节中使用的订单表tb_orders和商品表tb_product为例,这网个表之间的大联大系使用了一个中间表tb_ordersitem来维护,订单表tb_orders和商品表tb_product,都
    与中间表tb_ordersitem形成了一对多关联关系,即中间表tb_ordersitem将订单表tb_orders和商品表tb_product拆分成了两个一对多的关联关系。

接下来,以4.4节中使用的订单表tb_orders、商品表tb_product和中间表tb_ordersitem为例,详细讲解多对多关联查询,具体步骤如下。
在订单持久化类(Orders.jav)中增加商品集合的属性及其对应的getter/setter方法,

  • STEP 01
    并修改Orders类和Product类中的toString()方法。
    Override
    public String toString() {return “Orders{“ +
    “id=” + id + “, number=” + number +
    “, productList=” + productList + ‘}’;}@Override
    public String toString() {return “Product{“ +
    “id=” + id + “, name=” + name + “, price=” + price + ‘};}

(2)在项目的com.itheima.dao包下的OrdersMapper
select * from tb_orders

  • STEP 02
    selectOrdersByld()方法,该方法用于通过id查询订单信息
    select * from tb_product
    selectproduct_ia fro
    @Select(“select * from tb_orders where id=#{id}”)
    @Results({@Result(id = true,column = “id”,property = “id”),
    @Result(column = “number”,property = “number”),@Result(column = “id”,property = “productList”,man= @Many(select =
    “com.itheima.dao.ProductMapper.selectProductByOrdersld”))})Orders selectOrdersByld(int id);

  • STEP 03

  • STEP 04
    public void selectOrdersByldTest() {//1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();
    OrdersMapper mapper = session.getMapper(OrdersMapper.class);//⒉.查询id为3的订单的信息
    Orders orders = mapper.selectOrdersByld(3);System.out.printIn(orders.toString());
    session.close();}

案例:基于MyBatis注解的学生管理程序

使用MyBatis注解实现下列要求
(1) MyBatis注解实现查询操作。根据表1和表2在数据库分别创建一个学生表s_student和一个班级表c_class,并查询id为2的学生的信息。
(2)MyBatis注解实现修改操作。修改id为4的学生的姓名修改为李雷,年龄修改为21。
(3)MyBatis注解实现一对多查询。查询出二班所有学生的信息。

  • STEP 02
    数据库准备:在名为mybatis的数据库中,创建两个数据表,和班级表c_class,同时在表中预先插入几条测试数据。
    USE mybatis;

#创建一个名称为c_class的表CREATE TABLE c_class
id int(32)PRIMARY KEY AUTO_INCREMENT,classname varchar(40);
INSERT INTO c_class VALUES (1,’一班’);INSERTINTO c_class VALUES (2,’二班’);#创建s student表同理

  • STEP 03
    POJO类准备:在项目的src/main/java目录下创建com.itheima.poio包.在com.itheima.pojo包中创建持久化类IClass,并在类中定义相头以
    封装lClass对象的id、班级名称以及关联的学生集合等属性。
    public class lClass
    private Integer id;private String classname;l/主键id,班级名称private List studentList;
    @Override
    public String toString() return “IClass{“ +”id=” + id +
    “ , classname=’” + classname +”, studentList=” + studentList + ‘Y ;
    }
  • STEP 04
    创建Mapper接口:在项目的src/main/java目录下创建com.itheincom.itheima.dao包下创建lStudentMapper接口,用于编与@ vrec
    查询语句。
    package com.itheima.dao;
    import com.itheima.pojo.IStudent;
    import org.apache.ibatis.annotations.Select;public interface lStudentMapper {
    @Select(“select * from s_student where id = #{id}”)lStudent selectStudent(int id);}