案例:图书管理(SpringBoot Thymeleaf SpringData-JPA)

添加图书:图书基本信息及封面图片的上传及入库

图书详细:图书基本信息和封面图片显示

  • SpringData JPA 使用
  • 上传页面的三个必须要求
  • 图片上传接收和处理
  • 资源映射(图片回显)
  • 全局异常处理

数据库脚本

CREATE DATABASE wdzldb`
 
USE `wdzldb`;
 
DROP TABLE IF EXISTS `book`;
 
CREATE TABLE `book` (
  `bookid` int(11) NOT NULL AUTO_INCREMENT,
  `bookName` varchar(120) DEFAULT NULL,
  `price` float DEFAULT NULL,
  `pubDate` date DEFAULT NULL,
  `author` varchar(20) DEFAULT NULL,
  `version` int(11) DEFAULT '0',
  `state` int(11) DEFAULT NULL,
  `pic` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`bookid`)
) ENGINE=InnoDB AUTO_INCREMENT=157 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
/*Data for the table `book` */
 
insert  into `book`(`bookid`,`bookName`,`price`,`pubDate`,`author`,`version`,`state`,`pic`) values
(22,'Java实战开发3',34,'2021-07-28','王磊',1,1,NULL),
(53,'Java实战开发666',120,'2021-07-24','诸葛亮',0,1,NULL),
(61,'Java实战开发1',39,'2021-07-29','王磊',0,1,NULL),
(62,'Java实战开发1',39,'2021-07-29','王磊',0,0,NULL),
(66,'Java实战开发1',39,'2021-07-29','王磊',0,0,NULL),
(67,'SpringCloud微服务实战',45,'2021-08-11','王帆',0,0,NULL),
(68,'SPringBoot整合JDBC',56,'2021-08-11','周瑜',0,1,NULL),
(70,'SpringBoot入门与提高',78,'2021-08-11','曹操',0,1,NULL),
(71,'Java实战开发5',100,'2021-07-23','诸葛亮',0,0,NULL),
(72,'Java虚拟机深入',23,'2021-08-11','赵紫阳',0,1,NULL),
(73,'深入学习Java虚拟机',69,'2021-08-05','黄盖',0,0,NULL),
(74,'JSP开发技术',34,'2021-08-12','王超',0,1,NULL)

框架搭建

先搭建基本框架,完成业务层、DAO 层、pojo 等模块编写,并调试通过 springdata 正常使用。

pom.xml 依赖

web 启动器、thymeleaf 模板启动器、springdata 启动器及数据库驱动等

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<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>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

配置文件

application.yml 数据源的基本信息配置、jpa 是否显示 sql 及 下划线等

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/wdzldb?allowMultiQueries=true&useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    show-sql: true
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

application.properties

下面多数是在使用阿里的初始化工具时,自动生成的

除了下面的上传保存的路径是自定义的

# 应用名称
spring.application.name=springboot_other
# 应用服务 WEB 访问端口
server.port=8080
# THYMELEAF (ThymeleafAutoConfiguration)
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=false
# 检查模板是否存在,然后再呈现
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=HTML
# 在构建 URL 时添加到视图名称前的前缀(默认值: classpath:/templates/ )
spring.thymeleaf.prefix=classpath:/templates/
# 在构建 URL 时添加到视图名称后的后缀(默认值: .html )
spring.thymeleaf.suffix=.html
 
#上传的绝对路径
file.upload.path=d://save/images/
#绝对路径下的相对路径
file.upload.relativePath=/images/
#文件上传大小限制
spring.servlet.multipart.max-file-size=2048000

实体类

注意:日期和图片处理

@Data
@Entity(name = "book") //要求必须有@Id 也
@ApiModel(value = "图书实体", description = "图书中明细属性")
public class Book {
 
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY) //注意:默认的是序列,针对Oracle的
  private Integer bookId;
 
  @ApiModelProperty(value = "图书名")
  private String bookName;
 
  @ApiModelProperty(value = "图书作者")
  private String author;
 
  private Float price;
 
  @Column(name = "pic")
  private String picpath; // 封面
 
  @DateTimeFormat(pattern = "YYYY-MM-dd")
  private Date pubDate; //出版日期
}

DAO

dao 接口直接使用 springdata 提供的统一接口 JpaRepository ,其中已经包含了基本的操作。也可以按约定规则自己定义其他方法

注意:继承接口时需要指定泛型

public interface IBookDao extends JpaRepository<Book, Integer> {
  List<Book> queryBooksByBookName(String bookName);
  List<Book> findBooksByPriceBetween(Float min, Float max);
  List<Book> findBooksByBookNameLike(String bookName);
 
  List<Book> findAllByPriceOrderByPrice(float price);
 
  @Query(
    "select bookId,bookName,price,author from Book where bookName like :bookname"
  )
  Object[] queryBook(@Param("bookname") String bookName);
 
  //HQL 语句  select book from Book book where ...
  @Query("from Book where bookName like :bookname")
  List<Book> queryBooks(@Param("bookname") String bookName);
}

Service

接口和实现类

public interface IBookService {
  void add(Book book);
  void delete(Integer bookId);
  Book detail(Integer bookId);
  List<Book> queryAll();
  void update(Book book);
}
package com.wdzl.service.impl;
 
import com.wdzl.dao.IBookDao;
import com.wdzl.pojo.Book;
import com.wdzl.service.IBookService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
/**
 * @Author: zhang
 * @Date:2022/8/12
 * @Description:
 */
@Service
public class BookService implements IBookService {
 
  @Autowired
  private IBookDao bookDao;
 
  @Override
  public void add(Book book) {
    System.out.println(book.getBookId());
    bookDao.save(book); // 注意: 如果对象在数据库中存在的,执行修改。
    System.out.println(book.getBookId());
  }
 
  @Override
  public void delete(Integer bookId) {
    bookDao.deleteById(bookId);
  }
 
  @Override
  public Book detail(Integer bookId) {
    return bookDao.findById(bookId).get();
  }
 
  @Override
  public List<Book> queryAll() {
    return bookDao.findAll();
  }
 
  @Override
  public void update(Book book) {
    //如果对象是存在时,就是修改操作,如果不存在则插入操作
    bookDao.save(book);
  }
}

到这里,就可以使用单元测试来测试 springdata 是否能正常使用了。

文件上传

添加页面

页面必须

1.method必须是post

2.enctype="multipart/form-data" 必须

3.<input  type="file"/>必须

static 目录 下的 add.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <!--
    文件上传时:页面必须
    1.method必须是post
    2.enctype="multipart/form-data"  必须
    3.<input type="file">  必须
-->
 
    <h2>添加图书</h2>
    <form action="add" method="post" enctype="multipart/form-data">
      <p>图书名字:<input name="bookName" /></p>
      <p>图书价格:<input name="price" /></p>
      <p>图书作者:<input name="author" /></p>
      <p>出版日期:<input name="pubDate" type="date" /></p>
      <p>图书封面:<input name="pic" type="file" /></p>
      <p><input type="submit" value="保存" /></p>
    </form>
  </body>
</html>

控制器

注意文件上传处理:单独上传、文件名重命名、保存路径的配置等

package com.wdzl.controller;
 
import com.wdzl.pojo.Book;
import com.wdzl.service.IBookService;
import com.wdzl.util.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
 
/**
 * @Author: zhang
 * @Date:2022/8/12
 * @Description:
 *
 */
@Controller
public class BookController {
 
  @Autowired
  private IBookService bookService;
 
  @Value("${file.upload.savepath}")
  private String savePath; //保存文件根目录
 
  @Value("${file.upload.relativePath}")
  private String relativePath;
 
  /**
   * 日期处理
   * 文件上传
   * @param book
   * @return
   */
  @PostMapping("add")
  public String add(Book book, MultipartFile pic) {
    System.out.println("============add()========");
 
    String oriName = pic.getOriginalFilename(); //原始文件命名
 
    //判断目录是否存在并创建
    File rootDir = new File(savePath);
    if (rootDir.exists() == false) {
      rootDir.mkdirs();
    }
    if (!pic.isEmpty()) {
      //文件名
      String fileName = FileUtils.rename(oriName);
 
      File saveFile = new File(rootDir, fileName);
      //转存到指定文件中
      try {
        pic.transferTo(saveFile);
        System.out.println(">>>>>>文件保存在:"   saveFile.getAbsolutePath());
      } catch (IOException e) {
        e.printStackTrace();
      }
 
      // 文件相对路径  用来入库和回显
      fileName = relativePath   fileName;
      book.setPicpath(fileName);
    }
 
    //入库
    bookService.add(book);
    return "redirect:list"; // /list
  }
 
  @GetMapping("list")
  public String list(ModelMap modelMap) {
    List<Book> list = bookService.queryAll();
    modelMap.put("booklist", list);
    return "list"; ///templates/list.html
  }
 
  @GetMapping("detail")
  public String detail(Integer bookId, ModelMap modelMap) {
    Book book = bookService.detail(bookId);
    modelMap.put("book", book);
    return "detail";
  }
}

列表页面

添加成功后,跳转到列表页面显示,下面使用的 thymeleaf 遍历显示

注意:下面图片路径、链接等处理

templates 下的 list.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <title>图书列表</title>
    <style type="text/css" rel="stylesheet">
      div {
        margin: 30px;
        text-align: center;
      }
    </style>
  </head>
  <body>
    <a href="add.html">添加图书</a>
    <hr />
    <div>
      <table width="75%">
        <thead>
          <tr bgcolor="#556b2f">
            <th>序号</th>
            <th>书名</th>
            <th>作者</th>
            <th>价格</th>
            <th>出版日期</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr th:each="book,st:${booklist}" th:bgcolor="${st.even?'#aac':''}">
            <td th:text="${st.count}"></td>
            <td>
              <a
                th:href="${'detail?bookId=' book.bookId}"
                th:text="${book.bookName}"
              ></a>
            </td>
            <td th:text="${book.author}"></td>
            <td th:text="${book.price}"></td>
            <td th:text="${#dates.format(book.pubDate,'yyyy年MM月dd日')}"></td>
            <td>
              <a th:href="${'del?bookId=' book.bookId}">删除</a>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </body>
</html>

运行测试问题

到此,可以通过前端的 add.html 来实现添加和上传操作了。

注意:默认文件上传大小是 1M,大于 1M 的会 500 异常。

可以通过配置修改默认文件大小限制:

#文件上传大小限制
spring.servlet.multipart.max-file-size=2048000

全局异常处理

在项目发布运行中,不希望直接显示 500 异常页面时,可以配置全局异常解析器来进行处理

异常处理在 springboot 中有多种方式,下面介绍两种

1. @ControllerAdvice @ExceptionHandler

位置:在启动类同包及子包下定义类

@ControllerAdvice
public class GlobalExceptionHander {
 
  @ExceptionHandler(Exception.class)
  public String doException(Exception ex) { // 不能使用model 无法传参到页面显示
    System.out.println(">>>>==异常了:"   ex.toString());
    return "error"; // 转发到 error.html 页面
  }
}

上面的 @ControllerAdvice 注解中已经包含 @Component 注解,所以直接会被 spring 扫描加入容器中。

2. @Configuration SimpleMappingExceptionResolver

/**
 * @Author: zhang
 */
@Configuration
public class ApplicationConfig {
 
  /**
   * 全局异常配置
   * 页面可以通过 exception对象来获取
   */
  @Bean
  public SimpleMappingExceptionResolver doException() {
    SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
    Properties properties = new Properties();
    properties.setProperty("java.lang.Exception", "error"); //映射异常类型和转发的页面对应关系
    resolver.setExceptionMappings(properties);
    System.out.println("===========异常配置======");
    return resolver;
  }
}

上面是一个 @Configuration 标注的配置类。异常对象会被转发到页面。

图片回显

在完成上面的文件上传和图书信息添加之后,跳转到图书列表页面,可以通过图书名链接打开图书详细信息。

但在显示图片静态资源时,路径问题导致图片无法正常显示。

下面来再来处理下文件上传和回显下载或显示问题

1. 回顾保存方式

先来回顾上传时对于图片保存路径的设置

首先我们先在配置文件中自定义了两个路径:绝对路径和相对路径

#上传的绝对路径, 文件保存的真正的目录位置
file.upload.path=d://save/images/
#绝对路径下的相对路径  用于前端请求调用的逻辑地址,默认是不能直接使用的
file.upload.relativePath=/images/

在控制器保存图片时,使用上面地址保存图片和入库记录

@PostMapping("add") //注意这里 pic 需要单独和页面元素对应,实体类中只是不同名的字符串用来存地址
public String add(Book book, @ApiParam(value = "图片文件", required = true)MultipartFile pic){
    System.out.println(book "====" pic);
    String fileName = pic.getOriginalFilename();
    //重命名
    fileName = FileUtils.rename(fileName);
    // 保存到磁盘
    File saveDir = new File(savePath);  //====================这里是真实保存目录
    if(saveDir.exists()==false){
        saveDir.mkdirs();//创建保存图片的目录
    }
    File saveFile = new File(saveDir,fileName);
    try {
        pic.transferTo(saveFile);
        System.out.println("图片保存在:" saveFile.getAbsolutePath());
    } catch (IOException e) {
        e.printStackTrace();
    }
 
    //保存的相对路径
    String picPath = relativePath   fileName;  //==========这里逻辑目录  虚拟的
    System.out.println(">>>入库路径:" picPath);
    book.setPicpath(picPath); //保存到实体类 入库
 
 
    //入库
    bookService.add(book);
    return "redirect:list";
}

首先从上面代码中可以看到,保存的磁盘的目录和入库的路径是不同的,默认是不对应不能访问的。

页面中使用的路径为数据库中的相对逻辑路径

<img th:src="${book.picpath}" width="200" />
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <title>图书明细</title>
  </head>
  <body>
    <div>
      <p>
        <img th:src="${book.picpath}" width="200" />
      </p>
      <p>书名:<span th:text="${book.bookName}"></span></p>
      <p>作者:<span th:text="${book.author}"></span></p>
      <p>价格:<span th:text="${book.price}"></span></p>
      <p>
        出版日期:<span
          th:text="${#dates.format(book.pubDate,'yyyy-MM-dd')}"
        ></span>
      </p>
    </div>
  </body>
</html>

2. 配置资源映射

如果需要能正常的访问,则使用下面的配置进行映射

实现 WebMvcConfigurer 同时 标注 @Configuration

/**
 * @Author: zhang
 * @Date:2022/8/11
 * @Description:
 */
@Configuration
public class ApplicationConfig implements WebMvcConfigurer {
 
  @Value("${file.upload.path}")
  private String savePath;
 
  @Value("${file.upload.relativePath}")
  private String relativePath;
 
  /**
   * 资源路径映射
   * 注意:路径前加 "file:/"
   * @param registry
   */
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    System.out.println(relativePath   "=============="   savePath);
    registry
      .addResourceHandler(relativePath   "/**")
      .addResourceLocations("file:/"   savePath);
  }
}

通过上面的配置后,再去访问就可以正常显示图片了。

以上就是基于SpringBoot实现图片上传及图片回显的详细内容,更多关于SpringBoot图片上传 回显的资料请关注Devmax其它相关文章!

基于SpringBoot实现图片上传及图片回显的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  3. 微信小程序如何获取图片宽度与高度

    这篇文章主要给大家介绍了关于微信小程序如何获取图片宽度与高度的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. 利用Python上传日志并监控告警的方法详解

    这篇文章将详细为大家介绍如何通过阿里云日志服务搭建一套通过Python上传日志、配置日志告警的监控服务,感兴趣的小伙伴可以了解一下

  5. PHP实现文件上传与下载实例与总结

    这篇文章主要介绍了PHP实现文件上传与下载实例与总结的相关资料,需要的朋友可以参考下

  6. 小程序实现图片裁剪上传

    这篇文章主要为大家详细介绍了小程序实现图片裁剪上传,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  7. js实现头像上传并且可预览提交

    这篇文章主要介绍了js如何实现头像上传并且可预览提交,帮助大家更好的理解和使用js,感兴趣的朋友可以了解下

  8. SpringBoot本地磁盘映射问题

    这篇文章主要介绍了SpringBoot本地磁盘映射问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  9. java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    这篇文章主要介绍了java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

  10. SpringBoot整合Javamail实现邮件发送的详细过程

    日常开发过程中,我们经常需要使用到邮件发送任务,比方说验证码的发送、日常信息的通知等,下面这篇文章主要给大家介绍了关于SpringBoot整合Javamail实现邮件发送的详细过程,需要的朋友可以参考下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部