SpringBoot+Thymeleaf+MyBatis 实现RESTful API
1.项目结构
2.创建数据库表
这里使用MySQL5.7。
CREATE TABLE `user` (
`id` int(13) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(33) DEFAULT NULL COMMENT '姓名',
`age` int(3) DEFAULT NULL COMMENT '年龄',
`createTime` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
3.代码详解
引入Thymeleaf、SpringBoot整合mybatis的jar包、mysql的驱动包。
<!-- pom.xml -->
<!-- ... -->
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version><!--$NO-MVN-MAN-VER$-->
</dependency>
</dependencies>
<!-- ... -->
# application.properties
server.port=8081
server.servlet.context-path=/restful-demo
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.check-template-location=true
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.datasource.url=jdbc:mysql://localhost:3306/restful_sql?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.mvc.hiddenmethod.filter.enabled=true
mybatis.config-location=classpath:mybatis-config.xml
mybatis.mapper-locations=classpath:mapper-mapping/*.xml
logging.level.com.example.demo.mapper=debug
spring.thymeleaf.*
是Thymeleaf相关的配置,spring.datasource.*
是连接数据库相关的配置。
spring.mvc.hiddenmethod.filter.enabled=true
启用HiddenHttpMethodFilter
,以允许PUT请求或者DELETE请求。
mybatis.config-location=classpath:mybatis-config.xml
指定mybatis的配置文件。
mybatis.mapper-locations=classpath:mapper-mapping/*.xml
指定mybatis的映射文件。
logging.level.com.example.demo.mapper=debug
配置打印包com.example.demo.mapper
的日志。
实体类:
package com.example.demo.model;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
public class User {
private Integer id;
private String name;
private int age;
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private LocalDateTime createTime;
// setter and getter
}
Controller:
package com.example.demo.controller;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
@Controller
public class UserController {
@Autowired
private UserService userService;
@ModelAttribute
public void getUserById(@RequestParam(value="id",required=false) Integer id, Model model){
if (id != null) {
User user = userService.getUserById(id);
if(user != null) {
model.addAttribute("user", user);
}
}
}
// ...
@RequestMapping("/index2")
public ModelAndView index2(){
ModelAndView modelAndView = new ModelAndView("user/index2");
// modelAndView.addObject("users", userService.findAll());
return modelAndView;
}
@ResponseBody
@RequestMapping("/getUsers")
public Map<String, Object> getUsers(HttpServletRequest request){
int pageNO = Integer.parseInt(request.getParameter("page"));// 当前页
int pageSize = Integer.parseInt(request.getParameter("rows"));// 每页行数
Map<String, Object> params = new HashMap<>();
params.put("name", request.getParameter("name"));
return userService.getUsers(pageNO, pageSize, params);
}
/*
** Restful API设计
/user POST 保存
/user/{id} PUT 更新
/user/{id} GET 查询
/user/{id} DELETE 删除
*/
@ResponseBody
@RequestMapping(value="/user", method=RequestMethod.POST)
public String saveUser(User user){
user.setCreateTime(LocalDateTime.now());
userService.saveUser(user);
return "success";
}
@ResponseBody
@RequestMapping(value="/user/{id}", method=RequestMethod.PUT)
public String updateByPrimaryKey(@PathVariable("id") Integer id, @ModelAttribute("user") User user){
// 参数中的id值已经封装到User对象里面,而且这里借助@ModelAttribute使得修改不会影响User对象的其他字段值,而只修改表单中提交的字段值。
userService.updateByPrimaryKey(user);
return "success";
}
@ResponseBody
@RequestMapping(value="/user/{id}", method=RequestMethod.GET)
public User getUserById(@PathVariable("id") Integer id){
return userService.getUserById(id);
}
@ResponseBody
@RequestMapping(value="/user/{id}", method=RequestMethod.DELETE)
public String deleteUserById(@PathVariable("id") Integer id){
userService.deleteByPrimaryKey(id);
return "success";
}
}
Service:
package com.example.demo.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.mapper.UserMapper;
import com.example.demo.mapper.UserMapper2;
import com.example.demo.model.User;
@Transactional
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private UserMapper2 userMapper2;
// ...
public Map<String, Object> getUsers(int pageNO, int pageSize, Map<String, Object> params) {
List<User> users = userMapper2.getUsers(params);
int count = 0;
if(users.size()>=1){
count = users.size();
int fromIndex = (pageNO-1)*pageSize;
int toIndex = pageNO*pageSize;
users = users.subList(fromIndex, count>toIndex?toIndex:count); // 分页
}
Map<String, Object> map = new HashMap<>();
map.put("rows", users);
map.put("total", count);
return map;
}
public User getUserById(Integer id) {
return userMapper.getUserById(id);
}
public void saveUser(User user) {
userMapper2.insert(user);
}
public void updateByPrimaryKey(User user) {
userMapper2.updateByPrimaryKey(user);
}
public void deleteByPrimaryKey(Integer id) {
userMapper2.deleteByPrimaryKey(id);
}
}
Mapper:
@Mapper
public interface UserMapper2 {
List<User> getUsers(Map<String, Object> params);
void insert(User user);
void updateByPrimaryKey(User user);
void deleteByPrimaryKey(Integer id);
@Select("SELECT * FROM User u WHERE u.id = #{id}")
User getUserById(@Param("id") Integer id);
}
注解@Mapper
标注的类可以被其他类通过@Autowired
注入。
可以直接在接口上面通过注解@Select
来写SQL,也可以通过映射文件来写SQL。
mybatis映射文件:
<!-- UserMapper.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.example.demo.mapper.UserMapper2">
<select id="getUsers" parameterType="Map" resultType="com.example.demo.model.User">
select * from user u where 1=1
<if test="name!=null">
and u.name like concat('%',#{name},'%')
</if>
</select>
<insert id="insert" parameterType="com.example.demo.model.User" >
insert into user (name, age, createTime) values (#{name}, #{age}, #{createTime})
</insert>
<update id="updateByPrimaryKey" parameterType="com.example.demo.model.User" >
update user set name = #{name}, age = #{age} where id = #{id}
</update>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from user where id = #{id}
</delete>
</mapper>
HTML页面:
<!-- index2.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<script th:src="@{/jquery-easyui/jquery.min.js}"></script>
<script th:src="@{/jquery-easyui/jquery.easyui.min.js}"></script>
<script th:src="@{/jquery-easyui/locale/easyui-lang-zh_CN.js}"></script>
<link rel="stylesheet" type="text/css" th:href="@{/jquery-easyui/themes/default/easyui.css}" />
<link rel="stylesheet" type="text/css" th:href="@{/jquery-easyui/themes/icon.css}" />
<style>
input[class^="easyui-"] {
width: 160px;
}
#toolbar,
.userForm {
font-size:1rem;
}
#toolbar>p {
margin:0;
margin-top:1em;
margin-left:1em;
margin-bottom:.5em;
}
#toolbar>p label,
.userForm label {
margin-right:1em;
}
.userForm p {
width:50%;
margin-left:auto;
margin-right:auto;
}
#toolbar .button {
margin-left:1em;
margin-bottom:.5em;
}
.saveButton {
text-align:center;
margin-top:1em;
margin-bottom:1em;
}
</style>
<title>User Index</title>
</head>
<body>
<table id="users"></table>
<div id="toolbar">
<p><label for="name_s">名称</label><input class="easyui-searchbox" id="name_s" /></p>
<div class="button">
<a class="easyui-linkbutton" data-options="iconCls:'icon-add',plain:true" onclick="addUser()">添加</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-edit',plain:true" onclick="editUser()">修改</a>
<a class="easyui-linkbutton" data-options="iconCls:'icon-remove',plain:true" onclick="removeUser()">删除</a>
</div>
</div>
<div class="easyui-dialog" data-options="closed:true,modal:true" id="userDialog" style="width:450px;">
<div class="userForm">
<form id="userForm" method="post">
<input id="id" name="id" type="hidden" />
<p><label for="name">名称</label><input class="easyui-textbox" id="name" name="name" data-options="required:true" /></p>
<p><label for="age">年龄</label><input class="easyui-numberbox" id="age" name="age" data-options="required:true,min:0" /></p>
</form>
<div class="saveButton">
<a class="easyui-linkbutton" data-options="iconCls:'icon-save'" onclick="saveUser()" id="save">保存</a>
</div>
</div>
</div>
<script th:inline="javascript">
var basePath = /*[[${#request.getContextPath()}]]*/ "http://localhost:8081/restful-demo";
</script>
<!-- 下面使用"th:inline='none'",避免报错! -->
<script th:inline="none">
$(function(){
$('#users').datagrid({
url:basePath+'/getUsers',
rownumbers:true,
pagination:true,
singleSelect:true,
pageNumber:1,
pageSize:5,
pageList:[5,10,15],
toolbar : '#toolbar',
columns:[[
{
field:'id',
title:'ID',
width:100
},
{
field:'name',
title:'名称',
width:100
},
{
field:'age',
title:'年龄',
width:100
},
{
field:'createTime',
title:'创建时间',
width:150,
align:'center',
formatter:function (value) {
return value?`<span title="${value}">${value}</span>`:'';
}
},
]]
});
$('#name_s').searchbox({
searcher:function(value){
$('#users').datagrid('load',{
name: value,
});
}
});
$('#userForm').form({
onLoadSuccess: function(data){
if(data){
// 当加载完表单数据后,才启用保存按钮。
$('#save').linkbutton('enable');
}
}
});
});
function addUser(){
$('#userDialog').panel('setTitle', '添加用户').dialog('center').dialog('open');
}
async function editUser(){
var row = $("#users").datagrid('getSelected');
if(row){
$('#save').linkbutton('disable');
var user = await $.get(basePath+'/user/'+row.id);
$('#userForm').form('load', user);
$('#userDialog').panel('setTitle', '修改用户').dialog('center').dialog('open');
} else {
$.messager.alert('警告操作!', '请选择一条记录!', 'info');
}
}
function removeUser(){
var row = $("#users").datagrid('getSelected');
if(row){
$.messager.confirm('确认操作', "确定要删除选中的记录?", function (flag) {
if(flag){
$.post(basePath+'/user/'+row.id,{_method:'DELETE'},function (data) {
if(data==="success"){
$.messager.alert('提示信息', '删除成功!','info');
$('#users').datagrid('reload');
} else {
$.messager.alert('提示信息','删除失败!','info');
}
});
}
});
} else {
$.messager.alert('警告操作!', '请选择一条记录!', 'info');
}
}
function saveUser(){
var url = basePath+'/user';
var id = $('#id').val();
if(id){
url += "/"+id;
}
$('#userForm').form('submit',{
url: url,
onSubmit: function(param){
if(id){
param._method='PUT';
}
return $(this).form('enableValidation').form('validate');
},
success: function(data){
if(data==="success"){
clearForm();
$("#users").datagrid('reload');
$.messager.alert('提示信息','保存成功!','info');
}
}
});
}
function clearForm(){
$('#userForm').form('clear');
$('#userDialog').dialog('close');
}
</script>
</body>
</html>
HTML页面使用了Thymeleaf的语法和EasyUI框架。
展示页面:
示例代码下载地址:https://pan.baidu.com/s/17Kq4gUrUh160jr3fLTSjKg(提取码:r14t)