springboot相较于springmvc来说,又精简了不少,这次做了个简单CURD小项目,简介一下:
基于springboot的简单增删查改
0,静态资源不太会用,所以页面不好看
1,使用thymeleaf模板(我不用这个模板时找不到resources下面的html或jsp,需要放在WEB-INF下面,然后在properties文件中配置前缀后缀,jsp需要另外导包)
2,使用jpa封装数据库操作(这个实在是太好用了,不用到数据库建表,不用写各种dao,service,mapper,一个类解决)
3,对controller设置全局异常处理,文件上传,带参重定向演示(controller里总不能把异常信息抛给客户吧)
4,简单使用redis(缓存加快效率,也降低数据库连接负担,不太懂这个)
5,分页和排序联合使用(mybatis有pagehelper插件,这里用的工具类,封装了按什么字段以什么顺序排序,以及如何分页的功能。使用的时候直接将pageable对象传入查询方法。
分页功能转载地址:
https://blog.csdn.net/zsl129/article/details/52884571?locationNum=3&fps=1#commentBox)
--------------------------------------------------------------------------------------------------------------
首先是个效果图吧:
一,配置文件
A;pom.xml,如下,附上注释了,前后自动生成的文本去除了
<!-- 祖师包 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> -->
<!-- web包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- thymeleaf模板包,其包含spring-boot-starter包,而spring-boot-starter包
又包含了spring-boot-starter-logging包,所以日志包不用,默认会使用logback -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 开发体验包,支持热加载等。测试包就没上 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- 数据库操作封装包jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- sql不用多说,连接池没弄 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis的分页插件,这里没用 -->
<!-- <dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.2</version>
</dependency> -->
<!-- 用于管理静态资源,很好,不过我很多样式怎么写忘了很多 -->
<!-- <dependency>
<groupId>org.webjars.bower</groupId>
<artifactId>jquery</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.webjars.bower</groupId>
<artifactId>bootstrap</artifactId>
<version>3.0.3</version>
</dependency> -->
<!-- 以下2个是redis的包 -->
<!-- 第一个过时 被spring-boot-starter-data-redis代替【官网说的】 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
------------------------------------------------------------------------------------------
B;application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.http.multipart.max-file-size=10MB
spring.http.multipart.max-request-size=10MB
#most are by default
spring.thymeleaf.cache=false
spring.thymeleaf.content-type=text/html
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
#thymeleaf end
#many,choose one
spring.redis.database=0
#which your redis-server installed on
spring.redis.host=localhost
#default port
spring.redis.port=6379
#whether you set a password
spring.redis.password=fangfang
spring.redis.pool.max-active=8
#wait always
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.timeout=0
------------------------------------------------------------------------------------------------------
C;logback用的xml,命名也很重要,不然不会自动扫描到
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
<!-- additivity=false表示子Logger不会在父Logger里输出 -->
<logger name="org.springframework.web" level="error" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="com.cl" level="debug" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<root level="error">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
--------------------------------------------------------------------------------------------------------------------
二,包&类
首先,结构图如下,注意如果启动主类为父结构(com.cl),那么子结构(如com.cl.domain)会自动扫描到。
----------------------------------------------------------------------------------------------------
A;启动类
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
// 大文件上传设置
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbedded() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol<?>)) {
// -1 means unlimited
((AbstractHttp11Protocol<?>) connector.getProtocolHandler()).setMaxSwallowSize(-1);
}
});
return tomcat;
}
}
-----------------------------------------------------------------------------------------------------------------
B;配置类
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.filters.RemoteIpFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//简单自定义过滤器的配置
@Configuration
public class WebConfiguration {
@Bean
public RemoteIpFilter remoteIpFilter() {
return new RemoteIpFilter();
}
@Bean
public FilterRegistrationBean testFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyFilter());
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("MyFilter");
registration.setOrder(1);
return registration;
}
public class MyFilter implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain filterChain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest request = (HttpServletRequest) srequest;
System.out.println("this is MyFilter,url :" + request.getRequestURI());
filterChain.doFilter(srequest, sresponse);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
}
--------------------------------------------------------------------------------------------------------------
Redis配置
import java.lang.reflect.Method;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport{
//方法及参数构成key
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append("_"+method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
//缓存管理
@SuppressWarnings("rawtypes")
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//设置缓存过期时间
//rcm.setDefaultExpiration(60);//秒
return rcm;
}
//redis模板,可以看出json格式
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
-------------------------------------------------------------------------------------------------------
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
//session共享
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class RedisSessionConfiguration {
}
----------------------------------------------------------------------------------------------------------------------
C;实体类
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(nullable = false)
private String sex;
@Column(nullable = false)
private Integer age;
//@Column(nullable = true, unique = true)unique=true不能和nullable=true一起用,否则只能允许一个非空存在
@Column(nullable = false,unique=true)
private String email;
@Column(nullable = true)
private String photoPath;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhotoPath() {
return photoPath;
}
public void setPhotoPath(String photo) {
this.photoPath = photo;
}
}
--------------------------------------------------------------------------------------------------
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
//有了这个不用service层了,方法命名要符合约定
public interface StudentRepo extends JpaRepository<Student, Long> {
// Student findById(Long id);
Page<Student> findAllByName(String name,Pageable pageable);
//注意如果方法名为findByName,可能返回多个id导致异常,方法名一定要规范
Integer countByName(String name);
}
----------------------------------------------------------------------------------------------
D;封装工具类
public class SortDto {
// 排序方式
private String pattern;
// 排序字段
private String field;
//默认desc
public SortDto(String field) {
pattern = "desc";
this.field = field;
}
public SortDto(String pattern, String field) {
this.pattern = pattern;
this.field = field;
}
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
-------------------------------------------------------------------------------
import org.springframework.data.domain.Sort;
//排序封装类
public class SortTools {
// default
public static Sort basicSort() {
return basicSort("desc", "id");
}
public static Sort basicSort(String pattern, String field) {
Sort sort = new Sort(Sort.Direction.fromString(pattern), field);
return sort;
}
//这个方法允许我们以复杂的方式排序
public static Sort basicSort(SortDto... dtos) {
Sort result = null;
for (int i = 0; i < dtos.length; i++) {
SortDto sd = dtos[i];
if (result == null)
result = new Sort(Sort.Direction.fromString(sd.getPattern()), sd.getField());
else
result.and(new Sort(Sort.Direction.fromString(sd.getPattern()), sd.getField()));
}
return result;
}
}
--------------------------------------------------------------------------------------------------
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
//分页封装类
public class PageableTools {
public static Pageable basicPage(Integer page, Integer size, SortDto... dtos) {
Sort sort = SortTools.basicSort(dtos);
page = (page == null || page < 0) ? 0 : page;
size = (size == null || size <= 0) ? 10 : size;
Pageable pageable = new PageRequest(page, size, sort);
return pageable;
}
/**
* 获取基础分页对象,每页条数默认15条 - 默认以id降序排序
*
* @param page
* 获取第几页
* @return
*/
public static Pageable basicPage(Integer page) {
return basicPage(page, 0, new SortDto("desc", "id"));
}
/**
* 获取基础分页对象,每页条数默认15条
*
* @param page
* 获取第几页
* @param dtos
* 排序对象数组
* @return
*/
public static Pageable basicPage(Integer page, SortDto... dtos) {
return basicPage(page, 0, dtos);
}
/**
* 获取基础分页对象,排序方式默认降序
*
* @param page
* 获取第几页
* @param size
* 每页条数
* @param orderField
* 排序字段
* @return
*/
public static Pageable basicPage(Integer page, Integer size, String orderField) {
return basicPage(page, size, new SortDto("desc", orderField));
}
/**
* 获取基础分页对象 - 每页条数默认15条 - 排序方式默认降序
*
* @param page
* 获取第几页
* @param orderField
* 排序字段
* @return
*/
public static Pageable basicPage(Integer page, String orderField) {
return basicPage(page, 0, new SortDto("desc", orderField));
}
}
E;controller类
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
//全局异常处理,2个注解
@ControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException(Exception e, RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", e.getCause().getMessage());
return "redirect:/status";
}
}
------------------------------------------------------------------------------------------------------
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.cl.domain.Student;
import com.cl.domain.StudentRepo;
import com.cl.sortAndPage.PageableTools;
import com.cl.sortAndPage.SortDto;
@Controller
public class StudentController {
//图片上传
private static String UPLOADED_FOLDER = "D:\\upload\\";
@Autowired
private StudentRepo rep;
@GetMapping("/")
public String starting() {
return "redirect:/index";
}
@GetMapping("/index")
// 分页暂时只在首页实现了,
public String index(Integer page, Model model) {
// 第一次进入默认显示第一页
if (page == null)
page = 0;
// 查找当前页面下的条目
Page<Student> students = rep.findAll(PageableTools.basicPage(page, 0, new SortDto("asc", "id")));
int total = (int) rep.count();
int lastPage = (total + 10 - 1) / 10 - 1;
model.addAttribute("students", students);
// 点击某个跳转链接后,page传进来,再传回去,方便调整页面链接位置
model.addAttribute("currentPage", page);
// 最后一页还是每次都计算一下,防止删除操作导致页面不一致
model.addAttribute("lastPage", lastPage);
return "home";
}
@RequestMapping("/toEdit")
public String toEdit(Long id, Model model) {
Student student = rep.findOne(id);
model.addAttribute("student", student);
return "edit";
}
@RequestMapping("/edit")
public String edit(@RequestParam("file") MultipartFile file, Student student) {
if (file.isEmpty()) {
rep.save(student);
return "redirect:/index";
}
try {
String oldPath = student.getPhotoPath();
byte[] bytes = file.getBytes();
Path path = Paths.get(UPLOADED_FOLDER + UUID.randomUUID().toString().replaceAll("-", "")
+ file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")));
Files.write(path, bytes);
student.setPhotoPath(path.toString());
rep.save(student);
if (oldPath != null) {
Path path2 = Paths.get(oldPath);
Files.delete(path2);// 别忘了删除旧图片
}
} catch (IOException e) {
e.printStackTrace();
}
return "redirect:/index";
}
@RequestMapping("/toAdd")
public String toAdd() {
return "add";
}
@RequestMapping("/add")
public String add(@RequestParam("file") MultipartFile file, Student student) {
if (file.isEmpty()) {
rep.save(student);
return "redirect:/index";
}
try {
// 首先上传图片
byte[] bytes = file.getBytes();
String photoPath = UPLOADED_FOLDER + UUID.randomUUID().toString().replaceAll("-", "")
+ file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
Path path = Paths.get(photoPath);
Files.write(path, bytes);
// 然后设置图片路径
student.setPhotoPath(photoPath);
// 存入数据库
rep.save(student);
} catch (IOException e) {
e.printStackTrace();
}
return "redirect:/index";
}
@RequestMapping("/toDel")
public String delete(Long id) {
String path = rep.findOne(id).getPhotoPath();
if (path == null) {
rep.delete(id);
return "redirect:/index";
}
Path path2 = Paths.get(path);
try {
Files.deleteIfExists(path2);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rep.delete(id);
return "redirect:/index";
}
@RequestMapping("/toFind")
public String toFind() {
return "find";
}
// 下面2个本来可以通过一个方法用model等返回数据到html的,使用带参重定向的好处是uri中屏蔽了参数,更加安全
@RequestMapping("/find")
public String find(Integer page, String name, RedirectAttributes reAttr) {
// 第一次进入默认显示第一页
if (page == null)
page = 0;
// 查找当前页面下的条目
Page<Student> students = rep.findAllByName(name, PageableTools.basicPage(page, 10, new SortDto("asc", "id")));
// 方法命名约束
int total = rep.countByName(name);
int lastPage = (total + 10 - 1) / 10 - 1;
reAttr.addFlashAttribute("students", students);
reAttr.addFlashAttribute("currentPage", page);
reAttr.addFlashAttribute("lastPage", lastPage);
reAttr.addFlashAttribute("name", name);
return "redirect:/findResult";
}
@RequestMapping("/findResult")
public String findResult() {
return "findResult";
}
@RequestMapping("/status")
public String status() {
return "status";
}
@RequestMapping("/toBack")
public String back() {
return "redirect:/index";
}
}
页面类:
A: status.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<h1 th:text="${message}?${message}"></h1>
</html>
--------------------------------------------------
B: home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<h1>Student Manage</h1>
<div>
<table>
<thead>
<tr>
<th><a th:href="@{/toFind}">Find</a></th>
<th><a th:href="@{/toAdd}">Add</a></th>
</tr>
<tr>
<th>***</th>
<th>name</th>
<th>sex</th>
<th>age</th>
<th>email</th>
<th>photo</th>
<th>function1</th>
<th>function2</th>
</tr>
</thead>
<tbody th:if="${students}">if可以不用
<tr th:each="student:${students}">
<td th:text="${student.id}"/>
<td th:text="${student.name}"/>
<td th:text="${student.sex}"/>
<td th:text="${student.age}"/>
<td th:text="${student.email}?${student.email}:'null'"/>
<td><img th:if="${student.photoPath}" th:src="@{${student.photoPath}}" height="80px"/></td>
<td><a th:href="@{/toEdit(id=${student.id})}">Edit</a></td>
<td><a th:href="@{/toDel(id=${student.id})}">Delete</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 下面是分页的实现 -->
<div>
<a th:href="@{/index(page=0)}">首页</a>
<a th:href="@{/index(page=${currentPage}-2)}" th:if="${currentPage ge 2}"><span th:text="${currentPage}-2"></span></a>
<a th:href="@{/index(page=${currentPage}-1)}" th:if="${currentPage ge 1}"><span th:text="${currentPage}-1"></span></a>
<a th:href="@{/index(page=${currentPage})}"><span th:text="${currentPage}"></span></a>
<a th:href="@{/index(page=${currentPage}+1)}" th:if="${currentPage+1 le lastPage}"><span th:text="${currentPage}+1"></span></a>
<a th:href="@{/index(page=${currentPage}+2)}" th:if="${currentPage+2 le lastPage}"><span th:text="${currentPage}+2"></span></a>
<a th:href="@{/index(page=${lastPage})}">尾页</a>
</div>
</body>
</html>
------------------------------------------------------------------
C: add.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<form method="post" th:action="@{/add}" enctype="multipart/form-data">
<input type="hidden" name="id"/>
姓名:<input type="text" name="name" /><br />
性别:<input type="text" name="sex" /><br />
年龄:<input type="text" name="age" /><br />
邮箱:<input type="text" name="email" placeholder="nullable"/><br />
图片:<input type="file" name="file"/><br />
<input type="submit" value="Submit" />
</form>
</html>
---------------------------------------------------------------------------------------------
D: edit.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<form method="post" th:action="@{/edit}" th:object="${student}" enctype="multipart/form-data">
<input type="hidden" name="id" th:value="*{id}" /><br /> <!-- 使用object后可以直接用*取属性-->
姓名:<input type="text" name="name" th:value="*{name}" /><br />
性别:<input type="text" name="sex" th:value="${student.sex}" /><br />
年龄:<input type="text" name="age" th:value="${student.age}" /><br />
邮箱:<input type="text" name="email" th:value="${student.email}" /><br />
图片:<img th:src="@{${student.photoPath}}" height="100px"/><input type="file" name="file" /><br /><br/>
<input type="submit" value="Submit" />
</form>
</html>
----------------------------------------------------------------------------------------------------
E: find.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<form method="post" th:action="@{/find}">
姓名:<input type="text" name="name"/>
<input type="submit" value="Submit"/>
</form>
</html>
--------------------------------------------------------------
F: findResult.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div>
<table>
<thead>
<tr>
<th>***</th>
<th>name</th>
<th>sex</th>
<th>age</th>
<th>email</th>
<th>photo</th>
<th>function1</th>
<th>function1</th>
</tr>
</thead>
<tbody>
<tr th:each="student:${students}" th:object="${student}">
<td th:text="*{id}"></td>
<td th:text="${name}"></td>
<td th:text="*{sex}"></td>
<td th:text="*{age}"></td>
<td th:text="*{email}"></td>
<td><img th:if="*{photoPath}" alt="" th:src="@{*{photoPath}}" height="100px"/></td>
<td><a th:href="@{/toEdit(id=*{id})}">Edit</a></td>
<td><a th:href="@{/toDel(id=*{id})}">Delete</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td><a th:href="@{/toBack}">Back to home</a></td>
</tr>
</tfoot>
</table>
</div>
<div>
<a th:href="@{/find(page=0,name=${name})}">首页</a>
<a th:href="@{/find(page=${currentPage}-2,name=${name})}" th:if="${currentPage ge 2}"><span th:text="${currentPage}-2"></span></a>
<a th:href="@{/find(page=${currentPage}-1,name=${name})}" th:if="${currentPage ge 1}"><span th:text="${currentPage}-1"></span></a>
<a th:href="@{/find(page=${currentPage},name=${name})}"><span th:text="${currentPage}"></span></a>
<a th:href="@{/find(page=${currentPage}+1,name=${name})}" th:if="${currentPage+1 le lastPage}"><span th:text="${currentPage}+1"></span></a>
<a th:href="@{/find(page=${currentPage}+2,name=${name})}" th:if="${currentPage+2 le lastPage}"><span th:text="${currentPage}+2"></span></a>
<a th:href="@{/find(page=${lastPage},name=${name})}">尾页</a>
</div>
</html>
----------------------------------
数据库不用自己建表,redis需要自己安装然后开启服务才能连接到,有windows版的。
总结:刚开始学springboot,感觉比ssm等精简很多,这里thymeleaf模板,jpa都非常好用,分页的实现也非常简单。