mybatis03
内容介绍
- mybatis的注解开发
- 掌握单表的操作
- 理解或者了解多表的操作
- web案例
- 查询所有
- 分页查询
一 配置文件【了解】
properties【了解】
抽取数据库的连接信息
1.将数据库的连接信息放入到一个properties文件中
jdbc.url=jdbc:mysql:///mybatisdb
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=1234
2.在核心配置文件中通过properties标签导入
<!--加载src目录下的db.properties文件-->
<properties resource="db.properties"/>
3.在数据源中就可以通过el表达式的方式去获取数据库连接的4个参数
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
连接池【了解】
mybatis提供了三个方式:
- POOLED:可以用,使用mybatis内置的连接池 PooledDataSource
- UNPOOLED:凑合用,使用mybatis内置的连接池 UnPooledDataSource(伪连接池),每次使用连接的时候都使用jdbc的原始操作获取一个连接
- JNDI:一般不用
等我们学习完spring框架之后,数据源(连接池)交给spring管理了
配置日志【了解】
<setting name="logImpl" value="STDOUT_LOGGING"/>
二 mybatis注解
注解开发是企业的一个趋势,能够简化代码开发.提高开发效率
mybatis学习注解的要求
对于单表CRUD操作,【掌握】
对于多表复杂查询操作,【理解】
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 用户表的CRUD(掌握)
public interface UserDao {
//@Select("select * from user where id = #{uid}")
@Select("select id uid,username uname,birthday,sex gender,address addr from user where id = #{uid}")
@Results(id = "BaseResultMap",value = {
//主键字段和属性的对应
@Result(property = "id",column = "uid",id = true),
//其他字段和属性的对应
@Result(property = "username",column = "uname"),
@Result(property = "birthday",column = "birthday"),
@Result(property = "sex",column = "gender"),
@Result(property = "address",column = "addr"),
})
User findById(int id);
//@Select("select * from user")
@Select("select id uid,username uname,birthday,sex gender,address addr from user")
@ResultMap("BaseResultMap")
List<User> findAll();
@Delete("delete from user where id = #{uid}")
void delete(int id);
@Insert("insert into user values (null,#{username},#{birthday},#{sex},#{address})")
void save(User user);
@Update("update user set username = #{username},address = #{address} where id = #{id}")
void update(User user);
}
2 多表嵌套查询(了解)
一对一
需求:查询一个订单,与此同时查询出该订单所属的用户
步骤分析:
- 在订单类中添加用户属性
- 在UserDao中编写:User findUserByUid(int uid)
- 在方法上添加注解
- 编写OrderDao接口,提供方法: Order findOrderByOidWithUser(int oid);
- 在方法上添加注解,完成嵌套查询:通过查询结果中uid,调用UserDao.findUserByUid(int uid);
代码实现:
1.在UserDao中添加方法
@Select("select * from user where id = #{uid}")
User findUserByUid(int uid);
2.在OrderDao中添加方法
public interface OrderDao {
@Select("select * from orders where id = #{oid}")
//嵌套查询
@Results(value = {
@Result(property = "id",column = "id",id = true),
@Result(property = "orderTime",column = "ordertime"),
@Result(property = "money",column = "money"),
@Result(property = "user",column = "uid",javaType = User.class,
one = @One(select = "com.itheima.dao.UserDao.findUserByUid")
)
})
Order findOrderByOidWithUser(int oid);
}
一对多
需求:查询一个用户,与此同时查询出该用户具有的订单
步骤分析:
- 先在用户实体类中提供订单的集合属性
- 在OrderDao中编写:List findAllOrdersByUid(int uid);
- 使用注解配置
- 在UserDao中编写一个方法: User findUserByUidWithOrders(int uid);
- 配置一对多的嵌套查询,通过用户id作为条件,调用OrderDao.findAllOrdersByUid(int uid)
代码实现:
1.在OrderDao中添加的方法
@Select("select * from orders where uid = #{uid}")
List<Order> findAllOrdersByUid(int uid);
2.在UserDao中添加的方法
@Select("select * from user where id = #{uid}")
@Results(value = {
@Result(property = "id",column = "id",id = true),
@Result(property = "username",column = "username"),
@Result(property = "birthday",column = "birthday"),
@Result(property = "sex",column = "sex"),
@Result(property = "address",column = "address"),
@Result(property = "orderList",column = "id",
many = @Many(select = "com.itheima.dao.OrderDao.findAllOrdersByUid")
)
})
User findUserByUidWithOrders(int uid);
扩展:懒加载
使用局部懒加载.为了测试方便需要在核心配置文件中配置什么方法都不触发懒加载
扩展:获取新增记录的主键
扩展:动态sql
https://blog.csdn.net/k393393/article/details/94614552
三案例
1 查询所有
需求:在页面上展示所有用户
在项目的首页上有一个超链接,“查询所有用户”,一点此连接就可以将所有的用户数据展示在页面上.
步骤分析
- 搭建环境
- 新建一个web模块(mybatis_case),导入依赖(驱动,mybatis,日志,jstl)
- 导入日志配置文件
- 复制mybatis的核心配置文件,复制MybatisUtils,复制User实体类
- 创建包结构,web,service,dao及其需要的类或者接口
代码实现
-
在index.jsp上提供连接
-
FindAllServlet
package com.itheima.web.servlet; import com.itheima.domain.User; import com.itheima.service.UserService; import com.itheima.service.impl.UserServiceImpl; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @WebServlet(value = "/findAll") public class FindAllServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.创建service对象,调用service.findAll方法,接受返回值:list UserService userService = new UserServiceImpl(); List<User> list = userService.findAll(); //2.将list放入request域中 request.setAttribute("list",list); //3.请求转发到list.jsp request.getRequestDispatcher("/list.jsp").forward(request,response); } }
-
在UserService中添加findAll方法
package com.itheima.service.impl; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.service.UserService; import com.itheima.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import java.util.List; public class UserServiceImpl implements UserService { @Override public List<User> findAll() { //1.获取sqlSession对象和UserDao对象 SqlSession sqlSession = MybatisUtils.openSession(); UserDao userDao = sqlSession.getMapper(UserDao.class); //2.调用userDao.findAll(),接受返回值 list List<User> list = userDao.findAll(); //3.提交事务和释放资源 MybatisUtils.commitAndClose(sqlSession); //4.返回list return list; } }
-
在UserDao中添加findAll方法,提供对应的映射文件
package com.itheima.dao; import com.itheima.domain.User; import java.util.List; public interface UserDao { List<User> findAll(); }
<?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.itheima.dao.UserDao"> <select id="findAll" resultType="user"> select * from user </select> </mapper>
-
创建list.jsp
2 分页查询
需求:以分页的形式在页面上展示所有用户
分析:
分页的分类:
- 物理分页:每次去数据库中只查询当前页的数据,保证数据的及时性.
- 逻辑分页:一次性将所有的数据查询出来,放入内存中,若需要某页数据的话再从内存去读取即可.
mysql中实现物理分页需要使用:limit
limit m,n;
m:开始的索引
n:向后查询的记录数
m的规律:
若要做分页查询:servlet需要获取俩数据:查询页码和每页显示的条数
页面上需要展示的数据:
- 当前页的数据集合 limit m,n
- 总记录数
- 总页数
- 当前页
- 每页显示的条数
为了方便在各层之间进行数据的传递,一般会把上面的5个基本信息封装成一个实体类,例如:PageBean
pageBean中的字段和方法
- List dataList;//当前页的数据集合 limit m,n
- int totalCount;//总记录数
- int totalPage;//总页数
- int pageNumber;//当前页
- int pageSize;//每页显示的条数
方法:
- 重写getTotalPage的方法
- 总记录数和每页显示的条数进行计算
- 提供一个getStartIndex的方法(开始索引)
- return (pageNumber-1)*pageSize;
- 提供一个带有页码和每页显示的条数的构造器
PageBean
public class PageBean<T> {
private List<T> dataList;//当前页的数据集合 limit m,n
private int totalCount;//总记录数
//int totalPage;//总页数
private int pageNumber;//当前页
private int pageSize;//每页显示的条数
public PageBean(int pageNumber, int pageSize) {
this.pageNumber = pageNumber;
this.pageSize = pageSize;
}
//重写获取总页数的方法
public int getTotalPage() {
//可以使用三元运算符 或者 if判断
return (int)Math.ceil(totalCount*1.0/pageSize);
}
//获取开始索引的方法
public int getStartIndex(){
return (pageNumber - 1)*pageSize;
}
public List<T> getDataList() {
return dataList;
}
public void setDataList(List<T> dataList) {
this.dataList = dataList;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
/*public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}*/
public int getPageNumber() {
return pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
}
步骤分析:
代码实现:
-
修改index.jsp
-
FindByPageServlet
package com.itheima.web.servlet; import com.itheima.domain.User; import com.itheima.service.UserService; import com.itheima.service.impl.UserServiceImpl; import com.itheima.utils.PageBean; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(value = "/findByPage") public class FindByPageServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.设置默认页和默认的条数 int pageNumber = 1; int pageSize = 5; //2.获取页面上传递过来的页码 String pageNumberStr = request.getParameter("pageNumber"); //3.若有页码的话,就需要修改要查询的页码 try { pageNumber = Integer.parseInt(pageNumberStr); } catch (NumberFormatException e) { } //4.创建UserService对象,调用方法,查询当前页的所需要的数据,返回值:PageBean UserService userService = new UserServiceImpl(); PageBean<User> pageBean = userService.findByPage(pageNumber,pageSize); //5.将pageBean放入request域中 request.setAttribute("pb",pageBean); //6.请求转发到list_page.jsp request.getRequestDispatcher("/list_page.jsp").forward(request,response); } }
-
在UserService中添加findByPage方法
@Override //查询指定页上的所有数据 public PageBean<User> findByPage(int pageNumber, int pageSize) { //1.创建pageBean对象 PageBean<User> pb = new PageBean<>(pageNumber,pageSize); //2.获取sqlSession和UserDao对象 SqlSession sqlSession = MybatisUtils.openSession(); UserDao userDao = sqlSession.getMapper(UserDao.class); //3.调用dao的方法,查询总记录数 int totalCount = userDao.findTotalCount(); //4.调用dao的方法,查询当前页的数据集合 List<User> dataList = userDao.findByPage(pb.getStartIndex(),pb.getPageSize()); //5.别忘记释放资源 MybatisUtils.commitAndClose(sqlSession); //6.将总记录数和当前页的数据集合封装到pageBean中 pb.setTotalCount(totalCount); pb.setDataList(dataList); //7.返回pagebean return pb; }
-
在UserDao中添加俩方法及其映射文件
int findTotalCount(); List<User> findByPage(@Param("startIndex") int startIndex,@Param("pageSize") int pageSize);
<select id="findTotalCount" resultType="int"> select count(*) from user </select> <select id="findByPage" resultType="user"> select * from user limit #{startIndex},#{pageSize} </select>
-
复制list.jsp,重命名为list_page.jsp
list.jsp编写步骤
- 先展示当前页的数据集合 遍历pb.dataList
- 处理分页条
- 先展示第几页 共几页
- 处理首页和上一页
- 再处理尾页和下一页
- 处理所有页码,使用普通for循环 从1展示到总页数
- 若是当前页的话,不加超链接,加上一个class=“active”
3 前五后四
![image-20210516161305783](07.mybatis03.assets/image-20210516161305783.png)
//修改pagebean代码
private int start;
private int end;
public int getStart() {
calcStartAndEnd();
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
//计算开始页和结束页
private void calcStartAndEnd(){
//1.判断总页数<=10
if (getTotalPage()<=10) {
start = 1;
end = getTotalPage();
}else {
start = pageNumber - 5;
end = pageNumber + 4;
//2.处理溢出
if (start < 1) {
start = 1;
end = 10;
}
if(end > getTotalPage()){
start = getTotalPage() - 9;
end = getTotalPage();
}
}
}
4 扩展:新增操作注意事项
在servlet中封装数据的时候,若使用BeanUtils封装会出现问题,原因是:BeanUtils不能将传过来的生日字符串转换成日期类型.解决方案如下:
- 方案1:不使用BeanUtils封装
- 方案2:自定义字符串转日期的转换器
以下为自定义转换器的编写和使用步骤
-
自定义类,实现
org.apache.commons.beanutils.Converter
接口,重写convert方法public Object convert(Class aClass, Object o) { //1.将参数o强转为字符串 //2.将字符串转换成日期对象返回 return null; }
-
在调用BeanUtils封装数据之前,先注册自定义类型转换器:ConvertUtils.register(类型转换器对象, Date.class);