javaee之黑马乐优商城2

简单分析一下商品分类表的结构

先来说一下分类表与品牌表之间的关系

 再来说一下分类表和品牌表与商品表之间的关系

 面我们要开始就要创建sql语句了嘛,这里我们分析一下字段

用到的数据库是heima->tb_category这个表

现在去数据库里面创建好这张表

 

 下面我们再去编写一个实体类之前,我们去看一下这个类的请求方式,请求路径,请求参数,返回数据都是什么

下面再去编写实体类

实体都是放到 

出现了一个小插曲,开始的时候,我maven项目右边的模块有些是灰色的,导致我导入依赖之后,所有的注解什么都不能用,解决方案如下

然后把依赖重新导入一下

我们先去完成我们商品分类表的一个实体类

 Category.java

package com.leyou.item.pojo;import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;import javax.persistence.Id;
import javax.persistence.Table;/*** Created by Administrator on 2023/8/28.*/
@Table(name="tb_category")
@Data
public class Category {@Id@KeySql(useGeneratedKeys = true)private Long id;private String name;private Long parentId;private Boolean isParent;private Integer sort;
}

然后去到ly-item-service去写具体的业务逻辑,比如mapper,service,web都在这里面

这里来说一个依赖问题

 引入了spring-boot-starter-web这个依赖,也包含了spring的核心依赖

说一下在写这个controller类的时候,我们的路径是什么,路径就是我们访问每一个接口传递过来的url

ResponseEntity这个类是干嘛的

 格式用法有两种

CollectionUtils工具类

 这个是Spring给我们提供的一个工具类

我们可以来做如下检测

 下面我们贴上这个CategoryController的代码

package com.leyou.item.web;import com.leyou.item.pojo.Category;
import com.leyou.item.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** Created by Administrator on 2023/8/29.*/
@RestController
@RequestMapping("/category")
public class CategoryController {@Autowiredprivate CategoryService categoryService;/*** 根据父节点的id查询商品分类* @param pid* @return*/@GetMapping("/list")public ResponseEntity<List<Category>> queryCategoryListByPid(@RequestParam("pid")Long pid) {try {if(pid == null || pid.longValue() < 0) {//会返回带着状态码的对象400 参数不合法// return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();//可以做一格优化,下面的类似return ResponseEntity.badRequest().build();}//开始利用service执行查询操作List<Category> categoryList = categoryService.queryCategoryListByParentId(pid);if(CollectionUtils.isEmpty(categoryList)) {//如果结果集为空,响应404return ResponseEntity.notFound().build();}//查询成功,响应200return ResponseEntity.ok(categoryList);//这里才真正放了数据} catch (Exception e) {e.printStackTrace();}//自定义状态码,然后返回//500返回一个服务器内部的错误//这里也可以不返回,程序出错,本身就会返回500return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()}}

下面我们去Service创建queryCategoryListByParentId这个方法

看一下完整代码

package com.leyou.item.service;import com.leyou.item.mapper.CategoryMapper;
import com.leyou.item.pojo.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;/*** Created by Administrator on 2023/8/29.*/
@Service
public class CategoryService {@Autowiredprivate CategoryMapper categoryMapper;/*** 根据父节点的id来查询子结点* @param pid* @return*/public List<Category> queryCategoryListByParentId(Long pid) {Category category = new Category();category.setParentId(pid);return categoryMapper.select(category);}}

上面都做完了,现在去数据库操作把分类中的数据给插入一下,类似于如下这些数据

 下面就是在数据中存在的数据

 下面我开始去启动:

我们的数据肯定是去走网关的

网关很明显我们是可以看到数据的

 但是在项目里面点击就出不来

 上面明显就是出现了跨域的问题

跨域我们就是在服务端进行一个配置

说的简单点,服务器就给给我们配置如下信息

我们这里在服务器搭配一个类来配置这些信息就可以了

我们这里用SpringMVC帮我们写的一个cors跨域过滤器来做:CrosFilter

具体代码如下

package com.leyou.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;/*** Created by Administrator on 2023/8/31.*/
@Configuration
public class LeyouCorsConfiguration {@Beanpublic CorsFilter corsFilter() {//1.添加CORS配置信息CorsConfiguration config = new CorsConfiguration();//1) 允许的域,不要写*,否则cookie就无法使用了config.addAllowedOrigin("http://manage.leyou.com");config.addAllowedOrigin("http://www.leyou.com");//2) 是否发送Cookie信息config.setAllowCredentials(true);//3) 允许的请求方式config.addAllowedMethod("OPTIONS");config.addAllowedMethod("HEAD");config.addAllowedMethod("GET");config.addAllowedMethod("PUT");config.addAllowedMethod("POST");config.addAllowedMethod("DELETE");config.addAllowedMethod("PATCH");// 4)允许的头信息config.addAllowedHeader("*");//2.添加映射路径,我们拦截一切请求UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();configSource.registerCorsConfiguration("/**", config);//3.返回新的CorsFilter.return new CorsFilter(configSource);}
}

重新启动一下网关服务器

下面来讲品牌查询 

 

我们现在要做的就是查询出上面的品牌

 我们必须弄明白请求方式,请求路径,请求参数,响应数据决定返回值

一般来说如果页面要展示一个列表的话,就要返回一个List集合对象或者返回一个分页对象

我们就必须定义一个分页对象

分页对象后面大家都要用,我们就放到common里面去

先来把这个分页对象给做了

package com.leyou.common.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;/*** Created by Administrator on 2023/9/2.*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageResult<T> {private Long total;//总条数private Integer totalPage;//总页数private List<T> items;//当前页面数据对象public PageResult(Long total,List<T> items) {this.total = total;this.items = items;}
}

下面我们来做一下前端页面

 先去找这个页面,在menu.js里面,去查看商品的路径在什么位置

上面就是品牌的路径/item/brand,下面我们看组件在哪里

去到下面这个位置

这个位置去找我们的路由页面

 

 

所有的页面组件全部都在pages里面放着

我们这里自己来写一下组件

我们自己定义一个MyBrand1.vue组件

 

我们这个页面主要还是去做一个分页的表格

可以去Vuetify里面查找

我们这里应该去找从服务端就已经分页与排序好的数据

下面我们可以去看到这里面的模板代码

 上面就是我们要用的模板代码

数据脚本当然是你在script里面,可以查看一下

<script>const desserts = [{name: 'Frozen Yogurt',calories: 159,fat: 6.0,carbs: 24,protein: 4.0,iron: '1',},{name: 'Jelly bean',calories: 375,fat: 0.0,carbs: 94,protein: 0.0,iron: '0',},{name: 'KitKat',calories: 518,fat: 26.0,carbs: 65,protein: 7,iron: '6',},{name: 'Eclair',calories: 262,fat: 16.0,carbs: 23,protein: 6.0,iron: '7',},{name: 'Gingerbread',calories: 356,fat: 16.0,carbs: 49,protein: 3.9,iron: '16',},{name: 'Ice cream sandwich',calories: 237,fat: 9.0,carbs: 37,protein: 4.3,iron: '1',},{name: 'Lollipop',calories: 392,fat: 0.2,carbs: 98,protein: 0,iron: '2',},{name: 'Cupcake',calories: 305,fat: 3.7,carbs: 67,protein: 4.3,iron: '8',},{name: 'Honeycomb',calories: 408,fat: 3.2,carbs: 87,protein: 6.5,iron: '45',},{name: 'Donut',calories: 452,fat: 25.0,carbs: 51,protein: 4.9,iron: '22',},]const FakeAPI = {async fetch ({ page, itemsPerPage, sortBy }) {return new Promise(resolve => {setTimeout(() => {const start = (page - 1) * itemsPerPageconst end = start + itemsPerPageconst items = desserts.slice()if (sortBy.length) {const sortKey = sortBy[0].keyconst sortOrder = sortBy[0].orderitems.sort((a, b) => {const aValue = a[sortKey]const bValue = b[sortKey]return sortOrder === 'desc' ? bValue - aValue : aValue - bValue})}const paginated = items.slice(start, end)resolve({ items: paginated, total: items.length })}, 500)})},}export default {data: () => ({itemsPerPage: 5,headers: [{title: 'Dessert (100g serving)',align: 'start',sortable: false,key: 'name',},{ title: 'Calories', key: 'calories', align: 'end' },{ title: 'Fat (g)', key: 'fat', align: 'end' },{ title: 'Carbs (g)', key: 'carbs', align: 'end' },{ title: 'Protein (g)', key: 'protein', align: 'end' },{ title: 'Iron (%)', key: 'iron', align: 'end' },],serverItems: [],loading: true,totalItems: 0,}),methods: {loadItems ({ page, itemsPerPage, sortBy }) {this.loading = trueFakeAPI.fetch({ page, itemsPerPage, sortBy }).then(({ items, total }) => {this.serverItems = itemsthis.totalItems = totalthis.loading = false})},},}
</script>

下面我们去看一下品牌表展示什么样的内容,我们看一下数据库里面的字段,先来创建一张产品表,然后把数据也给插入进去

下面我们把数据给插进去,类似于插入下面这些数据

看一下,很明显这个表的数据就已经存在了

我们表头我们直接可以从下面的位置修改

下面直接展示品牌页面前端所有代码

<template><v-card><v-card-title><v-btn color="primary" @click="addBrand">新增品牌</v-btn><!--搜索框,与search属性关联--><v-spacer/><v-flex xs3><v-text-field label="输入关键字搜索" v-model.lazy="search" append-icon="search" hide-details/></v-flex></v-card-title><v-divider/><v-data-table:headers="headers":items="brands":pagination.sync="pagination":total-items="totalBrands":loading="loading"class="elevation-1"><template slot="items" slot-scope="props"><td class="text-xs-center">{{ props.item.id }}</td><td class="text-xs-center">{{ props.item.name }}</td><td class="text-xs-center"><img v-if="props.item.image" :src="props.item.image" width="130" height="40"><span v-else>无</span></td><td class="text-xs-center">{{ props.item.letter }}</td><td class="justify-center layout px-0"><v-btn flat icon @click="editBrand(props.item)" color="info"><i class="el-icon-edit"/></v-btn><v-btn flat icon @click="deleteBrand(props.item)" color="purple"><i class="el-icon-delete"/></v-btn></td></template></v-data-table><!--弹出的对话框--><v-dialog max-width="500" v-model="show" persistent scrollable><v-card><!--对话框的标题--><v-toolbar dense dark color="primary"><v-toolbar-title>{{isEdit ? '修改' : '新增'}}品牌</v-toolbar-title><v-spacer/><!--关闭窗口的按钮--><v-btn icon @click="closeWindow"><v-icon>close</v-icon></v-btn></v-toolbar><!--对话框的内容,表单 这里是要把获得的brand 数据传递给子组件,使用自定义标签::oldBrand 而父组件值为oldBrand--><v-card-text class="px-5" style="height:400px"><brand-form @close="closeWindow" :oldBrand="oldBrand" :isEdit="isEdit"/></v-card-text></v-card></v-dialog></v-card>
</template><script>// 导入自定义的表单组件,引入子组件 brandformimport BrandForm from './BrandForm'export default {name: "brand",data() {return {search: '', // 搜索过滤字段totalBrands: 0, // 总条数brands: [], // 当前页品牌数据loading: true, // 是否在加载中pagination: {}, // 分页信息headers: [{text: 'id', align: 'center', value: 'id'},{text: '名称', align: 'center', sortable: false, value: 'name'},{text: 'LOGO', align: 'center', sortable: false, value: 'image'},{text: '首字母', align: 'center', value: 'letter', sortable: true,},{text: '操作', align: 'center', value: 'id', sortable: false}],show: false,// 控制对话框的显示oldBrand: {}, // 即将被编辑的品牌数据isEdit: false, // 是否是编辑}},mounted() { // 渲染后执行// 查询数据--搜索后页面还处在第几页,只要搜索,页面渲染后重新查询this.getDataFromServer();},watch: {pagination: { // 监视pagination属性的变化deep: true, // deep为true,会监视pagination的属性及属性中的对象属性变化handler() {// 变化后的回调函数,这里我们再次调用getDataFromServer即可this.getDataFromServer();}},search: { // 监视搜索字段handler() {this.pagination.page =1;this.getDataFromServer();}}},methods: {getDataFromServer() { // 从服务的加载数的方法。// 发起请求this.$http.get("/item/brand/page", {params: {key: this.search, // 搜索条件page: this.pagination.page,// 当前页rows: this.pagination.rowsPerPage,// 每页大小sortBy: this.pagination.sortBy,// 排序字段desc: this.pagination.descending// 是否降序}}).then(resp => { // 这里使用箭头函数this.brands = resp.data.items;this.totalBrands = resp.data.total;// 完成赋值后,把加载状态赋值为falsethis.loading = false;//})},addBrand() {// 修改标记,新增前修改为falsethis.isEdit = false;// 控制弹窗可见:this.show = true;// 把oldBrand变为null,因为之前打开过修改窗口,oldBrand数据被带过来了,导致新增this.oldBrand = null;},editBrand(oldBrand){//test 使用//this.show = true;//获取要编辑的brand//this.oldBrand = oldBrand;//requestParam,相当于把http,url ?name=zhangsan&age=21 传给方法//pathvarable 相当与把url www.emporium.com/1/2 传给方法//如果不需要url上的参数controller不需要绑定数据// 根据品牌信息查询商品分类, 因为前台页面请求是拼接的, data 类似于jquery 里面回显的数据this.$http.get("/item/category/bid/" + oldBrand.id).then(({data}) => {// 修改标记this.isEdit = true;// 控制弹窗可见:this.show = true;// 获取要编辑的brandthis.oldBrand = oldBrand// 回显商品分类this.oldBrand.categories = data;})},closeWindow(){// 重新加载数据this.getDataFromServer();// 关闭窗口this.show = false;}},components:{BrandForm}}
</script><style scoped></style>

 

下面开始写后台逻辑

 

开始写后台,先写一个产品类

 Brand.java

package com.leyou.item.pojo;import lombok.Data;import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;/*** Created by Administrator on 2023/9/2.*/
@Data
@Table(name="tb_brand")
public class Brand {@Id@GeneratedValue(strategy= GenerationType.IDENTITY)private Long id;private String name;//品牌名称private String image;//品牌图片private Character letter;
}

接下来我们写上我们的通用Mapper类

下面我们去写service接口

 下面去写Controller类

分析一下

返回的是什么:当前页的数据(list集合)和总条数 

也就是上面返回的是如下一个分页对象,在ly-common模块里面,如果需要用到这个模块的对象,那么我们就需要把这个模块当成依赖引入到另外一个模块里面

这里是ly-item下面的模块ly-item-service需要用到PageResult对象

下面就是Controller中的代码

下面去完成Service中的方法

package com.leyou.item.service;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.leyou.common.pojo.PageResult;
import com.leyou.item.mapper.BrandMapper;
import com.leyou.item.pojo.Brand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;/*** Created by Administrator on 2023/9/2.*/
@Service
public class BrandService {//内部需要一个mapper调用@Autowiredprivate BrandMapper brandMapper;/**** @param page 当前页* @param rows 每页大小* @param sortBy 排序字段* @param desc 是否降序* @param key 搜索关键字* @return*/public PageResult<Brand> queryBrandByPageAndSort(Integer page,Integer rows, String sortBy, Boolean desc, String key) {//开启分页//这个会自动拼接到后面的sql语句上面PageHelper.startPage(page,rows);//传进来一个页码和展示多少行的数据,//过滤Example example = new Example(Brand.class);if(key != null && !"".equals(key)) {//进来有一模糊查询//把这个语句拼接上example.createCriteria().andLike("name","%" + key + "%").orEqualTo("letter",key);}if(sortBy != null && !"".equals(sortBy)) {//根据sortBy字段进行排序String orderByClause = sortBy + (desc ? " DESC " : " ASC ");example.setOrderByClause(orderByClause);}//利用通用mapper进行查询Page<Brand> pageInfo = (Page<Brand>) brandMapper.selectByExample(example);//返回结果//这里面传递总条数和页面信息return new PageResult<>(pageInfo.getTotal(),pageInfo);}
}

说一下,用Autowired注入Mapper的时候,提示注入不了,爆红

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/101582.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Multimedia-播放器-架构2

目录 引言 问题1&#xff1a; 数据缓冲区 多线程模型 缓冲区的特点&#xff1a; 点播和直播场景中的缓冲区&#xff1a; 问题2&#xff1a; 同步方式 同步实现过程 引言 上一篇梳理了播放器的基本工作与处理流程&#xff0c;本片内容主要梳理一下其中会遇到的问题&am…

宠物行业如何进行软文营销

如今&#xff0c;宠物已经成为了人们生活中不可或缺的一部分&#xff0c;大众对于萌宠的喜爱与日俱增&#xff0c;随着“萌宠经济”升温&#xff0c;越来越多的商机开始出现&#xff0c;伴随着宠物市场竞争的日益激烈&#xff0c;宠物行业的营销光靠硬广告很难吸引受众&#xf…

【C++二叉树】进阶OJ题

【C二叉树】进阶OJ题 目录 【C二叉树】进阶OJ题1.二叉树的层序遍历II示例代码解题思路 2.二叉搜索树与双向链表示例代码解题思路 3.从前序与中序遍历序列构造二叉树示例代码解题思路 4.从中序与后序遍历序列构造二叉树示例代码解题思路 5.二叉树的前序遍历&#xff08;非递归迭…

Matlab进阶绘图第27期—水平双向堆叠图

在上一期文章中&#xff0c;分享了Matlab双向堆叠图的绘制方法&#xff1a; 进一步&#xff0c;再来看一下水平双向堆叠图的绘制方法&#xff08;由于Matlab中未收录水平双向堆叠图的绘制函数&#xff0c;因此需要大家自行设法解决&#xff09;。 先来看一下成品效果&#xff…

[HNCTF 2022] web 刷题记录

文章目录 [HNCTF 2022 Week1]easy_html[HNCTF 2022 Week1]easy_upload[HNCTF 2022 Week1]Interesting_http[HNCTF 2022 WEEK2]ez_SSTI[HNCTF 2022 WEEK2]ez_ssrf[HNCTF 2022 WEEK2]Canyource [HNCTF 2022 Week1]easy_html 打开题目提示cookie有线索 访问一下url 发现要求我们…

Vue教程

官网vue快速上手 vue示例图 请点击下面工程名称&#xff0c;跳转到代码的仓库页面&#xff0c;将工程 下载下来 Demo Code 里有详细的注释 代码&#xff1a;LearnVue

【核心复现】基于合作博弈的综合能源系统电-热-气协同优化运行策略(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

OpenCV(二十八):连通域分割

目录 1.介绍连通域分割 2.像素领域介绍 3.两遍法分割连通域 4.连通域分割函数 1.介绍连通域分割 连通域分割是一种图像处理技术&#xff0c;用于将图像中的相邻像素组成的区域划分为不同的连通域。这些像素具有相似的特性&#xff0c;如相近的灰度值或颜色。连通域分割可以…

java八股文面试[数据库]——索引覆盖

覆盖索引是一种避免回表查询的优化策略: 只需要在一棵索引树上就能获取SQL所需的所有列数据&#xff0c;无需回表&#xff0c;速度更快。 具体的实现方式: 将被查询的字段建立普通索引或者联合索引&#xff0c;这样的话就可以直接返回索引中的的数据&#xff0c;不需要再通过聚…

手写Spring:第2章-创建简单的Bean容器

文章目录 一、目标&#xff1a;创建简单的Bean容器二、设计&#xff1a;创建简单的Bean容器三、实现&#xff1a;创建简单的Bean容器3.0 引入依赖3.1 工程结构3.2 创建简单Bean容器类图3.3 Bean定义3.4 Bean工厂 四、测试&#xff1a;创建简单的Bean容器4.1 用户Bean对象4.2 单…

TDengine 官网换了新“皮肤”,来看看这个风格是不是你的菜

改版升级&#xff0c;不同以“网”&#xff01;为了更好地服务客户&#xff0c;让大家能够更便捷、清晰地了解我们的产品和功能&#xff0c;我们决定给 TDengine 官网换个新“皮肤”~精心筹备下&#xff0c;新官网终于成功与大家见面啦——https://www.taosdata.com/。TDengine…

【开发】安防监控/视频汇聚/云存储/AI智能视频融合平台页面新增地图模式

AI智能分析网关包含有20多种算法&#xff0c;包括人脸、人体、车辆、车牌、行为分析、烟火、入侵、聚集、安全帽、反光衣等等&#xff0c;可应用在安全生产、通用园区、智慧食安、智慧城管、智慧煤矿等场景中。将网关硬件结合我们的视频汇聚/安防监控/视频融合平台EasyCVR一起使…