目录
ORM思想
常规JDBC的弊端
Mybatis
Mybatis特点
Mybatis入门案例
导入jar包
编辑POJO对象
编写xml形式的核心配置文件
编写持久层mapper的接口
编写接口实现类的xml形式的映射文件
核心配置文件中配置mybatis关联的映射文件
编写测试类进行测试
Mybatis调用流程
根据ID查询数据
编写业务接口
编辑xml映射文件
编辑测试API
mybatis入门操作
简化Mybatis操作
@BeforeEach注解说明
mybatis参数封装说明
报错说明:
常见封装策略
测试案例:
参数知识点总结:
#号和$符用法
规则说明
案例实现
Mybatis 常规CURD操作
新增、删除、更改操作
编写测试方法
编写业务接口
编写xml文件
总结
Mybatis中的转义标签
xml转义语法
编写测试方法
编写业务接口
编写xml文件
Mybatis集合用法--批量
需求分析
封装方式
编写测试方法
编写业务接口
编写xml文件
总结:
模糊查询说明
Mybatis的优化设置
Mybatis的核心配置文件是有顺序的
Mybatis的简化---别名
Mybatis的简化---SQL标签
Mybatis优化之开启驼峰映射规则
Mybatis之动态sql
IF-WHERE用法
动态sql----SET标签
动态Sql-choose when otherwise
resultType和resultMap用法
标签说明
编辑测试类
编辑接口
编辑xml映射文件
resultType的返回类型
对象类型
简单类型
Map类型
关联关系
常见的关联关系
一对一
一对多
Mybatis的缓存机制
什么是缓存机制
一级缓存
二级缓存
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换
概括: 用对象的方式操作数据库
衍生:
对象应该与数据库中的表一一映射.
对象中的属性应该与表中的字段一一映射.
其中的映射应该由程序自动完成.无需人为干预.
//利用jdbc,完成新增的功能 private static void method2() throws Exception{ //1,注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2,获取数据库的连接 //数据传输协议 数据库的ip 端口号 数据库名 String url = "jdbc:mysql://localhost:3306/cgb2107"; Connection c = DriverManager.getConnection(url,"root","root"); //3,获取传输器 Statement s = c.createStatement(); //4,利用传输器执行 增删改的SQL //executeUpdate()用来执行增删改的SQL,只返回影响行数 int rows = s.executeUpdate( "INSERT INTO emp(ename,job) VALUES('rose','副总')"); //5,释放资源 //r.close();//结果集 s.close();//传输器 c.close();//连接 }
弊端
无论如何执行都必须获取数据库链接。 链接池: c3p0 druid HK链接池
操作sql语句时,步骤繁琐. 不便于学习记忆.
资源必须手动关闭.
优点
操作数据库最快的方式就是JDBC. 协议 TCP
引出:
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。(mybatis在内部将JDBC封装).
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
知识整理:
持久化:计算机在计算时,数据都在内存中.如果断电则数据清空,所以要求将内存数据保存到磁盘中. 概念.
持久层:程序通过Dao/Mapper 与数据库进行交互的层级代码 (Controller层 Service层 Dao/Mapper层) 具体操作.
小结: Mybatis是一个优秀的持久层框架,基于ORM设计思想,实现了以对象的方式操作数据库.
了解:Mybatis的ORM并不完全,只完成了结果集映射,但是Sql需要自己手写.所以也称之为半自动化的ORM映射框架.
简单易学
灵活
解除sql与程序代码的耦合
提供映射标签,支持对象与数据库的orm字段关系映射
提供对象关系映射标签,支持对象关系组建维护
提供xml标签,支持编写动态sql
准备资源:
org.projectlombok lombok org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.0 mysql mysql-connector-java
package com.jt.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.io.Serializable; @Data//1.实现get/set/...方法 @Accessors(chain = true)//2.链式加载--->设置值 @NoArgsConstructor//3.无参构造 @AllArgsConstructor//4.有参构造 public class DemoUser implements Serializable{//5.实现序列化 private Integer id; private String name; private Integer age; private String sex; }
package com.jt.mapper; import com.jt.pojo.DemoUser; import java.util.List; /** * 说明: * 1.根据面向接口开发思想需要定义一个Mapper接口 * 2.在接口中可以写方法,谁调用谁实现 * 3.Mybatis中的实现类以xml文件形式存在 */ public interface DemoUserMapper { //查询所有的User列表信息 ListfindAll(); DemoUser findOne(int id); }
package com.jt; import com.jt.mapper.DemoUserMapper; import com.jt.pojo.DemoUser; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class TestMybatis { @Test public void testDemo1() throws IOException { /*创建SqlSessionFactory*/ //指定配置文件地址 String resource = "mybatis/mybatis-config.xml"; //通过IO流 加载指定的配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //动态生成SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); /*从SqlSessionFactory中获取sqlSession 类比 数据库链接*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*获取mapper接口,执行接口方法*/ DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); //获取数据 ListuserList = demoUserMapper.findAll(); System.out.println(userList); //关闭流 sqlSession.close(); } @Test public void findOne() throws IOException { String resource = "mybatis/mybatis-config.xml"; //通过IO流 加载指定的配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //动态生成SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //获取接口 DemoUserMapper mapper = sqlSession.getMapper(DemoUserMapper.class); int id=1; DemoUser demoUser = mapper.findOne(id); System.out.println(demoUser); sqlSession.close(); } }
在入门案例中已经实现了,这里分离出来
public interface DemoUserMapper { //1.查询所有的表数据 public ListfindAll(); DemoUser findOne(int id);}
/*** * 需求: 根据ID查询数据库记录 id=1的数据 */ @Test public void testFindOne() throws IOException { //指定配置文件地址 String resource = "mybatis/mybatis-config.xml"; //通过IO流 加载指定的配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //动态生成SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //获取接口 DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); int id = 1; DemoUser demoUser = demoUserMapper.findOne(id); System.out.println(demoUser); //关闭链接 sqlSession.close(); }
public class TestMybatis2 { //定义公共的属性 private SqlSessionFactory sqlSessionFactory; @BeforeEach public void init() throws IOException { String resources = "mybatis/mybatis-config.xml"; InputStream inputStream =Resources.getResourceAsStream(resources); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testMybatis(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); Listlist = demoUserMapper.findAll(); System.out.println(list); sqlSession.close(); }
该注解的作用是在执行@Test方法前调用. 是测试方法提供的测试API
规则:
mybatis如果遇到多值传参时,默认条件是采用下标的方式获取数据.
mybatis天生只支持单值传参,如果遇到多值的问题,则应该将多值封装为单值.
封装为实体对象
更为常用方式:Map集合
如果传递的数据有多个,使用注解 @Param 封装为Map集合
使用方式: List
编写测试方法
//1.封装为实体对象 @Test public void find1(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); //编程思想:面向对象 DemoUser csc = new DemoUser(); csc.setAge(18).setSex("女"); Listlist = demoUserMapper.findSA(csc); System.out.println(list); sqlSession.close(); } //2.封装为Map集合 @Test public void find2(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); Map map = new HashMap<>(); map.put("sex", "女"); map.put("age", 18); List list = demoUserMapper.findSA2(map); System.out.println(list); sqlSession.close(); } //3.使用注解@Param封装 @Test public void find3(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); String sex = "女"; int age = 18; List list = demoUserMapper.findSA3(sex,age); System.out.println(list); sqlSession.close(); }
编写业务接口
public interface DemoUserMapper { ListfindSA(DemoUser csc); List findSA2(Map map); //利用注解@Param将参数封装为Map集合 List findSA3(@Param("sex") String sex, @Param("age") int age); }
编写xml映射文件
如果参数采用对象封装,则可以使用#{属性}取值.
如果参数有多个,可以封装为map实现参数的传递. 可以利用#{key}获取数据
也可以使用@Param将多个参数封装为map, 利用#{key}的方式获取数据
使用#{} 获取数据时,默认有预编译的效果.防止sql注入攻击.
mybatis使用#{}获取数据时,默认为数据添加一对""号.
当以字段名称为参数时,一般使用${},但是这样的sql慎用. 可能出现sql注入攻击问题.
注意:一般条件下,能用#{},尽量不用${}
编写测试方法
/** *需求:按照指定的age排序 * sql:select * from demo_user order by age * #号 与 $符用法: * */ @Test public void testFindOrder(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); String csc = "age"; Listlist = demoUserMapper.findFO(csc); System.out.println(list); sqlSession.close(); }
编写业务接口
public interface DemoUserMapper { ListfindFO(String csc); }
编写xml文件
/** * 需求:实现用户入库的操作 */ @Test public void saveUser(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(null,"常世超",18,"女" ); int rows= demoUserMapper.saveUser(demoUser); if(rows>0){ System.out.println("影响的行数"+rows); sqlSession.commit(); } sqlSession.close(); } /** * 作业: * 1.把id=1的数据 name 改为 "守山大使" age=5000 * 2.将name="常世超"的数据 删除 */ @Test public void updateUser(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(1, "守山大使", 5000, ""); int rows = demoUserMapper.updateUser(demoUser); if(rows>0){ System.out.println("影响行数"+rows); sqlSession.commit(); } sqlSession.close(); } @Test public void deleteUser(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); String name = "常世超"; demoUserMapper.deleteUser(name); System.out.println("删除成功"); sqlSession.close(); }
public interface DemoUserMapper { int saveUser(DemoUser demoUser); int updateUser(DemoUser demoUser); void deleteUser(String name); }
insert into demo_user value (null ,#{name} ,#{age},#{sex}) update demo_user set name = #{name} ,age = #{age} where id =#{id} delete from demo_user where name =#{name}
编写删除、更改、新增操作时,需要提交事务---》mybatis会自动回滚事务
提交事务的两种方式:
SqlSession sqlSession = sqlSessionFactory.openSession(true);
sqlSession.commit();
xml文件中的转义字符: > > 大于 < < 小于 & & 号 说明:如果sql中有大量的转义字符 建议使用转义标签体 语法:
/** * 需求: 查询age> 18 and age< 100 的用户信息. * 规则: 如果不能使用对象封装,则一般使用Map集合 */ @Test public void testSelect01(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); Mapmap = new HashMap<>(); map.put("minAge",18); map.put("maxAge",100); List userList = demoUserMapper.findByAge(map); System.out.println(userList); sqlSession.close(); }
public interface DemoUserMapper { ListfindByAge(HashMap map); }
要求批量的删除数据库中的记录.
规则: 如果遇到相同的多个数据,则一般采用集合的方式封装数据
array
list
map
//1.array封装 @Test public void deleteIds(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); int[] ids = {231,232,233}; demoUserMapper.deleteIds(ids); System.out.println("删除操作成功"); sqlSession.close(); } //2.list封装 @Test public void deleteList(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); List list = new ArrayList(); list.add(231); list.add(232); list.add(233); demoUserMapper.deleteList(list); System.out.println("删除操作成功"); sqlSession.close(); } //3.map封装 @Test public void deleteMap(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); List list = new ArrayList(); list.add(231); list.add(232); list.add(233); Map map = new HashMap(); map.put("ids", list); demoUserMapper.deleteMap(map); System.out.println("删除操作成功"); sqlSession.close(); }
public interface DemoUserMapper { void deleteIds(int[] ids); void deleteList(List list); void deleteMap(Map map); }
delete from demo_user where id in #{id} delete from demo_user where id in #{id} delete from demo_user where id in #{id}
批量删除数据 难点:如果使用#{集合}获取的是集合的对象,删除无效 思路:将数组拆分为单个数据===》通过遍历数组的方式获取 语法:mybatis中为了遍历方便,提供了遍历的标签 ===》foreach 关于标签参数的说明: 1.collection 1).如果参数传参为数组, 则collection="array" 2).如果参数传参为list集合, 则collection="list" 3).如果参数传参为map集合, 则collection="map集合中的key值" 关于标签属性的说明: 1.collection 集合的名称 2.item 每次遍历的数据的性参变量 3.open 循环开始的标签 4.close 循环结束的标签 5.separator 循环遍历的分割符 6.index 循环遍历下标
需求: 查询name中包含"精"的数据.并且按照年龄降序排列
在传参为:String = "%精%"
在SQL语句中:select * from demo_user where name like "%"#{name}"%"
顺序为:
The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
别名标签
别名包
使用注解定义别名
别名标签
位置:核心配置文件中
说明:只对某个类生效
别名包
位置:核心配置文件中
说明:对包下的所有对象类生效
使用注解
位置:POJO类上
说明:只对某个类生效
@Alias("DemoUser")--->起别名 public class DemoUser implements Serializable{//5.实现序列化 private Integer id; private String name; private Integer age; private String sex; }
说明
mybatis的xml映射文件中会有大量的Sql语句. 随着业务的增加,Sql语句的数量也会增加. 其中有部分"Sql片段"则可能重复. 如果想简化Sql语句,则可以使用Sql标签简化操作.
例子:
select id,name,age,sex from demo_user where id = 1 select id,name,age,sex from demo_user where name = xxx
sql标签的用法
select id,name,age,sex from demo_user
sql标签的优点和缺点
优点:
使用Sql标签可以节省xml的文件大小.
代码的结构相对简单.
缺点:
Sql只能抽取公共的Sql语句,局限性稍大.
如果大量的使用Sql标签,则代码的可读性差
说明:
在mybatis映射数据时,经常出现字段名称与属性名称不一致的现象. 但是其中一部分可以采用驼峰规则的方式完成自动映射. 所以有如下的配置
编写xml核心配置文件
位置:核心配置文件中configuration配置标签之下
需求:根据user对象查询数据.要求根据对象中不为null的属性充当where条件.实现动态的查询
解释:
用户可以根据用户名查询数据、也可以根据用户名+身份证号查询数据、也可以跟据3个条件查询
编辑测试方法
/** * 封装DemoUser的对象,根据对象中不为null的属性查询 */ @Test public void testWhere(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(); demoUser.setName("金角大王") .setAge(3000) .setSex("男"); Listlist = demoUserMapper.findWhere(demoUser); System.out.println(list); sqlSession.close(); }
编辑接口
public interface DemoUserMapper { ListfindWhere(DemoUser demoUser); }
编辑xml映射文件
说明:根据对象不为null的属性当做set条件
编辑测试方法
/** * 根据ID,动态的实现数据的更新 */ @Test public void testUpdateSet(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(); demoUser.setName("黑黑") .setId(1) .setSex("女") .setAge(10000); demoUserMapper.findUpdateSet(demoUser); System.out.println("修改成功"); sqlSession.close(); }
编辑接口
public interface DemoUserMapper { void findUpdateSet(DemoUser demoUser); }
编辑xml文件
update demo_user where id = #{id}; name = #{name}, age = #{age}, sex = #{sex}
需求:
根据条件实现数据的查询. 如果存在name则按照name查询,否则按照sex查询. 补充说明: 条件: name = “张三” , sex=“男” select * from demo_user where name = #{name}
编写测试类
/** * 需求:如果存在name则按照name查询,否则按照sex查询 */ @Test public void testSelectChoose(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(); demoUser.setName("白骨精").setSex("女"); Listlist = demoUserMapper.findChoose(demoUser); System.out.println(list); sqlSession.close(); }
编写接口
public interface DemoUserMapper { ListfindChoose(DemoUser demoUser); }
编写xml映射文件
resultType说明: 当结果集中的字段名称,如果与属性的名称一致时,才会实现自动的数据封装
resultMap说明: 当结果集中的字段名称,与对象中的属性不一致时,可以使用resultMap实现自定义的封装
@Test public void testFindDept(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class); Listlist = deptMapper.findAll(); System.out.println(list); sqlSession.close(); }
public interface DemoUserMapper { ListfindAll(); }
接口的返回值是对象
接口返回值是list集合,mybatis会把student对象放入list集合中
resultType 指定查询数据封装到那个对象中
定义接口
Student selectById(Integer id );
编辑xml映射文件
xml文件中执行sql语句,得到的一个值(一行一列)
resultType 需要指定java.long包下的数据类型
定义接口
public interface DemoUserMapper { long countStudent(); }
编辑xml映射文件
执行sql语句得到的是一个Map结构数据,Mybatis执行sql,把ResultSet转为map
sql执行结果, 列名做map的key,列值做value
sql执行得到的是一行记录,转为map结构是正确的
dao接口返回是一个map,sql语句最多能获取一行记录,多余一行是错误的
resultType 需要指定java.util.HashMap
定义接口
//查询结构返回是一个map public interface DemoUserMapper { Map
编辑xml映射文件
一对一
一对多
多对多
规则:使用对象封装
关联查询实现一对一查询
编写测试类
/** * 完成一对一映射 * 规定:一个员工对应一个部门 * 选取方向:员工方 * 需求:需要在员工中,完成部门的封装 */ @Test public void testOneToOne(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); Listlist = empMapper.findAll(); System.out.println(list); sqlSession.close(); }
编写接口
public interface EmpMapper { ListfindAll(); }
编写xml映射文件
子查询实现一对一查询
编写测试类
@Test public void testOneToOne2(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); Listlist = empMapper.findAllWhere(); System.out.println(list); sqlSession.close(); }
编写接口
ListfindAllWhere();
编写xml映射文件
关联查询与子查询的区别
编写测试文件
@Test public void testOneToMore(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DeptMapper deptMapper= sqlSession.getMapper(DeptMapper.class); Listlist = deptMapper.findDept(); System.out.println(list); sqlSession.close(); }
编写接口
ListfindDept();
编写xml文件
引入缓存可以有效降低用户访问物理设备的频次.提高用户响应速度.
扩展: 1.mybatis自身缓存 一级缓存/二级缓存 2.Redis缓存 读取10万次/秒, 写 8.6万次/秒
概念说明: Mybatis默认开启一级缓存, 一级缓存可以在同一个SqlSession对象中查询相同的数据,可以实现数据的共享(缓存操作).
缓存测试
/** * Mybatis一级缓存:默认开启 * 规则:同一个SqlSession内部有效 * 不同的SqlSession内部无效 * 测试: * 看sql执行几次 * 执行一次有缓存 */ @Test public void cache1(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); Listlist1 = empMapper.findAll(); List list2 = empMapper.findAll(); List list3 = empMapper.findAll(); System.out.println(list1==list2); sqlSession.close(); }
说明: 二级缓存mybatis中默认也是开启的.但是需要手动标识. 二级缓存可以在同一个SqlSessionFactory内部有效.
注:要启动全局的二级缓存,需要在其SQL映射文件中添加
缓存测试
/** * 二级缓存说明: * SqlSession查询数据之后,会将缓存信息保存到一级缓存中,但是不会立即将数据交给二级缓存保存 * 如果需要使用二级缓存,则必须将SqlSession业务逻辑执行成功之后,再关闭 */ @Test public void cache2(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); empMapper.findAll(); sqlSession.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); EmpMapper empMapper2 = sqlSession2.getMapper(EmpMapper.class); empMapper2.findAll(); sqlSession2.close(); }