0. 框架的基本概念
三层架构
- 界面层: 和用户打交道的, 接收用户的请求参数, 显示处理结果的。(jsp ,html ,servlet)
- 业务逻辑层: 接收了界面层传递的数据,计算逻辑,调用数据库,获取数据
- 数据访问层: 就是访问数据库, 执行对数据的查询,修改,删除等等的。
三层对应的包
- 界面层:
controller包 (servlet) - 业务逻辑层:
service包(XXXService类) - 数据访问层:
dao包(XXXDao类)
- 界面层:
三层中类的交互
用户使用界面层—> 业务逻辑层—->数据访问层(持久层)—>数据库(mysql)三层对应的处理框架
- 界面层—-
servlet—-springmvc(框架) - 业务逻辑层—-
service类—spring(框架) - 数据访问层—-
dao类—mybatis(框架)
- 界面层—-
- 框架
框架是一个舞台, 一个模版
模版:
- 规定了好一些条款,内容。
- 加入自己的东西
框架是一个模块
1. 框架中定义好了一些功能。这些功能是可用的。2. 可以加入项目中自己的功能, 这些功能可以利用框架中写好的功能。框架是一个软件,半成品的软件,定义好了一些基础功能, 需要加入你的功能就是完整的。
基础功能是可重复使用的,可升级的。框架特点:
- 框架一般不是全能的, 不能做所有事情
- 框架是针对某一个领域有效。 特长在某一个方面,比如mybatis做数据库操作强,但是他不能做其它的。
- 框架是一个软件
1. 基本概念
- MyBatis 是一款优秀的持久层框架
- 它支持定制化 SQL、存储过程以及高级映射。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
总结:
mybatis是一个sql映射框架,提供的数据库的操作能力。增强的JDBC,
使用mybatis让开发人员集中精神写sql就可以了,不必关心Connection,Statement,ResultSet
的创建,销毁,sql的执行。
maven仓库:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency>
2. 基本应用
2.1 入门配置

2.1.1 配置数据库
CREATE DATABASE `mybatis`;USE `mybatis`;CREATE TABLE `user`(`id` INT(20) NOT NULL PRIMARY KEY,`name` VARCHAR(30) DEFAULT NULL)DEFAULT CHARSET=utf8;INSERT INTO `user`(`id`,`name`) VALUES(10,'ahang'),(20,'haha'),(30,'kaka')+----+-------+| id | name |+----+-------+| 10 | ahang || 20 | haha || 30 | kaka |+----+-------+
2.1.2 在maven的pom.xml中导入依赖
<dependencies><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.40</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency></dependencies>
并且配置好配置资源定位
<!--在build中配置resources,来防止我们资源导出失败的问题--><build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources></build>
2.1.3 配置User类
在pojo包中定义一个User类,和数据库中user表对应
package com.ahang.pojo;public class User {private int id;private String name;public User() {}public User(int id, String name){this.id = id;this.name = name;}}
2.1.4 增加接口UserDao.java
package com.ahang.Dao;public interface UserDao {List<User> getUserList();}
2.1.5 数据库操作配置UserDaoImpl.xml
相当于将原来的UserDaoImpl.java改为了配置文件形式
在UserDaoImpl.xml中配置对应的增删改查语句,获取user表的全部条目
注意点:此配置中存在中文注释可能会报错,不要添加中文注释在此
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.ahang.Dao.UserDao"><select id="getUserList" resultType="com.ahang.pojo.User">select * from Mybatis.user</select></mapper>
2.1.6 配置mybatis的核心配置文件来连接数据库
mappers配置绑定不要忘记
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><!--configuration核心配置文件--><configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/MyBatis"/><property name="username" value="root"/><property name="password" value="1598"/></dataSource></environment></environments><!--配置绑定不要忘记--><mappers><mapper resource="com/ahang/Dao/UserDaoImpl.xml"></mapper></mappers></configuration>
2.1.7 配置数据库工具类MybatisUtils
配置基本固定,只有mybatis.xml需要修改
public class MybatisUtils {private static SqlSessionFactory sqlSessionFactory;static {try {String resource = "mybatis.xml";InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);}catch (Exception e){e.printStackTrace();}}public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}}
可以通过设置事务自动提交,否则对于插入修改删除语句需要手动的使用commit()来完成
public static SqlSession getSqlSession(){return sqlSessionFactory.openSession(true);}
2.1.8 测试类
SqlSession sqlSession = MybatisUtils.getSqlSession();UserDao userDao = sqlSession.getMapper(UserDao.class);List<User> userList = userDao.getUserList();for(User e: userList){ System.out.println(e);}sqlSession.close();
2.2 错误排查
- 错误code1:
Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 7; columnNumber: 9; 1 字节的 UTF-8 序列的字节 1 无效

解决:删除中文注解或者注解中中文乱码错误详解 - 错误CODE2:
org.apache.ibatis.binding.BindingException: Type interface com.ahang.Dao.UserDao is not known to the MapperRegistry
解决:在mybatis-config.xml配置文件中缺少<mappers> <mapper resource="com/ahang/Dao/UserDaoImpl.xml"></mapper> </mappers>注意路径通过\连接
- 错误CODE3 数据库连接错误:
Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown database 'mybati'
解决:查看数据库书写是否正确 - 错误CODE4:
The error may exist in com/ahang/Dao/UserDaoImpl.xml; Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/ahang/Dao/UserDaoImpl.xml缺少资源包,在定义maven的pom.xml中默认没有添加资源包下的配置文件,需要手动添加
<build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources></build>
添加在pom.xml中<project>中
3. 增删改查
在数据库中创建一张表user如下
+----+-------+| id | name |+----+-------+| 10 | ahang || 20 | kaka |+----+-------+
定义一个User类,和数据库中user表对应
package com.ahang.pojo;public class User {private int id;private String name;public User() {}public User(int id, String name){this.id = id;this.name = name;}...setXXX...getXXX...}
增加接口UserDao.java
public interface UserDao {List<User> getUserList();User getUserById(int id);int addUser(User user);int updateUser(User user);int deleteUser(int id);}
在UserDaoImpl.xml中配置对应的增删改查语句
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.ahang.Dao.UserDao"><select id="getUserList" resultType="com.ahang.pojo.User">select * from Mybatis.user</select><select id="getUserById" parameterType="int" resultType="com.ahang.pojo.User">select * from mybatis.user where id = #{id}</select><insert id="addUser" parameterType="com.ahang.pojo.User">insert into mybatis.user values( #{id}, #{name} );</insert><update id="updateUser" parameterType="com.ahang.pojo.User">update mybatis.user set name=#{name} where id=#{id};</update><delete id="deleteUser" parameterType="int">delete from mybatis.user where id=#{id};</delete></mapper>
最后测试结果在测试类中
public class test {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserDao userDao = sqlSession.getMapper(UserDao.class);// 查询全部条目放在列表中List<User> userList = userDao.getUserList();for(User e: userList){System.out.println(e);}// 条件查询通过idUser user = useDao.getUserById(20);System.out.println(user);// 增加一条用户信息,最后需要commit()才能生效int a = userDao.addUser(new User(40, "haha"));if(a > 0) System.out.println("insert sucessed");sqlSession.commit();// 更新用户信息int u = userDao.updateUser(new User(20, "kaka"));if(u > 0) System.out.println("update sucessed");sqlSession.commit();// 删除一条信息int d = mapper.deleteUser(30);if(d > 0) System.out.println("delete sucessed");sqlSession.commit();// 最后一定要关闭释放资源sqlSession.close();}}
4. mybatis.xml配置解析
4.1 标签顺序
标签的顺序—从上到下 :
properties
settings
typeAliases
typeHandlers
objectFactory
objectWrapperFactory
reflectorFactory
plugins
environments
databaseIdProvider
mappers
4.2 环境配置和属性配置
通过default设置当前使用的配置环境,可以配置多个环境,但是只能同时使用一个
可以引入外部的配置db.properties,当外部和内部配置的属性冲突时,优先使用外部配置的属性,如内部有username属性而外部没有则直接加入,内部和外部都有password属性时优先使用外部的
定义一个db.properties
driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatispassword=6666
在mybatis.xml中配置外部属性源和多个配置环境
<properties resource="db.properties"><property name="username" value="root"></property><property name="password" value="1111"></property></properties><!--通过default设置当前使用的配置环境,可以配置多个环境,但是只能同时使用一个--><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><environment id="test"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/MyBatis"/><property name="username" value="root"/><property name="password" value="1598"/></dataSource></environment></environments></environments>
4.3 类型别名(typeAliases)
方式一:
在mybatis.xml中配置类型别名
<typeAliases><typeAlias type="com.ahang.pojo.User" alias="Users"></typeAlias></typeAliases>
然后在UserDaoImpl.xml中使用该别名简化配置,避免多次使用较长的类名
<select id="getUserList" resultType="com.ahang.pojo.User">select * from Mybatis.user</select><select id="getUserList" resultType="Users">select * from Mybatis.user</select>
方式二:直接在定义类的时候添加别名注解
设置别名@Alias前添加包,此时会产生默认别名为小写的类名user,可以通过@Alias指定
<!--可以给实体类起别名--><typeAliases><package name="com.kuang.pojo"/></typeAliases>
@Alias("users")public class User {private int id;private String name;}
4.4 映射绑定
每一个Mapper.xml都需要在mybatis.xml配置中绑定注册
方式一:手动指定位置
<mappers><mapper resource="com/ahang/Dao/UserDaoImpl.xml"></mapper></mappers>
方式二:通过指定UserDao类自动寻找对应的UserDao.xml,需要配置文件和类同名
<mappers><mapper class="com.ahang.Dao.UserDao"></mapper></mappers>
方式三:通过指定一个包,自动根据类名寻找对应的配置文件,需要配置文件和类同名
<mappers><package name="com.ahang.Dao"></package></mappers>
5. 数据库属性名和类定义的属性名不同
数据库中使用了name,而对应结果类中属性定义了uname,两者不一致
mysql> select * from user;+----+-------+| id | name |+----+-------+| 10 | ahang || 20 | kaka |+----+-------+
@Alias("users") // 设置别名,供后面mybatis.xml类名的使用public class User {private int id;private String uname;}
默认查出来的结果uname为空:
User(id=10, uname=null)User(id=20, uname=null)
在userDaoImpl.xml中配置
<select id="getUserList" resultType="users">select * from mybatis.user;</select>
5.1 通过查询时设置别名
设置userDaoImpl.xml中查询语句,将原name返回结果设置为uname
<select id="getUserList" resultType="users">select id,name as uname from mybatis.user;</select>
5.2 设置resultMap
设置resultMap来绑定属性名,property中属性为User类中, column中属性为数据库中值。
注意点:以下配置会报错:org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserMap'. Cause: java.lang.ClassNotFoundException: Cannot find class: UserMap at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement
<resultMap id="UserMap" type="users"><result property="id" column="id"></result><result property="uname" column="name"></result></resultMap><select id="getUserList" resultType="UserMap">select * from mybatis.user;</select>
- 首先需要添加
resultMap绑定对应关系 - 然后需要修改select标签中的
resultType="users"—>resultMap="UserMap" - 正确配置如下:
<resultMap id="UserMap" type="users"><!--相同属性名不需要修改,可以不写 <result property="id" column="id"></result>--><result property="uname" column="name"></result></resultMap><select id="getUserList" resultMap="UserMap">select * from mybatis.user;</select>
6. 日志工厂
- 在
mybatis.xml中配置STDOUT_LOGGING
<settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings>
Log4j- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
先导包,在maven配置文件中
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>
在
mybatis.xml中添加配置<settings><setting name="logImpl" value="LOG4J"/></settings>
在
resourses资源包下添加log4j.properties配置
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码log4j.rootLogger=DEBUG,console,file#控制台输出的相关设置log4j.appender.console = org.apache.log4j.ConsoleAppenderlog4j.appender.console.Target = System.outlog4j.appender.console.Threshold=DEBUGlog4j.appender.console.layout = org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=[%c]-%m%n#文件输出的相关设置log4j.appender.file = org.apache.log4j.RollingFileAppenderlog4j.appender.file.File=./log/ahng.loglog4j.appender.file.MaxFileSize=10mblog4j.appender.file.Threshold=DEBUGlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#日志输出级别log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sql.PreparedStatement=DEBUG
7. 分页
主要使用select的limit语句select * from user limit 2, 3
SELECT * from user limit startIndex,pageSize;
- 先在接口中添加
public interface UserDao {List<User> getUserByLimit(Map<String,Integer> map);}
- 然后在
UserDao.xml接口对应配置文件中添加查询语句
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">select * from mybatis.user limit #{startIndex},#{pageSize}</select>
- 最后测试,在里面添加对应的
map
@Testpublic void getUserByLimit(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserDao mapper = sqlSession.getMapper(UserDao.class);HashMap<String, Integer> map = new HashMap<String, Integer>();map.put("startIndex", 1);map.put("pageSize", 2);List<User> userList = mapper.getUserByLimit(map);for (User user : userList) {System.out.println(user);}sqlSession.close();}
或者通过MyBatis的分页插件PageHelper来实现
8. 注解开发
8.1 面向接口编程
- 大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
- 根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好
- 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
- 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
关于接口的理解
- 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
- 接口的本身反映了系统设计人员对系统的抽象理解。
- 接口应有两类:
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
- 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
三个面向区别
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构
MyBatis详细执行流程

8.2 使用注解开发与参数详解
@Select("select id, name as uname from user where id=#{id} and name=#{uname}")对于该语句来说,id和name对应都是数据库里面的属性名,而uname是User类中属性名,一般两者相同
首先需要在mybatis.xml中绑定接口
<!--绑定接口--><mappers><mapper class="com.ahang.Dao.UserDao"></mapper></mappers>
注解只能对一些简单的语句使用,对于一些复杂的语句无法使用,如下第二条语句。
public interface UserDao {@Select("select id, name as uname from user")List<User> getUserList();List<User> getUserByLimit(Map<String,Integer> map);// 对于单个参数时,可以指定@Param("参数别名"),也可以不写@Select("select id, name as uname from user where id=#{uid}")User getUserById(@Param("uid") int id);// 对于多个参数时,必须使用@Param("参数别名")来指定,否则报错@Select("select id, name as uname from user where id=#{id} and name=#{uname}")User getUserByUser(@Param("id") int id,@Param("uname") String uname);@Insert("insert into user values ( #{id}, #{uname} )")int addUser(User user);@Update("update user set name=#{uname} where id=#{id}")int updateUser(User user);@Delete("delete from user where id=#{id}")int deleteUser(int id);}
- 一般都使用
#{}来设置参数,MyBatis会创建PreparedStatement参数占位符,并通过占位符安全地设置参数(就像使用?一样) ${}设置时,MyBatis 就不会修改或转义该字符串了,会直接替换存在安全性问题- 对于单个参数时,可以指定
@Param("参数别名"),也可以不写 - 对于多个参数时,必须使用
@Param("参数别名")来指定,否则报错 - 对于引用类型,不需要添加如:
User user类型
9. Lombok
使用步骤:
- 在IDEA中安装
Lombok插件! - 在
maven项目pom.xml中导入lombok的jar包
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version></dependency>
- 在实体类上加注解即可!
@Data:无参构造,get、set、tostring、hashcode,equals@AllArgsConstructor:有参构造@NoArgsConstructor:无参构造...
10. 多对一
在数据库中添加student和teacher两个表
CREATE TABLE `teacher` (`id` INT(10) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO teacher(`id`, `name`) VALUES (1, 'ahang');CREATE TABLE `student` (`id` INT(10) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,`tid` INT(10) DEFAULT NULL,PRIMARY KEY (`id`),KEY `fktid` (`tid`),CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)) ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', 'aa', '1');INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', 'bb', '1');INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', 'cc', '1');INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', 'dd', '1');INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', 'ee', '1');
其中多个学生对应一个老师
mysql> select * from teacher;+----+-------+| id | name |+----+-------+| 1 | ahang |+----+-------+1 row in set (0.00 sec)mysql> select * from student;+----+----------+------+| id | name | tid |+----+----------+------+| 1 | haha | 1 || 2 | hahaha | 1 || 3 | hahahaha | 1 || 4 | gaga | 1 || 5 | gagaga | 1 |+----+----------+------+
需要查询获取每个学生对应的老师名称
首先在com.ahang.pojo包下建立学生Student和老师Teacher类:
public class Student {private int id;private String name;private Teacher teacher; // Teacher类...}public class Teacher {private int id;private String name;...}
然后设置StuDao接口来获取Student的列表
public interface StuDao {public List<Student> getStudent();}
我们在resourses的资源包下添加文件夹com.ahang.Dao和java包下的com.ahang.Dao对应,然后在资源包下添加配置文件名为接口的类名StuDao.xml,后面绑定mybatis.xml配置文件时,会自动根据同一个包名自动匹配到配置文件
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.ahang.Dao.StuDao"><select id="getStudent" resultMap="StudentTeacher">select * from student;</select><resultMap id="StudentTeacher" type="com.ahang.pojo.Student"><result property="id" column="id"></result><result property="name" column="name"></result><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"></association></resultMap><select id="getTeacher" resultType="Teacher">select * from Teacher where id=#{id};</select></mapper>
在mybatis.xml中添加绑定配置文件
<mappers><mapper class="com.ahang.Dao.StuDao"></mapper></mappers>
最后测试结果
@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();StuDao mapper = sqlSession.getMapper(StuDao.class);List<Student> student = mapper.getStudent();for (Student student1 : student) {System.out.println(student1);}}// Student(id=1, name=haha, teacher=Teacher(id=1, name=ahang))// Student(id=2, name=hahaha, teacher=Teacher(id=1, name=ahang))// Student(id=3, name=hahahaha, teacher=Teacher(id=1, name=ahang))// Student(id=4, name=gaga, teacher=Teacher(id=1, name=ahang))// Student(id=5, name=gagaga, teacher=Teacher(id=1, name=ahang))
<select id="getStudent" resultMap="StudentTeacher">select s.id sid ,s.name sname, s.tid tid, t.name tname from student s,teacher t where s.tid=t.id;</select><resultMap id="StudentTeacher" type="students"><result property="id" column="sid"></result><result property="name" column="sname"></result><!--对查出来的结果再做映射,获取想要的结果,通过association或collection--><association property="teacher" javaType="Teacher"><result property="id" column="tid"></result><result property="name" column="tname"></result></association></resultMap>
11. 一对多
12. 动态SQL
13. 缓存
13.1 什么是缓存 [ Cache ]?
存在内存中的临时数据。
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
- 为什么使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统效率。
- 什么样的数据能使用缓存?
经常查询并且不经常改变的数据。【可以使用缓存】
13.2 Mybatis缓存
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

13.3 一级缓存
一级缓存也叫本地缓存: SqlSession
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
测试步骤:
- 开启日志!
- 测试在一个
SqlSession中查询两次相同记录 - 查看日志输出
@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();StuDao mapper = sqlSession.getMapper(StuDao.class);List<Student> student = mapper.getStudent();for (Student student1 : student) {System.out.println(student1);}// sqlSession.clearCache(); 手动清理缓存List<Student> student1 = mapper.getStudent();for(Student e: student1){System.out.println(e);}System.out.println(student == student1); // 显示true,表示存在本地缓存,没有另外从数据库中查询sqlSession.close();}
如果通过sqlSession.clearCache() 手动清理了缓存,那么第二次查询首先二级没有开启,然后一级缓存被清理了,那么就会通过数据库查询。两次查询获取结果的对象地址不同,则为false
13.4 二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
- 首先默认
mybatis.xml中开启了缓存的,无需设置,也可以显现出来:
<!--显示的开启全局缓存--><setting name="cacheEnabled" value="true"/>
- 然后通过在接口对应的配置中
StuDao.xml中添加
<cache readOnly="true"/>
这样就会对缓存只读,如果不添加readOnly=”true”的话,那么就会报错,需要在Student和Teacher类中添加实现接口public class Student implement Serializable来序列化对象
- 最后测试
@Testpublic void test(){// 定义两个SqlSession连接SqlSession sqlSession = MybatisUtils.getSqlSession();SqlSession sqlSession2 = MybatisUtils.getSqlSession();StuDao mapper = sqlSession.getMapper(StuDao.class);StuDao mapper2 = sqlSession2.getMapper(StuDao.class);// 此时对数据库查询了一次,会添加到二级缓存中List<Student> student = mapper.getStudent();for (Student student1 : student) {System.out.println(student1);}// 关闭第一个连接sqlSession.close();System.out.println("========================");// 第二个连接去去读取相同的数据,此时先找二级缓存,发现有直接取出List<Student> students2 = mapper2.getStudent();for(Student e: students2){System.out.println(e);}System.out.println("---------------------");// 开启二级缓存后,在同一个namespace下的用同一个缓存,则两个对象地址相同trueSystem.out.println(student == students2);sqlSession2.close();}
