JavaWeb入门_模仿天猫整站Tmall_SSM实践项目

Tmall_SSM

技术栈 Spring MVC+ Mybatis + Spring + Jsp + Tomcat , 是 Java Web 入门非常好的练手项目

效果展示:

模仿天猫前台
模仿天猫后台

项目简介

关联项目
github - 天猫 JavaEE 项目
github - 天猫 SSH 项目
github - 天猫 SSM 项目

之前使用 JavaEE 整套技术和 SSH 框架来作为解决方案,实现模仿天猫网站的各种业务场景,现在开始使用 SSM 框架技术。

项目用到的技术如下:
Java: Java SE基础
前端: HTML,CSS, JavaScript, JQuery,AJAX, Bootstrap
J2EE:Tomcat, Servlet, JSP, Filter
框架:SpringSpring MVCMybatisSSM整合
数据库:MySQL
开发工具: IDEA ,Maven

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第1张图片

表结构

建表sql 已经放在 Github 项目的 /sql 文件夹下

表名 中文含义 介绍
Category 分类表 存放分类信息,如女装,平板电视,沙发等
Property 属性表 存放属性信息,如颜色,重量,品牌,厂商,型号等
Product 产品表 存放产品信息,如LED40EC平板电视机,海尔EC6005热水器
PropertyValue 属性值表 存放属性值信息,如重量是900g,颜色是粉红色
ProductImage 产品图片表 存放产品图片信息,如产品页显示的5个图片
Review 评论表 存放评论信息,如买回来的蜡烛很好用,么么哒
User 用户表 存放用户信息,如斩手狗,千手小粉红
Order 订单表 存放订单信息,包括邮寄地址,电话号码等信息
OrderItem 订单项表 存放订单项信息,包括购买产品种类,数量等

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第2张图片

Category-分类 Product-产品
Category-分类 Property-属性
Property-属性 PropertyValue-属性值
Product-产品 PropertyValue-属性值
Product-产品 ProductImage-产品图片
Product-产品 Review-评价
User-用户 Order-订单
Product-产品 OrderItem-订单项
User-用户 OrderItem-订单项
Order-订单 OrderItem-订单项
User-用户 User-评价

以上直接看可能暂时无法完全理解,结合后面具体到项目的业务流程就明白了。


开发流程

首先使用经典的 SSM 模式进行由浅入深地开发出第一个分类管理模块 ,
然后分析这种方式的弊端,再对其进行项目重构,使得框架更加紧凑,后续开发更加便利和高效率。

分类管理模块

Category 实体类

准备 Category 实体类,定义对应的字段即可。
举个例子,对于 分类 / category 的 实体类 和 表结构 设计如下:

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第3张图片


Mapper 接口

public interface CategoryMapper {
    List list();
} 

CategoryMapper.xml 指定映射的 sql 和结果集

com.caozhihu.tmall.mapper.CategoryMapper 对应上面的 Mapper 接口。mybatis 的 sql 是手打的,还好有逆向工程,后面重构会讲。


  

CategoryService 接口
public interface CategoryService{
    List list();
}

CategoryServiceImpl 实现类
@Service
public class CategoryServiceImpl  implements CategoryService {
    @Autowired
    CategoryMapper categoryMapper;
    public List list(){
        return categoryMapper.list();
    };
}

在 list() 方法中,通过其自动装配的一个 CategoryMapper 对象的 list() 方法来获取所有的分类对象。


CategoryController 控制类

@Controller //声明当前类是一个控制器
@RequestMapping("") //访问的时候无需额外的地址
public class CategoryController {
    @Autowired //自动装配进 categoryService 接口
    CategoryService categoryService;
    
    @RequestMapping("admin_category_list")
    public String list(Model model){
        List cs= categoryService.list();
        model.addAttribute("cs", cs); 
        return "admin/listCategory";
    }
}

在list方法中,通过 categoryService.list() 获取所有的 Category 对象,然后放在 "cs" 中,并服务端跳转到“admin/listCategory” 视图。


jdbc.properties 数据库配置文件

#数据库配置文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/tmall_ssm?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=admin

applicationContext.xml

这里配置使用了阿里巴巴的 druid 数据库连接池,这些配置基本都是固定写法,PSCache 就是 PreparedStatement 缓存,据说可以大幅提升性能。


    
    
    

    
    
    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    

    
    
        
        
        
            
                classpath:com/caozhihu/tmall/mapper/*.xml
                classpath:mapper/*.xml
            
        
        
        
            
                
                    
                        
                        
                    
                
            
        
    

    
    
        
    

这里只放了核心配置部分,头部命名空间已省略


springMVC.xml

   
   
   
       
   
   

   
   

   
   
       
       
       
   

   
   

web.xml

web.xml 主要提供如下功能

  1. 指定 spring 的配置文件为 classpath 下的 applicationContext.xml
  2. 设置中文过滤器
  3. 指定 spring mvc 配置文件为 classpath 下的 springMVC.xml



    
    
        contextConfigLocation
        classpath:applicationContext.xml
    
    
        org.springframework.web.context.ContextLoaderListener
    

    
    
        CharacterEncodingFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            utf-8
        
    
    
        CharacterEncodingFilter
        /*
    

    
    
        mvc-dispatcher
        org.springframework.web.servlet.DispatcherServlet
        
        
            contextConfigLocation
            classpath:springMVC.xml
        
        1
    
    
        mvc-dispatcher
        /
    

访问 jsp 显示数据

Controller 中的 Model 携带数据跳转到 jsp ,作为视图,担当的角色是显示数据,借助 JSTL 的 c:forEach 标签遍历从 CategoryController.list() 传递过来的集合。

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第4张图片

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第5张图片

管理分类剩下部分就不展开了
完整的CategoryMapper.xml代码如下


    

    

    
        insert into category ( name ) values (#{name})
    

    
        delete from category where id= #{id}
    
 
    
 
    
        update category set name=#{name} where id=#{id}
    

完整的CategoryMapper接口代码如下

public interface CategoryMapper {
     List list(Page page);
     int total();
     void add(Category category);
     void delete(int id);
     Category get(int id);
     void update(Category category);
}

完整的CategoryService接口代码如下

public interface CategoryService{
    int total();
    List list(Page page);
    void add(Category category);
    void delete(int id);
    Category get(int id);
     void update(Category category);
}

完整的CategoryServiceImpl实现类代码就不放着了,只是实现了每个方法,并在其中调用对应的 CategoryMapper 方法而已,如下:

public List list(Page page) { return categoryMapper.list(page); }

思路流程图

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第6张图片

项目重构

分类管理中的 CategoryMapper.xml 使用很直接的 SQL 语句开发出来,这样的好处是简单易懂,便于理解。可是,随着本项目功能的展开和复杂度的提升,使用这种直接的SQL语句方式的开发效率较低,需要自己手动写每一个SQL语句,而且其维护起来也比较麻烦。
所以我们做进一步的改进,主要是在分页方式和逆向工程方面做了重构。

  1. 分页方式
    目前的分页方式是自己写分页对应的 limit SQL 语句,并且提供一个获取总数的 count(*) SQL。 不仅如此, mapper, service, service.impl 里都要提供两个方法:
    list(Page page);
    count();
    分类是这么做的,后续其他所有的实体类要做分页管理的时候都要这么做,所以为了提高开发效率,把目前的分页方式改为使用 pageHelper 分页插件来实现。

  2. 逆向工程
    目前分类管理中 Mybatis 中相关类都是自己手动编写的,包括:Category.java, CategoryMapper.java和CategoryMapper.xml。
    尤其是 CategoryMapper.xml 里面主要是SQL语句,可以预见在接下来的开发任务中,随着业务逻辑的越来越复杂,SQL 语句也会越来越复杂,进而导致开发速度降低,出错率增加,维护成本上升等问题。
    为了解决手动编写 SQL 语句效率低这个问题,我们对 Mybatis 部分的代码,使用逆向工程进行重构。
    所谓的逆向工程,就是在已经存在的数据库表结构基础上,通过工具,自动生成 Category.java, CategoryMapper.java 和 CategoryMapper.xml,想想就很美好是吧。


pageHelper 分页

因为使用插件可以获取总数信息和实现分页查询了,所以关于分页操作的部分配置和代码要做修改。

修改 CategoryMapper.xml

  1. 去掉 total SQL 语句
  2. 修改 list SQL 语句,去掉其中的 limit

    
 
    
        insert into category ( name ) values (#{name})
    

    
        delete from category where id= #{id}
    
 
    
 
    
        update category set name=#{name} where id=#{id}
    

使用 PageHelper 提供的方法进行分页查询

CategoryMapper 接口 /CategoryService 接口 / CategoryServiceImpl 类 进行如下操作:

  1. 去掉 total() 方法
  2. 去掉 list(Page page) 方法
  3. 新增 list() 方法

使用分页插件后的 CategoryController.list()方法

@Controller
@RequestMapping("")
public class CategoryController {
    @Autowired
    CategoryService categoryService;
   
    @RequestMapping("admin_category_list")
    public String list(Model model,Page page){
        PageHelper.offsetPage(page.getStart(),page.getCount());
        List cs= categoryService.list();
        int total = (int) new PageInfo<>(cs).getTotal();
        page.setTotal(total);
        model.addAttribute("cs", cs);
        model.addAttribute("page", page);
        return "admin/listCategory";
    }
}

在 applicationContext.xml 配置 pagehelper 插件

其实在上面显示的已经是配置过插件了,这里再提一下,就是在 SqlSessionFactoryBean 命名空间内设置一个 plugins 的属性。


    

            
                
                    
                        
                        
                    
                
            

Mybatis 逆向工程

MybatisGenerator 插件是 Mybatis 官方提供的,这个插件存在一个问题 ,即当第一次生成了CategoryMapper.xml 之后,再次运行会导致 CategoryMapper.xml 生成重复内容,而影响正常的运行。
为了解决这个问题,需要自己写一个小插件类 OverIsMergeablePlugin 。

OverIsMergeablePlugin

这是复制别人的,具体原理还没研究。

public class OverIsMergeablePlugin extends PluginAdapter {
    @Override
    public boolean validate(List warnings) {
        return true;
    }
    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        try {
            Field field = sqlMap.getClass().getDeclaredField("isMergeable");
            field.setAccessible(true);
            field.setBoolean(sqlMap, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}

generatorConfig.xml 指定生成策略

这里提供一部分代码,具体的在 github


    
        
        
        
        
            
            
        
        
        
        
        
        
            
        
        
        
            
            
        
        
        
            
        
        
        
            
        

        
        
            
            
        

MybatisGenerator 生成执行类

运行即生成 mapper,pojo,xml 文件,核心代码如下

List warnnings = new ArrayList<>();
        boolean overwrite = true;
        InputStream is = MybatisGenerator.class.getClassLoader().getResource("generatorConfig.xml").openStream();//获取配置文件对应路径的输入流
        ConfigurationParser configurationParser = new ConfigurationParser(warnnings);
        Configuration configuration = configurationParser.parseConfiguration(is);
        is.close();

        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(configuration, callback, warnnings);
        myBatisGenerator.generate(null);

自动生成的 CategoryMapper.xml

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第7张图片

这是插件自动生成的 xml,与我们自己手动写的也差不了多少,主要区别在于提供了一个 id="Example_Where_Clause" 的 SQL,借助这个可以进行多条件查询。


自动生成的 pojo 类

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第8张图片

MybatisGenerator 会生成一个类叫做 XXXXExample 的。 它的作用是进行排序,条件查询的时候使用。
在分类管理里用到了排序,但是没有使用到其条件查询,在后续的属性管理里就会看到其条件查询的用法了。


自动生成的 mapper 接口

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第9张图片

与手动编写的 CategoryMapper 对比,CategoryMapper 也是提供 CURD 一套,不过方法名发生了变化,比如:
delete() 叫做 deleteByPrimaryKey(),
update 叫做 updateByPrimaryKey(),
除此之外,还提供了一个 updateByPrimaryKeySelective() 方法,其作用是只更新,即只修改新插入的不为 null 的字段。(比如当前数据是 {name,age} ,插入新数据是 {newName,null},如果使用此方法,则插入之后数据变为 {newName,age} 而不是 {newName,null})

还有个改动是 list() 方法 ,变成了selectByExample(CategoryExample example);

修改 CategoryServiceImpl 实现类

因为 CategoryMapper 的方法名发生了变化,所以 CategoryServiceImpl 要做相应的调整。
值得一提的是list方法:

public List list() {
        CategoryExample example =new CategoryExample();
        example.setOrderByClause("id desc");
        return categoryMapper.selectByExample(example);
    }

按照这种写法,传递一个 example 对象,这个对象指定按照 id 倒排序来查询
我查看了 xml 里的映射, 在对应的查询语句 selectByExample 里面,
会判断 orderByClause 是否为空,如果不为空就追加 order by ${orderByClause}
这样如果设置了 orderByClause 的值为“id desc” ,执行的 sql 则会是 order by id desc

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第10张图片

然后,我们再根据数据库字段,一次性生成所有的 实体类,example 类,mapper 和 xml,如果需要定制,直接在生成的东西上修改就行了,真是舒服啊。


后台还有其他管理页面的,比如属性管理、产品管理等,由于篇幅原因,具体的请移步github-Tmall_SSM项目。

前台页面展示

此处是 SSH 跑起来截的图,SSM 版本目前只做了后台,前台未做,敬请期待...
JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第11张图片

JavaWeb入门_模仿天猫整站Tmall_SSM实践项目_第12张图片

本文所讲不足整个项目的 1/10 ,有兴趣的朋友请移步 github 项目的地址 。

参考

天猫SSM整站学习教程 里面除了本项目,还有 Java 基础,前端,Tomcat 及其他中间件等教程, 可以注册一个账户,能保存学习记录。

转载于:https://www.cnblogs.com/czwbig/p/9965562.html

你可能感兴趣的:(数据库,人工智能,测试)