1.如何把mybatis也集成进spring中,有哪些步骤;
2.前端发送json对象,后端怎么接收;
3.前端传路径变量的rest风格,后端怎么接收处理;
4.spring的拦截的配置和使用;
<parent>
<groupId>org.springframework.bootgroupId>
<version>2.3.0.RELEASEversion>
<artifactId>spring-boot-starter-parentartifactId>
parent>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.2.12version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.1version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.16version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.8.11version>
dependency>
dependencies>
主启动类
package com.tianju;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* springMVC的主启动类
* 1.是主启动类;@SpringBootApplication
* 2.启动:SpringApplication.run(Main.class);
*/
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
application.yml文件,注意如果有mybatis包,则必须配置一下,不然会报错
server:
port: 80
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123
项目启动失败,报错信息如下:
报错信息:
Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
报错信息:
Failed to convert from type [java.lang.String] to type [java.util.Date] for value ‘2023-06-08’
解决方案:controller层接收参数上加注解
@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday
前端代码:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册title>
head>
<body>
注册
<form action="/user/register" method="post">
用户名:<input type="text" name="username">
密码:<input type="text" name="username">
性别:
<select name="sex">
<option value="male">男option>
<option value="female">女option>
select>
生日:
<input type="date" name="birthday">
<input type="submit" value="提交">
form>
body>
html>
实体类
package com.tianju.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.text.Format;
import java.util.Date;
/**
* user实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String username;
private String password;
private String sex;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
}
controller层代码
package com.tianju.controller;
import com.tianju.entity.ResData;
import com.tianju.entity.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date;
/**
* 用于处理用户相关请求的controller
* 1.在容器中;@Controller
* 2.访问路径;一级目录,@RequestMapping("/user")
*/
@Controller
@RequestMapping("/user")
public class UserController {
// 首先到登陆页面,响应一个页面
@RequestMapping("/registerPage")
public String registerPage(){
// 返回string类型
return "/user/register";
}
// 在登陆页面,用户点击登陆按钮,处理请求
@RequestMapping("register")
@ResponseBody
public ResData register(String username, String password, String sex,
@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday){
System.out.println(birthday);
User user = new User(username,password,sex,birthday);
System.out.println(user);
return new ResData(200, "ok", null);
}
}
要点:
前端发送对象的方式:
(1) user对象逐个赋值,发送post请求
let user = {}
user.username = this.username;
user.password = this.password;
user.sex = this.sex;
user.birthday = this.birthday;
axios.post("/user/register",user)
(2) 直接创建好user对象,发送post请求
let user = {
"username":this.username,
"password":this.password,
"sex":this.sex,
"birthday":this.birthday,
}
axios.post("/user/register",user)
后端接收要加上@RequestBody,在类上加 @JsonFormat(pattern = “yyyy-MM-dd”)
// 在登陆页面,用户点击登陆按钮,处理请求
@RequestMapping("register")
@ResponseBody
// 如果前端用json对象发,后端需要加上@RequestBody
public ResData register(@RequestBody User user){
System.out.println(user);
return new ResData(200, "ok", null);
}
之前的模式下:
在spring中集成mybatis
server:
port: 80
# 1.连接数据库——对应之前 xml文件的数据库
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/javaweb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123
# mybatis其他配置
mybatis:
# 2.给实体类起别名,首字母小写
type-aliases-package: com.tianju.entity
configuration:
# 3.开启驼峰命名
map-underscore-to-camel-case: true
# 4.让日志生效
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 5.扫描sql的位置
mapper-locations: classpath:/mapper/*Mapper.xml
<insert id="add" useGeneratedKeys="true" keyProperty="id">
INSERT INTO com_user(username,password,sex,birthday)
VALUES (#{username},#{password},#{sex},#{birthday})
insert>
前端导包:
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/js/jquery-3.5.1.js">script>
<script src="/bootstrap/js/bootstrap.js">script>
<script src="/js/vue.min-v2.5.16.js">script>
<script src="/js/axios.min.js">script>
要点:
@Autowired
private IUserService userService;
// 首先到注册页面,响应一个页面
@RequestMapping("/registerPage")
public String registerPage(){
// 返回string类型
return "/user/register";
}
// 在注册页面,用户点击注册按钮,处理请求
@RequestMapping("register")
@ResponseBody
// 如果前端用json对象发,后端需要加上@RequestBody
public ResData register(@RequestBody User user){
System.out.println(user);
System.out.println(EntityUtils.isAllNotNull(user));
// 1.判断前端输入不为null或空,除了自增的id
boolean allNotNull = EntityUtils.isAllNotNull(user);
if (!allNotNull){
System.out.println("输入有空");
return new ResData(1001,"输入为空",null);
}
// 1.判断两个输入的密码是否一致
if (!user.getPassword().equals(user.getRePassword())){
return new ResData(1001,"两次密码不一致",null);
}
// 2.判断是否有重名的
User userDB = userService.queryByUsername(user.getUsername());
if (userDB!=null){
System.out.println(userDB);
return new ResData(1002,"用户名重复",null);
}
// 3.插入数据库中
// 密码加密存储
user.setPassword(SecureUtil.md5(user.getRePassword()));
Integer addFlag = userService.add(user);
System.out.println(user);
if (addFlag<1){
return new ResData(3001,"系统繁忙,请稍后",null);
}
return new ResData(200, "ok", null);
}
EntityUtils.java文件
package com.tianju.util;
import java.lang.reflect.Field;
/**
* 传入一个对象,判断对象里面的每个属性是否为null,或空;
* 如果有null或空,返回false;否则,返回true;
*/
public class EntityUtils {
public static boolean isAllNotNull(Object obj) {
Class<?> aClass = obj.getClass();
// 获取所有的files
Field[] fields = aClass.getDeclaredFields();
for(Field field:fields){
// 如果第一个是id,id是数据库自增的,前端不输入,就跳过
if (field.getName().contains("id")){
continue;
}
field.setAccessible(true);
Object value = null;
try {
value = field.get(obj);
}
catch (Exception e) {
throw new RuntimeException(e);
}
if (value==null || "".equals(value)){
return false;
}
}
return true;
}
}
StringUtils.java文件
package com.tianju.util;
import java.util.Date;
/**
* 判读输入是否为空,支持可变长度参数
*/
public class StringUtils {
/**
* 最初始版本,只能判断string类型是否为null,空字符串
*/
public static Boolean isBlank(String str){
if(str==null || str.trim().equals("")){
return true;
}
return false;
}
/**
* 升级版本,
* 可以判断String,Integer,Date类型
* 是不是null,string判断是不是空
* @param objs 可变长度参数
* @return
*/
public static Boolean isBlank(Object... objs){
for (Object obj:objs){
// System.out.println(obj);
if (obj==null){
return true;
}
// 如果是字符串,判断是null,和 空字符串
if (String.class.equals(obj.getClass())){
String str = (String) obj;
if(str==null || str.trim().equals("")){
return true;
}
// Integer 和 Date类型判断是不是null
}else {
if (obj==null){
return true;
}
}
}
return false;
}
public static void main(String[] args) {
Object s = null;
String str = (String) s;
// System.out.println(str==null);
Integer i = null;
Boolean blank = isBlank(str,i,2);
System.out.println(blank);
System.out.println(isBlank("er",3,new Date()));
System.out.println("UserId".contains("id"));
}
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/js/jquery-3.5.1.js">script>
<script src="/bootstrap/js/bootstrap.js">script>
<script src="/js/vue.min-v2.5.16.js">script>
<script src="/js/axios.min.js">script>
head>
<body>
<div id="app">
Vue登陆页面<br>
用户名:<input type="text" v-model="username"><br>
输入密码:<input type="text" v-model="password"><br>
<button @click="loginBtn">提交button>
div>
<script>
let app = new Vue({
el:"#app",
data:{
username:"",
password:"",
},
methods:{
loginBtn(){
axios.post("/user/login",{"username":this.username,"password":this.password})
.then(response=>{
let resp = response.data;
console.log(resp)
if (resp.code==200){
alert(resp.msg)
// 跳转到index页面,也要过controller
location.href = "/user/listPage"
}else {
alert(resp.msg)
}
})
}
},
created(){
}
})
script>
body>
html>
// 到登陆页面
@RequestMapping("/loginPage")
public String loginPage(){
// 返回string类型
return "/user/login";
}
// 处理用户输入的用户名和密码
@RequestMapping("/login")
@ResponseBody
public ResData login(@RequestBody User user, HttpSession session){
System.out.println(user);
// 1.输入不为空
if (StringUtils.isBlank(user.getUsername(),user.getPassword())){
return new ResData(1001, "用户名|密码为空", null);
}
// 2.判断用户名,密码是否正确
User userDB = userService.queryByUsername(user.getUsername());
if (userDB==null || userDB.getPassword().equals(SecureUtil.md5(user.getPassword()))){
return new ResData(1001, "用户名|密码错误", null);
}
// 3.登陆成功,保存到session
session.setAttribute("user", userDB);
return new ResData(200, "OK", null);
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/js/jquery-3.5.1.js">script>
<script src="/bootstrap/js/bootstrap.js">script>
<script src="/js/vue.min-v2.5.16.js">script>
<script src="/js/axios.min.js">script>
head>
<body>
<div id="app">
Vue登陆页面<br>
用户名:<input type="text" v-model="username"><br>
输入密码:<input type="text" v-model="password"><br>
<button @click="loginBtn">提交button>
div>
<script>
let app = new Vue({
el:"#app",
data:{
username:"",
password:"",
},
methods:{
loginBtn(){
axios.post("/user/login",{"username":this.username,"password":this.password})
.then(response=>{
let resp = response.data;
console.log(resp)
if (resp.code==200){
alert(resp.msg)
// 跳转到index页面,也要过controller
location.href = "/user/listPage"
}else {
alert(resp.msg)
}
})
}
},
created(){
}
})
script>
body>
html>
要点:
// 到list页面
// 1.直接到页面
// @RequestMapping("/listPage")
public String listPage(){
// 返回string类型
return "/user/list";
}
// 2.共享值
@RequestMapping("/listPage")
public ModelAndView listPageMV(){
ModelAndView mv = new ModelAndView("/user/list"); // 到哪个页面
mv.addObject("username", "peter"); // 共享的值
return mv;
}
// 处理list页面的请求,pageNum,pageSize
@RequestMapping("/list/{pageNum}/{pageSize}")
@ResponseBody
public ResData userList(@PathVariable("pageNum") Integer pageNum,
// TODO:可以多个注解吗?答案:用路径变量必须都传,所以下面无效
// @RequestParam(value = "pageSize",defaultValue = "3")
@PathVariable("pageSize") Integer pageSize){
PageInfo<User> pageInfo = userService.queryList(pageNum, pageSize);
System.out.println(pageInfo);
return new ResData(200, "OK", pageInfo);
}
<!DOCTYPE html>
<html lang="en">
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>列表</title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/js/jquery-3.5.1.js"></script>
<script src="/bootstrap/js/bootstrap.js"></script>
<script src="/js/vue.min-v2.5.16.js"></script>
<script src="/js/axios.min.js"></script>
</head>
<body>
<div id="app">
列表页面[[${username}]]<br>
<div>
<!-- 进行搜索-->
<input type="text" v-model="pageSize">
<button @click="searchBtn">提交</button>
</div>
<!-- pageNum,pageSize,name,date,-->
<table class="table-condensed table-hover table-striped table-responsive table-cell table-row-cell table-view table-bordered">
<tr>
<th>id</th>
<th>username</th>
<th>gender</th>
<th>birthday</th>
</tr>
<tr v-for="user in pageInfo.list">
<td>{{user.id}}</td>
<td>{{user.username}}</td>
<td>{{user.sex}}</td>
<td>{{user.birthday}}</td>
</tr>
</table>
<div>
<!-- 首页,尾页,上下页,跳转到-->
<button v-show="!pageInfo.isFirstPage" @click="toFirstPage">首页</button>
<button v-show="pageInfo.hasNextPage" @click="toNextPage">下页</button>
<button v-show="pageInfo.hasPreviousPage" @click="toPreviousPage">上页</button>
<button v-show="!pageInfo.isLastPage" @click="toLastPage">尾页</button>
</div>
</div>
<script>
let app = new Vue({
el:"#app",
data:{
pageInfo:{},
pageSize:3,
},
methods:{
// 设置pageNum 和 pageSize
queryList(pageNum,pageSize){
axios.post("/user/list/"+pageNum+"/"+pageSize)
.then(response=>{
let resp = response.data;
console.log(resp)
this.pageInfo = resp.data;
})
},
searchBtn(){
this.queryList(1,this.pageSize)
},
// 首页,尾页,下页,上页
toFirstPage(){
this.queryList(1,this.pageSize)
},
toNextPage(){
this.queryList(this.pageInfo.pageNum+1,this.pageSize)
},
toPreviousPage(){
this.queryList(this.pageInfo.pageNum-1,this.pageSize)
},
toLastPage(){
this.queryList(this.pageInfo.pages,this.pageSize)
},
},
created(){
this.queryList(1,3);
}
})
</script>
</body>
</html>
拦截器是基于增强方法做的
拦截谁,在配置类中配置;——对应SpringMvcConfig.java文件
拦下来做什么;——interceptor/LoginAuthInterceptor.java文件
要点:
package com.tianju.config;
import com.tianju.inteceptor.LoginAuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* spring的配置类
* 1.在容器,@Configuration
* 2.是spring的配置类,
*/
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
LoginAuthInterceptor loginAuthInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginAuthInterceptor)
.addPathPatterns("/**") // 拦截器 /** 表示子孙目录
.excludePathPatterns(
"/user/loginPage","/user/login",
"/user/registerPage","/user/register",
"/js/**","/css/**","/bootstrap/**"
); // 在拦截的基础上,放行谁
}
}
要点:
package com.tianju.inteceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 拦截器相关,拦下来做什么
* 1.在容器中,@Component
* 2.是拦截器,implements HandlerInterceptor
*/
@Component
public class LoginAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果登陆了,就放行
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if (user!=null){
return true;
}else {
// 没有登陆,去登陆页面
response.sendRedirect("/user/loginPage");
return false;
}
}
}
报错:ERR_TOO_MANY_REDIRECTS
原因:.excludePathPatterns里面的路径反斜杠没加
1.集成mybatis,在application.yml配置文件中进行配置;
2.前端传参用Json对象传,后端接收需要加上@RequestBody注解,实体类中规定日期的格式, @JsonFormat(pattern = “yyyy-MM-dd”);
3.获取新增数据的id:useGeneratedKeys=“true” keyProperty=“id”;
4.路径变量传参"/list/{pageNum}/{pageSize}“,后端接收用@PathVariable(“pageNum”);
5.后端共享值用ModelAndView,前端获取用[[${username}]];
6.spring配置类:在soring的容器中,@Configuration;是spring的配置类 implements WebMvcConfigurer;
7.拦截器(@Component + implements HandlerInterceptor),拦下来做什么—response.sendRedirect(”/user/loginPage");
8.拦截器在Spring配置中使用addInterceptors方法;