前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手把手带你分析一个基于SSM的商城项目 | 万字 · 巨详细

手把手带你分析一个基于SSM的商城项目 | 万字 · 巨详细

作者头像
Regan Yue
发布2022-05-16 08:33:53
7320
发布2022-05-16 08:33:53
举报
文章被收录于专栏:ReganYue's Blog

文章目录

零、SSM项目配置文件与项目启动的一些需知

1. 项目启动读取配置文件过程
  1. 在Tomcat运行SSM项目时,容器最开始会去读取容器的配置文件web.xml,首先读取如下图这两个内容。
  1. 然后,Web容器会生成ServletContext,这个ServletContext是整个SSM项目共享,这是一个全局对象,所有Servlet都可以访问到。
  2. <context-param>的内容联系起来并发送给ServletContext。
  3. 容器根据<listener>创建监听器,这个ContextLoaderListener就是用来监听ServletContext的变化,并根据<context-param>加载applicationContext.xml文件。
2. 配置文件详解

我们使用了<context:annotation-config/> 能够简单的帮助我们自动完成相应bean的声明。

加载完applicationContext.xml之后,会扫描service层和dao层注解。

引入外部的属性配置文件

在applicationContext.xml中配置使用阿里巴巴Druid数据库连接池,后期需要可以在其中添加多个属性配置。

在 MyBatis 中,通过 SqlSessionFactoryBuilder来生成SqlSessionFactory ,最终来生成 SqlSession。我们引入 MyBatis-Spring 后, 让SqlSessionFactoryBean代替了SqlSessionFactoryBuilder生成SqlSessionFactory。

MapperScannerConfigurer能够自动帮我们装载SqlSessionFactory 或 SqlSessionTemplate所以我们没必要在applicationContext.xml中注入sqlSession和sqlSessionFactory。

下图为对dataSource进行事务管理和配置 Annotation 驱动,扫描@Transactional注解的类定义事务。

扫描这个controller包,让SpringMVC认为该包下带注解的是controller。

扩充注解驱动。

对静态资源进行放过,比如css,js,图片等。

有了该配置,可以实现文件上传功能。不然就要引入文件上传组件。

配置视图解析。

一、项目结构介绍

  1. dto层(也称为model、entity层),作用就是定义实体类,对数据表中的对象的映射。
  2. dao层主要用来封装对数据库的新增,删除,查询,修改。叫做数据访问层。
  3. service层,即服务层,相比Dao较高层次,可将多种方法封装起来。
  4. controller层,controller调用前面3层,前端调用对应接口,对应controller层接受前端传来的参数,处理好的数据也是通过controller层传递到前端显示的。
  5. util层,util是utiliy的缩写,是一个多功能,相当于工具的包,封装一些实用的方法和数据结构。
  6. resources存放资源文件:这里applicationContext.xml是spring的配置文件,create.sql是数据库脚本,generatorConfig.xml是引入的代码生成器配置文件(如下图),jdbc.properties是数据库连接配置文件, mybatis-config.xml是MyBatis核心配置文件,该文件配置了MyBatis的一些全局信息,包含数据库连接信息和Mybatis运行时所需的各种特性,以及设置和影响Mybatis行为的一些属性。 web项目启动时,读取web.xml配置文件,首先解析的是applicationContext.xml文件,其次才是spingmvc.xml文件,spingmvc.xml文件中主要的工作是:启动注解、扫描controller包注解;静态资源映射;视图解析(defaultViewResolver);文件上传(multipartResolver);返回消息json配置。
  1. webapp是放置网页相关文件的目录。

二、DTO

dto,我们可以使用代码生成器生成,和下面方法一致。

三、DAO

我们上面介绍项目结构时说过,我们使用了generatorConfig.xml这个配置文件,其对应的mybatis-generator是一款代码生成器,可以帮助我们快速构建DAO层常见的增删查改功能,一般能够满足我们所有要求。

四、Service层

1. AdminService

这个service调用了5个mapper:

代码语言:javascript
复制
    @Autowired
    private AdministratorMapper administratorMapper;
    @Autowired
    private GoodsTypeMapper goodsTypeMapper;
    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private GoodsImagesMapper goodsImagesMapper;
    @Autowired
    private GoodsImagesColorMapper goodsImagesColorMapper;

我们重点讲几个典型的,其实都大同小异。

(1) adminLogin
代码语言:javascript
复制
    @Override
    public List<Administrator> adminLogin(String username, String password) {
        AdministratorExample admin = new AdministratorExample();
        admin.createCriteria().andUsernameEqualTo(username).andPasswordEqualTo(password);
        List<Administrator> administrators = administratorMapper.selectByExample(admin);
        return administrators;
    }

在讲这个之前,我们先介绍什么是Criteria,如下图,Criteria包含一个Cretiron的集合,每一个Criteria对象内包含的Cretiron之间是由AND连接的,是逻辑与的关系。

而Criterion是最基本,它是最底层的Where条件,用于字段级的筛选,feild用于指代字段名字。

而对于oredCriteria,Example内有一个成员叫oredCriteria,是Criteria的集合,这个集合中的Criteria是由OR连接的,是逻辑或关系。

这里我们先利用CTO的Example生成一个admin对象,然后利用

代码语言:javascript
复制
admin.createCriteria().andUsernameEqualTo(username).andPasswordEqualTo(password);

List<Administrator> administrators = administratorMapper.selectByExample(admin);

可以查询参数在数据库中数据库中有没有。

(2)selectAll

再来看看selectAll:

代码语言:javascript
复制
    @Override
    public List<Goods> selectAll(){
        GoodsExample gc = new GoodsExample();
        gc.createCriteria();
        return goodsMapper.selectByExample(gc);
    }

这个操作是查询所有的商品,其对应的sql语句可以这样看待:

代码语言:javascript
复制
select *
from Goods
//因为createCriteria()之后没有and和or,所以没有where条件为空。
(3)deleteGoodsInfo
代码语言:javascript
复制
	@Override
    public void deleteGoodsInfo(int goodsId){
        goodsMapper.deleteByPrimaryKey(goodsId);
        GoodsImagesColorExample gc = new GoodsImagesColorExample();
        gc.createCriteria().andGoodsIdEqualTo(goodsId);
        goodsImagesColorMapper.deleteByExample(gc);
        GoodsImagesExample gc1 = new GoodsImagesExample();
        gc1.createCriteria().andGoodsIdEqualTo(goodsId);
        goodsImagesMapper.deleteByExample(gc1);
    }

因为我们图片和商品数据不在一个表中,所以我们删除商品,需要操作3个表,这里通过主键id在goods表中删除了一条,然后在GoodsImagesColor表中,删除了where条件goodsId为该函数参数的数据,同理,也删除了GoodsImages中的数据。

(4)searchGoodsWithName
代码语言:javascript
复制
    @Override
    public List<Goods> searchGoodsWithName(String name) {
        // 构建条件
        GoodsExample ge = new GoodsExample();
        ge.createCriteria().andNameLike("%" + name + "%");
        return goodsMapper.selectByExample(ge);
    }

这里我们使用了一个方法名中带有Like的方法,其作用如下: 实现mysql的like模糊查询一样的操作。这里需要自己拼接%或_。 这段代码用sql语句来描述是这样的:

代码语言:javascript
复制
select * from goods where name like ?
2.GoodsShippingServiceImpl

其实这里面和前面用到的知识都大同小异,不过出现了一个新的面孔。

代码语言:javascript
复制
@Override
public List<Goods> findGoodsWithToptype(GoodsType goodstype) {
    // 根据一级类型查询所有所属二级类型
    List<GoodsType> gt = goodsTypeServiceImpl.findSecondLevel(goodstype);
    // 查询所有二级类型下的所有商品
    List<Goods> goodsList = new ArrayList<>();
    for (GoodsType goodsType : gt) {
        List<Goods> goodses = this.findGoodsWithType(goodsType);
        goodsList.addAll(goodses);
    }
    return goodsList;
}

该操作是查询某一级目录下的所有二级目录的商品。此处调用了别处的Service。

3.GoodsTypeServiceImpl
(1) findTopLevel

此处多了个IsNull的方法。

代码语言:javascript
复制
    @Override
    public List<GoodsType> findTopLevel() {
        GoodsTypeExample gte = new GoodsTypeExample();
        gte.createCriteria().andPidIsNull();
        return goodsTypeMapper.selectByExample(gte);
    }

这段Java代码可以对应的sql语句为:

代码语言:javascript
复制
select * from goodswhere pid is null

即查找pid为空的目录,因为一级目录和二级目录放在一个表中,这样就可以快速查找一级目录了。

4.UserServiceImpl
(1)insetorder
代码语言:javascript
复制
    @Override
    public int insetorder(GoodsOrder goodsOrder){
        return goodsOrderMapper.insertSelective(goodsOrder);
    }

这里我们得了解insert和insertSelective的区别: 如果选择insert 那么所有的字段都会添加一遍,即使有的字段没有值,但是如果使用inserSelective会只给有值的字段赋值(会对传进来的值做非空判断)。

(2)updataorder
代码语言:javascript
复制
    @Override
    public int updataorder(GoodsOrder goodsOrder, int id){
        GoodsOrderExample gc = new GoodsOrderExample();
        gc.createCriteria().andIdEqualTo(id);
        return goodsOrderMapper.updateByExample(goodsOrder,gc);
    }

record对象则是我们需要修改的数据,example则是我们需要修改的对象 此处代表id为参数id的数据要被修改为参数goodsOrder。

5. UtilServiceImpl
(1)queryByPage
代码语言:javascript
复制
    @Override
    @Transactional(readOnly = true)
    public PageInfo queryByPage(int pageNum, int pageSize, int level, GoodsType goodsType) {
        PageHelper.startPage(pageNum,pageSize);
        List<Goods> goodsList = null;
        if(level==1) {
            goodsList = goodsShippingServiceImpl.findGoodsWithToptype(goodsType);
        } else if(level==2) {
            goodsList = goodsShippingServiceImpl.findGoodsWithType(goodsType);
        }
        PageInfo pageInfo=new PageInfo(goodsList);
        return pageInfo;
    }

这个util是利用PageHelper进行分页操作。

五、Controller层

这部分应该大家都能看懂,我挑几个重点的讲述一下。

1. adminController
(1)login
代码语言:javascript
复制
    @PostMapping(value = "/admin/login",produces = {"application/json;charset=UTF-8"})
    @ResponseBody
    public ResponseMessage login(@RequestParam String username, @RequestParam String password, HttpSession session){
        if (username==null || password==null){
            return ResponseMessage.error();
        }else {
            MD5 md5 = MD5.create();
            password = md5.digestHex(password);
            List<Administrator> admin = adminService.adminLogin(username, password);
            if (admin.size()==1){
                session.setAttribute("admin", admin.get(0));
                return ResponseMessage.success();
            }else {
                return ResponseMessage.error();
            }
        }
    }

这个是后台的登录逻辑,首先是判断用户名或密码是否为空,然后讲密码使用md5算法进行转换,然后传参到adminService的adminLogin,如果有对象返回,就存一个session,返回一个ResponseMessage。否则返回处理失败的ResponseMessage。

(2)searchGoods
代码语言:javascript
复制
    @GetMapping(value = "/searchGoods",produces = {"application/json;charset=UTF-8"})
    @ResponseBody
    public ModelAndView searchGoods(String keyword,String search_field,String pageNum){
        if (pageNum==null){
            pageNum="1";
        }
        System.out.println(keyword+" "+search_field);
        List<Goods> goods1=new ArrayList<>();
        if (search_field.equals("商品名")){
            goods1 = adminService.searchGoodsWithName(keyword);
        }else {
            goods1 = adminService.searchGoodsWithTypeId(Integer.parseInt(keyword));
        }
        int totalRecord = goods1.size();
        System.out.println(totalRecord);
        PageBean pb = new PageBean(Integer.parseInt(pageNum),20,totalRecord);
        int startIndex = pb.getStartIndex();
        List<Goods> goods = new ArrayList<>();
        for (int i = startIndex; i < startIndex+20; i++) {
            try {
                goods.add(goods1.get(i));
            }catch (Exception e){
                break;
            }
        }
        pb.setList(goods);
        ModelAndView mv = new ModelAndView();
        mv.addObject("goodsPageInfo",pb).addObject("keyword",keyword).addObject("search_field", search_field);
        mv.setViewName("/admin/lyear_pages_doc2");
        return mv;
    }

这个方法是用于后台Good List查询商品的。

此处根据search_field是id还是name来使用两种方法查询

keyword是在text input标签输入的内容。

然后进行查询分页展示。

2. IndexController
(1)userAddress
代码语言:javascript
复制
    @GetMapping("/userAddress")
    public ModelAndView userAddress(HttpSession session){
        ModelAndView mv = new ModelAndView();
        if (session.getAttribute("loginUser")==null){
            mv.setViewName("/login");
        }else {
            int userId = ((Consumer)session.getAttribute("loginUser")).getId();
            List<GoodsShippingAddress> addresses = userServiceImpl.selectAddress(userId);
            mv.addObject("addresses",addresses);
            mv.setViewName("userAddress");
        }
        return mv;
    }

此处用到了ModelAndView对象,这个是关键。 他能够将View与Model捆绑在一起。 这个操作是访问收货地址,如果session中没有记录,就前往登录界面。 否则,根据用户ID,在数据库中查找地址信息,然后与视图捆绑渲染。

其他controller的其他方法都大同小异~

六、与前端整合

这里主要使用了Thymeleaf和Ajax,Thymleaf主要是在每个需要后台传数据的标签前面加th:. 变量用${obj.id}类似的形式。

下面是文档: Documentation - Thymeleaf https://www.thymeleaf.org/documentation.html

至于Ajax,本项目主要是类似于下面这样的形式:

代码语言:javascript
复制
    $(function() {
        // 直接发送请求获取数据
        $.ajax({
            url: "/initIndex",
            method: "GET",
            success: function (response) {
                console.log(response);
                // 获取到后台返回的一级商品类型数据
                let $goodsTypes = response.objectMap.goodsTypeList;
                // 遍历商品类型
                for (let i = 0; i < $goodsTypes.length; i++) {
                    // 创建对应的标签
                    let $li = $("<li>").attr("id", $goodsTypes[i].id).text($goodsTypes[i].name);
                    let $span = $("<span>").addClass("layui-icon layui-icon-right");
                    // let $span = $("<span>").append($("<em>").addClass("glyphicon glyphicon-menu-right"));
                    $li.append($span)
                    // 将数据加载到网页中ul菜单中
                    $(".my-daohang2 ul").append($li);
                }
                // 加载楼层商品

                let $xiaomiphonelist = response.objectMap.goodsMap["30018"];
                loadGoods($xiaomiphonelist, $("#phone"));

                let $tvlist = response.objectMap.goodsMap["30011"];
                loadGoods($tvlist, $("#tv"));

                let $tablelist = response.objectMap.goodsMap["30019"];
                loadGoods($tablelist, $("#table"));


            },
            error: function () {
                console.log("请求迷路了....")
            }
        });

其中,url: "/initIndex"是将要触发传递的路径, method: "GET"代表POST还是GET或其他方法,success:代表如果请求成功将要进行的逻辑,error:代表请求失败要进行的逻辑。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 零、SSM项目配置文件与项目启动的一些需知
    • 1. 项目启动读取配置文件过程
      • 2. 配置文件详解
      • 一、项目结构介绍
      • 二、DTO
      • 三、DAO
      • 四、Service层
        • 1. AdminService
          • (1) adminLogin
          • (2)selectAll
          • (3)deleteGoodsInfo
          • (4)searchGoodsWithName
        • 2.GoodsShippingServiceImpl
          • 3.GoodsTypeServiceImpl
            • (1) findTopLevel
          • 4.UserServiceImpl
            • (1)insetorder
            • (2)updataorder
          • 5. UtilServiceImpl
            • (1)queryByPage
        • 五、Controller层
          • 1. adminController
            • (1)login
            • (2)searchGoods
          • 2. IndexController
            • (1)userAddress
        • 六、与前端整合
        相关产品与服务
        数据库
        云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档