Day2 动态sql
管理员新增功能:
点击+,如果输入的信息不符合要求,显示正则验证未通过?
点击保存修改,触发一个事件(函数):首先对参数进行校验,看是否符合要求,不符合规则的条件下,直接就返回,不会去使用axios发送HTTP请求。
无论前端还是服务端都应该对数据进行校验?为什么呢?
前端的页面是非常容易跳过的。
我只需要记住前端接下来发送的请求的地址以及请求的方式,其实完全可以自己去工具去发送这样的一个请求,那么如果你的代码中只是前端做了校验,那么就可以直接跳过校验。
postman、还可以写代码。
如果发现2.100上面的请求会携带id,但是你本地的没有携带id,为什么?
前端页面上面显示的任何数据都是你传给前端的,如果没有,那么往前追溯
遇到bug一般我是如何处理的?
1.排查状态码;如果failed(跨域失败、后端服务器异常(一般先执行mvn clean操作,再次执行mvn package操作));如果是500,去找错误日志(server、localhost log;自己去try catch )
package com.cskaoyan.mall.controller;
import com.alibaba.druid.util.StringUtils;
import com.cskaoyan.mall.model.Result;
import com.cskaoyan.mall.model.bo.AddAdminBO;
import com.cskaoyan.mall.model.bo.AdminLoginBO;
import com.cskaoyan.mall.model.bo.SearchAdminBO;
import com.cskaoyan.mall.model.vo.AllAdminVO;
import com.cskaoyan.mall.model.vo.LoginVo;
import com.cskaoyan.mall.service.AdminService;
import com.cskaoyan.mall.service.AdminServiceImpl;
import com.cskaoyan.mall.utils.HttpUtils;
import com.google.gson.Gson;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
/**
* 用来处理后台管理系统的管理员模块的增删改查等操作
*/
@WebServlet("/api/admin/admin/*")
public class AdminServlet extends HttpServlet {
private Gson gson = new Gson();
private AdminService adminService = new AdminServiceImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
String action = requestURI.replace(request.getContextPath() + "/api/admin/admin/", "");
if("login".equals(action)){
login(request,response);
}else if("addAdminss".equals(action)){
addAdminss(request, response);
}else if("getSearchAdmins".equals(action)){
getSearchAdmins(request, response);
}
}
/**
* 通过输入账户和昵称可以对管理员进行模糊查询
* @param request
* @param response
*/
private void getSearchAdmins(HttpServletRequest request, HttpServletResponse response) throws IOException {
String requestBody = HttpUtils.getRequestBody(request);
SearchAdminBO searchAdminBO = gson.fromJson(requestBody, SearchAdminBO.class);
List<AllAdminVO> adminVOS = adminService.searchAdmin(searchAdminBO);
response.getWriter().println(gson.toJson(Result.ok(adminVOS)));
}
/**
* 新增管理员账号的逻辑:
* 1.获取到请求参数-----key=value json字符串
* 2.执行具体的业务逻辑----保存到数据库
* 3.返回一个结果
* {"nickname":"admin1234","email":"66678@qq.com","pwd":"Aa12345%"}
* @param request
* @param response
*/
private void addAdminss(HttpServletRequest request, HttpServletResponse response) throws IOException {
String requestBody = HttpUtils.getRequestBody(request);
AddAdminBO addAdminBO = gson.fromJson(requestBody, AddAdminBO.class);
//无论前端还是服务端都应该对数据进行校验?为什么呢?
//对前端传递过来的参数进行校验 正则 TODO
int code = adminService.addAdmin(addAdminBO);
if(code == 200){
//成功
response.getWriter().println(gson.toJson(Result.ok()));
}else {
//失败
response.getWriter().println(gson.toJson(Result.error("当前用户名已经被占用")));
}
}
/**
* 登录的逻辑:首先需要接收到用户提交过来的请求参数
* 将请求参数放置到数据库中进行比对,判断用户名、密码是否正确
* 返回一个结果给客户端
* @param request
* @param response
*/
private void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
//请求参数位于请求体中,并且时以json字符串的形式存在
//如何拿到请求体 为什么请求体是以inputStream的形式存在
//因为请求体不仅可以存储文本类型的请求参数,还可以存储二进制类型数据 文件上传
String requestBody = HttpUtils.getRequestBody(request);
AdminLoginBO loginBO = gson.fromJson(requestBody, AdminLoginBO.class);
//校验
if(StringUtils.isEmpty(loginBO.getEmail()) || StringUtils.isEmpty(loginBO.getPwd())){
// TODO
Result result = new Result(10000, "参数不能为空", null);
response.getWriter().println(gson.toJson(result));
return;
}
//接下来应该调用模型的方法(三层架构里面的service和dao其实就是之前model的进一步解耦)
//调用service的方法,返回结果即可
int code = adminService.login(loginBO);
Result result = null;
if(code == 200){
//登录成功
LoginVo loginVo = new LoginVo(loginBO.getEmail(), loginBO.getEmail());
result = new Result(0, null, loginVo);
}else {
//登录失败
result = new Result(10000, "用户名、密码错误", null);
}
response.getWriter().println(gson.toJson(result));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
String action = requestURI.replace(request.getContextPath() + "/api/admin/admin/", "");
if("allAdmins".equals(action)){
allAdmins(request,response);
}else if("getAdminsInfo".equals(action)){
getAdminsInfo(request, response);
}
}
/**
* 通过id去取得当前管理员账号的详细信息
* @param request
* @param response
*/
private void getAdminsInfo(HttpServletRequest request, HttpServletResponse response) throws IOException {
String id = request.getParameter("id");
//校验
AllAdminVO adminVO = adminService.getAdmin(Integer.parseInt(id));
response.getWriter().println(gson.toJson(Result.ok(adminVO)));
}
/**
* 逻辑:1.获取请求参数(没有)
* 2.执行当前接口的具体业务逻辑:数据库里面的管理员账户信息全部返回给前端 JDBC
* 3.返回结果
* {"code":0,"data":[{"id":1,"email":"admin","nickname":"admin","pwd":"admin"}]}
* [{"id":1,"email":"admin","nickname":"admin","pwd":"admin"}]
* @param request
* @param response
*/
private void allAdmins(HttpServletRequest request, HttpServletResponse response) throws IOException {
List<AllAdminVO> adminVOS = adminService.allAdmins();
Result result = new Result(0, null, adminVOS);
response.getWriter().println(gson.toJson(result));
}
}
package com.cskaoyan.mall.service;
import com.cskaoyan.mall.dao.AdminDao;
import com.cskaoyan.mall.model.Admin;
import com.cskaoyan.mall.model.bo.AddAdminBO;
import com.cskaoyan.mall.model.bo.AdminLoginBO;
import com.cskaoyan.mall.model.bo.SearchAdminBO;
import com.cskaoyan.mall.model.vo.AllAdminVO;
import com.cskaoyan.mall.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
public class AdminServiceImpl implements AdminService {
@Override
public int login(AdminLoginBO loginBO) {
//需要读取mybatis.xml文件
SqlSession sqlSession = MybatisUtils.openSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin(null, loginBO.getEmail(), loginBO.getPwd(), null);
int count = adminDao.count(admin);
//即便时查询,也要把sqlSession给关闭,否则会出现死锁问题
//sqlSession一定不要写成成员变量,不能复用
sqlSession.commit();
sqlSession.close();
if(count == 1){
return 200;
}
return 404;
}
@Override
public List<AllAdminVO> allAdmins() {
//sqlSession一定不能设置成成员变量
SqlSession sqlSession = MybatisUtils.openSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
List<Admin> adminList = adminDao.allAdmins(new Admin());
sqlSession.commit();
sqlSession.close();
List<AllAdminVO> adminVOS = new ArrayList<>();
for (Admin admin : adminList) {
AllAdminVO adminVO = new AllAdminVO(admin.getId(), admin.getUsername(), admin.getPassword(), admin.getNickname());
adminVOS.add(adminVO);
}
return adminVOS;
}
@Override
public int addAdmin(AddAdminBO addAdminBO) {
SqlSession sqlSession = MybatisUtils.openSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin(null, addAdminBO.getEmail(), addAdminBO.getPwd(), addAdminBO.getNickname());
try {
adminDao.addAdmin(admin);
return 200;
}catch (Exception e){
}finally {
sqlSession.commit();
sqlSession.close();
}
return 500;
}
@Override
public AllAdminVO getAdmin(int id) {
SqlSession sqlSession = MybatisUtils.openSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin(id, null, null, null);
List<Admin> adminList = adminDao.allAdmins(admin);
sqlSession.commit();
sqlSession.close();
admin = adminList.get(0);
AllAdminVO adminVO = new AllAdminVO(admin.getId(), admin.getUsername(), admin.getPassword(), admin.getNickname());
return adminVO;
}
@Override
public List<AllAdminVO> searchAdmin(SearchAdminBO searchAdminBO) {
SqlSession sqlSession = MybatisUtils.openSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin(null, searchAdminBO.getEmail(), null, searchAdminBO.getNickname());
List<Admin> adminList = adminDao.allAdmins(admin);
List<AllAdminVO> adminVOS = new ArrayList<>();
for (Admin ad : adminList) {
AllAdminVO adminVO = new AllAdminVO(ad.getId(), ad.getUsername(), ad.getPassword(), ad.getNickname());
adminVOS.add(adminVO);
}
return adminVOS;
}
}
<?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.cskaoyan.mall.dao.AdminDao">
<select id="count" parameterType="com.cskaoyan.mall.model.Admin" resultType="java.lang.Integer">
select count(*) from admin where username = #{username} and password = #{password}
</select>
<select id="allAdmins" resultType="com.cskaoyan.mall.model.Admin" parameterType="com.cskaoyan.mall.model.Admin">
select id,username,password,nickname from admin
<where>
<if test="id != null">
id = #{id}
</if>
<if test="username != null and username != ''">
and username like concat("%", #{username}, "%")
</if>
<if test="nickname != null and nickname != ''">
and nickname like concat("%", #{nickname}, "%")
</if>
</where>
</select>
<insert id="addAdmin" parameterType="com.cskaoyan.mall.model.Admin">
insert into admin values (null, #{username}, #{password}, #{nickname})
</insert>
</mapper>
Day3 商品管理
后台管理系统中可以对前台的商品进行管理,比如发布、修改等,那么相对应的前台系统中商品信息也会随着同步发生修改。
实际上它们之间的关联就是通过数据库里面的表,一个修改表,一个查询表。
三层架构代码如何编写:
1.每个模块写一个controller,命名 见名知意。AdminServlet 或者AdminController,同时配套写一个service、dao
2.每新建一个新的controller,或者开启一个新的模块,那么都需要适配写一个新的service、dao
类目、商品、规格其实都可以写在一个servlet中。
如果添加类目,最终显示在页面上的名称中文乱码,如何分析,如何debug?
1.大体可以将整个过程先分为两个阶段,保存到数据库之前,一个是保存到数据库之后;
2.通过去debug排查,看一下究竟在哪个阶段它就已经出现了乱码问题。打断点------让程序执行。断点的作用是当程序执行的时候,运行到改行代码,那么会停顿下来。
接下来去新建商品表和规格表
一对多的关系。关系的维护写在多的一方(规格)
商品表:
id
name
typeId
image
description
price ------最低 如果商品表不存price,需要在查询的时候进行连接查询;如果商品表存price,直接查询单表即可;新增或者修改的时候,需要去维护一下数据即可
stockNum----顺带维护一个stockNum
规格表:
id
goodsId
name
stockNum
price
新增商品:
关于文件上传,一定不要去执行读取流转换成字符串操作,否则文件直接损坏。
新增商品的逻辑:
1.将参数保存到商品表(price、stockNum),在specList中的,将list里面的数据取出来迭代,取出最低价格,stockNum之和
2.将数据保存到规格表(里面需要goodsId)
怎么获取goods的id呢?因为商品表的id是自增的,是交给数据库去维护的,如何获取到goods 的id呢?
controller:数据的获取、封装、校验等,调用service代码逻辑,返回结果
service:具体的业务逻辑细节都应该在service层
dao:负责一个一个的方法
删除类目:自己思考决定如何实现该功能?1.如果下面有商品,那么不能删除 2.可以删除,全部都删除 3.可以删除类目,但是商品不删除,可以设置一个缺省的分类,如果类目删除了,把商品移到缺省类目中
删除商品的同时肯定也需要把规格一并删除
package com.cskaoyan.mall.controller;
import com.cskaoyan.mall.model.Result;
import com.cskaoyan.mall.model.Type;
import com.cskaoyan.mall.model.bo.AddGoodsBO;
import com.cskaoyan.mall.model.vo.GetGoodsByTypeVO;
import com.cskaoyan.mall.service.GoodsService;
import com.cskaoyan.mall.service.GoodsServiceImpl;
import com.cskaoyan.mall.utils.FileUploadUtils;
import com.cskaoyan.mall.utils.HttpUtils;
import com.google.gson.Gson;
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;
import java.util.Map;
@WebServlet("/api/admin/goods/*")
public class GoodsServlet extends HttpServlet {
private Gson gson = new Gson();
private GoodsService goodsService = new GoodsServiceImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
String action = requestURI.replace(request.getContextPath() + "/api/admin/goods/", "");
if("imgUpload".equals(action)){
imgUpload(request, response);
}else if("addGoods".equals(action)){
addGoods(request, response);
}
}
private void addGoods(HttpServletRequest request, HttpServletResponse response) throws IOException {
//1.获取请求参数 请求体 json字符串
String requestBody = HttpUtils.getRequestBody(request);
AddGoodsBO addGoodsBO = null;
try {
addGoodsBO = gson.fromJson(requestBody, AddGoodsBO.class);
}catch (Exception e){
response.getWriter().println(gson.toJson(Result.error("参数不合法")));
return;
}
goodsService.addGoods(addGoodsBO);
response.getWriter().println(gson.toJson(Result.ok()));
}
/**
* 还是使用我们之前介绍的commons-fileupload来处理即可
* @param request
* @param response
*/
private void imgUpload(HttpServletRequest request, HttpServletResponse response) throws IOException {
Map<String, Object> map = FileUploadUtils.parseRequest(request);
String file = (String) map.get("file");
response.getWriter().println(gson.toJson(Result.ok(file)));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
String action = requestURI.replace(request.getContextPath() + "/api/admin/goods/", "");
if("getType".equals(action)){
getType(request, response);
}else if("getGoodsByType".equals(action)){
getGoodsByType(request, response);
}
}
/**
* 后台管理系统中查询某个分类下商品信息的接口逻辑
* 1.获取请求参数typeId
* @param request
* @param response
*/
private void getGoodsByType(HttpServletRequest request, HttpServletResponse response) throws IOException {
String typeId = request.getParameter("typeId");
//校验
List<GetGoodsByTypeVO> goodsByTypeVOS = goodsService.getGoodsByType(Integer.parseInt(typeId));
response.getWriter().println(gson.toJson(Result.ok(goodsByTypeVOS)));
}
private void getType(HttpServletRequest request, HttpServletResponse response) throws IOException {
List<Type> typeList = goodsService.getType();
response.getWriter().println(gson.toJson(Result.ok(typeList)));
}
}
package com.cskaoyan.mall.service;
import com.cskaoyan.mall.dao.GoodsDao;
import com.cskaoyan.mall.model.Goods;
import com.cskaoyan.mall.model.Spec;
import com.cskaoyan.mall.model.Type;
import com.cskaoyan.mall.model.bo.AddGoodsBO;
import com.cskaoyan.mall.model.bo.AddGoodsSpecBO;
import com.cskaoyan.mall.model.vo.GetGoodsByTypeVO;
import com.cskaoyan.mall.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
public class GoodsServiceImpl implements GoodsService {
@Override
public List<Type> getType() {
SqlSession sqlSession = MybatisUtils.openSession();
GoodsDao goodsDao = sqlSession.getMapper(GoodsDao.class);
List<Type> types = goodsDao.getType();
sqlSession.commit();
sqlSession.close();
return types;
}
@Override
public List<GetGoodsByTypeVO> getGoodsByType(int typeId) {
SqlSession sqlSession = MybatisUtils.openSession();
GoodsDao goodsDao = sqlSession.getMapper(GoodsDao.class);
List<Goods> goodsList = goodsDao.getGoodsByType(typeId);
sqlSession.commit();
sqlSession.close();
List<GetGoodsByTypeVO> goodsByTypeVOS = new ArrayList<>();
for (Goods goods : goodsList) {
goodsByTypeVOS.add(new GetGoodsByTypeVO(goods.getId(),goods.getName(), goods.getTypeId(),goods.getImage(), goods.getPrice(), goods.getStockNum()));
}
return goodsByTypeVOS;
}
/**
* 事务:一组操作,要么全部成功,要么全部不成功
* ACID:原子性、一致性、隔离性、持久性
*隔离性更为重要一些:多个事务并发访问数据库时,数据库给每个事务提供互不干扰的一种保障
* 问题:脏读(一个事务读取了另外一个事务未提交的事务)、不可重复读(在一个事务内前后进行两次查询,查询的结果不一致,读取的是另外事务已提交的数据)、虚幻读(前后条数不一致 新增)
* mysql的repeatable read可以屏蔽三种问题 快照
* @param addGoodsBO
*/
@Override
public void addGoods(AddGoodsBO addGoodsBO) {
List<AddGoodsSpecBO> list = addGoodsBO.getSpecList();
int stockNum = 0;
double price = Double.MAX_VALUE;
for (AddGoodsSpecBO addGoodsSpecBO : list) {
if(addGoodsSpecBO.getUnitPrice() < price){
price = addGoodsSpecBO.getUnitPrice();
}
stockNum += addGoodsSpecBO.getStockNum();
}
SqlSession sqlSession = MybatisUtils.openSession();
GoodsDao goodsDao = sqlSession.getMapper(GoodsDao.class);
Goods goods = new Goods(null, addGoodsBO.getName(), addGoodsBO.getTypeId(), addGoodsBO.getImg(), addGoodsBO.getDesc(), price, stockNum);
//只需要执行完插入语句,那么goods的id就会封装到原先的goods对象中
goodsDao.addGoods(goods);
Integer goodsId = goods.getId();
List<Spec> specList = new ArrayList<>();
for (AddGoodsSpecBO addGoodsSpecBO : list) {
//做一个转换,将addGoodsSpecBo-----Spec
specList.add(new Spec(null, goodsId, addGoodsSpecBO.getSpecName(), addGoodsSpecBO.getStockNum(), addGoodsSpecBO.getUnitPrice()));
}
int i = 1 / 0;
goodsDao.addSpeces(specList);
sqlSession.commit();
sqlSession.close();
}
}
package com.cskaoyan.mall.dao;
import com.cskaoyan.mall.model.Goods;
import com.cskaoyan.mall.model.Spec;
import com.cskaoyan.mall.model.Type;
import java.util.List;
public interface GoodsDao {
List<Type> getType();
List<Goods> getGoodsByType(int typeId);
void addGoods(Goods goods);
void addSpeces(List<Spec> specList);
}
<?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.cskaoyan.mall.dao.GoodsDao">
<select id="getType" resultType="com.cskaoyan.mall.model.Type">
select id,name from type;
</select>
<select id="getGoodsByType" parameterType="java.lang.Integer" resultType="com.cskaoyan.mall.model.Goods">
select id,name,typeId,image,description,price,stockNum from goods where typeId = #{typeId}
</select>
<insert id="addGoods" useGeneratedKeys="true" keyProperty="id" parameterType="com.cskaoyan.mall.model.Goods">
insert into goods values (null, #{name}, #{typeId}, #{image}, #{description}, #{price}, #{stockNum})
</insert>
<insert id="addSpeces" parameterType="com.cskaoyan.mall.model.Spec">
insert into spec values
<foreach collection="list" item="sp" separator=",">
(null, #{sp.goodsId}, #{sp.name}, #{sp.stockNum}, #{sp.price})
</foreach>
</insert>
</mapper>
package com.cskaoyan.mall.model.bo;
import java.util.List;
public class AddGoodsBO {
private String img;
private String name;
private Integer typeId;
private String desc;
private List<AddGoodsSpecBO> specList;
public void setImg(String img) {
this.img = img;
}
public void setName(String name) {
this.name = name;
}
public Integer getTypeId() {
return typeId;
}
public void setTypeId(Integer typeId) {
this.typeId = typeId;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getImg() {
return img;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
public List<AddGoodsSpecBO> getSpecList() {
return specList;
}
public void setSpecList(List<AddGoodsSpecBO> specList) {
this.specList = specList;
}
}
package com.cskaoyan.mall.model.bo;
public class AddGoodsSpecBO {
private Double unitPrice;
private String specName;
private Integer stockNum;
public Double getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(Double unitPrice) {
this.unitPrice = unitPrice;
}
public String getSpecName() {
return specName;
}
public void setSpecName(String specName) {
this.specName = specName;
}
public Integer getStockNum() {
return stockNum;
}
public void setStockNum(Integer stockNum) {
this.stockNum = stockNum;
}
}
package com.cskaoyan.mall.model.vo;
/**
* int和Integer的区别是啥? 基本类型 包装类型 java 面向对象
* 默认赋值 int---0 Integer null
*/
public class GetGoodsByTypeVO {
private Integer id;
private String name;
private Integer typeId;
private String img;
private Double price;
private Integer stockNum;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getTypeId() {
return typeId;
}
public void setTypeId(Integer typeId) {
this.typeId = typeId;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getStockNum() {
return stockNum;
}
public void setStockNum(Integer stockNum) {
this.stockNum = stockNum;
}
public GetGoodsByTypeVO(Integer id, String name, Integer typeId, String img, Double price, Integer stockNum) {
this.id = id;
this.name = name;
this.typeId = typeId;
this.img = "http://localhost:8084/" + img;
this.price = price;
this.stockNum = stockNum;
}
public GetGoodsByTypeVO() {
}
}