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
框架:Spring
,Spring MVC
,Mybatis
,SSM整合
数据库:MySQL
开发工具:IDEA
,Maven
表结构
建表sql 已经放在 Github 项目的 /sql 文件夹下
表名 | 中文含义 | 介绍 |
---|---|---|
Category | 分类表 | 存放分类信息,如女装,平板电视,沙发等 |
Property | 属性表 | 存放属性信息,如颜色,重量,品牌,厂商,型号等 |
Product | 产品表 | 存放产品信息,如LED40EC平板电视机,海尔EC6005热水器 |
PropertyValue | 属性值表 | 存放属性值信息,如重量是900g,颜色是粉红色 |
ProductImage | 产品图片表 | 存放产品图片信息,如产品页显示的5个图片 |
Review | 评论表 | 存放评论信息,如买回来的蜡烛很好用,么么哒 |
User | 用户表 | 存放用户信息,如斩手狗,千手小粉红 |
Order | 订单表 | 存放订单信息,包括邮寄地址,电话号码等信息 |
OrderItem | 订单项表 | 存放订单项信息,包括购买产品种类,数量等 |
一 | 多 |
---|---|
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
的 实体类 和 表结构 设计如下:
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 主要提供如下功能
- 指定 spring 的配置文件为 classpath 下的 applicationContext.xml
- 设置中文过滤器
- 指定 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() 传递过来的集合。
管理分类剩下部分就不展开了
完整的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); }
思路流程图
项目重构
分类管理中的 CategoryMapper.xml 使用很直接的 SQL 语句开发出来,这样的好处是简单易懂,便于理解。可是,随着本项目功能的展开和复杂度的提升,使用这种直接的SQL语句方式的开发效率较低,需要自己手动写每一个SQL语句,而且其维护起来也比较麻烦。
所以我们做进一步的改进,主要是在分页方式和逆向工程方面做了重构。
分页方式
目前的分页方式是自己写分页对应的 limit SQL 语句,并且提供一个获取总数的 count(*) SQL。 不仅如此, mapper, service, service.impl 里都要提供两个方法:list(Page page);
count();
分类是这么做的,后续其他所有的实体类要做分页管理的时候都要这么做,所以为了提高开发效率,把目前的分页方式改为使用 pageHelper 分页插件来实现。逆向工程
目前分类管理中 Mybatis 中相关类都是自己手动编写的,包括:Category.java, CategoryMapper.java和CategoryMapper.xml。
尤其是 CategoryMapper.xml 里面主要是SQL语句,可以预见在接下来的开发任务中,随着业务逻辑的越来越复杂,SQL 语句也会越来越复杂,进而导致开发速度降低,出错率增加,维护成本上升等问题。
为了解决手动编写 SQL 语句效率低这个问题,我们对 Mybatis 部分的代码,使用逆向工程进行重构。
所谓的逆向工程,就是在已经存在的数据库表结构基础上,通过工具,自动生成 Category.java, CategoryMapper.java 和 CategoryMapper.xml,想想就很美好是吧。
pageHelper 分页
因为使用插件可以获取总数信息和实现分页查询了,所以关于分页操作的部分配置和代码要做修改。
修改 CategoryMapper.xml
- 去掉 total SQL 语句
- 修改 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 类
进行如下操作:
- 去掉 total() 方法
- 去掉 list(Page page) 方法
- 新增 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
这是插件自动生成的 xml,与我们自己手动写的也差不了多少,主要区别在于提供了一个 id="Example_Where_Clause" 的 SQL,借助这个可以进行多条件查询。
自动生成的 pojo 类
MybatisGenerator 会生成一个类叫做 XXXXExample 的。 它的作用是进行排序,条件查询的时候使用。
在分类管理里用到了排序,但是没有使用到其条件查询,在后续的属性管理里就会看到其条件查询的用法了。
自动生成的 mapper 接口
与手动编写的 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
然后,我们再根据数据库字段,一次性生成所有的 实体类,example 类,mapper 和 xml,如果需要定制,直接在生成的东西上修改就行了,真是舒服啊。
后台还有其他管理页面的,比如属性管理、产品管理等,由于篇幅原因,具体的请移步github-Tmall_SSM项目。
前台页面展示
此处是 SSH 跑起来截的图,SSM 版本目前只做了后台,前台未做,敬请期待...
本文所讲不足整个项目的 1/10 ,有兴趣的朋友请移步 github 项目的地址 。
参考
天猫SSM整站学习教程 里面除了本项目,还有 Java 基础,前端,Tomcat 及其他中间件等教程, 可以注册一个账户,能保存学习记录。