WEB综合案例
环境准备
先创建好web maven工程
搭建好mybatis的框架
(核心配置文件,pom依赖关系)
导入相关的数据库依赖,tomcat依赖还有封装好的sqlSessionFactoryUtils
还有我们写好的页面需要的
准备好数据库
查询所有
在HTML中还要将数据设置到模型上,这样模型一变,显示就变了
DAO
/** * 查询所有 * @return */ @Select("SELECT * FROM tb_brand") @ResultMap("brandResultMap") List<Brand> selectAll();
这里因为数据库中的列名和实体类中的名字不一样所以要用resultmap
<mapper namespace="com.ember.mapper.BrandMapper"> <resultMap id="brandResultMap" type="brand"> <result column="brand_name" property="brandName"></result> <result column="company_name" property="companyName"></result> </resultMap> </mapper>
service
这里和之前有点不一样,我们在service是通过写一个接口BrandService再写实现类来完成接口来写的,不像原来直接写成一个实现类(为了配合框架将service和web层解耦合)
public interface BrandService { /*查询所有*/ List<Brand> selectAll(); }
public class BrandServiceImpl implements BrandService { //.创建对应的sqlsessionfactory对象 SqlSessionFactory factory= SqlSessionFactoryUtils.getSqlSessionFactory(); @Override public List<Brand> selectAll() { //.获取sqlsession对象 SqlSession sqlSession = factory.openSession(); //.获取brandmapper BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); //.调用方法 List<Brand> brands = mapper.selectAll(); //.释放资源 sqlSession.close(); return brands; } }
Web
因为要用到json
所以我们要导入依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency>
@WebServlet(name = "SelectAllServlet", value = "/selectAllServlet") public class SelectAllServlet extends HttpServlet { private BrandService brandService=new BrandServiceImpl(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { List<Brand> brands = brandService.selectAll(); //.将集合转化为json,序列化数据 String string = JSON.toJSONString(brands); //.响应数据,数据带中文所以要设置 response.setContentType("text/json;charset=utf-8"); response.setContentType("text/json;charset=utf-8"); response.getWriter().write(string); }
随后我们执行tomcat访问selectServlet可以看到数据返回说明成功了(我们小程序中使用的api'就是这个原理)
前端代码
就是在页面中添加ajax(这里我们用的是axios所以要先导入axios.js文件)代码发送异步请求接收数据保存到data中
<script src="js/axios-0.18.0.js"></script>
mounted(){ //.当页面加载之后,发送异步请求获取数据 var _this=this; axios({ method:"get", url:"http://localhost:8080/cookie-demo/selectAllServlet" }).then(function (resp){ _this.tableData= resp.data; }) }
访问页面,数据成功显示则成功
新增品牌
三成结构代码大同小异
DAO
/*添加*/ @Insert("insert into tb_brand values(null,#{brandName},#{companyName},#{ordered},#{description},#{status})") void add(Brand brand);
service
/*添加*/ void add(Brand brand);
@Override public void add(Brand brand) { SqlSession sqlSession = factory.openSession(); BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); mapper.add(brand); sqlSession.commit(); sqlSession.close(); }
WEB
@WebServlet(name = "AddServlet", value = "/addServlet") public class AddServlet extends HttpServlet { BrandService brandService=new BrandServiceImpl(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //.接收品牌数据 BufferedReader reader = request.getReader(); String params = reader.readLine();//.json字符串 //.将字符串转为brand对象 Brand brand = JSON.parseObject(params, Brand.class); //.调用service来添加 brandService.add(brand); //.响应成功的标识 response.getWriter().write("success"); }
前端
找到对应的按钮对应的方法
因为经常要刷新页面我们直接将相关代码写到一个方法里
//.查询所有的方法 selectAll(){ var _this=this; axios({ method:"get", url:"http://localhost:8080/cookie-demo/selectAllServlet" }).then(function (resp){ _this.tableData= resp.data; }) }
那么原来写好的mounted就可以改成
mounted(){ //.当页面加载之后,发送异步请求获取数据 this.selectAll(); /* var _this=this; axios({ method:"get", url:"http://localhost:8080/cookie-demo/selectAllServlet" }).then(function (resp){ _this.tableData= resp.data; })*/ }
addBrand() { var _this=this //.发送ajax请求,添加数据 axios({ method: "post", url: "http://localhost:8080/cookie-demo/addServlet", data:_this.brand }).then(function (resp){ if(resp.data=="success"){ //.添加成功 //1.关闭窗口 _this.dialogVisible = false; //2.重新查询数据 _this.selectAll(); //3.弹出成功提示 _this.$message({ message: '恭喜你,添加成功', type: 'success' }); } }) }
servlet代码优化
问题:WEB层的servlet个数太多了,不利于管理和编写
优化:
将servlet进行归类,对于同一个实体操作方法,写到一个servlet中。比如Brand实体对应BrandServlet,User->UserServlet
我们现在就是要实现根据路径来实现方法分发,那么httpservlet这种根据请求方式实现方法分发的就不能用了
/*替换httpservlet的,根据请求的最后一段路径进行方法分发*/ public class BaseServlet extends HttpServlet { //.根据请求的最后一段路径进行方法分发 @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //.获取请求路径 String uri = req.getRequestURI();// /cookie-demo/brand/selectAll //.获取最后一段路径(方法名) int index=uri.lastIndexOf("/"); String methodName = uri.substring(index+1); //2.执行方法 //2.1获取BrandServlet/UserServlet的字节码对象(class对象) Class<? extends BaseServlet> cls = this.getClass(); //2.2获取方法 Method对象 try { Method method= cls.getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class); try { //.2.3执行方法 method.invoke(this,req,resp); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } } }
@WebServlet("/brand/*") public class BrandServlet extends BaseServlet{ private BrandService brandService=new BrandServiceImpl(); public void selectAll(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<Brand> brands = brandService.selectAll(); //.将集合转化为json,序列化数据 String string = JSON.toJSONString(brands); //.响应数据,数据带中文所以要设置 response.setContentType("text/json;charset=utf-8"); resp.setContentType("text/json;charset=utf-8"); resp.getWriter().write(string); } public void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //.接收品牌数据 BufferedReader reader = req.getReader(); String params = reader.readLine();//.json字符串 //.将字符串转为brand对象 Brand brand = JSON.parseObject(params, Brand.class); //.调用service来添加 brandService.add(brand); //.响应成功的标识 resp.getWriter().write("success"); } }
再更改页面中的路径
修改品牌信息
这里自己完成,我做的时候有几个难点
1.如何点击修改按钮获得当前行的id传给后端
2.如何实现表单中数据的回显
解决方法
1.参考了评论区
实现代码
<el-table-column label="操作" align="center" > <template slot-scope="scope"> <el-button type="primary" @click="selectById(scope.$index,scope.row)" >修改</el-button> <el-button type="danger" @click="deleteById(scope.$index,scope.row)" >删除</el-button> </template> </el-table-column>
这里我是在data中创建了个对象用于存放后端查询后返回的数据
//修改行的数据 selectBrand:{},
selectById(index,row){ var _this=this; console.log(row.id) this.update=true; axios({ method:"get", url:"http://localhost:8080/cookie-demo/brand/selectById?id="+row.id }).then(function (resp){ _this.selectBrand=resp.data; console.log(_this.selectBrand) //.显示修改对话框 _this.update=true; }) }
2.在对话框中设置回显,你看了上面对象你可能不知道为啥要新定义一个对象存数据(为了方便回显),应为vue是模型改变显示直接改变,所以当我们的输入框对应的模型中有数据时,输入框内就会显示出数据
下面给出代码
<!--修改对话框--> <el-dialog title="提示" :visible.sync="update" width="30%" :before-close="handleClose" :rules="rules"> <el-form ref="form" :model="form" label-width="80px" > <el-form-item label="品牌名称" prop="brandName"> <el-input v-model="selectBrand.brandName" ></el-input> </el-form-item> <el-form-item label="企业名称" prop="companyName"> <el-input v-model="selectBrand.companyName"></el-input> </el-form-item> <el-form-item label="排序"> <el-input v-model="selectBrand.ordered"></el-input> </el-select> </el-form-item> <el-form-item label="备注"> <el-input type="textarea" v-model="selectBrand.description"></el-input> </el-form-item> <el-form-item label="状态"> <el-switch v-model="selectBrand.status" active-value="1" inactive-value="0"></el-switch> </el-form-item> <el-form-item> <el-button type="primary" @click="updateBrand" >更新</el-button> <el-button @click="update=false">取消</el-button> </el-form-item> </el-form> </el-dialog>
效果展示
DAO
/*根据id查询*/ @Select("SELECT * FROM tb_brand WHERE id=#{id}") @ResultMap("brandResultMap") Brand selectById(int id); /*修改数据*/ @Update("UPDATE tb_brand " + "SET " + "brand_name=#{brandName}," + "company_name=#{companyName}, " + "ordered=#{ordered}," + "description=#{description}," + "status=#{status} WHERE id=#{id}") void update( Brand brand);
service
/*根据id查询*/ Brand selectById(int id); void update(Brand brand);
@Override public Brand selectById(int id) { //.获取sqlsession对象 SqlSession sqlSession = factory.openSession(); //.获取brandmapper BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); //.调用方法 Brand brand = mapper.selectById(id); //.释放资源 sqlSession.close(); return brand; } @Override public void update(Brand brand) { SqlSession sqlSession = factory.openSession(); BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); mapper.update(brand); sqlSession.commit(); sqlSession.close(); }
servlet
public void selectById(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // int id =Integer.parseInt(req.getQueryString()); int id =Integer.parseInt(req.getParameter("id")) ; Brand brand=brandService.selectById(id); String string = JSON.toJSONString(brand); resp.setContentType("text/json;charset=utf-8"); resp.getWriter().write(string); } public void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //.接收品牌数据 BufferedReader reader = req.getReader(); String params = reader.readLine();//.json字符串 //.将字符串转为brand对象 Brand brand = JSON.parseObject(params, Brand.class); //.调用service来添加 brandService.update(brand); // 3.转发到查询所有的servlet resp.getWriter().write("success"); }
删除和批量删除
DAO
mapper
/*删除数据*/ @Delete("DELETE FROM tb_brand WHERE id=#{id}") @ResultMap("brandResultMap") void deleteById(int id); /*批量删除要用动态sql所以不能使用注解,只能使用xml*/ void deleteByIds(@Param("ids") int[] ids);
service
service接口
/*删除*/ void deleteById(int id); /*批量删除*/ void deleteByIds(int[] ids);
service实现类
@Override public void deleteById(int id) { SqlSession sqlSession = factory.openSession(); BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); mapper.deleteById(id); sqlSession.commit(); sqlSession.close(); } @Override public void deleteByIds(int[] ids) { SqlSession sqlSession = factory.openSession(); BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); mapper.deleteByIds(ids); sqlSession.commit(); sqlSession.close(); }
servlet
public void deleteById(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int id =Integer.parseInt(req.getParameter("id")) ; brandService.deleteById(id); resp.getWriter().write("success"); } /*批量删除*/ public void deleteByIds(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //.接收品牌数据 BufferedReader reader = req.getReader(); String params = reader.readLine();//.json字符串 //.将字符串转为brand对象 int[] ints = JSON.parseObject(params, int[].class); //.调用service来添加 brandService.deleteByIds(ints); //.响应成功的标识 resp.getWriter().write("success"); }
前端
绑定点击事件
在data中增加数组存放被选中的值
方法实现
deleteById(index,row){ var _this=this; this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { axios({ method:"get", url:"http://localhost:8080/cookie-demo/brand/deleteById?id="+row.id // data:"id="+_this.brand.id }).then(function (resp){ if(resp.data=="success"){ //.添加成功 //1.关闭窗口 _this.dialogFormVisible = false; //2.重新查询数据 _this.selectAll(); //3.弹出成功提示 _this.$message({ message: '恭喜你,删除成功', type: 'success' }); } }) }).catch(() => { this.$message({ type: 'info', message: '已取消删除' }); });
我们看复选框的代码可以发现每次选中就会将值存入一个数组中
所以我们批量删除可以直接从这个数组中筛选每个值的id
方法实现
deleteByIds(){ //弹出确认的提示框 this.$confirm('此操作将删除该数据, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { //用户点确定按钮 //1.创建id数组[1,2,3],从multipleSelection中获取即可 // console.log(this.multipleSelection) for (let i = 0; i < this.multipleSelection.length; i++) { let selectionElement = this.multipleSelection[i]; this.selectedIds[i]=selectionElement.id; } //2.发送ajax请求 var _this=this //.发送ajax请求,添加数据 axios({ method: "post", url: "http://localhost:8080/cookie-demo/brand/deleteByIds", data:_this.selectedIds }).then(function (resp){ if(resp.data=="success"){ //.添加成功 //1.关闭窗口 _this.dialogVisible = false; //2.重新查询数据 _this.selectAll(); //3.弹出成功提示 _this.$message({ message: '恭喜你,删除成功', type: 'success' }); } }) }).catch(() => { this.$message({ // 用户点击取消按钮 type: 'info', message: '已取消删除' }); }) }
分页查询
sql语句关键字limit
SELECT * FROM LIMIT 参数1,参数2
第一个参数是开始的开始的索引
第二个参数是查询的条目数
页面传递的参数
当前页码 i
每页显示的条数n
第一个参数=(i-1)*n
第二个参数=n
业务逻辑分析
首先完成pagebean类
//.分页查询的JavaBean import java.util.List; public class PageBean<T> { //.总记录数 private int totalCount; //.当前页的数据 private List<T> rows; public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public List<T> getRows() { return rows; } public void setRows(List<T> rows) { this.rows = rows; } }
后端逻辑
DAO
/*分页查询*/ @Select("SELECT * FROM tb_brand LIMIT #{begin},#{size}") List<Brand> selectByPage(@Param("begin")int begin,@Param("size")int size);
/*查询总记录数 * */ @Select("SELECT COUNT(*) FROM tb_brand") int selectTotalCount();
service
/*分页 * @param currentPage 当前页码 * @param pageSize每页展示条数*/ PageBean<Brand> selectByPage(int currentPage,int pageSize);
@Override public PageBean<Brand> selectByPage(int currentPage, int pageSize) { SqlSession sqlSession = factory.openSession(); BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); //.计算开始索引 int begin=(currentPage-1)*pageSize; //.查询的条目数 int size=pageSize; //.查询当前页数据 List<Brand> rows = mapper.selectByPage(begin, size); //.查询总条数 int totalCount = mapper.selectTotalCount(); //.封装pageBean对象 PageBean<Brand> pageBean=new PageBean<>(); pageBean.setRows(rows); pageBean.setTotalCount(totalCount); //.释放资源 sqlSession.close(); return pageBean; }
servlet
/*分页查询*/ public void selectByPage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //.从前端接收当前页码和每页展示条数 url?currentPage=1&pageSize=5 String _currentPage = req.getParameter("currentPage"); String _pageSize = req.getParameter("pageSize"); int currentPage = Integer.parseInt(_currentPage); int pageSize = Integer.parseInt(_pageSize); PageBean<Brand> pageBean = brandService.selectByPage(currentPage, pageSize); //.将集合转化为json,序列化数据 String string = JSON.toJSONString(pageBean); //.响应数据,数据带中文所以要设置 response.setContentType("text/json;charset=utf-8"); resp.setContentType("text/json;charset=utf-8"); resp.getWriter().write(string); }
后来maven启动传入参数看是否能返回数据
前端代码
增加两个变量用于动态改变当前页显示条数和总条数
selectAll(){ var _this=this; axios({ method:"get", url:"http://localhost:8080/cookie-demo/brand/selectByPage?currentPage="+_this.currentPage+"&pageSize="+_this.pageSize+"" }).then(function (resp){ _this.tableData= resp.data.rows; _this.totalCount=resp.data.totalCount; }) },
更改点击分页的代码
//分页 handleSizeChange(val) { // console.log(`每页 ${val} 条`); this.pageSize=val; this.selectAll(); }, handleCurrentChange(val) { // console.log(`当前页: ${val}`); this.currentPage=val; this.selectAll(); },
条件查询
要使用动态sql
后端逻辑
DAO
/*分页条件查询*/ List<Brand> selectByPageAndCondition(@Param("begin")int begin,@Param("size")int size,@Param("brand")Brand brand); /*根据条件查询总记录数*/ int selectTotalCountByCondition(Brand brand);
<!-- WHERE brand_name=#{brand.brandName}--> <select id="selectByPageAndCondition" resultMap="brandResultMap"> SELECT * FROM tb_brand <where> <if test="brand.brandName!=null and brand.brandName!=''"> and brand_name LIKE #{brand.brandName} </if> <if test="brand.companyName!=null and brand.companyName!=''"> and company_name LIKE #{brand.companyName} </if> <if test="brand.status!=null "> and status=#{brand.status} </if> </where> LIMIT #{begin},#{size} </select> <select id="selectTotalCountByCondition" resultType="java.lang.Integer"> SELECT COUNT(*) FROM tb_brand <where> <if test="brandName!=null and brandName!=''"> and brand_name LIKE #{brandName} </if> <if test="companyName!=null and companyName!=''"> and company_name LIKE #{companyName} </if> <if test="status!=null "> and status=#{status} </if> </where> </select>
这里的难点就是sql语句的书写(动态sql)
SERVICE
重难点是对数据有模糊处理,这对sql基础有要求
/*分页条件查询 * @param currentPage 当前页码 * @param pageSize每页展示条数*/ PageBean<Brand> selectByPageAndCondition(int currentPage,int pageSize,Brand brand);
@Override public PageBean<Brand> selectByPageAndCondition(int currentPage, int pageSize,Brand brand) { SqlSession sqlSession = factory.openSession(); BrandMapper mapper = sqlSession.getMapper(BrandMapper.class); //.计算开始索引 int begin=(currentPage-1)*pageSize; //.查询的条目数 int size=pageSize; // 因为是模糊查询所以要处理下brand条件(在数据两边加上%) String brandName = brand.getBrandName(); if(brandName!=null&& brandName.length()>0){ brand.setBrandName("%"+brandName+"%"); } String companyName = brand.getCompanyName(); if(companyName!=null && companyName.length()>0){ brand.setCompanyName("%"+companyName+"%"); } //.查询当前页数据 List<Brand> rows = mapper.selectByPageAndCondition(begin, size,brand); //.查询总条数 int totalCount = mapper.selectTotalCountByCondition(brand); //.封装pageBean对象 PageBean<Brand> pageBean=new PageBean<>(); pageBean.setRows(rows); pageBean.setTotalCount(totalCount); //.释放资源 sqlSession.close(); return pageBean; }
selvlet
/*分页条件查询*/ public void selectByPageAndCondition(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //.从前端接收当前页码和每页展示条数 url?currentPage=1&pageSize=5 String _currentPage = req.getParameter("currentPage"); String _pageSize = req.getParameter("pageSize"); int currentPage = Integer.parseInt(_currentPage); int pageSize = Integer.parseInt(_pageSize); //.获取查询条件对象 BufferedReader br=req.getReader(); String params = br.readLine(); //转为Brand对象 Brand brand = JSON.parseObject(params, Brand.class); PageBean<Brand> pageBean = brandService.selectByPageAndCondition(currentPage, pageSize,brand); //.将集合转化为json,序列化数据 String string = JSON.toJSONString(pageBean); //.响应数据,数据带中文所以要设置 response.setContentType("text/json;charset=utf-8"); resp.setContentType("text/json;charset=utf-8"); resp.getWriter().write(string); }
前端
更改了seletAll代码
get->post
并且传入了输入的brand对象
前端代码优化
主要是js中方法中使用this要用参数引用,这一点很不方便
var _this=this
我们可以使用箭头函数,比如下面我们的selectAll方法就可以改成
selectAll(){ axios({ method:"post", url:"http://localhost:8080/cookie-demo/brand/selectByPageAndCondition?currentPage="+this.currentPage+"&pageSize="+this.pageSize, data:this.brand }).then(resp=>{ this.tableData= resp.data.rows; this.totalCount=resp.data.totalCount; }) },
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!