1. 从零开始 Spring Boot 工程
Spring Boot 可以快速构建基于 Spring 框架的 JavaWeb 应用,快速整合包括视图层、MVC架构以及持久化层在内的各种框架,不需要由开发者手动配置,Spring Boot 会实现自动装配。本文主要介绍如何从零实现常见的数据CRUD(增删改查)业务,技术上包含了 Spring Boot 整合 MyBatis 、Spring Boot 整合 Thymeleaf 模板。
使用 IDEA 新建 SpringBoot 工程;
选择【Spring Initializr】,勾选【Custom】常规并输入 https://start.aliyun.com/
,这里是考虑到直接使用默认网址会导致项目下载超时的情况(所以使用的是阿里的镜像环境),然后选择【Next】进入项目命名。
注意这里的打包方式是 Jar 包,还有就是合适的项目命名,【Package】选择到第二级 com.xxxxxx
即可。
在这一步里面需要勾选构建 SpringBoot 项目时的相关依赖,包括 【开发工具:Lombok】、【Web:Spring Web】、【模板引擎:Thymeleaf】以及【关系型数据库:MySQL Driver、MyBatis Framework】,然后点击下一步直到完成项目创建.
说明:其中 Thymeleaf 模板是视图层数据渲染的依赖,类似于JavaEE 中的 JSP,MyBatis 是数据持久层与数据库交互的重要框架,负责了后台数据库增删改查业务。
项目结构如下:
pom.xml
:项目的相关配置,主要是添加依赖的信息;java/com.trainingl
:后端业务代码的存放位置,包括控制层(Controller)、服务层(Service)、数据持久层(Repository)、实体类(Entity)等文件;Springboot1Application
:项目的启动类,运行其中的 main 方法即可启动项目;resources
:该目录下包括静态资源文件static
、HTML模板templates
以及一些全局配置application.properties
(端口号、数据源信息、视图解析器等);
打开 pom.xml
文件就可以看到项目自动导入了相关依赖,避免了开发者手动导入所引起版本不兼容的各种问题(SSM框架整合时的问题)。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<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.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
在开始写项目时,在 MySQL 数据库中创建一张用户数据表(user),并录入几条测试数据。
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',
`username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户密码',
`score` double(10, 0) NULL DEFAULT NULL COMMENT '用户分数',
`birthday` date NULL DEFAULT NULL COMMENT '用户生日',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
创建数据表对应的实体类 User
package com.trainingl.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private Double score;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
}
2. 三层架构实现 CRUD 业务
2.1 Spring Boot 配置文件
Spring Boot 去掉了所有的 XML 文件,因为 Spring Boot 可以实现自动装载,那么就不需要开发者手动在 XML 中进行组件的装配。但是一些个性化的配置仍然需要开发者手动配置,比如数据库连接信息,Spring Boot 提供了 application 配置文件进行个性化配置。application 配置文件位于 resources
目录下,支持.properties
和 .yml
两种格式,Spring Boot 默认是 properties,但是实际开发中更推荐使用 yml,因为它更简单直观。
在全局配置文件 resources/application.yml
中配置数据源信息、视图解析器以及端口号等相关配置等
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mode: HTML5
encoding: UTF-8
datasource:
url: jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
server:
port: 9090
mybatis:
type-aliases-package: com.trainingl.entity
mapper-locations: classpath:/mapper/*.xml
如果不写全限定类名,只写一个实体类的名称的话,那就需要在application.yml文件中设置 mybatis:type-aliases-package
参数.
2.2 业务代码
2.2.1 数据持化层
在路径 com > trainingl > repository 下创建接口 UserRepository
package com.trainingl.repository;
import com.trainingl.entity.User;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserRepository {
//CRUD
public List<User> findAll();
public User findById(Integer id);
public void save(User user);
public void update(User user);
public void deleteById(Integer id);
}
MyBatis 配置文件下编写 SQL 语句,XML配置路径:
resources/mapper/UserRepository.xml
<?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.trainingl.repository.UserRepository">
<select id="findAll" resultType="com.trainingl.entity.User">
select * from user;
</select>
<select id="findById" parameterType="Integer" resultType="com.trainingl.entity.User">
select * from USER where id = #{id};
</select>
<insert id="save" parameterType="com.trainingl.entity.User">
insert into
user(username, password, score, birthday)
values (#{username}, #{password}, #{score}, #{birthday})
</insert>
<update id="update" parameterType="com.trainingl.entity.User">
update user
set username=#{username}, password=#{password}, score=#{score}, birthday=#{birthday}
where id = #{id};
</update>
<delete id="deleteById" parameterType="Integer">
delete from user where id = #{id}
</delete>
</mapper>
为什么要在 resources 目录下创建 xml 文件,而不是直接在 repository 中创建呢?
其实在两个目录中都是可以创建对应的 SQL 实现文件的,只不过由于项目启动时无法读取到,repository 目录下的 xml 文件,还需要在 pom.xml 中开放读取权限,这样一来就显得比较繁琐,而直接在 resources 目录下创建的 xml 文件就可以直接被读取。
2.2.2 业务层
创建业务层接口 UserService
package com.trainingl.service;
import com.trainingl.entity.User;
import java.util.List;
public interface UserService {
public List<User> findAll();
public User findById(Integer id);
public void save(User user);
public void update(User user);
public void deleteById(Integer id);
}
业务层实现 UserServiceImpl
package com.trainingl.service.Impl;
import com.trainingl.entity.User;
import com.trainingl.repository.UserRepository;
import com.trainingl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public List<User> findAll() {
return userRepository.findAll();
}
@Override
public User findById(Integer id) {
return userRepository.findById(id);
}
@Override
public void save(User user) {
userRepository.save(user);
}
@Override
public void update(User user) {
userRepository.update(user);
}
@Override
public void deleteById(Integer id) {
userRepository.deleteById(id);
}
}
2.2.3 控制层
创建 UserController 控制器,主要负责接收客户端的浏览器请求与响应,Spring Boot支持 REST Ful 风格。
package com.trainingl.controller;
import com.trainingl.entity.User;
import com.trainingl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//查询数据库的所有记录
@GetMapping("/index")
public ModelAndView findAll(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("index");
modelAndView.addObject("users", userService.findAll());
return modelAndView;
}
//查询某一条记录
@GetMapping("/findById/{id}")
public ModelAndView findById(@PathVariable Integer id){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("update");
modelAndView.addObject("user",userService.findById(id));
return modelAndView;
}
//增加某一条记录
@PostMapping("/save")
public String save(User user){
userService.save(user);
return "redirect:/user/index";
}
//修改某一条记录
@PostMapping("/update")
public String update(User user){
userService.update(user);
return "redirect:/user/index";
}
//删除某一条记录
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id){
userService.deleteById(id);
return "redirect:/user/index";
}
}
说明:以上的类用
@Repository
、@Service
、@Controller
注解后,项目启动就会在 Spring IOC 容器中生成对应的对象,另外如果有些类的成员变量通过@Autowired
自动装载生成的对应对象。这些工作在 SSM 框架中都是由开发人员手动配置才能完成的,但是在 Spring Boot 中经过默认配置之后,自动完成,极大地简化了配置。
2.4 视图层
信息展示页 resources > templates > index.html
,将数据库查询的信息以表格的形式展示给用户
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<html xmlns:th="http://www.thymeleaf.org"></html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h2>用户列表</h2>
<table>
<tr>
<th>用户编号</th>
<th>用户姓名</th>
<th>用户密码</th>
<th>学籍分数</th>
<th>出生年月</th>
<th>操作</th>
</tr>
<tr th:each="user:${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${user.password}"></td>
<td th:text="${user.score}"></td>
<td th:text="${#dates.format(user.birthday,'yyyy-MM-dd')}"></td>
<td><a th:href="@{/user/findById/{id}(id=${user.id})}" class="btn btn-primary">修改</a>
<a th:href="@{/user/delete/{uid}(uid=${user.id})}" class="btn btn-danger">删除</a></td>
</tr>
</table>
<a th:href="@{/save.html}">添加新用户</a>
</body>
</html>
信息修改页面 resources > templates > update.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<html xmlns:th="http://www.thymeleaf.org"></html>
<head>
<meta charset="UTF-8">
<title>修改信息</title>
</head>
<body>
<h2>修改用户信息</h2>
<form th:action="@{/user/update}" method="post">
用户编号:<input type="text" th:value="${user.id}" name="id" readonly><br>
用户姓名:<input type="text" th:value="${user.username}" name="username"><br>
用户密码:<input type="text" th:value="${user.password}" name="password"><br>
用户分数:<input type="text" th:value="${user.score}" name="score"><br>
出生年月:<input type="date" th:value="${#dates.format(user.birthday,'yyyy-MM-dd')}" name="birthday"><br>
<input type="submit" value="提交修改"><br>
</form>
</body>
</html>
添加用户页面 resources > static> save.html
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>新增用户</title>
</head>
<body>
<h2>增加用户信息</h2>
<form action="/user/save" method="post">
用户姓名:<input type="text" name="username"><br>
用户密码:<input type="text" name="password"><br>
用户分数:<input type="text" name="score"><br>
出生年月:<input type="date" name="birthday"><br>
<input type="reset" value="重置"> <input type="submit" value="提交"><br>
</form>
</body>
</html>
注意:如果希望客户端可以直接访问 HTML 资源,将这些资源放置在 static 路径下即可,否则必须通过 Controller 的后台映射才可以访问静态资源。本例中的增加用户信息页面就是希望直接从一个 HTML 资源跳转到另外一个 HTML 页面,所以写在 static 路线中。
2.3 启动类 Application
package com.trainingl;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.trainingl.repository")
public class Springboot001Application {
public static void main(String[] args) {
SpringApplication.run(Springboot001Application.class, args);
}
}
@MapperScan(...)
的作用是指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类。添加位置是在 SpringBoot 启动类的上面,添加@MapperScan("com.trainingl.repository")
注解以后, com.trainingl.repository
包下面的接口类,在编译之后都会生成相应的实现类。
总结:本例比较完成地实现了 Spring Boot + Thymeleaf + MyBatis 的前后端业务的数据交互,尽管视图层的页面比较单一简陋,但作为一个快速搭建 Spring Boot 项目很好的实例。