做者:追梦1819
原文:https://www.cnblogs.com/yanfei1819/p/10936304.html
版权声明:本文为博主原创文章,转载请附上博文连接!
html
总结前面几章,咱们了解了 SpringBoot 的基本用法及其配置,整合各大 ORM 框架,并了解了 Thymelaf 的基本用法。java
本章将综合前面的知识,作一个完整Java web 的增删改查的示例。一来是对知识的整合,二来是考虑到不少读者是新手,一个完整的示例可能更加有助于其对 SpringBoot 的理解和掌握。mysql
前面的ORM 框架有多种,本章中选取的是 MyBatis(没有使用 MyBatis 相关插件) ,做者比较喜欢该框架,同时在行业中的应用也很普遍。git
本示例中,创建手机表,字段包括:名称、价格、颜色和生产日期。github
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; CREATE TABLE `mobile_phone` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '名称', `price` decimal(7, 2) NOT NULL COMMENT '价格', `color` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '颜色', `production_date` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL COMMENT '生产日期', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact; INSERT INTO `mobile_phone` VALUES (1, '苹果', 6988.00, '银色', '2018-12-12'); INSERT INTO `mobile_phone` VALUES (2, '华为', 3988.00, '白色', '2019-12-09'); SET FOREIGN_KEY_CHECKS = 1;
如下是做者用 idea 建立的示例项目:web
项目结构是做者的我的习惯,基本也符合 Java Web 的分层结构。下面也再次说明一下项目结构的含义:spring
com.yanfei1819.springbootmybatisthymeleafdemo.db.dao
sql
项目的持久化层,与数据库交互;数据库
com.yanfei1819.springbootmybatisthymeleafdemo.db.dto
json
与数据库交互的实体类;
com.yanfei1819.springbootmybatisthymeleafdemo.entity.response
接口返回的参数实体类;
com.yanfei1819.springbootmybatisthymeleafdemo.entity.vo
与页面交互的实体类;
com.yanfei1819.springbootmybatisthymeleafdemo.service
项目业务层的接口和实现类;
com.yanfei1819.springbootmybatisthymeleafdemo.web.controller
项目的 controller 层,外部直接访问;
com.yanfei1819.springbootmybatisthymeleafdemo.SpringbootMybatisThymeleafDemoApplication
项目入口启动类;
\src\main\resources\static
存放项目静态资源文件;
\src\main\resources\templates
SpringBoot 默认的模板引擎存放位置(可自定义)。
一、 代码是按照做者的以往的编写习惯写的,并不是标准,仅供你们参考;
二、 示例中的代码,都是严格按照分层的,包括数据接收的实体类;
三、 有读者有疑问,项目中为何会有两个属性彻底一致的实体类,xxxDTO 和 xxxVO ?其实这是为了增长项目的扩展性和健壮性而设的,xxxDTO 与数据库交互,xxxVO 与页面交互,严格分层了。其实实体类的种类根据项目规模大小而定,项目越大,可能定义的实体类的种类越多。名称能够参照《阿里巴巴Java开发手册》:
四、 示例代码,做者虽然尽可能遵照了代码规范,可是因为篇幅所限,仍是简化了逻辑,只演示了基本的功能。时间项目中的业务逻辑可能比这个复杂不少,好比,实际项目中可能有分页查询、条件查询、排序等。因此读者要注意;
五、 在本文中,通用因为篇幅的缘由,只列出示例中的核心代码,若是读者想获取完整的代码,能够移步到 本人的GitHub 进行下载;
六、 示例中只作了功能的演示,没有 CSS 样式,读者可自行添加。
1、引入 maven 依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies>
2、配置相关的信息:
### 数据库信息 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=root ### 模板引擎 spring.thymeleaf.servlet.content-type=text/html spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html ### 驼峰命名法转换 mybatis.configuration.map-underscore-to-camel-case=true
其他的信息,可自行配置。
3、建立 db 层:
实体类:
public class MobilePhoneDTO { private Long id; private String name; private Double price; private String color; private String productionDate; // get/set 省略 }
dao类:
public interface MobilePhoneDAO { @Select("select * from mobile_phone ") List<MobilePhoneDTO> listMobilePhones(); @Select("select * from mobile_phone where id = #{id}") MobilePhoneDTO getMobilePhoneById(Long id); @Update("insert into mobile_phone(`name`,price,color,production_date) values(#{name},#{price},#{color},#{productionDate}") int insertMobilePhone(MobilePhoneDTO dto); @Update("UPDATE mobile_phone set `name`=#{name},price=#{price},color=#{color},production_date=#{productionDate} WHERE id=#{id}") int updateMobilePhone(MobilePhoneDTO dto); @Delete("DELETE FROM mobile_phone WHERE id = #{id}") int deleteMobilePhoneById(Long id); }
4、建立 service 层:
接口:
public interface MobilePhoneService { List<MobilePhoneVO> listMobilePhones(); MobilePhoneVO getMobilePhoneById(Long id); BaseResponse updateMobilePhone(MobilePhoneVO vo); BaseResponse insertMobilePhone(MobilePhoneVO vo); BaseResponse deleteMobilePhoneById(Long id); }
实现类:
@Service public class MobilePhoneServiceImpl implements MobilePhoneService { @Autowired private MobilePhoneDAO mobilePhoneDAO; @Override public List<MobilePhoneVO> listMobilePhones() { List<MobilePhoneDTO> dtos = mobilePhoneDAO.listMobilePhones(); List<MobilePhoneVO> vos = new ArrayList<>(); for (MobilePhoneDTO dto : dtos) { MobilePhoneVO vo = new MobilePhoneVO(); BeanUtils.copyProperties(dto,vo); vos.add(vo); } return vos; } @Override public MobilePhoneVO getMobilePhoneById(Long id) { MobilePhoneDTO dto = mobilePhoneDAO.getMobilePhoneById(id); MobilePhoneVO vo = new MobilePhoneVO(); BeanUtils.copyProperties(dto,vo); return vo; } @Override public BaseResponse updateMobilePhone(MobilePhoneVO vo) { MobilePhoneDTO dto = new MobilePhoneDTO(); BeanUtils.copyProperties(vo,dto); int updateCount = mobilePhoneDAO.updateMobilePhone(dto); if(updateCount<1){ return new BaseResponse("数据更新失败"); } return new BaseResponse(); } @Override public BaseResponse insertMobilePhone(MobilePhoneVO vo) { MobilePhoneDTO dto = new MobilePhoneDTO(); BeanUtils.copyProperties(vo,dto); int insertCount = mobilePhoneDAO.insertMobilePhone(dto); if(insertCount<1){ return new BaseResponse("数据插入失败"); } return new BaseResponse(); } @Override public BaseResponse deleteMobilePhoneById(Long id) { int deleteCount = mobilePhoneDAO.deleteMobilePhoneById(id); if(deleteCount<1){ return new BaseResponse("数据删除失败"); } return new BaseResponse(); } }
以上的实现中,包含了增删改查等功能。
此处为了更加友好的返回数值,增长了 BaseResponse 类,该类是:
public class BaseResponse<T> { private Integer code; private String msg; private T data; // set/get 省略 public static BaseResponse defaultNo(String msg) { return new BaseResponse(1,msg); } public static BaseResponse defaultOk() { return new BaseResponse(); } public BaseResponse() { this.code = 0; this.msg = "success"; } public BaseResponse(String msg) { this.code = 1; this.msg = msg; } public BaseResponse(T data) { this.data = data; this.code = 0; this.msg = "success"; } public BaseResponse(Integer code, String msg){ this.code = code; this.msg = msg; } }
细心的读者可能会发现以上的一个细节,有的方法返回了 BaseResponse,可是有的方法直接返回了页面的实体类 MobilePhoneVO。
此处为了作演示,特地作了两种返回参数处理状况。一般状况下,咱们在开发先后端分离项目的时候,都会将返回的数值封装为 json 格式,以便后端能够与 PC 端和 APP 端同时进行交互。若是非先后端分离,那基本就不作要求了。
不过,做者给你们的建议是,能统一格式时尽可能统一,代码看起来也更加优雅一些。
5、建立 controller 层:
@Controller @RequestMapping("/mobile/phone") public class MobilePhoneController { @Autowired private MobilePhoneService service; @GetMapping("/listMobilePhones") public String listMobilePhones(Model model){ List<MobilePhoneVO> vos = service.listMobilePhones(); model.addAttribute("response",vos); return "mobilephoneList"; } @GetMapping("/get/{id}") @ResponseBody public BaseResponse getMobilePhoneById(@PathVariable Long id){ MobilePhoneVO vo = service.getMobilePhoneById(id); return new BaseResponse(vo); } @PostMapping("/insertMobilePhone") @ResponseBody public BaseResponse insertMobilePhone(@RequestBody MobilePhoneVO vo){ return service.insertMobilePhone(vo); } // 进入编辑页面 @GetMapping("/toEditMobilePhone") public String toAddMobilePhone(Model model,Long id){ MobilePhoneVO vo = service.getMobilePhoneById(id); model.addAttribute("vo",vo); return "toEditMobilePhone"; } @PostMapping("/updateMobilePhone") @ResponseBody public String updateMobilePhone(@RequestBody MobilePhoneVO vo){ service.updateMobilePhone(vo); return "redirect:mobilephoneList"; // 跳转至列表页 } @GetMapping("/delete/{id}") @ResponseBody public BaseResponse deleteMobilePhoneById(@PathVariable Long id){ return service.deleteMobilePhoneById(id); } }
controller 层包括增删改查接口,同时提供页面跳转的接口(此处只作了编辑功能,其他的都相似)。在作增删改后,通常是跳转至列表页面。固然,你也能够加入其它的交互逻辑。
6、建立页面:
列表页 mobilephoneList:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>追梦1819的SpringBoot教程</title> <style> table, table tr th, table tr td { border: 1px solid #0094ff; } </style> </head> <body> <div class="container-fluid"> <div class="row"> <main role="main"> <div class="content_bottom_right" id="mobilephoneList"> <div class="input-group"> <table class="table text-nowrap imagetable"> <thead> <tr> <th>序号</th> <th>名称</th> <th>价格</th> <th>颜色</th> <th>生产日期</th> <th>操做</th> </tr> </thead> <tbody th:each="mobilephone,item:${response}"> <tr> <td>[[${item.count}]]</td> <td>[[${mobilephone.name}]]</td> <td>[[${mobilephone.price}]]</td> <td>[[${mobilephone.color}]]</td> <td>[[${mobilephone.productionDate}]]</td> <td> <a>详情</a> <a th:href="@{/mobile/phone/toEditMobilePhone(id=${mobilephone.id})}">编辑</a> <a>删除</a> </td> </tr> </tbody> </table> </div> </div> </main> </div> </div> </body> </html>
添加页:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>追梦1819的SpringBoot教程</title> </head> <body class="container"> <br/> <h2>修改手机</h2> <br/> <div> <form class="form-horizontal" th:action="@{/mobile/phone/updateMobilePhone}" th:object="${vo}" method="post"> <div> <label>名称</label> <input name="name" id="name" th:value="*{name}"> </div> <div> <label>颜色</label> <input name="color" id="color" th:value="*{color}"> </div> <div> <label>价格</label> <input name="price" id="price" th:value="*{price}"> </div> <div> <label>价格</label> <input name="productionDate" id="productionDate" th:value="*{productionDate}"> </div> <input type="submit" value="添加"/> </form> </div> </body> </html>
以上页面说明几点:
xmlns:th="http://www.thymeleaf.org"
;最后,能够看看效果:
最后,补充一些关于thymelaf经常使用的属性值配置:
# THYMELEAF (ThymeleafAutoConfiguration) #开启模板缓存(默认值:true) spring.thymeleaf.cache=true #Check that the template exists before rendering it. spring.thymeleaf.check-template=true #检查模板位置是否正确(默认值:true) spring.thymeleaf.check-template-location=true #Content-Type的值(默认值:text/html) spring.thymeleaf.content-type=text/html #开启MVC Thymeleaf视图解析(默认值:true) spring.thymeleaf.enabled=true #模板编码 spring.thymeleaf.encoding=UTF-8 #要被排除在解析以外的视图名称列表,用逗号分隔 spring.thymeleaf.excluded-view-names= #要运用于模板之上的模板模式。另见StandardTemplate-ModeHandlers(默认值:HTML5) spring.thymeleaf.mode=HTML5 #在构建URL时添加到视图名称前的前缀(默认值:classpath:/templates/) spring.thymeleaf.prefix=classpath:/templates/ #在构建URL时添加到视图名称后的后缀(默认值:.html) spring.thymeleaf.suffix=.html #Thymeleaf模板解析器在解析器链中的顺序。默认状况下,它排第一位。顺序从1开始,只有在定义了额外的TemplateResolver Bean时才须要设置这个属性。 spring.thymeleaf.template-resolver-order= #可解析的视图名称列表,用逗号分隔 spring.thymeleaf.view-names=
至此,基本完成了 SpringBoot + MyBatis + Thymelaf 的综合演示。功能很简单,用法也很简单,只不过作了总体的综合。本章中演示的 ORM 框架是 MyBatis,读者能够自行将 SpringBoot + Thymelaf + 其余 ORM 框架整合,以加深理解。
源码:个人GitHub