一、了解orm框架
1.什么是ORM框架:对象关系映射(Object Relational Mapping,简称ORM),该模式是为了解决面向对象与关系数据库互补匹配的现象的技术;orm框架是连接数据库的桥梁,主要提供了人持久化与表之间的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据建库中;
2.ORM框架的主要作用:为了解决面向对象关系与关系数据库互补匹配的问题;
3.ORM常用框架:
(1)Hibernate全自动映射框架,需要些hql语句
(2)iBATIs半自动框架,自己编写sql语句,可操作性强
(3)mybits半自动ORM映射框架
(4)eclipseLink
(5)JFinal
4. ORM的优缺点:
优点:
(1)提高开发效率,降低开发成本
(2)使开发更加对象化
(3)可移植
(4)可以很方便地引入数据缓存之类的附加功能
缺点:
(1)自动化进行关系数据库的映射需要消耗系统性能。其实这里的性能消耗还好啦,一般来说都可以忽略之。
(2)在处理多表联查、where条件复杂之类的查询时,ORM的语法会变得复杂。
二、半自动与全自动的区别
Hibrnate和mybatis是当前流行的ORM框架。hibernate对数据库结构提供了较为完整的封装。mybatis主要着力点在于java对象与SQL之间的映射关系。
Hibrnate:使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以Hibernate是全自动ORM映射框架;
Mybits:在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以Mybits是半自动ORM映射框架;
三、动态sql一对一、一对多、多对多的理解
前提:创建user表和orders表
package com.itheima.domain;import java.util.Date;
import java.util.List;public class User {private int id;private String username;private String password;private Date birthday;//当前用户具备哪些角色private List roleList;public List getRoleList() {return roleList;}public void setRoleList(List roleList) {this.roleList = roleList;}//描述的是当前用户具有的订单private List orderList;public List getOrderList() {return orderList;}public void setOrderList(List orderList) {this.orderList = orderList;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", birthday=" + birthday +", roleList=" + roleList +'}';}
}
package com.itheima.domain;import java.util.Date;public class Order {private int id;private Date ordertime;private double total;//当前订单属于哪一个用户private User user;public int getId() {return id;}public void setId(int id) {this.id = id;}public Date getOrdertime() {return ordertime;}public void setOrdertime(Date ordertime) {this.ordertime = ordertime;}public double getTotal() {return total;}public void setTotal(double total) {this.total = total;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}@Overridepublic String toString() {return "Order{" +"id=" + id +", ordertime=" + ordertime +", total=" + total +", user=" + user +'}';}
}
在sqlMapConfig.xml中加载映射关系
在web层中应用
package com.itheima.test;import com.itheima.domain.Order;
import com.itheima.domain.User;
import com.itheima.mapper.OrderMapper;
import com.itheima.mapper.UserMapper;
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.Before;
import org.junit.Test;import java.io.IOException;
import java.io.InputStream;
import java.util.List;public class MyBatisTest2 {private OrderMapper mapper;@Beforepublic void before() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession(true);mapper = sqlSession.getMapper(OrderMapper.class);}}
1.一对一:
orders—>user:一个订单只由一个用户创建,一对一
在测试类中实现测试一对一方法
package com.itheima.test;import com.itheima.domain.Order;
import com.itheima.domain.User;
import com.itheima.mapper.OrderMapper;
import com.itheima.mapper.UserMapper;
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.Before;
import org.junit.Test;import java.io.IOException;
import java.io.InputStream;
import java.util.List;public class MyBatisTest2 {private OrderMapper mapper;@Beforepublic void before() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession(true);mapper = sqlSession.getMapper(OrderMapper.class);}@Testpublic void test(){Order orders = mapper.findByOid(1);System.out.println(orders);}}
OrderMapper
接口实现代码:
@Select("select * from orders o,user u where o.id=#{id} and o.uid=u.id")@Results({@Result(column = "id",property = "id"),@Result(column = "ordertime",property = "ordertime"),@Result(column = "total",property = "total"),@Result(property = "user", //要封装的属性名称column = "uid", //根据那个字段去查询user表的数据javaType = User.class, //要封装的实体类型//select属性 代表查询那个接口的方法获得数据one = @One(select = "com.itheima.mapper.UserMapper.findById"))})public Order findByOid(int id);
2.一对多:
user---->orders:一个用户可以创建多个订单,一对多
测试类实现代码:
package com.itheima.test;import com.itheima.domain.Order;
import com.itheima.domain.User;
import com.itheima.mapper.OrderMapper;
import com.itheima.mapper.UserMapper;
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.Before;
import org.junit.Test;import java.io.IOException;
import java.io.InputStream;
import java.util.List;public class MyBatisTest2 {private OrderMapper mapper;@Beforepublic void before() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession(true);mapper = sqlSession.getMapper(OrderMapper.class);}@Testpublic void test(){Order orders = mapper.findByOid(1);System.out.println(orders);}@Testpublic void test1(){List orders = mapper.findByUid(1);for (Order order : orders) {System.out.println(order);}}}
OrderMapper
接口实现代码:
@Select("select * from orders o,user u where uid=#{uid} and o.uid=u.id")@Results({@Result(column = "id",property = "id"),@Result(column = "ordertime",property = "ordertime"),@Result(column = "total",property = "total"),@Result(property = "user", //要封装的属性名称column = "uid", //根据那个字段去查询user表的数据javaType = User.class, //要封装的实体类型//select属性 代表查询那个接口的方法获得数据one = @One(select = "com.itheima.mapper.UserMapper.findById"))})public List findByUid(int uid);
3.多对多:
完善一个中间类Role
package com.itheima.domain;public class Role {private int id;private int roleId;private String roleName;private String roleDesc;private Order order;private User user;public int getRoleId() {return roleId;}public void setRoleId(int roleId) {this.roleId = roleId;}public Order getOrder() {return order;}public void setOrder(Order order) {this.order = order;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getRoleName() {return roleName;}public void setRoleName(String roleName) {this.roleName = roleName;}public String getRoleDesc() {return roleDesc;}public void setRoleDesc(String roleDesc) {this.roleDesc = roleDesc;}@Overridepublic String toString() {return "Role{" +"id=" + id +", roleId=" + roleId +", order=" + order +", user=" + user +'}';}
}
Role---> user---->orders:一个配送员对应多个订单号和客户信息
测试类实现代码:
@Testpublic void test(){List list = roleMapper.findAll();for (Role role:list){System.out.println(role);}}
接口实现代码:
@Select("select * from sys_user_role ur,user u,orders o where ur.userID=u.id and ur.roleId=o.id")@Results({@Result(column = "userID",property = "id"),@Result(column = "roleId",property = "roleId"),@Result(property = "user", //要封装的属性名称column = "userID", //根据那个字段去查询user表的数据javaType = User.class, //要封装的实体类型//select属性 代表查询那个接口的方法获得数据many = @Many(select = "com.itheima.mapper.UserMapper.findById")),@Result(property = "order",column = "roleId",javaType = Role.class,many = @Many(select = "com.itheima.mapper.OrderMapper.findByOid"))})List findAll();
Role{id=1, roleId=1, order=Order{id=1, ordertime=Sat Oct 29 03:12:21 CST 2022, total=3000.0, user=User{id=1, username='张三', password='123456', birthday=null, roleList=null}}, user=User{id=1, username='张三', password='123456', birthday=null, roleList=null}}
Role{id=1, roleId=2, order=Order{id=2, ordertime=Fri Oct 28 08:00:00 CST 2022, total=5800.0, user=User{id=1, username='张三', password='123456', birthday=null, roleList=null}}, user=User{id=1, username='张三', password='123456', birthday=null, roleList=null}}
Role{id=2, roleId=2, order=Order{id=2, ordertime=Fri Oct 28 08:00:00 CST 2022, total=5800.0, user=User{id=1, username='张三', password='123456', birthday=null, roleList=null}}, user=User{id=2, username='李四', password='123456', birthday=null, roleList=null}}
Role{id=2, roleId=3, order=Order{id=3, ordertime=Sat Oct 29 03:13:20 CST 2022, total=323.0, user=User{id=2, username='李四', password='123456', birthday=null, roleList=null}}, user=User{id=2, username='李四', password='123456', birthday=null, roleList=null}}
四、一级缓存、二级缓存
1.mybatis中为什么会存在缓存:
缓存的意义是将用户查询的数据放入缓存(内存)中,用户再去查询相应的数据就不会直接从关系数据库中查询,直接从缓存中查询,从而提高了并发开发中的系统的性能问题
2.Mybits的一级缓存是一个sqlSession级别的缓存,只能访问自己的一级缓存数据,而二级缓存是Mapper级别的缓存,是跨SQL Session的不同sqlSession是可以共享的缓存数据;
3.一级缓存的原理:
第一次发出查询请求,sql 查询的结果写入SqlSession的一级缓存当中,缓存使用的数据结构是一个map
key : hashcode + sql + sql输入参数 + 输出参数 (sql的唯一标识)
value : 用户信息
同一个SqlSession再次发出相同的sql,就会从缓存中读取而不走数据库,如果两次操作之间出现commit(修改、输出、添加)操作,那么本SqlSession中一级缓存区域全部清空,下次再去缓存中查不到所以要从数据库中查询,从数据库再写入一级缓存。
https://blog.csdn.net/zy_zhangruichen/article/details/122592504http://一级缓存原理
Mybatis 中一级缓存需要注意的点 :
Mybatis 中一级缓存是默认开启的,不需要手动配置。
Mybatis 和 Spring 整合后进行 mapper 代理开发后,不支持一级缓存。Mybatis 和 Spring 整合,Spring 按照 mapper 的模板去生成 mapper 代理对象,模板中在最后会统一关闭 SqlSession。
4.二级缓存原理:
二级缓存的范围是mapper级别(mapper同一个命名空间),mapper以命名空间为单位创建缓存数据结构,结构是map
(1)需要在Mybatis的配置文件中
标签中配置二级缓存:
(2)Mybatis的二级缓存的范围是mapper级别的,因此我们mapper如果想要使用二级缓存,还需要在对应的映射文件中配置
标签
五、Mybatis和Hibrnate区别
1.Hibrnate:使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以Hibernate是全自动ORM映射框架;
2. Mybits:在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以Mybits是半自动ORM映射框架;但mybits可以通过xml或注解方式灵活配置要运行的sql语句并将java对象和sql语句映射成最终执行的sql,最后将sql执行的结果在映射生成java对象
3.Mybits学习门槛低,简单易学,程序员直接编写原生态sql,可以严格控制sql执行性灵活;但是mybits无法做到与数据库无关性,如果要实现支持多种数据库软件则需要自定义多套sql映射文件,工作量较大
4.Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如
需求固定的定制化软件)如果用 hibernate 开发可以节省很多代码,提高效率。但是Hibernate 的缺点是学习门槛高,要精通门槛更高,而且怎么设计 O/R 映射,在性能和对象模型之间如何权衡,以及怎样用好 Hibernate 需要具有很强的经验和能力才行。总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
六、分页查询插件
1.需要在pom.xml文件中导入依赖
com.github.pagehelper pagehelper 3.7.5 com.github.jsqlparser jsqlparser 0.9.1
2.配置分页插件: 在sqlMapConfig.xml文件中配置
3.分页插件的使用
测试类中的代码实现:
@Testpublic void test3() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);//设置分页相关参数 当前页+每页显示的条数PageHelper.startPage(1,3);List userList = mapper.findAll();for (User user : userList) {System.out.println(user);}//获得与分页相关参数PageInfo pageInfo = new PageInfo(userList);System.out.println("当前页:"+pageInfo.getPageNum());System.out.println("每页显示条数:"+pageInfo.getPageSize());System.out.println("总条数:"+pageInfo.getTotal());System.out.println("总页数:"+pageInfo.getPages());System.out.println("上一页:"+pageInfo.getPrePage());System.out.println("下一页:"+pageInfo.getNextPage());System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());sqlSession.close();}
测试结果:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
七、延时加载
1.什么是延迟加载:Mybatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询,例如进行一对多查询时,之查询出一方数据,当程序中需要多方数据时,mybatis在发出sql语句进行查询,这样延迟加载就可以减少对数据库压力。Mybatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句
2.加载时机:直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。侵入式延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。深度延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。
注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
3.入侵式延迟加载
①、sqlMapConfig.xml配置文件,首先开启延迟加载,然后再配置侵入式加载
八、Mybits中#与$的区别
4.深入式延迟加载
sqlMapConfig.xml配置文件,首先开启延迟加载,然后再配置深度加载
延迟加载