ORM(Object Relational Mapping)思想的提出:
数据库中数据是以表的形式存在的,而java中使用的数据都是对象型的。所以不得不要将表数据转换成对象数据。 这样就会产生大量的没有技术含量的纯 “体力” 型代码。
while(rs.next()) { Business business = new Business(); business.setBusinessId(rs.getInt("businessId")); business.setPassword(rs.getString("password")); business.setBusinessName(rs.getString("businessName")); business.setBusinessAddress(rs.getString("businessAddress")); business.setBusinessExplain(rs.getString("businessExplain")); business.setStarPrice(rs.getDouble("starPrice")); business.setDeliveryPrice(rs.getDouble("deliveryPrice")); list.add(business); }
上面代码就属于纯 “体力” 型代码。这些代码工作量大、单调枯燥、占用大量开发时间。为了解决这个 问题,出现了ORM思想。 ORM(对象-关系映射):完成对象数据到关系型数据映射的机制称为对象-关系映射。
MyBatis框架:
MyBatis就是一个ORM框架。当然,也是一个持久层框架。 MyBatis封装了JDBC, 将数据库中的表数据自动封装到对象中。这样就可以以面向对象的方式操作数 据了。它的出现,使得开发工作量变小了,可以将精力集中在业务逻辑的处理上。
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映 射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。 实际上,MyBatis最核心的功能,就是实现了输入映射和输出映射。
sql输出映射参数
sql输入映射参数
Mybatis配置
SqlMapConfig.xml/application.yml
HashMap
String,Integer基本数据类型
Java对象
HashMap
String,Integer基本数据类型
Java对象
mapper.xml/sql注解
配置运行环境:数据库连接池+数据库事务管理等
SqlSessionFactory
SqlSession
Executor
Mapped Statement
数据库
输入映射总结
当sql语句需要一个参数时:
接口方法参数为一个基本数据类型;parameterType配置一个基本数据类型; 当sql语句需要多个参数时:
接口方法参数为一个实体对象类型;parameterType配置一个实体对象类型;
接口方法参数为一个集合类型(List、Map);parameterType配置集合中元素的类型;
注意:当sql语句中需要判断一个基本数据类型的值是否为空时:
值的类型必须为包装类。
即使是只传一个基本数据类型,也要使用实体对象传值。 因为:如果在parameterType中设置 Integer类型,那么Mybatis会自动寻找get方法来获取对象属性值。因此会出现没有get方法异常。
输出映射总结
当sql语句中的字段名与实体对象中的属性名一致时,使用resultType:
返回一条记录时,resultType可以配置成对象类型。
返回多条记录时,resultType也要配置成对象类型(表示集合中存储的对象)。
返回一条记录,且只有一列时,resultType可以配置成简单数据类型。
注意:当sql语句中的字段名与实体对象中的属性名不一致时,使用resultMap:
在resultMap中,显式的书写sql字段名与实体对象属性名的映射
resultMap的使用
当sql语句中的字段名与实体对象中的字段名不一致时,可以使用resultMap来显式的进行映射。
注意:
resultMap标签中的 id 属性:是此resultMap的唯一标识。
resultMap标签中的 type 属性:是此resultMap 映射的实体对象(也就是model包的Student类)。
子标签id 和子标签 result 中的 property 属性,对应实体类中的属性,column 属性对应sql语句中的字段。
resultMap标签中的子标签id,用来配置主键的映射;子标签 result 用来配置其它字段的映射。
mybatis-config.xml中还可以配置数据源(如jdbc驱动、数据库用户名,用户密码等),但是本工程把这些配置放在了application.yml里。
datasource: # 数据库地址 url: jdbc:mysql://119.3.199.81:3366/neuq_examples?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false # 连接数据库用户名 username: example # 连接数据库密码 password: neuq168168 # 设置驱动 driver-class-name: com.mysql.cj.jdbc.Driver # 连接池设置 type: com.alibaba.druid.pool.DruidDataSource # 初始化时建立物理连接的个数 initialSize: 10 # 最大连接池数量 maxActive: 50 # 最小连接池数量 minIdle: 10 # MyBatis配置 mybatis: # 搜索指定包别名 typeesAliasPackage: cn.codnoy.springboot.examples.model # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml
MyBatis开发DAO层有两种方式:
原始dao方式
mapper代理方式
由于本工程采取的是mapper代理方式,且原始dao方式有诸多弊端(如存在大量重复代码、调用sqlSession方法时,将statement的id硬编码了等),我们在这就不予讲解使用原始dao方式封装持久层。
Mapper代理
程序员只需要mapper接口和mapper.xml映射文件(也可使用注解来配置映射语句),Mybatis可以自动生成mapper接口实现类代理对象。程序员编写mapper接口需要遵循一些开发规范。 mapper代理方式开发规范:
映射文件中的 namespace 必须是 mapper 接口的地址。
映射文件中 statement 的 id 必须与 mapper 接口中的方法名一致。
映射文件中 parameterType 必须与 mapper 接口中的方法参数类型一致。
映射文件中 resultType 必须与 mapper 接口中的返回值类型一致。(实际上,代理对象就是根据返 回值类型来判断是使用selectOne方法还是selectList方法)
package com.neusoft.mapper; import java.util.List; import com.neusoft.po.Emp; public interface EmpMapper { public Emp getEmpById(int empno); public ListlistEmpAll(); }
MyBatis XML映射文件一般在resources文件夹的子文件夹mapper下。
本项目里的xml映射文件过大,笔者就单独写了个xml映射文件。
Emp emp = new Emp(); emp.setJob("经"); emp.setDeptno(20); Listlist = mapper.listEmp(emp); for(Emp e : list) { System.out.println(e); }
注意:
parameterType只有⼀个。所以,有多个参数时使⽤对象传值(这就是输⼊映射)。
{} 中书写的是实体对象的属性名,所以要严格区分⼤⼩写。
由于 <(⼩于号)是标签关键词,因此不能识别⼩于号等。所以MyBatis中设计了⼀些转义字符,来代替⼀些特殊字符:
Mybatis转义字符表
Mybatis语句 | 代表符号 | 含义 |
---|---|---|
< |
< | 小于 |
> |
> | 大于 |
& |
& | 与 |
' |
' | 单引号 |
" |
" | 双引号 |
Listlist = mapper.listEmpBySal(2000.0); for(Emp e : list) { System.out.println(e); }
int count = mapper.listEmpCount(); System.out.println(count);
注意:只有返回⼀⾏⼀列,resultType才能使⽤基本数据类型
insert into emp(ename,job,hiredate,deptno) values(#{ename},#{job},#{hiredate},#{deptno})
SqlSession sqlSession = Util.getSqlSessionFactory().openSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = new Emp(); emp.setEname("张三"); emp.setJob("职员"); emp.setHiredate("2020-09-04"); emp.setDeptno(10); int result = mapper.insertEmp1(emp); sqlSession.commit(); //注意:要commit提交 System.out.println(result);
注意:增删改都会返回int值,表示影响的⾏数。但是,insert标签中不能书写resultType属性
select LAST_INSERT_ID() insert into emp(ename,job,hiredate,deptno) values(#{ename},#{job},#{hiredate},#{deptno})
SqlSession sqlSession = Util.getSqlSessionFactory().openSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = new Emp(); emp.setEname("张三"); emp.setJob("职员"); emp.setHiredate("2020-09-04"); emp.setDeptno(10); int result = mapper.insertEmp2(emp); sqlSession.commit(); System.out.println(result); System.out.println(emp.getEmpno()); //获取返回的主键
注意:
selectKey标签中的 select LAST_INSERT_ID() 语句就能获取⽣成的主键
selectKey标签中的keyProperty属性就是主键名,MyBatis会⾃动将获取的主键封装给此属性。
order的值有两种:BEFORE、AFTER
BEFORE:先获取主键,然后执⾏insert; ⽐如 Oracle数据库。
AFTER:先执⾏insert,然后获取主键; ⽐如 MySql数据库。
insert into emp(ename,job,hiredate,deptno) values(#{ename},#{job},#{hiredate},#{deptno})
SqlSession sqlSession = Util.getSqlSessionFactory().openSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = new Emp(); emp.setEname("张三"); emp.setJob("职员"); emp.setHiredate("2020-09-04"); emp.setDeptno(10); int result = mapper.insertEmp3(emp); sqlSession.commit(); System.out.println(result); System.out.println(emp.getEmpno()); //获取返回的主键
useGeneratedKeys设置为true后,mybatis会使⽤JDBC的getGeneratedkeys⽅法获取由数据库内部 ⾃动⽣成的主键,并将该值赋值给由keyProperty指定的属性;
注意:此种⽅式只适合于有⾃增⻓列的 数据库(mysql、sqlserver等)
update emp set job=#{job},sal=#{sal} where empno=#{empno}
Emp emp = new Emp(); emp.setEmpno(7934); emp.setJob("经理"); emp.setSal(2000.0); int result = mapper.updateEmp(emp); sqlSession.commit(); System.out.println(result);
注意:增删改都会返回int值,表示影响的⾏数。但是,update等”增删改“标签中不能书写resultType属性
delete from emp where empno=#{empno}
int result = mapper.deleteEmp(7939); sqlSession.commit(); System.out.println(result);
MyBatis也⽀持使⽤注解来配置映射语句。 主要有四种注解来实现增删改查:@Select、@Insert、 @Update、@Delete
package com.neusoft.mapper; import java.util.List; import org.apache.ibatis.annotations.Select; import com.neusoft.po.Emp; public interface EmpMapper { @Select("select * from emp where empno = #{empno}") public Emp getEmpById(int empno); @Select("select * from emp order by empno") public ListlistEmpAll(); }
注意:
在映射⽂件中使⽤的所有的CRUD操作,都可以使⽤注解的形式完成。
当使⽤基于注解的映射器接⼝时,就不再需要映射配置⽂件了。
在实际开发中,可以单独使⽤映射⽂件,也可以单独使⽤注解,也可以混合使⽤。
动态sql主要⽤于解决查询条件不确定的情况。
也就是说:在实际开发中,经常需要根据⽤户是否输⼊了 某个值,来确定是否需要这个条件。
MyBatis中⽤于动态sql的元素主要有:if、where、trim、set、 foreach、choose等
Emp emp = new Emp(); //emp.setJob("经"); //emp.setDeptno(10); //注意:deptno属性必须为Integer类型,方便判断非空与否 Listlist = mapper.listEmp(emp); for(Emp e : list) { System.out.println(e); }
if+where会实现以下功能:
⾃动添加where
不需要考虑where后是否加and,mybatis会⾃动处理
不需要考虑是否加空格,mybatis会⾃动处理
没有 else 标签,也没有 else if 标签。
注意: job!= '' 此处只可以判断是否为空,不能判断是否为某个值或比较大小。也就是说:job!= '经理' 是不好使 的。
Emp emp = new Emp(); emp.setJob("职员"); emp.setDeptno(10); Listlist = mapper.listEmp(emp); for(Emp e : list) { System.out.println(e); }
choose会实现如下功能:
多个 when 标签中,只能执⾏⼀个。也就是说:当⼀个 when 条件满⾜并执⾏后,其它的 when 将 不再执⾏。
当所有 when 都不满⾜条件时,执⾏ otherwise 标签。
if 与 choose 的区别:if 相当于java中的if语句; choose相当于java中的switch语句。
trim标签可以在⾃⼰包含的内容中加上某些前缀或后缀,与之对应的属性是:prefix、suffix。
还可以把 包含内容的开始内容覆盖,即忽略。也可以把结束的某些内容覆盖,对应的属性是:prefixOverrides、 suffixOverrides
insert into emp ename,deptno, job, hiredate, #{ename},#{deptno}, #{job}, #{hiredate},
SqlSession sqlSession = Util.getSqlSessionFactory().openSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = new Emp(); emp.setEname("张三"); emp.setJob("职员"); //emp.setHiredate("2020-09-04"); emp.setDeptno(10); int result = mapper.insertEmp1(emp); sqlSession.commit(); System.out.println(result);
注意:
prefix与suffix可以在sql语句中拼接出⼀对⼩括号。
suffixOberrides可以将最后⼀个逗号去掉。(自动筛选效果)
set标签主要⽤于更新操作时使⽤
update emp where empno=#{empno} job=#{job}, sal=#{sal},
Emp emp = new Emp(); emp.setEmpno(7943); //emp.setJob("职员1"); emp.setSal(3000.0); int result = mapper.updateEmp(emp); sqlSession.commit(); System.out.println(result);
注意:
set可以在sql语句中⾃动加上set关键词。
可以⾃动将最后的逗号去掉。
上⾯写法中,必须要保证有⼀个if成⽴
foreach标签可以在sql中迭代⼀个集合或数组,主要⽤于拼接in条件。下⾯是⼀个批量删除示例:
delete from emp where empno in #{empno}
int[] arr = {7941,7942,7943}; int result = mapper.deleteEmp(arr); //注意:此接⼝参数应为int数组 sqlSession.commit(); System.out.println(result);
foreach标签的属性:
collection:需要遍历的类型,值有:list、array
item:表示遍历出来的对象
open:表示语句的开始部分
close:表示语句的结束部分
separator:表示每次迭代之间以什么符号为间隔
index:每次迭代的位置索引,就是循环变量
我们知道,在数据库中,表关系有:一对一、多对一、一对多、多对多。 那么在实际开发中,很多查询 都是通过多个表之间的关系,进行多表连接查询的。 那么,在多表连接查询中,MyBatis如何实现输出映射呢,这就要使用MyBatis的关联查询。 MyBatis关联查询有两种:
多表连接形式(嵌套结果)
单独查询形式(嵌套查询)
在讲解关联查询之前,我们先来简单了解下SQL JOIN操作符。
SQL join 用于根据两个或多个表中的列之间的关系,从这些表中查询数据。
SQL JOIN有
JOIN:: 如果表中有至少一个匹配,则返回行
INNNER JOIN:在表中存在至少一个匹配时,INNER JOIN 关键字返回行
LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行
RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行
FULL JOIN(MYSQL不支持):只要其中一个表中存在匹配,就返回行
实例:
“Person”表:
Id_P | LastName | FirstName | Address | City |
---|---|---|---|---|
1 | Adams | John | Oxford Street | London |
2 | Bush | George | Fifth Avenue | New York |
3 | Carter | Thomas | Changan Street | Beijing |
"Orders" 表:
Id_O | OrderNo | Id_P |
---|---|---|
1 | 77895 | 3 |
2 | 44678 | 3 |
3 | 22456 | 1 |
4 | 24562 | 1 |
5 | 34764 | 65 |
请注意,"Id_O" 列是 Orders 表中的的主键,同时,"Orders" 表中的 "Id_P" 列用于引用 "Persons" 表中的人,而无需使用他们的确切姓名。
请留意,"Id_P" 列把上面的两个表联系了起来。
我们应该怎么样得知谁订购了产品,并且他们订购了什么产品呢?
如果我们希望列出所有人的定购,可以使用下面的 SELECT 语句:
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo FROM Persons INNER JOIN Orders ON Persons.Id_P = Orders.Id_P ORDER BY Persons.LastName
多对一关联查询
修改实体类
Dept是one的一方,Emp是many的一方; 在many的一方添加one的一方的对象,这就配置了实体类 之间的多对一关联。
使用resultMap映射关联
association表示配置单个对象的关联映射 :
association标签中的property:many的一方的实体类中,添加的属性名。
association标签中的javaType:many的一方的实体类中,添加的属性类型。
id标签和result标签中的property:one的一方的实体类中的属性名。
id标签和result标签中的column:one的一方的,查询出来的字段名。
一对多关联查询
修改实体类
Dept是one的一方,Emp是many的一方; 在one的一方添加many的一方的集合,这就配置了实体类 之间的一对多关联。
使用resultMap映射关联
collection表示配置多个对象的集合的关联映射 :
collection标签中的property:one的一方的实体类中,添加的集合属性名。
collection标签中的ofType:one的一方的实体类中,添加的集合中的元素类型。
id标签和result标签中的property:many的一方的实体类中的属性名。
id标签和result标签中的column: many的一方的,查询出来的字段名。
多对一关联查询
先在one的一方添加关联查询
注意:此查询可以不在接口中书写响应的方法
many的一方添加查询
一对多关联查询
先在many的一方添加关联查询
注意:此查询可以不在接口中书写响应的方法
one的一方添加查询
使用resultMap实现关联映射时:
使用association标签完成多对一或一对一映射。 a. association标签:将关联查询信息映射到一个po对象中。 b. association标签中的javaType属性:表示该po对象的类型。 c. association标签中的select属性:表示应用哪一个关联查询。 d. association标签中的column属性:表示应用关联查询的条件。
使用collection标签完成一对多,多对多映射。 a. collection标签:将关联查询信息映射到一个list集合中。 b. collection标签的ofType属性:表示该集合中的元素对象的类型。 c. collection标签中的select属性:表示应用哪一个关联查询。 d. collection标签中的column属性:表示应用关联查询的条件。