【SpringBoot】(三)SpringBoot + Mybatis + Thymeleaf 整合

这篇博文将前两篇博文的知识内容给串一串,展示如何将之前零碎的东西给整合成一个小的、完整的东西,并且,介绍一个前端模板引擎 ------ Thymeleaf

示例:

这是一个对用户进行增的示例,可支持文件本地上传

sql 文件:

CREATE TABLE `tab_user` (
  `id` varchar(50) NOT NULL,
  `user_name` varchar(255) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  `photo` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

实体类:

public class TabUser {
          
   
    private String id;
    private String userName;
    private String password;
    private String photo;
    // getter/setter
}

【开发环境】:

    IDEA-2019.1 SpringBoot-2.1.1.RELEASE MAVEN-3.5.3 MySQL-5.7 Thymeleaf

【工程结构图】:

1、引入依赖:

<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.0</version>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
  </dependency>
</dependencies>

2、添加 application.yml 配置:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/zzc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
    password: root
    username: root
    driver-class-name: com.mysql.jdbc.Driver
  thymeleaf:  # thymeleaf 配置
    cache: false  # 关闭 thymeleaf 缓存
    prefix: classpath:/templates/  # 配置模板(非必要) 默认是 templates
    suffix: .html

mybatis:
  type-aliases-package: com.zzc.entity # 实体类别名
  mapper-locations: classpath:mybatis/mapper/*.xml # mapper 配置文件(必要)
  configuration:
    #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志输出位置
    map-underscore-to-camel-case: true # 驼峰命名

#logging:
#  level:
#    com.zzc.mapper: debug # 设置日志级别

file:
  uploadPath: E:/upload # 头像上传路径

这里面主要添加了 thymeleaf 的配置

3、测试 Thymeleaf 模板是否引入成功:

在路径 resources/templates 路径下添加一个 html 文件(如:index.html),然后,添加一个控制器进行访问:

@Controller
@RequestMapping("/test")
public class TestController {
          
   
    @GetMapping("/index")
    public String index() {
          
   
        return "index";
    }
}

浏览器访问:http://localhost:8080/test/index

如果页面出现了内容,则表示访问成功。

UserController:

@Controller
public class UserController {
          
   

    @Autowired
    private UserService userService;

	// 跳转到 user/user.html 页面
    @GetMapping("/user")
    public String user() {
          
   
        return "user/user";
    }

	// 添加用户信息,并以json格式返回用户信息给前台
    @ResponseBody
    @PostMapping("/add")
    public UserVo addUser(MultipartFile file, UserVo userVo) {
          
   
        return userService.addUser(file, userVo);
    }

}

UserService:

public interface UserService {
          
   
    UserVo addUser(MultipartFile multipartFile, UserVo userVo);
}

UserServiceImpl:

@Service
public class UserServiceImpl implements UserService {
          
   

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserVo addUser(MultipartFile multipartFile, UserVo userVo) {
          
   
        if (null == multipartFile) {
          
   
            return userVo;
        }
        // 1.上传图片
        String fileName = null;
        try {
          
   
            fileName = FileUtil.uploadFile(multipartFile, userVo);
        } catch (Exception e) {
          
   
            e.printStackTrace();
        }
        // 2.添加到数据库
        userVo.setPhoto(fileName);
        TabUser tabUser = UserUtil.transformTabUser(userVo);
        int i = userMapper.addUser(tabUser);
        return userVo;
    }

}

添加用户方法做了两个逻辑处理:上传图片到本地;存用户信息到数据库

FileUtil#uploadFile():上传图片到本地

public class FileUtil {
          
   

    private static FileConfig fileConfig = ApplicationContextHolder.getContext().getBean(FileConfig.class);

    public static String uploadFile(MultipartFile file, UserVo userVo) throws Exception {
          
   
        // 1.获取上传的文件名称
        String sOldFileName = file.getOriginalFilename();
        // 2.获取上传文件路径
        String uploadPath = fileConfig.getUploadPath() + File.separator + userVo.getUserName();
        // 3.生成父路径
        File uploadFile = mkdirs(uploadPath);
        String suffixPath = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "_" + sOldFileName;
        String fileFullPath = uploadPath + File.separator  + suffixPath;
        file.transferTo(new File(fileFullPath));
        // 4.拼接存在数据库的文件名
        return userVo.getUserName() + File.separator + suffixPath;
    }

    // 生成相应的目录
    public static File mkdirs(String path) {
          
   
        File file = new File(path);
        if(!file.exists() || !file.isDirectory()) {
          
   
            file.mkdirs();
        }
        return file;
    }

}

1、上传图片到本地

uploadFile() 方法:将前台上传的图片上传到指定的路径下,并按照新的规则生成新的图片名(用户名/当前日期_上传的文件名) 存到数据库中。其中,指定的路径通过 FileConfig 类中的 uploadPath 属性获取。它配置在 application.yml:

file:
  uploadPath: E:/upload # 头像上传路径

FileConfig:

@Component
@ConfigurationProperties(prefix = "file")
public class FileConfig {
          
   

    // 上传路径
    private String uploadPath;

    // getter/setter
}

ApplicationContextHolder:

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
          
   

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
          
   
        context = applicationContext;
    }

    public static ApplicationContext getContext() {
          
   
        return context;
    }

    public static Object getBean(String name) {
          
   
        return context != null ? context.getBean(name) : null;
    }

    public static <T> T getBean(Class<T> clz) {
          
   
        return context != null ? context.getBean(clz) : null;
    }

    public static <T> T getBean(String name, Class<T> clz) {
          
   
        return context != null ? context.getBean(name, clz) : null;
    }

    public static void addApplicationListenerBean(String listenerBeanName) {
          
   
        if (context != null) {
          
   
            ApplicationEventMulticaster applicationEventMulticaster = (ApplicationEventMulticaster)context.getBean(ApplicationEventMulticaster.class);
            applicationEventMulticaster.addApplicationListenerBean(listenerBeanName);
        }
    }
}

2、存用户信息到数据库

UserUtil:

public class UserUtil {
          
   

    public static List<UserVo> transformUserVos(List<TabUser> tabUsers) {
          
   
        List<UserVo> userVos = tabUsers.stream().map(tabUser -> {
          
   
            UserVo userVo = new UserVo();
            userVo.setsId(tabUser.getId());
            userVo.setUserName(tabUser.getUserName());
            userVo.setPassword(tabUser.getPassword());
            return userVo;
        }).collect(Collectors.toList());
        return userVos;
    }

    public static TabUser transformTabUser(UserVo userVo) {
          
   
        TabUser tabUser = new TabUser();
        String sId = UUID.randomUUID().toString().trim().replaceAll("-", "");
        if (StringUtils.isEmpty(userVo.getsId())) {
          
   
            tabUser.setId(sId);
            userVo.setsId(sId);
        } else {
          
   
            tabUser.setId(userVo.getsId());
        }
        tabUser.setUserName(userVo.getUserName());
        tabUser.setPassword(userVo.getPassword());
        tabUser.setPhoto(userVo.getPhoto());
        return tabUser;
    }

}

UserVo:

public class UserVo {
          
   
    private String sId;
    private String userName;
    private String password;
    private String photo;
    // getter/setter
}

UserMapper 接口:

public interface UserMapper {
          
   
    int addUser(TabUser tabUser);
}

UserMapper.xml 文件:

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zzc.mapper.UserMapper">

    <insert id="addUser" parameterType="TabUser">
        INSERT INTO TAB_USER (id, user_name, password, photo) values (#{id}, #{userName}, #{password}, #{photo})
    </insert>

</mapper>

user.html:用户页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/css/user.css" />
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<h1>注册用户信息</h1>
<form id="myForm">
    <table>
        <tr>
            <td>用户名</td>
            <td><input type="text" name="userName" placeholder="用户名"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password" placeholder="密码"></td>
        </tr>
        <tr>
            <td>头像</td>
            <td><input type="file" accept="*/*" name="file" id="FileImg" onchange="uploadImg(this)" /></td>
        </tr>
        <tr>
            <td>上传的头像</td>
            <td><img src="" alt="默认头像地址(可以自己填)" id="avarimgs" style="border-radius: 50%" width="200px" height="200px"></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="button" value="注册" id="registerBtn" onclick="reg()">
            </td>
        </tr>
    </table>

</form>
<hr>
<h1>用户注册的信息</h1>
<table>
    <tr>
        <td>用户名</td>
        <td><input type="text" name="userName" id="myUsername" placeholder="用户名" readonly></td>
    </tr>
    <tr>
        <td>密码</td>
        <td><input type="text" name="password" id="myPassword" placeholder="密码" readonly></td>
    </tr>
    <tr>
        <td>头像</td>
        <td>
            <img src="" id="myImg" style="border-radius: 50%" width="200px" height="200px" />
        </td>
    </tr>
</table>
<script>
    // 回显图片
    function uploadImg(obj) {
            
     
        var file = obj.files[0];
        var reader = new FileReader();
        reader.onload = function (e) {
            
     
            // e 就是 ProgressEvent 对象,里面的target.result就是base64编码
            var img = document.getElementById("avarimgs");
            img.src = e.target.result; //或者 img.src = this.result;  e.target == this
        }
        reader.readAsDataURL(file)
    }

    // 注册
    function reg() {
            
     
        let form = new FormData($("#myForm")[0])
        $.ajax({
            
     
            //接口地址
            url: /add,
            type: POST,
            data: form,
            async: false,
            cache: false,
            contentType: false,
            processData: false,
            success:function (data) {
            
     
                console.log(data)
                var userName = data.userName;
                var password = data.password;
                
                // 图片回显
                var photo = mystatic + / + data.photo
                
                $(#myUsername).val(userName)
                $(#myPassword).val(password)
                $(#myImg).attr(src, photo)
            }
        });
    }
</script>

</body>
</html>

【注意】:js 中的 reg() 函数代码:var photo = mystatic + / + data.photo:

在后台中,我们将一个图片上传到一个本地路径下(FileUtil#uploadFile()),正常情况下,SpringBoot 工程中是无法访问到静态资源的。所以,我们必须添加资源映射 ``:

@Configuration
public class MyStaticConfig implements WebMvcConfigurer {
          
   

    private static String UPLOAD_FILE_PATH = "file:E:/upload/";

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
          
   
        // 访问路径以 “/mystatic” 开头时,会去 “mystatic” 路径下找静态资源
        registry
                .addResourceHandler("/mystatic/**")
                .addResourceLocations(UPLOAD_FILE_PATH);
    }
}

上述代码的意思:在本地磁盘的路径 E:/upload/ 下的文件,都可以以 /mystatic/ 进行访问。

所以,var photo = mystatic + / + data.photo 代码中,是通过 mystatic 进行拼接的(前台和后台保持一致即可!!)

经验分享 程序员 微信小程序 职场和发展