IDEA 使用Spring Boot 超快速搭建 SSM (完整版)
author:Cris
文章目录
零、序
想起小王子里那句话:爱不是相互凝望,而是望向同一个方向,异地恋最大的问题其实不是距离,而是距离带来的沟通无力和词不达意
摧毁异地恋的,从来都不是时间、空间与距离,而是欲望与不坚定。庆幸遇到的这个人,给予我的安全感 ,只是因为我们喜欢彼此 ,分开的日子总有一天会过去
如果有一天,我爱的人离开我
我只回答两个字:好的
绝口不问“你怎么能这样对我”,“到底我哪里做得不对”
经历让我明白,若对方决定分开
必定准备好了理由
我不想听谋划许久冠冕堂皇的借口
凡是离开的必然本就不属于我,只祝好运
从此云淡风轻,过往一笔勾销
人生短暂,我不活在记忆里
—— 摘自虎扑App
温馨提示:本篇笔记紧随上一篇 《IDEA 使用Spring Boot 超快速整合SSM(精简版)》,具体的前期搭建已经给出了详实的流程,感兴趣的同学可以参考;本篇笔记适用于熟悉或者了解或者想要了解 Spring boot 的同学,博主尽量使用易懂的语言为大家带来经过亲身验证的经验,干货满满?
该项目完整代码请参考:https://github.com/zc-cris/SpringBoot_SSM
一、环境修改以及实际场景介绍
实际场景介绍
我们这里引入实际场景来为整篇笔记做下铺垫,否则无法让大家深入体会使用 Spring boot 实际开发的好处,同时该场景也是经过大量精简后的简单场景,请勿较真~
我们模拟用户买书的场景:从查询图书的库存,到比较用户的余额和图书的价格,到最后用户成功购买图书,余额减去图书价格,图书库存-1。一个非常常见的场景,看看如何使用 Spring boot 完成
环境修改
在上一篇笔记的基础上,替换掉 JDBCTemplate,使用 Mybatis ,然后引入 IDEA 插件 MybatisCodeHelperPro 快速帮我们进行开发
具体搭建环境如下:
idea 2018.1.x
java8
spring boot 1.5.6
druid 1.1.10
MySQL 5.7
Navicat Premium 12
maven 3.5
Lombok 插件
Mybatis 3.4.6
MybatisCodeHelperPro 插件
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cris</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.16.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
可以看到,pom.xml 文件还是很小的
MybatisCodeHelperPro
一款可以大大提高开发效率的 Mybatis 插件,个人感觉是非常不错的,具体的安装很简单,如下图
具体的使用技巧,参考这里
如何在 IDEA 中连接数据库
数据库可视化工具不使用 Navicat 也是可以的,因为 IDEA 自带了数据库连接工具,可以进行轻量级的开发,十分方便
后面有时间,我会对其好用的地方做一个简单的整理
除了上面的 user 表,还有一张 book 表
二、代码编写
目标:将实际场景用户买书的流程实现,同时使用事务控制整个流程
2.1、额外注意
项目结构图
因为引入了 Mybatis,需要在 application.yml 文件中进行简单配置
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/sql_primary_study?useSSL=false
driver-class-name: com.mysql.jdbc.Driver
# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 取消 thymeleaf 的模板缓存以及指定渲染文件夹
thymeleaf:
cache: false
prefix: classpath:/templates/
# 这里设置 mybatis 的 mapper 文件所在文件夹
mybatis:
mapper-locations: classpath:/mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
banner 文件可以自己 diy,这里给出参考
${AnsiColor.BRIGHT_RED}
CRIS I LOVE U!!!
关于 Spring boot 的启动类最好如下配置
这里使用 Spring boot 自带的 Tomcat,并且因为是 1.x 版本,最好设置一下映射路径后缀
/**
* @author zc-cris
*/
@SpringBootApplication
@MapperScan("com.cris.dao")
public class DemoApplication extends WebMvcConfigurerAdapter {
/**
* 设置路径匹配规则
*
* @param configurer 路径配置类
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// setUseSuffixPatternMatch : 设置是否是后缀模式匹配,如“/user”是否匹配/user.*,默认真即匹配;
configurer.setUseSuffixPatternMatch(false);
// setUseTrailingSlashMatch : 设置是否自动后缀路径模式匹配,如“/user”是否匹配“/user/”,默认真即匹配;
configurer.setUseTrailingSlashMatch(false);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2.2、开始编码吧~
Ⅰ、Entity
/**
* book表的对应po
*
* @author zc-cris
* @version 1.0
**/
@SuppressWarnings("unused")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Book {
private Integer isbn;
private Integer stock;
private Double price;
}
/**
* 对应user表的po
*
* @author zc-cris
* @version 1.0
**/
@SuppressWarnings("unused")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {
private Integer id;
private String name;
private Integer age;
private Integer deptId;
private Double money;
}
Ⅱ、Dao
BookMapper
/**
* 定义对book表的基础dao操作
*
* @author zc-cris
* @version 1.0
**/
@Mapper
public interface BookMapper {
/**
* 根据isbn查询图书的价格
*
* @param isbn 图书的isbn号
* @return 图书的价格
*/
BigDecimal getPriceByIsbn(@Param("isbn") Integer isbn);
/**
* 根据isbn查询图书的库存
*
* @param isbn 图书的isbn号
* @return 图书的库存量
*/
Integer getStockByIsbn(@Param("isbn") Integer isbn);
/**
* 根据isbn减去一本书的库存
*
* @param isbn 图书的isbn号
* @return 返回1表示(本书库存成功-1),否则返回0
*/
Integer updateStockBySubOne(@Param("isbn") Integer isbn);
/**
* 根据isbn 查询图书的信息
*
* @param isbn 图书的isbn 号
* @return 图书完整信息
*/
Book getBookByIsbn(Integer isbn);
}
对应的 mapper 文件
<?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.cris.dao.BookMapper">
<select id="getPriceByIsbn" resultType="java.math.BigDecimal">
select price
from sql_primary_study.book
where isbn = #{isbn}
</select>
<select id="getStockByIsbn" resultType="java.lang.Integer">
select stock
from sql_primary_study.book
where isbn = #{isbn}
</select>
<update id="updateStockBySubOne">
update sql_primary_study.book
set stock = stock - 1
where isbn = #{isbn}
</update>
<select id="getBookByIsbn" resultType="com.cris.entity.Book">
select
isbn,
stock,
price
from sql_primary_study.book
where isbn = #{isbn}
</select>
</mapper>
UserMapper
/**
* 定义对user表的基础dao操作
*
* @author zc-cris
* @version 1.0
**/
@Mapper
public interface UserMapper {
/**
* 根据用户id查询余额
*
* @param id 用户id
* @return 返回用户的余额
*/
BigDecimal getMoneyById(@Param("id") Integer id);
/**
* 根据图书金额减去用户的余额
*
* @param price 图书金额
* @param id 用户id
* @return 返回1表示执行成功,返回0表示执行失败
*/
Integer updateMoneyByBookPrice(@Param("price") BigDecimal price, @Param("id") Integer id);
}
对应的 mapper 文件
<?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.cris.dao.UserMapper">
<select id="getMoneyById" resultType="java.math.BigDecimal">
select money
from sql_primary_study.user
where id = #{id}
</select>
<update id="updateMoneyByBookPrice">
update sql_primary_study.user
set money = money - #{price}
where id = #{id}
</update>
</mapper>
值得一提的是,从 Dao 层编写代码的时候最好养成边写边测试的好习惯,否则后面除了问题就很惨咯
看看 Cris 小哥哥 的测试用例?
@SpringBootTest()
@RunWith(SpringRunner.class)
public class BookMapperTest {
@Autowired
BookMapper bookMapper;
@Test
public void getPriceByIsbn() {
Assert.assertNotNull(bookMapper.getPriceByIsbn(1234));
}
@Test
public void getStockByIsbn() {
Assert.assertNotNull(bookMapper.getStockByIsbn(1234));
}
@Test
public void updateStockBySubOne() {
Assert.assertEquals(1, bookMapper.updateStockBySubOne(1234).intValue());
}
}
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserMapperTest {
@Autowired
UserMapper userMapper;
@Test
public void getMoneyById() {
Assert.assertNotNull(userMapper.getMoneyById(1));
}
@Test
public void updateMoneyByBookPrice() {
Assert.assertEquals(1, userMapper.updateMoneyByBookPrice(new BigDecimal(30.0), 1).intValue());
}
}
这里省略了 Druid 的配置类书写,感兴趣的可以去上一篇笔记查看,这里就不重复了~
Ⅲ、service
这里省略了 接口层,偷个懒难道不行嘛?
BookServiceImpl
/**
* book的service层
*
* @author zc-cris
* @version 1.0
**/
@Service
public class BookServiceImpl {
private final
BookMapper bookMapper;
@Autowired
public BookServiceImpl(BookMapper bookMapper) {
this.bookMapper = bookMapper;
}
/**
* 根据isbn号卖出一本书
*
* @param isbn 图书的isbn号
*/
public void saleOne(Integer isbn) {
bookMapper.updateStockBySubOne(isbn);
}
/**
* 根据isbn获取该书的库存
*
* @param isbn 图书的isbn
* @return 图书的库存
*/
Integer getStock(Integer isbn) {
return bookMapper.getStockByIsbn(isbn);
}
/**
* 根据isbn获取图书的价格
*
* @param isbn 图书的isbn
* @return 图书的价格
*/
BigDecimal getPrice(Integer isbn) {
return bookMapper.getPriceByIsbn(isbn);
}
/**
* 根据isbn获取图书的信息
*
* @param isbn 图书的isbn
* @return 对应的图书信息
*/
public Book getBookByIsbn(Integer isbn) {
return bookMapper.getBookByIsbn(isbn);
}
}
UserServiceImpl
/**
* user的service层
*
* @author zc-cris
* @version 1.0
**/
@Service
@Slf4j
public class UserServiceImpl {
private final
UserMapper userMapper;
private final
BookServiceImpl bookService;
@Autowired
public UserServiceImpl(BookServiceImpl bookService, UserMapper userMapper) {
this.bookService = bookService;
this.userMapper = userMapper;
}
@Transactional(rollbackFor = Exception.class)
public boolean buyOneBook(Integer id, Integer isbn) {
log.info("id为{}的用户准备买isbn为{}的图书", id, isbn);
Integer stock = bookService.getStock(isbn);
if (stock <= 0) {
throw new RuntimeException(String.format("isbn为%s的图书库存不足!", isbn));
}
BigDecimal price = bookService.getPrice(isbn);
BigDecimal money = userMapper.getMoneyById(id);
int compare = price.compareTo(money);
if (compare > 0) {
throw new RuntimeException("用户的钱不够买书");
}
userMapper.updateMoneyByBookPrice(price, id);
log.info("id为{}的用户已经付款{}员", id, price);
bookService.saleOne(isbn);
log.info("id为{}的用户已经买了isbn为{}的一本书,库存已经减去一", id, isbn);
return true;
}
}
值得一提的是:开发中要保持打印日记的好习惯哦,这样子出问题了可以快速定位(⊙o⊙)
最后老样子,测试用例如下:
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class UserServiceImplTest {
@Autowired
UserServiceImpl userService;
@Test
public void buyOneBook() {
// 测试库存不足
// boolean b = userService.buyOneBook(1, 1234);
// 测试用户钱不足
boolean b = userService.buyOneBook(1, 1111);
log.info("b=" + b);
}
}
Ⅳ、controller
这里写一个 controller 即可
/**
* 测试controller
*
* @author zc-cris
*/
@Controller
public class TestController {
private final
BookServiceImpl bookService;
private final
UserServiceImpl userService;
@Autowired
public TestController(BookServiceImpl bookService, UserServiceImpl userService) {
this.bookService = bookService;
this.userService = userService;
}
@PutMapping("/buy")
public String buyBook(HttpServletRequest request) {
String pageStr;
Integer id = Integer.valueOf(request.getParameter("id"));
Integer isbn = Integer.valueOf(request.getParameter("isbn"));
try {
userService.buyOneBook(id, isbn);
pageStr = "success";
} catch (Exception e) {
pageStr = "failed";
}
return pageStr;
}
@GetMapping("/getBook/{isbn}")
@ResponseBody
public Book getUser(@PathVariable("isbn") Integer isbn) {
return bookService.getBookByIsbn(isbn);
}
@GetMapping("/index")
public String index() {
return "index";
}
}
这里使用了 restful 风格的开发风格,不知道小伙伴们看出来么?
ⅴ、静态资源
因为小哥哥实在是不擅长前端,所以这里就随性写了。好像找个前端的小姐姐带带我~
注意:页面需要放在 template 文件夹下
大坑:使用 IDEA 新建的 html5 页面如下
大家看出来 test.html 和上面的 html 页面有什么细小的区别吗?如果看不出来,静态页面是无法访问的?,大家可以自己找找看?
Spring boot 默认支持 restful 风格,不需要额外配置
2.3、让程序跑起来?
启动我们的项目后,console 清空如下:
然后测试一下是否可以直接访问 静态资源 ( /static 文件夹下的文件)
然后开始测试
①测试获取图书信息
②测试买书(重点)
先看看数据库的数据
测试库存不足是否可以买书
再来看看数据库的数据
接下来测试用户余额不足
再来看看数据库的数据
最后测试钱够,库存够的情况

自此,使用 Spring boot 搭配的 SMM 完整版项目测试完毕
三、总结
- 该笔记是个人总结的使用
Spring boot快速搭建SSM项目的相对完整骨架版本(这样说是因为实际开发的项目比该案例大的多,但是基本骨架已经搭建好了,后续只需要在上面添加各种模块即可),相比于网上的复制粘贴,个人觉得还是比较靠谱的?- 该案例仅仅展示了单应用模式下的搭建流程,鉴于现在越来越流行的微服务架构,Cris 会将以前做的
Spring Cloud搭建笔记整理并分享出来,等不及的可以看 Cris 小哥哥的 GitHub 个人地址:https://github.com/zc-cris- 该项目的特点:
- 使用
MybatisCodeHelperPro和Lombok大大简化开发流程- 整个项目使用
restful风格- 通过简化的实际场景需求令读者更加清晰明了
Spring boot的强大和优雅- 严格按照
阿里代码规范进行编写,规范的测试用例?
- 注意点:
Spring boot对restful的天然支持Spring boot 1.x版本对于访问路径后缀名的处理最好自定义IDEA生成html5页面的神坑
来源:CSDN
作者:重庆吴亦凡
链接:https://blog.csdn.net/cris_zz/article/details/82979615