
本文共 23364 字,大约阅读时间需要 77 分钟。
Mybatis进阶
1.接口代理方式实现Dao层
1.1 接口代理方式-实现规则
传统方式实现Dao层,我们既要写接口,还要写实现类。而MyBatis框架可以帮助我们省略编写Dao层皆苦实现类的步骤。程序员只需要编写接口,有MyBatis框架根据接口的定义来创建该接口的动态代理对象
实现规则:
- 映射配置文件中的名称空间必须和Dao层接口的全类名相同
- 映射配置文件中的增删改查标签的id属性必须和Dao层接口的方法名相同
- 映射配置文件中的增删改查标签的parameterType属性必须和Dao层接口方法的参数相同
- 映射配置文件中的增删改查标签的resultType属性必须和Dao层接口方法的返回值相同
1.2 接口代理方式-代码实现
- 删除mapper(dao)层接口的实现类
- 修改映射配置文件
- 修改service层接口的实现类,采用接口代理方式实现功能
修改映射配置文件:名称空间改为全类名
修改service层接口实现类:
/* 业务层实现类 */public class StudentServiceImpl implements StudentService { @Override public ListselectAll() { List list = null; SqlSession sqlSession = null; InputStream is = null; try { //1.加载核心配置文件 is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.获取SqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通过工厂对象获取SqlSession对象 sqlSession = sqlSessionFactory.openSession(true); //4.获取StudentMapper接口的实现类对象 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);//StudentMapper mapper = new StudentMapperImpl(); //5.通过实现类对象调用方法,接收结果 list = mapper.selectAll(); } catch (IOException e) { e.printStackTrace(); }finally { //6.释放资源 if (sqlSession != null) { sqlSession.close(); } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } //7.返回结果 return list; } @Override public Student selectById(Integer id) { Student student = null; SqlSession sqlSession = null; InputStream is = null; try { //1.加载核心配置文件 is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.获取SqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通过工厂对象获取SqlSession对象 sqlSession = sqlSessionFactory.openSession(true); //4.获取StudentMapper接口的实现类对象 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);//StudentMapper mapper = new StudentMapperImpl(); //5.通过实现类对象调用方法,接收结果 student = mapper.selectById(id); } catch (IOException e) { e.printStackTrace(); }finally { //6.释放资源 if (sqlSession != null) { sqlSession.close(); } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } //7.返回结果 return student; } @Override public Integer insert(Student student) { Integer result = 0; SqlSession sqlSession = null; InputStream is = null; try { //1.加载核心配置文件 is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.获取SqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通过工厂对象获取SqlSession对象 sqlSession = sqlSessionFactory.openSession(true); //4.获取StudentMapper接口的实现类对象 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);//StudentMapper mapper = new StudentMapperImpl(); //5.通过实现类对象调用方法,接收结果 result = mapper.insert(student); } catch (IOException e) { e.printStackTrace(); }finally { //6.释放资源 if (sqlSession != null) { sqlSession.close(); } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } //7.返回结果 return result; } @Override public Integer update(Student student) { Integer result = null; SqlSession sqlSession = null; InputStream is = null; try { //1.加载核心配置文件 is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.获取SqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通过工厂对象获取SqlSession对象 sqlSession = sqlSessionFactory.openSession(true); //4.获取StudentMapper接口的实现类对象 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);//StudentMapper mapper = new StudentMapperImpl(); //5.通过实现类对象调用方法,接收结果 result = mapper.update(student); } catch (IOException e) { e.printStackTrace(); }finally { //6.释放资源 if (sqlSession != null) { sqlSession.close(); } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } //7.返回结果 return result; } @Override public Integer delete(Integer id) { Integer result = null; SqlSession sqlSession = null; InputStream is = null; try { //1.加载核心配置文件 is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.获取SqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通过工厂对象获取SqlSession对象 sqlSession = sqlSessionFactory.openSession(true); //4.获取StudentMapper接口的实现类对象 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);//StudentMapper mapper = new StudentMapperImpl(); //5.通过实现类对象调用方法,接收结果 result = mapper.delete(id); } catch (IOException e) { e.printStackTrace(); }finally { //6.释放资源 if (sqlSession != null) { sqlSession.close(); } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } //7.返回结果 return result; }}
1.3 接口代理方式实-源码分析
分析动态代理对象如何生成的?
通过动态代理开发模式,我们只编写一个接口,不写实现类,我们通过getMapper()方法最终获取到org.apache.ibatis.binding.MapperProxy代理对象,然后执行功能,而这个代理对象正是MyBatis使用了JDK的动态代理技术,帮助我们生成了代理实现类对象,从而可以进行相关持久化操作。
分析方法是如何执行的?
动态代理实现类对象在执行方法的时候最终调用了mapperMethod.execute()方法,这个方法通过switch语句根据操作类型来判断是新增、修改、删除、查询操作,最后一步回到MyBatis最原生的SqlSession方式来执行增删改查
2.动态sql
2.1 动态sql介绍
MyBatis映射配置文件中,前面我们的sql都是比较简单的,有些时候业务逻辑复杂时,我们的sql就是动态变化的,此时在前面学习的sql就不能满足要求了
多条件查询
id:3 name:王五 age:25
select * from student where id= ?and name = ?and age = ?
id:3 name:王五
select * from student where id= ?and name = ?
动态sql标签
- <if>:条件判断标签
- <foreach>:循环遍历标签
2.2 <if>标签
<where>:条件标签。如果有动态条件,则使用该标签代替where关键字
<if>:条件判断标签
查询条件拼接
映射配置文件中标签设置:
测试类中方法:
@Test public void selectCondition() throws IOException { InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);// Student student = new Student(2,"李四",23); Student student = new Student(2,"李四",null); Listlist = mapper.selectCondition(student); System.out.println(list); sqlSession.close(); is.close(); }
-
当传递参数student = new Student(2,“李四”,null)时,测试结果打印信息:
DEBUG [main] -==> Preparing: select * from student WHERE id = ? and name = ?
DEBUG [main] -==> Parameters: 2(Integer), 李四(String) DEBUG [main] -<== Total: 1 [Student{id=2, name=‘李四’, age=23}] -
当传递参数student = new Student(2,“李四”,23)时,测试结果打印信息:
DEBUG [main] -==> Preparing: select * from student WHERE id = ? and name = ? and age = ?
DEBUG [main] -==> Parameters: 2(Integer), 李四(String), 23(Integer) DEBUG [main] -<== Total: 1 [Student{id=2, name=‘李四’, age=23}]
2.3 <foreach>标签
<foreach>:循环遍历标签。适用于多个参数或者的关系
获取参数
属性:
- collection:参数容器类型,list集合,array数组
- open:开始的sql语句
- close:结束的sql语句
- item:参数变量名
- separator:分隔符
映射配置文件中标签设置:
测试类中方法:
@Testpublic void selectByIds() throws IOException { InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); Listids = new ArrayList (); ids.add(1); ids.add(2); //ids.add(3); List list = mapper.selectByIds(ids); for (Student student : list) { System.out.println(student); } sqlSession.close(); is.close();}
运行测试方法结果:
-
当list集合中添加1,2时
DEBUG [main] -==> Preparing: select * from student WHERE id in ( ? , ? )
DEBUG [main] -==> Parameters: 1(Integer), 2(Integer) DEBUG [main] -<== Total: 2 Student{id=1, name=‘张三’, age=20} Student{id=2, name=‘李四’, age=23} -
当再向集合添加一个3时
DEBUG [main] -==> Preparing: select * from student WHERE id in ( ? , ? , ? )
DEBUG [main] -==> Parameters: 1(Integer), 2(Integer), 3(Integer) DEBUG [main] -<== Total: 3 Student{id=1, name=‘张三’, age=20} Student{id=2, name=‘李四’, age=23} Student{id=3, name=‘王五’, age=18}
2.4 sql片段抽取
我们可以将一些重复性的sql语句进行抽取,以达到复用的效果
<sql>:抽取sql语句标签
- <sql id=“片段唯一标识”>抽取的sql语句</sql>
<include>:导入sql片段标签
- <include refid = “片段唯一标识” />
sql片段抽取后映射配置文件:
select * from student
3.分页插件
3.1 分页插件介绍
分页的特点:
-
分页可以将很多条结果进行分页显示
-
如果当前在第一页,则没有上一页。如果当前在最后一页,则没有下一页
-
需要明确当前是第几页,这一页中显示多少条结果
分页的实现:
在企业级开发中,分页也是一种常见的技术。而目前使用的MyBatis是不带分页功能的,如果想实现分页功能,需要我们手动编写limit语句。但是,不同数据库实现分页的sql语句也不相同,所以手动编写成本较高。这个时候就可以借助分页插件来帮助我们实现分页功能。
PageHelper:第三方分页助手,将复杂的分页操作进行封装,从而让分页功能变得非常简单
3.2 分页插件实现步骤
-
导入jar包
下载地址:
-
在核心配置文件中集成分页助手插件
-
在测试类中使用分页助手相关API实现分页功能
PageHelper.startPage(3,3);//当前页,页面数据量
测试类:
@Test public void selectPaging() throws IOException { InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); //通过分页助手来实现分页功能 //第一页:显示3条数据// PageHelper.startPage(1,3); //第二页:显示3条数据// PageHelper.startPage(2,3); //第三页:显示3条数据 PageHelper.startPage(3,3); Listlist = mapper.selectAll(); for (Student student : list) { System.out.println(student); } sqlSession.close(); is.close(); }
3.3 分页插件相关参数
PageInfo:封装分页相关参数的功能类
核心方法:
返回值 | 方法名 | 说明 |
---|---|---|
long | getTotal() | 获取总条数 |
int | getPages() | 获取总页数 |
int | getPageNum() | 获取当前页 |
int | getPageSize() | 获取每页显示条数 |
int | getPrePage() | 获取上一页 |
int | getNextPage() | 获取下一页 |
boolean | isIsFirstPage() | 获取是否是第一页 |
boolean | isIsLastPage() | 获取是否是最后一页 |
@Test public void selectPaging() throws IOException { InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); //通过分页助手来实现分页功能 //第一页:显示3条数据// PageHelper.startPage(1,3); //第二页:显示3条数据// PageHelper.startPage(2,3); //第三页:显示3条数据 PageHelper.startPage(3,3); Listlist = mapper.selectAll(); //PageInfo:分页相关参数和功能类 PageInfo info = new PageInfo (list); System.out.println("总条数:"+info.getTotal()); System.out.println("总页数:"+info.getPages()); System.out.println("当前页:"+info.getPageNum()); System.out.println("每页条数:"+info.getPageSize()); System.out.println("上一页:"+info.getPrePage()); System.out.println("下一页:"+info.getNextPage()); System.out.println("是否为第一页:"+info.isIsFirstPage()); System.out.println("是否为最后一页:"+info.isIsLastPage()); for (Student student : list) { System.out.println(student); } sqlSession.close(); is.close(); }
4.多表操作
4.1 多表模型
我们之前学习的都是基于单表操作的,而实际开发中,随着业务难度加深,肯定需要多表操作的
多表模型分类:
- 一对一:在任意一方建立外键,关联对方的主键
- 一对多:在多的一方建立外键,关联一的一方的主键
- 多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键
4.2 一对一
一对一模型:人和身份证,一个人只有一个身份证
环境准备
创建数据库、表:
CREATE DATABASE db2USE db2CREATE TABLE person( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), age INT)ENGINE=INNODB DEFAULT CHARSET=utf8;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, CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id))ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO card VALUES(NULL,'123456',1);INSERT INTO card VALUES(NULL,'223456',2);INSERT INTO card VALUES(NULL,'323456',3);
实体类:Person
package cn.itcast.bean;public class Person { private Integer id; //主键id private String name;//人的姓名 private Integer age;//人的年龄 public Person() { } public Person(Integer id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Person{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; }}
实体类:Card
package cn.itcast.bean;public class Card { private Integer id; //主键id private String number;//身份证号 private Person p; //所属人的对象 public Card() { } public Card(Integer id, String number, Person p) { this.id = id; this.number = number; this.p = p; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public Person getP() { return p; } public void setP(Person p) { this.p = p; } @Override public String toString() { return "Card{" + "id=" + id + ", number='" + number + '\'' + ", p=" + p + '}'; }}
核心配置文件:
映射配置文件:
- <resultMap>:配置字段和对象属性的映射关系标签
- id属性:唯一标识
- type属性:实体对象类型
- <id>:配置主键映射关系标签
- <result>:配置非主键映射关系标签
- column属性:表中字段名称
- property属性:实体对象变量名称
- <association>:配置被包含对象的映射关系标签
- property属性:被包含对象的变量名
- javaType属性:被包含对象的数据类型
测试类:
@Testpublic void selectAll() throws IOException { //1.加载核心配置文件 InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.获取SqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通过SqlSession工厂对象获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //4.获取OneToOneMapper接口的实现类对象 OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class); //5.调用实现类方法,接收结果 Listlist = mapper.selectAll(); //6.处理结果 for (Card card : list) { System.out.println(card); } //7.释放资源 sqlSession.close(); is.close();}
测试方法运行结果:
Card{id=1, number=‘123456’, p=Person{id=1, name=‘张三’, age=23}}
Card{id=2, number=‘223456’, p=Person{id=2, name=‘李四’, age=24}} Card{id=3, number=‘323456’, p=Person{id=3, name=‘王五’, age=25}}4.3 一对多
一对多模型:班级和学生,一个班级可以有多个学生
环境准备
创建表并添加数据:
CREATE TABLE classes( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20))ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO classes VALUES(NULL,'一班'),(NULL,'二班');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))ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO student VALUES(NULL,'张三',23,1),(NULL,'李四',24,1),(NULL,'王五',25,2),(NULL,'赵六',26,2);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;
实体类:Classes
package cn.itcast.bean;import java.util.List;public class Classes { private Integer id; //主键id private String name; //班级名称 private Liststudents; //班级中所有学生对象 public Classes() { } public Classes(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getStudents() { return students; } public void setStudents(List students) { this.students = students; } @Override public String toString() { return "Classes{" + "id=" + id + ", name='" + name + '\'' + ", students=" + students + '}'; }}
实体类:Student
package cn.itcast.bean;public class Student { private Integer id; //主键id private String name; //学生姓名 private Integer age; //学生年龄 public Student() { } public Student(Integer id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; }}
核心配置文件:
映射配置文件:
- <resultMap>:配置字段和对象属性的映射关系标签
- id属性:唯一标识
- type属性:实体对象类型
- <id>:配置主键映射关系标签
- <result>:配置非主键映射关系标签
- column属性:表中字段名称
- property属性:实体对象变量名称
- <collection>:配置被包含集合对象的映射关系标签
- property属性:被包含对象的变量名
- ofType属性:集合中保存的对象的数据类型
测试类:
@Testpublic void selectAll() throws IOException { //1.加载核心配置文件 InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.获取SqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通过SqlSession工厂对象获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //4.获取OneToManyMapper接口的实现类对象 OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class); //5.调用实现类方法,接收结果 Listlist = mapper.selectAll(); //6.处理结果 for (Classes c : list) { System.out.println(c.getId()+", "+c.getName()); List students = c.getStudents(); for (Student student : students) { System.out.println("\t"+student); } } //7.释放资源 sqlSession.close(); is.close();}
测试方法运行结果:
1, 一班
Student{id=1, name=‘张三’, age=23} Student{id=2, name=‘李四’, age=24} 2, 二班 Student{id=3, name=‘王五’, age=25} Student{id=4, name=‘赵六’, age=26}4.4 多对多
多对多模型:学生和课程,一个学生可以选择多门课程,一个课程也可以被多个学生所选择
环境准备
创建表并添加数据:
CREATE TABLE course( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20))ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO course VALUES(NULL,'语文'),(NULL,'数学');CREATE TABLE stu_cr( id INT PRIMARY KEY AUTO_INCREMENT, sid INT, cid INT, CONSTRAINT sc_fk1 FOREIGN KEY(sid) REFERENCES student(id), CONSTRAINT sc_fk2 FOREIGN KEY(cid) REFERENCES course(id))ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO stu_cr VALUES(NULL,1,1),(NULL,1,2),(NULL,2,1),(NULL,2,2);SELECT s.id sid, s.name sname, s.age sage, c.id cid, c.name cname FROM student s,course c,stu_cr WHERE s.id = stu_cr.sid AND c.id = stu_cr.cid;
实体类:Student(该类在上面Student类基础上添加一个属性:private List<Course> courses; //学生所选课程的集合)
实体类:Course
package cn.itcast.bean;public class Course { private Integer id; //主键id private String name;//课程名称 public Course() { } public Course(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Course{" + "id=" + id + ", name='" + name + '\'' + '}'; }}
核心配置文件:
映射配置文件:
- <resultMap>:配置字段和对象属性的映射关系标签
- id属性:唯一标识
- type属性:实体对象类型
- <id>:配置主键映射关系标签
- <result>:配置非主键映射关系标签
- column属性:表中字段名称
- property属性:实体对象变量名称
- <collection>:配置被包含集合对象的映射关系标签
- property属性:被包含对象的变量名
- ofType属性:集合中保存的对象的数据类型
测试类:
@Testpublic void selectAll() throws IOException { //1.加载核心配置文件 InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.获取SqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通过SqlSession工厂对象获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //4.获取ManyToManyMapper接口的实现类对象 ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class); //5.调用实现类方法,接收结果 Listlist = mapper.selectAll(); //6.处理结果 for (Student stu : list) { System.out.println(stu.getId()+", "+stu.getName()); List courses = stu.getCourses(); for (Course course : courses) { System.out.println("\t"+course); } } //7.释放资源 sqlSession.close(); is.close();}
测试方法运行结果:
1, 张三
Course{id=1, name=‘语文’} Course{id=2, name=‘数学’} 2, 李四 Course{id=1, name=‘语文’} Course{id=2, name=‘数学’}相关文章:、
发表评论
最新留言
关于作者
