MyBatis接口代理方式实现Dao层
区别
1、selectlist和getMapper区别
//4.执行映射配置文件中的sql语句,并接收结果
list = sqlSession.selectList("StudentMapper.selectAll");
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//5.通过实现类对象调用方法,接收结果
result = mapper.delete(id);
两者其实并无区别,只不过StudentMapper利用代理对象操作sqlSession.selectList();
2、namespace的作用
<mapper namespace="StudentMapper">
<mapper namespace="com.zhu.mapper.StudentMapper">
1.完全限定名(比如“com.mypackage.MyMapper.selectAllThings”)将被直接查找并且找到即用。
2.短名称(比如“selectAllThings”)如果全局唯一也可以作为一个单独的引用。如果不唯一,有两个或两个以上的相同名称(比如“com.foo.selectAllThings ”和“com.bar.selectAllThings”),那么使用时就会收到错误报告说短名称是不唯一的,这种情况下就必须使用完全限定名。
Mybatis中namespace用于绑定dao接口,dao接口的方法对应mapper中的sql语名
接口代理方式 - 实现规则
1)映射配置文件中的名称空间必须和Dao层接口的全类名相同
2)映射配置文件中的增删改查的标签id属性必须和Dao层接口层的方法名相同
3)映射配置文件中的增删改查标签的parameterType属性必须和Dao层接口方法的参数相同
4)映射配置文件中的增删改查标签的resultType属性必须和Dao层接口方法的返回值相同
接口代理方式 - 代码实现
1)删除mapper层接口的实现类
2)修改映射配置文件
3)修改service层接口的实现类,采用接口代理方式实现功能
在写StudentMapper.java时
注意
1、
resultType="student"
方法的返回值为List<Student>
2、
parameterType="student"
方法的参数为Student student
3、如何取出参数中的值 #{}
· MyBatisConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/>
<settings>
<setting name="logImpl" value="log4j"/>
</settings>
<!--默认名为类名首字母小写-->
<typeAliases>
<package name="com.zhu.bean"/>
</typeAliases>
<environments default="mysql">
<!--environment配置数据库环境 id 属性唯一标识 -->
<environment id="mysql">
<!-- transactionManager事务管理。type属性,采用JDBC默认的事务-->
<transactionManager type="JDBC"></transactionManager>
<!--dataSource数据源信息 type属性 连接池 MyBatis默认有三种数据源-->
<dataSource type="POOLED">
<!--property获取数据库连接的配置信息-->
<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>
<!-- mapper 引入指定的映射配置文件 resource属性指定映射配置文件的名称 -->
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
· StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
mapper: 核心根标签
namespace属性: 名称空间
-->
<mapper namespace="com.zhu.mapper.StudentMapper">
<select id="selectAll" resultType="student">
SELECT * FROM student
</select>
<insert id="insert" parameterType="student">
INSERT INTO student VALUES (#{id},#{name},#{age});
</insert>
<delete id="delete" parameterType="int">
DELETE FROM student WHERE id=#{id};
</delete>
</mapper>
· controller - studentController.java
public class StudentController {
private StudentService studentService = new StudentServiceImpl();
@Test
public void selectAll(){
List<Student> list = studentService.selectAll();
for (Student student : list) {
System.out.println(student);
}
}
@Test
public void insert(){
Student st = new Student(10,"路飞",23);
Integer insert = studentService.insert(st);
System.out.println(insert);
}
@Test
public void delete(){
Integer s = 3;
Integer i = studentService.delete(3);
System.out.println(i);
}
}
· mapper - StudentMapper.java
public interface StudentMapper{
/*
注意: resultType 值为 student 指定结果映射的对象类型
方法的返回值为 List<Student>
resultType:
1、基本类型 :resultType=基本类型
2、List类型 :resultType=List中元素的类型
3、Map类型 单条记录:resultType =map
多条记录:resultType =Map中value的类型
方法的返回值可以为List<Student>
parameterType:
基本数据类型:int,string,long,Date;
复杂数据类型:类和Map
如何获取参数中的值:
基本数据类型:#{value}或${value} 获取参数中的值
复杂数据类型:#{属性名}或${属性名} ,map中则是#{key}或${key}
*/
public abstract List<Student> selectAll();
Integer insert(Student student);
Integer delete(Integer integer);
}
· service - StduentService.java - StudentServiceImpl.java
public interface StudentService {
List<Student> selectAll();
Integer insert(Student student);
Integer delete(Integer integer);
}
public class StudentServiceImpl implements StudentService {
@Override
public List<Student> selectAll() {
List<Student> list = null;
SqlSession sqlSession = null;
InputStream is = null;
try {
is = Resources.getResourceAsStream("MyBatisConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(true);
//获取StudentMapper接口的实现类对象
//获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果。
//操作数据库主要是通过SQL语句,那么只要找到SQL语句然后执行不就可以
//sqlSession.getMapper()的内部产生了StudentMapper的实现类,那怎么产生的呢?
//动态代理
/*
被代理对象:真实的对象
代理对象:内存中的一个对象
*/
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//StudentMapper studentMapper1 = new StudentServiceImpl();
//return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
/*
类加载器:和被代理对象使用相同的类加载器
接口类型Class数组:和被代理对象使用相同接口
代理规则:完成代理增强的功能
*/
//通过代理对象调用方法,接收结果
list = mapper.selectAll();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(sqlSession != null) {
sqlSession.close();
}
if(is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return list;
}
接口代理方式 - 问题
分析动态代理对象如何生成的?
通过动态代理开发模式,我们只编写一个接口,不写实现类,我们通过getMapper() 方法最终获取到 org.apache.ibatis.binding.MapperProxy代理对象,然后执行功能,而这个代理对象正是 MyBatis使用了 JDK 的动态代理技术,帮助我们生成了代理实现类对象。从而可以进行相关持久化操作。
分析方法是如何执行的?
动态代理实现类对象在执行方法的时候最终调用了mapperMethod.execute() 方法,这个方法中通过 switch 语句根据操作类型来判断是新增、修改、删除、查询操作,最后一步回到了MyBatis 最原生的 SqlSession方式来执行增删改查。
小结
-
原生:Dao ---> DaoImpl
-
接口:Mapper ---> xxMapper.xml
-
接口代理方式可以让给我们只编写接口即可,而实现类对象由MyBatis生成
-
获取动态代理对象
SqlSession功能类中的getMapper(Mapper接口.class)方法。
MyBatis映射配置文件 - 动态SQL
"if标签" +
<select id="selectCondition" resultType="student" parameterType="student">
SELECT * FROM student
<where>
<if test="id != null">
id = #{id}
</if>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
"foreach"
foreach 用来迭代用户传过来的List或者Array
如:使用foreach元素来构建in子语句
collection
: 指定要遍历的集合名称 list、array
item
: 用来临时存放迭代集合中当前元素的值,便于在foreach中使用
open
:将该属性指定的值添加到foreach迭代后拼出字符串的开始
close
:将该属性指定的值添加到foreach迭代拼出字符串的结尾
separator
: 用来分割foreach元素迭代的每个元素
<!--SELECT * FROM student WHERE id IN (1,2);
-->
<select id="selectByIds" resultType="student" parameterType="list">
SELECT * from student
<where>
<foreach collection="list" open="id IN(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
<select id="selectByIds2" resultType="student" parameterType="list">
SELECT * from student where id IN
<foreach collection="list" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
<!--如果是 selectById(List<Student> stus) 这种-->
<select id="selectByIds3" resultType="student" parameterType="list">
SELECT * from student
<where>
<foreach collection="list" open="id IN(" close=")" item="stu" separator=",">
#{stu.id}
</foreach>
</where>
</select>
使用
StudentMapper.java
List<Student> selectByIds(List<Integer> list);
StudentService.java
List<Student> selectByIds(List<Integer> list);
sql片段抽取
<sql id="select">SELECT * FROM student</sql>
使用
<include refid="select"/>
小结
- 动态 SQL 指的就是 SQL 语句可以根据条件或者参数的不同进行动态的变化。
:条件标签。 :条件判断的标签。 :循环遍历的标签。 :抽取SQL 片段的标签。 :引入SQL 片段的标签。
MyBatis 核心配置文件 - 分页查询
使用PageHelper
功能实现
(1) 导入jar包 分页插件pagehelper-5.1.10.jar 和依赖的包 jsqlparser-3.1.jar
(2) 在核心配置文件中集成分页助手插件
(3) 在测试类中使用分页助手相关API实现分页功能。
MyBatisConfig.xml
<!-- 环境配置顺序
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,
objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
使用
StudentServiceImpl.java
PageHelper.startPage(1,3); 一定要写在SQL执行之前
//通过分页助手来实现分页功能
// 第一页:显示3条数据
//PageHelper.startPage(1,3);
// 第二页:显示3条数据
//PageHelper.startPage(2,3);
// 第三页:显示3条数据
PageHelper.startPage(3,3);
//5.调用实现类的方法,接收结果
List<Student> list = mapper.selectAll();
相关API
返回值 | 方法名 | 说明 |
---|---|---|
long | getTotal() | 获取总条数 |
int | getPages() | 获取总页数 |
int | getPageNum() | 获取当前页 |
int | getPageSize() | 获取每页显示条数 |
int | getPrePage() | 获取上一页 |
int | getNextPage() | 获取下一页 |
boolean | isIsFirstPage() | 获取是否是第一页 |
boolean | isIsLastPage() | 获取是否是最后一页 |
MyBatis 多表操作
外键字段的建立是体现表与表关系的所在
- 一对一 : 在任意一方建立外键,关联对方的主键
用户基本信息表 和 用户详细信息表
- 一对多 : 在多的一方建立外键,关联一的一方主键
店铺表 和 商品表
用户表 和 订单表
- 多对多 : 借助中间表,中间表至少两个字段,分别关联两张表的主键
学生表 和 课程表
用户表 和 兴趣爱好表
多表操作 一对一
模型 : 人和身份证 、 一个人只有一张身份证
数据准备
CREATE DATABASE db2;
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,
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);
bean
Student.java
public class Person {
private Integer id; //主键id
private String name; //人的姓名
private Integer age; //人的年龄
}
Card.java
public class Card {
private Integer id;
private String number;
private Person p;
}
OneToOneMapper.xml
<mapper namespace="com.mybatis.oneToOne.OneToOneMapper">
<!--
<resultMap>是Mybatis的结果集封装
-->
<!--通过 <resultMap>: 配置字段和对象属性的映射关系标签
属性
id 属性:resultMap 的唯一标识,此 id 值用于 select 元素 resultMap 属性的引用。
type 属性:表示该 resultMap 的映射结果类型(通常是 Java 实体类)。
子节点
id 子节点:一般对应数据库中该行的主键 id,设置此项可以提升 MyBatis 性能。
配置主键映射关系标签
result 子节点:映射到 JavaBean 的某个 “简单类型” 属性,如基础数据类型、包装类等。
配置非主键映射关系标签
子节点属性
column 属性:表示从数据库中查询的字段名或别名。表中字段名称
property 属性:表示查询出来的字段对应的值赋给实体对象的哪个属性。实体对象变量名称
说明:子节点 id 和 result 均可实现最基本的结果集映射,将列映射到简单数据类型的属性。
这两者唯一不同的是:在比较对象实例时 id 将作为结果集的标识属性。
这有助于提高总体性能,特别是应用缓存和嵌套结果映射的时候。
而若要实现高级结果映射,就需要学习下面两个配置项: association 和 collection。
association:映射到 JavaBean 的某个 “复杂类型” 属性,比如 JavaBean 类,
即 JavaBean 内部嵌套一个复杂数据类型(JavaBean)属性,这种情况就属于复杂类型的关联。
但是需要注意: association 仅处理一对一的关联关系。
association:配置被包含对象的映射关系
property:被包含对象的变量名
javaType:被包含对象的数据类型
-->
<resultMap id="oneToOne" type="card">
<id column="cid" property="id"/>
<result column="number" property="number"/>
<association property="p" javaType="person">
<id column="pid" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</association>
</resultMap>
<select id="selectAll" resultMap="oneToOne">
SELECT
c.id cid,
number,
pid,
NAME,
age
FROM
card c , person p
WHERE
c.pid = p.id
</select>
</mapper>
MyBatisConfig.xml
<mappers>
<!-- mapper 引入指定的映射配置文件 resource属性指定映射配置文件的名称 -->
<mapper resource="com/mybatis/oneToOne/OneToOneMapper.xml"></mapper>
</mappers>
测试
//4.获取OneToOneMapper接口的实现类对象
OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);
//5.调用实现类的方法,接收结果
List<Card> list = mapper.selectAll();
//6.处理结果
for (Card c : list) {
System.out.println(c);
}
多表操作 一对多
数据准备
多表操作 一对多 班级对学生
CREATE TABLE classes(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARBINARY(20)
);
INSERT INTO classes VALUES (NULL,'s一班');
INSERT INTO classes VALUES (NULL,'s二班');
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);
bean
public class Student {
private Integer id;
private String name;
private Integer age;
}
public class Classes {
private Integer id;
private String name;
private List<Student> students; //班级中所有学生对象
}
OneToManyMapper.xml
<mapper namespace="com.mybatis.oneToMany.OneToManyMapper">
<resultMap id="oneToMany" type="classes">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
<!--
collection:配置被包含的集合对象映射关系
property:被包含对象的变量名
ofType:被包含对象的实际数据类型
-->
<collection property="students" ofType="student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
</collection>
</resultMap>
<select id="selectAll" resultMap="oneToMany">
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
</select>
测试
//4.获取OneToManyMapper接口的实现类对象
OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
//5.调用实现类的方法,接收结果
List<Classes> classes = mapper.selectAll();
//6.处理结果
for (Classes cls : classes) {
System.out.println(cls.getId() + "," + cls.getName());
List<Student> students = cls.getStudents();
for (Student student : students) {
System.out.println(" " + student);
}
}
多表操作 多对多
数据准备
学生和课程 一个学生可以选择多门课程 一个课程可以被多个学生选择
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,
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);
bean
Course.java
public class Course {
private Integer id;
private String name;
}
Student.java
public class Student {
private Integer id;
private String name;
private Integer age;
private List<Course> courses; //学生所选的课程集合
}
ManyToManyMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.manyToMany.ManyToManyMapper">
<resultMap id="ManyToMany" type="student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
<!-- <collection>:配置被包含集合对象的映射关系标签。
属性:
property 属性:被包含集合对象的变量名
ofType 属性:集合中保存的对象数据类型-->
<collection property="courses" ofType="course">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</collection>
</resultMap>
<select id="selectAll" resultMap="ManyToMany">
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
</select>
</mapper>
处理结果
//5.调用实现类的方法,接收结果
List<Student> students = mapper.selectAll();
//6.处理结果
for (Student student : students) {
System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
List<Course> courses = student.getCourses();
for (Course cours : courses) {
System.out.println(" " + cours);
}
}
小结
<!--一对一 所有标签如下
<resultMap>:配置字段和对象属性的映射关系标签。
属性:
id 属性:唯一标识
type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
属性:
column 属性:表中字段名称
property 属性: 实体对象变量名称
(*)<association>:配置被包含对象的映射关系标签。 属性:
property 属性:被包含对象的变量名
javaType 属性:被包含对象的数据类型
-->
<!--多对多 & 一对多 所有标签如下
<resultMap>:配置字段和对象属性的映射关系标签。
属性:
id 属性:唯一标识
type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
属性:
column 属性:表中字段名称
property 属性: 实体对象变量名称
(*)<collection>:配置被包含集合对象的映射关系标签。
属性:
property 属性:被包含集合对象的变量名
ofType 属性:集合中保存的对象数据类型
-->
扩展
针对结果集而创建对应的javabean类,来接收结果集数据,这种javabean在领域驱动模型中称之为 : VO