回顾
1. 嵌套查询
将多表的联合,拆成多个单表查询,在通过mybatis嵌套组合
步骤:一对一举例
1)根据订单id查询订单
2)根据订单uid查询用户
3)最后由mybatis嵌套组合
2. 加载策略
模型在关联时,是否要查询所管理的数据模型
立即加载:一对一
延迟加载:一对多、多对多
3. 缓存机制
提高查询效率
一级缓存:不需要我们做任何配置,底层就是map集合
二级缓存:需要在映射文件配置<cache></cache> ,给实体类实现序列化接口【了解】
4. 核心配置文件回顾
所有的配置文件,每一个标签必须加中文注释...
MyBatis注解&综合练习
今日目标
1. 注解开发
单表【重点】
多表【了解】
2. 综合案例练习
查询所有
分页查询【重点:3遍】
一 MyBatis注解
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
1.1 MyBatis常用注解
* @Insert:实现新增,代替了<insert></insert>
* @Update:实现更新,代替了<update></update>
* @Delete:实现删除,代替了<delete></delete>
* @Select:实现查询,代替了<select></select>
* @Result:实现结果集封装,代替了<result></result>
* @Results:可以与@Result 一起使用,封装多个结果集,代替了<resultMap></resultMap>
* @One:实现一对一结果集封装,代替了<association></association>
* @Many:实现一对多结果集封装,代替了<collection></collection>
环境搭建
1.2 MyBatis单表操作【重点】
需求:基于user模块通过注解实现,增删改查
① UserMapper接口
public interface UserMapper {
// 查询所有
@Select("select id as uid,username as uname,birthday as bir , sex as gender, address as addr from user")
@Results({ // resultMap标签手动映射
@Result(column = "uid",property = "id",id=true), // result标签映射封装
@Result(column = "uname",property = "username"),
@Result(column = "bir",property = "birthday"),
@Result(column = "gender",property = "sex"),
@Result(column = "addr",property = "address")
})
public List<User> findAll();
// id查询
@Select("select * from user where id = #{id}")
public User findById(Integer id);
// 新增
@Insert("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
public void save(User user);
// 修改(动态sql还是推荐使用xml)
@Update("update user set username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} where id = #{id}")
public void update(User user);
// 删除
@Delete("delete from user where id = #{id}")
public void delete(Integer id);
}
② 测试
public class UserMapperTest extends BaseMapperTest {
// 单表测试
@Test
public void test01() throws Exception {
// 获取代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 查询所有
List<User> list = userMapper.findAll();
System.out.println(list);
// 查询一个
/* User user = userMapper.findById(41);
System.out.println(user);*/
// 新增
/* User user = new User();
user.setUsername("吉克隽逸");
user.setBirthday(new Date());
user.setSex("女");
user.setAddress("彝族....");
userMapper.save(user);*/
// 更新
/* User user = new User();
user.setId(53);
user.setUsername("迪丽热巴");
user.setBirthday(new Date());
user.setSex("女");
user.setAddress("新疆....");
userMapper.update(user);*/
// 删除
// userMapper.delete(53);
}
}
1.3 MyBatis多表操作【了解】
注解多表操作是基于嵌套查询来实现
1.3.1 一对一查询
需求:查询一个订单,与此同时查询出该订单所属的用户
一对一查询语句
SELECT * FROM orders where id = #{id};
SELECT * FROM `user` WHERE id = #{订单的uid};
① OrderMapper接口
public interface OrderMapper {
// 一对一嵌套注解
@Select("select * from orders where id = #{id}")
@Results({
@Result(column = "id",property = "id",id=true),
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "money",property = "money")
})
public Order findByIdWithUser(Integer id);
}
② UserMapper接口
public interface UserMapper {
// id查询
@Select("select * from user where id = #{id}")
public User findById(Integer id);
}
③ 注解嵌套
④ 测试
public class OrderMapperTest extends BaseMapperTest { // 继承父类,就可以直接使用 父类的方法和成员变量了
// 一对一嵌套测注解试
@Test
public void test01() throws Exception {
// 获取代理对象
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
// 根据id查询
Order order = orderMapper.findByIdWithUser(1);
System.out.println(order);
}
}
1.3.2 一对多查询
需求:查询一个用户,与此同时查询出该用户具有的订单
一对多查询语句
SELECT * FROM `user` where id = #{id};
SELECT * FROM orders where uid = #{用户id};
① UserMapper接口
public interface UserMapper {
// 一对多注解嵌套查询
@Select("select * from user where id = #{id}")
@Results({ // resultMap标签手动映射
@Result(column = "id",property = "id",id=true), // result标签映射封装
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address")
})
public User findByIdWithOrders(Integer id);
}
② OrderMapper接口
public interface OrderMapper {
@Select("select * from orders where uid = #{id}")
public List<Order> findByUid(Integer uid);
}
③ 注解嵌套
④ 测试
// 一对多注解测试
@Test
public void test02() throws Exception {
// 获取代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findByIdWithOrders(41);
System.out.println(user);
System.out.println(user.getOrderList());
}
1.3.3 多对多查询
需求:查询用户同时查询出该用户的所有角色
多对多查询语句
SELECT * FROM `user` where id = #{id};
SELECT * FROM role r INNER JOIN user_role ur ON r.`id` = ur.`rid`
WHERE ur.`uid` = #{用户id};
① UserMapper接口
public interface UserMapper {
// 多对多注解嵌套查询
@Select("select * from user where id = #{id}")
@Results({ // resultMap标签手动映射
@Result(column = "id",property = "id",id=true), // result标签映射封装
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address")
})
public User findByIdWithRoles(Integer id);
}
② RoleMapper接口
public interface RoleMapper {
@Select("SELECT * FROM role r INNER JOIN user_role ur ON ur.`rid` = r.`id` WHERE ur.`uid` =#{uid}")
@Results({
@Result(column = "id",property = "id",id=true),
@Result(column = "role_name",property = "roleName"),
@Result(column = "role_desc",property = "roleDesc")
})
public List<Role> findByUid(Integer uid);
}
③ 注解嵌套
④ 测试
// 多对多注解测试
@Test
public void test03()throws Exception{
// 获取代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findByIdWithRoles(41);
System.out.println(user);
System.out.println(user.getRoleList());
}
1.4 局部延迟加载
不管是一对多还是多对多 ,在注解配置中都有fetchType的属性
* fetchType = FetchType.LAZY 表示懒加载
* fetchType = FetchType.EAGER 表示立即加载
* fetchType = FetchType.DEFAULT 表示使用全局配置
1.5 二级缓存
配置SqlMapConfig.xml文件开启二级缓存的支持
<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
为true代表开启二级缓存;为false代表不开启二级缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>
在Mapper接口中使用注解配置二级缓存
@CacheNamespace
public interface UserMapper {...}
1.6 知识小结
1. 注解开发和xml配置相比,从开发效率来说,注解编写更简单,效率更高。
2. 从可维护性来说,注解如果要修改,必须修改源码,会导致维护成本增加。xml维护性更强。
* 经验:单表简单CRUD可以使用注解、多表及动态sql你就用xml
二 MyBatis案例练习
2.1 编程风格
浏览器:Chrome、Firefox
包目录:cn(com).公司名.项目名(都是小写)
类:大驼峰式命名
方法名:小驼峰式命名
帅哥用啥你用啥,帅哥写啥你写啥
2.2 环境搭建
① 准备数据库和表
② 创建web工程,导入jar包
③ 导入页面资源
④ 创建三层包结构
⑤ 导入mybatis相关初始化配置
⑥ 编写中文过滤器
@WebFilter("/*")
public class EncodeFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
// 接收请求字符集
servletRequest.setCharacterEncoding("utf-8");
// 放行
chain.doFilter(servletRequest, servletResponse);
}
public void destroy() {
}
}
⑦ 测试环境
2.3 查询所有
2.3.1 需求和效果实现
通过三层架构+接口+mybatis,查询员工信息,在页面展示
2.3.2 需求分析
2.3.3 代码实现
① index.jsp
<a href="${pageContext.request.contextPath}/EmpServlet?action=findAll">员工列表</a>
② Emp实体类
public class Emp {
private Integer id;
private String ename;
private String sex;
private String joindate; // 通过字符串也可以表示日期
private Double salary;
private String address;
}
③ EmpServlet
@WebServlet("/EmpServlet")
public class EmpServlet extends HttpServlet {
// 重写service方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取action请求参数
String action = request.getParameter("action");
// 判断
if (action.equals("findAll")) {
this.findAll(request, response);
}
}
// 查询所有
protected void findAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.调用service查询
EmpService empService = new EmpServiceImpl();
List<Emp> list = empService.findAll();
// 2.将list写入request域
request.setAttribute("list", list);
// 3.转发
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
}
④ EmpServiceImpl
public class EmpServiceImpl implements EmpService {
@Override
public List<Emp> findAll() {
// 通过mybatis工具类获取sqlSession
SqlSession sqlSession = MyBatisUtils.openSession();
// 创建EmpDao代理对象
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
// 查询
List<Emp> list= empDao.findAll();
// 关闭sqlSession
MyBatisUtils.close(sqlSession);
return list;
}
}
⑤ EmpDao(接口+映射)
⑥ list.jsp
<c:forEach items="${list}" var="emp">
<tr>
<td>${emp.id}</td>
<td>${emp.ename}</td>
<td>${emp.joindate}</td>
<td>${emp.salary}</td>
<td>${emp.address}</td>
</tr>
</c:forEach>
2.4 分页查询【重点☆☆☆☆】
2.4.1 导入数据
insert into `emp`(`id`,`ename`,`sex`,`joindate`,`salary`,`address`) values(6,'王昭君','女','2010-12-17',28500,'北京'),(7,'刘备','男','2014-07-18',24500,'广州'),(8,'小二郎','男','2004-11-23',30000,'广州'),(9,'小龙女','女','2009-05-18',50000,'深圳'),(10,'貂蝉','女','2014-07-30',15000,'深圳'),(11,'刘三','男','2019-06-23',11000,'上海'),(12,'李逵','男','2012-07-05',9500,'广州'),(13,'李楠','女','2012-07-05',11500,'北京'),(14,'小白龙','男','2011-09-18',30000,'深圳'),(15,'西施','女','2015-07-06',13000,'北京'),(16,'刘茹','女','2019-08-07',6000,'北京');
2.4.2 分页介绍
在实际开发中,如果数据库数据太多,一般我们需要进行分页查询,提高效率...
分页技术实现
物理分页:数据库实现(MySQL、Oracle)
内存分页:查询全部,在通过java代码进行分页
今天我们来使用MySQL操作物理分页
* 语法:
select * from 表名 limit 开始索引,每页个数;
* 模拟百度分页,一个显示5条,数据库共有16条记录
第一页
select * from 表名 limit 0,5;
第二页
select * from 表名 limit 5,5;
第三页
select * from 表名 limit 10,5;
第四页
select * from 表名 limit 15,5;
* 索引公式
开始索引=(当前页-1) × 每页个数
* 如何获得当前页和每页个数?
前端页提供的
2.4.3 需求和效果实现
通过mysql物理分页,一个显示5条,数据库共有16条记录
2.4.3 需求分析
后端代码流程图
2.4.4 代码实现
① index.jsp
② PageBean
public class PageBean<E> {
private Integer totalCount; // 总记录数
private Integer totalPage;// 总页数
private List<E> list; // 结果集
private Integer currentPage; // 当前页
private Integer pageSize; // 每页个数
}
③ EmpServlet
EmpService empService = new EmpServiceImpl();
// 分页查询
protected void findByPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.接收请求参数
String currentPageStr = request.getParameter("currentPage");
String pageSizeStr = request.getParameter("pageSize");
// 2.转为整型
int currentPage = Integer.parseInt(currentPageStr);
int pageSize = Integer.parseInt(pageSizeStr);
// 3.调用service查询
PageBean<Emp> pb = empService.findByPage(currentPage, pageSize);
// 4.设置到request域
request.setAttribute("pb", pb);
// 5.转发
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
④ EmpServiceImpl
@Override
public PageBean<Emp> findByPage(int currentPage, int pageSize) {
// 通过mybatis工具类获取sqlSession
SqlSession sqlSession = MyBatisUtils.openSession();
// 创建EmpDao代理对象
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
// 1.创建 PageBean
PageBean<Emp> pageBean = new PageBean<>();
// 2.封装当前页和每页个数
pageBean.setCurrentPage(currentPage);
pageBean.setPageSize(pageSize);
// 3.调用dao查询总记录数并封装
Integer totalCount = empDao.findCount();
pageBean.setTotalCount(totalCount);
// 4.计算并封装总页数
int totalPage = (int)Math.ceil(totalCount * 1.0/ pageSize);
pageBean.setTotalPage(totalPage);
// 5.计算开始索引
int index = (currentPage - 1) * pageSize;
// 6.调用dao查询结果集并封装
List<Emp> list = empDao.findList(index,pageSize);
pageBean.setList(list);
// 关闭sqlSession
MyBatisUtils.close(sqlSession);
// 7.返回pageBean对象
return pageBean;
}
⑤ EmpDao(接口+映射)
⑥ list.jsp
<body>
<table border="1" cellpadding="0" cellspacing="0" width="600px">
<tr>
<td>姓名</td>
<td>性别</td>
<td>入职日期</td>
<td>薪资</td>
<td>住址</td>
</tr>
<c:forEach items="${pb.list}" var="emp">
<tr>
<td>${emp.id}</td>
<td>${emp.ename}</td>
<td>${emp.joindate}</td>
<td>${emp.salary}</td>
<td>${emp.address}</td>
</tr>
</c:forEach>
</table>
<table>
<tr>
<td style="text-align: left">总共检索到${pb.totalCount}条记录,共分${pb.totalPage}页</td>
</tr>
</table>
<table id="page">
<tr>
<c:if test="${pb.currentPage>1}">
<td style="width:50px">
<a style="text-decoration: none" href="${pageContext.request.contextPath}/EmpServlet?action=findByPage¤tPage=${pb.currentPage-1}&pageSize=5">上一页</a>
</td>
</c:if>
<c:forEach begin="1" end="${pb.totalPage}" var="page">
<c:if test="${page == pb.currentPage}">
<td bgcolor="#ffd700">
<a style="text-decoration: none" href="${pageContext.request.contextPath}/EmpServlet?action=findByPage¤tPage=${page}&pageSize=5">${page}</a>
</td>
</c:if>
<c:if test="${page != pb.currentPage}">
<td>
<a style="text-decoration: none" href="${pageContext.request.contextPath}/EmpServlet?action=findByPage¤tPage=${page}&pageSize=5">${page}</a>
</td>
</c:if>
</c:forEach>
<c:if test="${pb.currentPage < pb.totalPage}">
<td style="width:50px">
<a style="text-decoration: none" href="${pageContext.request.contextPath}/EmpServlet?action=findByPage¤tPage=${pb.currentPage+1}&pageSize=5">下一页</a>
</td>
</c:if>
</tr>
</table>
</body>
总结
1. mybatis注解 晚上敲
单表【重点】
@Insert
@Update
@Delete
@Select
@Results
@Result
多表【休息日再敲....】
2. 综合案例 下午敲
编程风格(跟帅哥一致...)
环境搭建
查询所有
JDBC
分页查询【晚上敲】
3. 明天课程非常轻松...