System.out.println(student);
}
}
public void selectPaging() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream(“MyBatisConfig.xml”);
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(“true”);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//5.调用实现类的方法,接受结果
List list = mapper.selectAll();
//其他分页的数据
PageInfo pageInfo = new PageInfo<>(list);
System.out.println(“总条数:”+pageInfo.getTotal());
System.out.println(“总页数:”+pageInfo.getPages());
System.out.println(“当前页:”+pageInfo.getPageNum());
System.out.println(“每页显示条数:”+pageInfo.getPageSize());
System.out.println(“上一页:”+pageInfo.getPrePage());
System.out.println(“下一页:”+pageInfo.getNextPage());
System.out.println(“是否第一页:”+pageInfo.isIsFirstPage());
System.out.println(“是否最后一页:”+pageInfo.isIsLastPage());
//6.处理结果
for(Student student:list){
System.out.println(student);
}
}
分页:可以将很多条结果进行分页显示。
-
分页插件 jar 包: pagehelper-5.1.10.jar jsqlparser-3.1.jar
-
<plugins>
:集成插件标签。 -
分页助手相关 API
1.PageHelper:分页助手功能类。
-
startPage():设置分页参数
-
PageInfo:分页相关参数功能类。
-
getTotal():获取总条数
-
getPages():获取总页数
-
getPageNum():获取当前页
-
getPageSize():获取每页显示条数
-
getPrePage():获取上一页
-
getNextPage():获取下一页
-
isIsFirstPage():获取是否是第一页
-
isIsLastPage():获取是否是最后一页
===============================================================================
我们之前学习的都是基于单表操作的,而实际开发中,随着业务难度的加深,肯定需要多表操作的。
-
多表模型分类
-
一对一:在任意一方建立外键,关联对方的主键。
-
一对多:在多的一方建立外键,关联一的一方的主键。
-
多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键。
-
一对一模型: 人和身份证,一个人只有一个身份证
-
代码实现
- 步骤一: sql语句准备
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT
);
INSERT INTO person VALUES (NULL,‘张三’,23);
INSERT INTO person VALUES (NULL,‘李四’,24);
INSERT INTO person VALUES (NULL,‘王五’,25);
CREATE TABLE card(
id INT PRIMARY KEY AUTO_INCREMENT,
number VARCHAR(30),
pid INT,
– 银行卡表的pid指向person表中的主键id
CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id)
);
INSERT INTO card VALUES (NULL,‘12345’,1);
INSERT INTO card VALUES (NULL,‘23456’,2);
INSERT INTO card VALUES (NULL,‘34567’,3);
- 步骤二:实体类和接口
public class Person{
private Integer id; //主键id
private String name; //人的姓名
private Integer age; //人的年龄
//省略get/set,有参/无参方法
}
public class Card{
private Integer id; //主键id
private String number; //身份证号
private Person p; //所属人的对象
//省略get/set,有参/无参方法
}
public interface OneToOneMapper{
//查询全部
public abstract List selectAll();
}
- 步骤三:配置文件
SELECT c.id cid,number,pid,NAME,age FROM card c,person p WHERE c.pid=p.id
- 步骤四:在MyBatisConfig.xml中引入配置文件
- 步骤五:测试类
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream(“MyBatisConfig.xml”);
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取OneToOneMapper接口的实现类对象
OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);
//5.调用实现类的方法,接收结果
List list = mapper.selectAll();
//6.处理结果
for (Card c : list) {
System.out.println©;
}
//7.释放资源
sqlSession.close();
is.close();
}
2.2.1、ResultMap
-
当涉及多表操作时,用resultMap
-
当只是单表操作时,用resultType
- resultMap:用来自定义结果集和实体类的映射
-
id
属性:相当于这个resultMap的唯一标识 -
type
属性:用来指定映射到哪个实体类
- id 标签:用来指定主键列的映射规则
-
column
属性::数据库表的列名 -
property
属性:: 对应实体类的属性名
- result 标签:用来指定普通列的映射规则
-
column
属性::数据库表的列名 -
property
属性: : 对应实体类的属性名
- association标签:配置被包含对象的映射规则
-
property
属性:被包含对象的变量名 -
javaType
属性:被包含对象的数据类型
- collection标签:配置被包含集合对象的映射规则。
-
property
属性:被包含集合对象的变量名 -
ofType
属性:集合中保存的对象数据类型
结果集映射ResultMap
要解决的问题:属性名和字段名不一致
2.2.2、查询为null问题
- 例如数据库字段名
- Java中实体类设计
public class User {
private int id; //id
private String name; //姓名
private String password; //密码和数据库不一样!
//构造
//set/get
//toString()
}
- UserMapper接口
public interface UserMapper {
//根据id查询用户
User selectUserById(int id);
}
- mapper 映射文件
select * from user where id = #{id}
- 测试
@Test
public void testSelectUserById() {
SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
}
结果
-
User{id=1,name='狂神',password='null'}
-
password 字段为空,因为实体类和数据库字段不一致,一个是pwd,一个是password
分析
-
select * from mybatis.user where id = #{id}
可以看作 -
select id,name,pwd from mybatisuser where id = #{}
-
mybatis 会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写),去对应的实体类中查找相应列名的 set 方法设值,由于找不到 setPwd() ,所以password返回null
2.2.3、解决方案
方案一:为列名指定别名,别名和 java 实体类的属性名一致
select id , name , pwd as password from mybatis.user where id = #{id}
方案二:使用结果集映射 ➡ ResultMap (推荐)
select id , name , pwd from mybatis.user where id = #{id}
-
一对多模型: 一对多模型:班级和学生,一个班级可以有多个学生。
-
sql语句准备
– 班级表
CREATE TABLE classes(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO classes VALUES (NULL,‘黑马一班’);
INSERT INTO classes VALUES (NULL,‘黑马二班’);
– 学生表的cid指向班级表的主键id
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
cid INT,
CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
);
INSERT INTO student VALUES (NULL,‘张三’,23,1);
INSERT INTO student VALUES (NULL,‘李四’,24,1);
INSERT INTO student VALUES (NULL,‘王五’,25,2);
INSERT INTO student VALUES (NULL,‘赵六’,26,2);
- 实体类和接口
public class Student {
private Integer id; //主键id
private String name;//学生姓名
private Integer agel//学生年龄
//省略get/set,有参/无参方法
}
public class Classes {
private Integer id; //主键id
private String name;//班级名称
private List students; //班级中所有学生对象
}
public interface OneToManyMapper {
//查询全部
public abstract List selectAll();
}
- 配置文件
SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage FROM classes c,student s WHERE c.id=s.cid
- 在MyBatisConfig.xml中引入配置文件
- 测试类
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream(“MyBatisConfig.xml”);
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取OneToManyMapper接口的实现类对象
OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
//5.调用实现类的方法,接收结果
List classes = mapper.selectAll();
//6.处理结果
for (Classes cls : classes) {
System.out.println(cls.getId() + “,” + cls.getName());
List students = cls.getStudents();
for (Student student : students) {
System.out.println("\t" + student);
}
}
//7.释放资源
sqlSession.close();
is.close();
}
- resultMap:用来自定义结果集和实体类的映射
-
id
属性:相当于这个resultMap的唯一标识 -
type
属性:用来指定映射到哪个实体类
- id 标签:用来指定主键列的映射规则
-
column
属性::数据库表的列名 -
property
属性:: 对应实体类的属性名
- result 标签:用来指定普通列的映射规则
-
column
属性::数据库表的列名 -
property
属性: : 对应实体类的属性名
- association标签:配置被包含对象的映射规则
-
property
属性:被包含对象的变量名 -
javaType
属性:被包含对象的数据类型
- collection标签:配置被包含集合对象的映射规则。
-
property
属性:被包含集合对象的变量名 -
ofType
属性:集合中保存的对象数据类型
-
多对多模型:学生和课程,一个学生可以选择多门课程、一个课程也可以被多个学生所选择。
-
代码实现
- 步骤一:sql语句准备
CREATE TABLE course(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO course VALUES (NULL,‘语文’);
INSERT INTO course VALUES (NULL,‘数学’);
CREATE TABLE stu_cr(
id INT PRIMARY KEY AUTO_INCREMENT,
sid INT,
cid INT,
– sid指向学生表的主键id
– cid指向课程表的主键id
CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
);
INSERT INTO stu_cr VALUES (NULL,1,1);
INSERT INTO stu_cr VALUES (NULL,1,2);
INSERT INTO stu_cr VALUES (NULL,2,1);
INSERT INTO stu_cr VALUES (NULL,2,2);
- 步骤二:实体类和接口
public class Course {
private Integer id; //主键id
private String name; //课程名称
//省略get/set,有参/无参方法
}
//在学生Student实体类当中增添变量,表示当前学生选择了哪些课程
public class Student {
private Integer id; //主键id
private String name; //学生姓名
private Integer age; //学生年龄
private List courses;//学生所选择的课程集合
//省略get/set,有参/无参方法
}
public interface ManyToManyMapper {
//查询全部
public abstract List selectAll();
}
- 步骤三:配置文件
SELECT sc.sid,s.name sname,s.age sage,sc.cid,c.name cname FROM student s,course c,stu_cr sc WHERE sc.sid=s.id AND sc.cid=c.id
- 步骤四:在MyBatisConfig.xml中引入配置文件
- 步骤五:测试
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream(“MyBatisConfig.xml”);
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取ManyToManyMapper接口的实现类对象
ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class);
//5.调用实现类的方法,接收结果
List students = mapper.selectAll();
//6.处理结果
for (Student student : students) {
System.out.println(student.getId() + “,” + student.getName() + “,” + student.getAge());
List courses = student.getCourses();
for (Course cours : courses) {
System.out.println("\t" + cours);
}
}
//7.释放资源
sqlSession.close();
is.close();
}
-
当涉及多表操作时,用resultMap
-
当只是单表操作时,用resultType
- resultMap:用来自定义结果集和实体类的映射
-
id
属性:相当于这个resultMap的唯一标识 -
type
属性:用来指定映射到哪个实体类
- id 标签:用来指定主键列的映射规则
-
column
属性::数据库表的列名 -
property
属性:: 对应实体类的属性名
- result 标签:用来指定普通列的映射规则
-
column
属性::数据库表的列名 -
property
属性: : 对应实体类的属性名
- association标签:配置被包含对象的映射规则
-
property
属性:被包含对象的变量名 -
javaType
属性:被包含对象的数据类型
- collection标签:配置被包含集合对象的映射规则。
-
property
属性:被包含集合对象的变量名 -
ofType
属性:集合中保存的对象数据类型
=============================================================================
Mybatis的缓存其实就是把之前查到的数据存入内存(map),下次如果还是查相同的东西,就可以直接从缓存中取,从而提高效率。
Mybatis有一级缓存和二级缓存之分,一级缓存(默认开启)是sqlsession级别的缓存。二级缓存相当于mapper级别的缓存。
-
默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
-
二级缓存需要手动开启和配置,它是基于 namespace 级别的缓存
- 测试在一个Session中查询两次相同记录
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UerMapper.class);
User user1 = mapper.queryUserById(1);
User user2 = mapper.queryUserById(1);
System.out.println(user1==user2);
// true
sqlSession.close();
}
几种不会使用一级缓存的情况
-
调用相同方法但是传入的参数不同
-
调用相同方法参数也相同,但是使用的是另外一个SqlSession
-
如果查询完后,对同一个表进行了增,删改的操作,都会清空这sqlSession上的缓存
-
如果手动调用
SqlSession.clearCache
方法清除缓存了,后面也使用不了缓存
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UerMapper.class);
User user1 = mapper.queryUserById(1);
sqlSession.close();
// 不走缓存,一个SqlSession是一个连接
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UerMapper.class);
User user1 = mapper.queryUserById(1);
sqlSession.close();
}
注意:只在sqlsession调用了close或者commit后的数据才会进入二级缓存。
3.2.1、开启二级缓存
- 全局开启:在Mybatis核心配置文件中配置
- 局部开启:在要开启二级缓存的mapper映射文件中设置 cache标签
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UerMapper.class);
User user1 = mapper.queryUserById(1);
sqlSession.close();
// 走缓存,只要是同一个mapper
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UerMapper.class);
User user1 = mapper.queryUserById(1);
sqlSession.close();
}
3.2.2、实际开发
二级缓存在实际开发中基本不会使用。在一些笔试题会出现。
===================================================================================
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
注意:利用注解开发就不需要 mapper.xml 映射文件了
| 注解 | 说明 |
| — | — |
| @Select(“查询的SQL语句”) | 执行查询操作注解 |
| @Insert(“新增的SQL语句”) | 执行新增操作注解 |
| @Update(“修改的SQL语句”) | 执行修改操作注解 |
| @Delete(“删除的SQL语句”) | 执行删除操作注解 |
我们完成简单的student表的增删改查的操作
- 步骤一:在接口中添加注解
public interface StudentMapper {
//查询全部
@Select(“SELECT * FROM student”)
public abstract List selectAll();
//新增操作
@Insert(“INSERT INTO student VALUES (#{id},#{name},#{age})”)
public abstract Integer insert(Student stu);
//修改操作
@Update(“UPDATE student SET name=#{name},age=#{age} WHERE id=#{id}”)
public abstract Integer update(Student stu);
//删除操作
@Delete(“DELETE FROM student WHERE id=#{id}”)
public abstract Integer delete(Integer id);
}
- 步骤二:在核心配置文件中配置mapper接口所在的包名
修改MyBatis的核心配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可