P2. 配置MySQL和用户注册登录模块

P2. 配置MySQL和用户注册登录模块

    • [0 概述](#0 概述)
    • Tips
    • [1 预备知识](#1 预备知识)
      • [1.1 SpringBoot 常用模块](#1.1 SpringBoot 常用模块)
      • [1.2 pojo层的实现](#1.2 pojo层的实现)
      • [1.3 mapper层的实现](#1.3 mapper层的实现)
      • [1.4 controller层调试CRUD](#1.4 controller层调试CRUD)
    • [2 Spring Security](#2 Spring Security)
      • [2.1 Spring Security 介绍](#2.1 Spring Security 介绍)
      • [2.2 Spring Security 对接数据库](#2.2 Spring Security 对接数据库)
      • [2.3 密码的加密](#2.3 密码的加密)
    • [3 Jwt验证](#3 Jwt验证)
      • [3.1 传统Session验证方式](#3.1 传统Session验证方式)
      • [3.2 Jwt验证方式](#3.2 Jwt验证方式)
      • [3.3 Jwt验证所需的配置](#3.3 Jwt验证所需的配置)
    • [4 用户注册和登录模块的实现](#4 用户注册和登录模块的实现)
      • [4.1 用户注册和登录 API 的实现](#4.1 用户注册和登录 API 的实现)
        • [4.1.1 /user/account/login/](#4.1.1 /user/account/login/)
        • [4.1.2 /user/account/info/ /user/account/register/](#4.1.2 /user/account/info/ /user/account/register/)
      • [4.2 用户登录模块的前端](#4.2 用户登录模块的前端)
        • [4.2.1 登录页面和 Login 函数及全局变量 user 的定义与使用](#4.2.1 登录页面和 Login 函数及全局变量 user 的定义与使用)
        • [4.2.2 获取 user 信息和退出功能](#4.2.2 获取 user 信息和退出功能)
      • [4.3 前端页面授权](#4.3 前端页面授权)
      • [4.4 登录状态持久化及小问题处理](#4.4 登录状态持久化及小问题处理)
    • [5 配置MySQL](#5 配置MySQL)
      • [5.1 MySQL的基本结构](#5.1 MySQL的基本结构)
      • [5.2 IDEA中配置MySQL](#5.2 IDEA中配置MySQL)
      • [5.3 Spring使用MySQL需要的依赖](#5.3 Spring使用MySQL需要的依赖)
      • [5.4 图形化使用MySQL](#5.4 图形化使用MySQL)

0 概述

以用户注册登录模块为例,学习实现一个具体的业务逻辑的流程

  • 明白 SpringBoot 如何和 MySQL 结合,如何调用数据库中的数据。

  • 了解 pojo, mapper, service, controller 各层的功能及其实现,学会自己写一个业务逻辑。

  • 了解 Jwt 验证原理,知道密码加密的实现。

  • 重点掌握 SpringBoot 实现一个业务 API 功能的3个流程service, service.impl, controller

  • 重点掌握前端如何绑定变量,如何定义全局变量 vuex,如何调用全局变量的函数

    也就是要求掌握第 4 小节所有的代码逻辑。

由于配置 MySQL 不涉及逻辑内容,因此放置在本文的最后介绍。

Tips

  • 在写项目的时候要减少搭轮子的时间 ,去找网上现成的轮子,比如 JwtUtil 这种。
  • 要放行更多接口的时候可以在 config.SecurityConfig 类中添加。
  • 使用 Mybatis-plus 从而避免写 SQL 语句,不用写简单的 CRUD 操作。具体的接口可以查看Mybatis-plus指南。一些简单的使用在1.4 小节中进行了示范 (querywrapper, userMapper.xxx)。
  • 强烈换成阿里源去下依赖,用外网下依赖直接给我整崩溃了,搞了半天重开了个项目,换成阿里源下载依赖才搞好。
  • 实现和重写接口的方法可以通过 alt + insert 快捷键添加。

1 预备知识

1.1 SpringBoot 常用模块

习惯上会在写后端的时候分层(文件夹)去实现,一共分成 4 层: pojo, mapper, service, controller

  • pojo: 将 MySQL 中的表 table 翻译成 Java 中的类 class

  • mapper: 对 class 的CRUD操作转化成 SQL 语句。

    每个 class 需要相应的CRUD操作,操作完成后最终会存到数据库中,因此需要涉及到 SQL 语句。

  • service: 业务通常要处理多张表,因此组合多个 mapper 实现具体业务。

  • controller: 调度 service,接收前端请求和参数,选择将参数传给哪个 service,再把 service 结果返回给前端。

1.2 pojo层的实现

pojo 是将 MySQL 中的 user 表转化成 User 类,每个类需要自己的属性和方法,属性需要自己对照数据库中的字段定义,可以通过安装的依赖 lombok 自动实现这些机械化的方法,如 get, set, equals, toString 及有参,无参构造函数。(依赖的安装见 5.3 节)

backend.pojo.User

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
}

1.3 mapper层的实现

本来是需要写大量的 SQL 语句,但 MyBatis-plus 已经帮我们实现好了 SQL 语句,只需要通过 extends BaseMapper<class_name> 集成过来就行,因此不需要写任何 SQL 语句了。

注意 mapper 是一个 interface 而不是 class

java 复制代码
@Mapper
public interface UserMapper extends BaseMapper<User> {
}

1.4 controller层调试CRUD

实现完 pojo, mapper 后可以在 controller 层中进行一些调试工作。

让我们测试一下上面的 pojo, mapper,学习一下怎么调用 MyBatis-plus 实现的接口。

@RestController 的作用可以理解成把返回值转成 Json 格式再返回给前端。

下面的CRUD操作仅为了演示怎么调试及使用接口,实际实现某个业务功能的时候有另外的写法,而且一般在 service 实现。

之后在 http://localhost:3000/.../ 中就能查看是否正确取得结果。

注意是自己设置的后端端口号,resources/application.propertiesserver.port=3000 设置。

java 复制代码
@RestController
public class UserController {

    @Autowired
    UserMapper userMapper;

    @RequestMapping("/user/all/")
    public List<User> getAllUser() {
        return userMapper.selectList(null);
    }
    
    @RequestMapping("/user/{userId}/")
    public User getUser(@PathVariable int userId) {
        return userMapper.selectById(userId);
    }
    
    // 可以通过封装 queryWrapper 实现复杂一点的操作,这边的功能和上一个函数相同
    @RequestMapping("/user/querywrapper/{userId}/")
    public User getQueryUser(@PathVariable int userId) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", userId);
        return userMapper.selectOne(queryWrapper);
    }
    
    // 查询符合多个条件的直接在 queryWrapper 后面加条件即可
    @RequestMapping("/user/querywrapper/{userId}")
    public List<User> getSomeUser(@PathVariable int userId) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("id", 1).le("id", 2);
        return userMapper.selectList(queryWrapper);
    }
    
    @RequestMapping("/user/add/{userId}/{username}/{password}/")
    public String addUser(
            @PathVariable int userId,
            @PathVariable String username,
            @PathVariable String password) {
        User user = new User(userId, username, password);
        userMapper.insert(user);
        return "add success";
    }

    @RequestMapping("/user/delete/{userId}/")
    public String deleteUser(@PathVariable int userId) {
        userMapper.deleteById(userId);
        return "delete success";
    }
}

2 Spring Security

2.1 Spring Security 介绍

某些功能需要用户拥有权限才能访问,因此需要加上授权机制,进行权限判断 ,安装依赖 spring-boot-starter-securitySpring 实现好的模块集成进来。会发现上面的操作都需要登录之后才能运行,默认用户名为 user,密码在 IDEA 的控制台中输出。

2.2 Spring Security 对接数据库

现在有个问题就是只能用它默认的用户进行登录,我们希望通过数据库里的用户进行登录,因此需要对 Spring Security 进行配置。

实现 service.impl.UserDetailsServiceImpl 类,继承 UserDetailsService 接口,用来接入数据库信息。

重写其 loadUserByUsername 方法,该方法是通过 username 找到对应用户的 username, password (UserDetails 类型)。

service.impl.UserDetailServiceImpl

java 复制代码
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        User user = userMapper.selectOne(queryWrapper);

        if (user == null) throw new RuntimeException("用户不存在!");

        return new UserDetailsImpl(user);
    }
}

实现辅助类 UserDetailsImpl,该类实现接口 UserDetails,该接口的所有方法在 Spring Security 中指出。

这下就可以通过数据库中用户来登录,但是要先把用户密码改成 {noop}<password> 形式,告诉数据库当前字段是未加密的明文。

2.3 密码的加密

这里就有一个很有趣的问题,为什么忘记密码之后只能重置密码,不能知道原来的密码是什么呢?

这是由于密码的加密算法是一个单向的加密,反向解码基本不可能。注册存储的是加密后的密码 encoded_pw_save,每次用户登录输入密码 pw,调用 match 方法看 pw, encoded_pw_save 是否匹配,如果匹配则成功登录。当用户忘记密码之后,数据库存储的是加密后的密码,无法解密出原来的密码,因此只能重置。

config.SecurityConfig

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

之后可以在 test 中的 BackendApplicationTests 中进行一些测试,首先输出一下 pw 对应的加密后密文(每次加密结果可能不同),再看一下 pw, encoded_pw 能否匹配,结果为 True,之后把数据库中的密码修改成密文格式就能够正常登陆了(加 {noop} 也无法登录,只能密文登录)。

java 复制代码
void contextLoads() {
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        System.out.println(passwordEncoder.encode("<pw>"));
        System.out.println(passwordEncoder.matches("<pw>", "<encoded_pw>"));
}

之后可以修改 controller 添加用户 addUser 的实现,直接存储加密之后的密码

java 复制代码
@RequestMapping("/user/add/{userId}/{username}/{password}/")
public String addUser(
        @PathVariable int userId,
        @PathVariable String username,
        @PathVariable String password) {
    PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    String encodedPassword = passwordEncoder.encode(password);
    User user = new User(userId, username, encodedPassword);
    userMapper.insert(user);
    return "add success";
}

3 Jwt验证

3.1 传统Session验证方式

所有的页面可以简单的分为2种,一种是公开页面,也就是不登录就可以查看的页面(比如 login, register, homepage);一种是授权页面,也就是需要登录才能查看的页面(其实可以细分成有某个页面的权限才能访问等,但我们的项目暂时用不到这么细致)。

那么问题就来了,Server 如何判断某个用户是否能够访问某个授权页面呢?

首先,先介绍一下传统的 Session 验证方式(如下图所示),仅做了解,并不使用这种方式。

3.2 Jwt验证方式

对于最近的应用都会遇到跨域问题,通常有多个端(Web, App ...),或是多个服务器,这时 Session 方式就难以处理。

每个服务器都要存下用户的 SessionID

因此提出新的验证方式: Jwt验证,这个方式的优点在于: 方便处理跨域问题,不需要在 Server 端存储信息。

3.3 Jwt验证所需的配置

Jwt验证所需配置具体实现代码

  • 安装依赖 jjwt-api, jjwt-impl, jjwt-jackson
  • 实现 utils.JwtUtil 类,作用是创建和解析 jwt_token
  • 实现 config.filter.JwtAuthenticationTokenFilter 类,用来验证 jwt_token,如果验证成功则提取 user 信息到 Spring Security 的上下文中(存储用户的认证信息,全局共享),以便进行后续的权限验证。
  • 配置 config.SecurityConfig 类,放行登录、注册等接口。

4 用户注册和登录模块的实现

4.1 用户注册和登录 API 的实现

首先更改一下用户 user 的属性,id 改成自增,在 pojo 中为 id 添加注解 @TableId(type = IdType.AUTO)

一共需要实现3个 API:

  • /user/account/token/: 验证用户名密码,验证成功后返回 jwt_token
  • /user/account/info/: 根据 jwt_token 返回用户信息;
  • /user/account/register/: 注册账号。

SpringBoot 实现 API 可以分为 service 声明接口,service.impl 实现接口的具体逻辑,controller 接收前端 url

4.1.1 /user/account/login/

service.user.account.loginService

一般习惯上返回一个 Map<String, String> 接收各种信息,key 要和前端对应起来。

java 复制代码
public interface LoginService {
    Map<String, String> login(String username, String password);
}

service.impl.user.account.loginServiceImpl

先封装明文的用户名密码,再验证用户是否登录(失败则进行自动处理),之后取出用户 user,最后把 user_id 封装成 jwt_token

记得加上 @Service 的注解。

java 复制代码
@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public Map<String, String> login(String username, String password) {
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(username, password);

        // 如果认证失败(例如,用户名或密码不正确),此方法将抛出一个异常
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        
        UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal();
        User user = loginUser.getUser();

        String jwt = JwtUtil.createJWT(user.getId().toString());

        Map<String, String> map = new HashMap<>();
        map.put("error_message", "success");
        map.put("token", jwt);

        return map;
    }
}

controller.user.account.LoginController

密码需要用密文形式传输,而 get 请求是明文传输,因此 用post 请求,同时要记得把 "/user/account/token/"url 放行。

这里传入的参数就是前端传过来的参数,以 map 形式传入,接口传入的参数需要把对应的 key 抽出来再传入。

记得加上 @RestController 注解。

java 复制代码
@RestController
public class LoginController {

    @Autowired
    private LoginService loginService;

    @PostMapping("/user/account/token/")
    public Map<String, String> getToken(@RequestParam Map<String, String> map) {
        String username = map.get("username");
        String password = map.get("password");
        return loginService.getToken(username, password);
    }
}

在前端中可以对 /user/account/token/ 进行调试,resp 就是 LoginServiceImpl 中返回的 Map<String, String>,在前端浏览器中的控制台可以查看结果。

javascript 复制代码
  $.ajax({
    url: "http://localhost:3000/user/account/token/",
    type: "post",
    data: {
      username: "user_1",
      password: "p1",
    },
    success(resp) {
      console.log(resp);
    },
    error(resp) {
      console.log(resp);
    }
  });
4.1.2 /user/account/info/ /user/account/register/

另外两个接口 API 的流程就不再赘述了,过程是一样的,只介绍一下具体逻辑 ServiceImpl 的实现,Controller 中的请求为 Get, Post 也要记得区分,修改删除添加一般都是 Post,获取一般是 Get

service.impl.InfoServiceImpl

这边根据 token 取出 user 的代码直接背过(实现是基于之前 3.3 节的配置),之后但凡要取出 user 直接复制这段代码。

java 复制代码
@Service
public class InfoServiceImpl implements InfoService {
    @Override
    public Map<String, String> getinfo() {
        UsernamePasswordAuthenticationToken authentication =
        (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        UserDetailsImpl loginUser = (UserDetailsImpl) authentication.getPrincipal();
        User user = loginUser.getUser();
        
        Map<String, String> res = new HashMap<>();
        res.put("error_message", "success");
        res.put("id", user.getId().toString());
        res.put("username", user.getUsername());
        
        return res;
    }
}

前端测试代码,需要在请求报文的表头带上自己的 jwt_tokentoken 根据第一个 api 获取:

这边是需要登录之后得到 jwt_token 才能获取 user,也就是说需要授权,因此需要加上报文头 headersjwt_token 头部的内容在 config.filter.JwtAuthenticationTokenFilter 中定义,以此验证 jwt_token

javascript 复制代码
    $.ajax({
  	  url: "http://localhost:3000/user/account/info/",
      type: "get",
      headers: {
        Authorization: "Bearer " + "<jwt_token>",
      },
      success(resp) {
        console.log(resp);
      },
      error(resp) {
        console.log(resp);
      }
    });

对于 /user/account/register/ 注册的实现,需要判断用户名和密码是否合法,用户名是否已存在,两次密码是否一致等等,最后再把用户添加到数据库,由于密码的加密,数据库的查询和添加等操作在前面已经介绍过了,请大家尝试自行实现以验证是否理解之前内容。

4.2 用户登录模块的前端

4.2.1 登录页面和 Login 函数及全局变量 user 的定义与使用

用户注册和登录的前端页面的实现(html)就不介绍了,自行去 Bootstrap 搜索喜欢的样式改一改就行。

登陆之后需要记录当前用户 user,这里介绍一下全局变量 user 的定义和使用,可以使用 vuex 使用全局变量。

store.index

需要在 index.js 中把 user 导入到 modules

javascript 复制代码
import MoudleUser from './user'

export default createStore({
  /* ... */
  modules: {
    user: MoudleUser,
  }
})

store.user

简单的来说 state 定义 user 的属性,mutations 定义更新 state 属性的函数,actions 定义需要修改 state 属性的相关函数。

javascript 复制代码
import $ from 'jquery'

export default {
    state: {
        id: "",
        username: "",
        photo: "",
        token: "",
        is_login: false,
    },
    getters: {
    },
    mutations: {
        updateUser(state, user) {
            state.username = user.username;
            state.id = user.id;
            state.photo = user.photo;
            state.is_login = user.is_login;
        },
        updateToken(state, token) {
            state.token = token;
        }
    },
    actions: {
        login(context, data) {
            $.ajax({
                url: "http://localhost:3000/user/account/token/",
                type: "post",
                data: {
                    username: data.username,
                    password: data.password,
                },
                success(resp) {
                    if (resp.error_message == "success") {
                        context.commit("updateToken", resp.token);
                        data.success(resp);
                    } else {
                        data.error(resp);
                    }
                },
                error(resp) {
                    data.error(resp);
                }
            });
        }
    },
    modules: {
    }
}

接下来就需要在登录的时候调用 login 函数即可,介绍一下如何调用和传入参数。

views.user.account.UserAccountLoginView

以下仅展示核心部分代码,首先表单提交时要绑定触发 login 函数(并不是 store 中的 login),另外要将变量 ref 和输入框 input 进行绑定,报错信息通过 {``{ error_message }} 绑定。

在自定义的表单提交后触发的 login 中要调用 store.useraction 的函数 store.dispatch("<函数名>, data"),这样之后就能获得用户的 jwt_token,在成功登录之后需要跳转到主页面。

修改 ref 变量时要记得通过 .value 修改值。

javascript 复制代码
<form @submit.prevent = "login">
    <div class="mb-3">
      <label for="username" class="form-label">用户名</label>
      <input v-model = "username" type="text" id="username" class="form-control" placeholder="输入用户名">
    </div>
    <div class="mb-3">
      <label for="password" class="form-label">密码</label>
      <input v-model = "password" type="password" id="password" class="form-control" placeholder="...">
    </div>

     <div class="error-message">{{ error_message }}</div>

     <button type="submit" class="btn btn-primary">提交</button>
</form>


<script>
import { useStore } from 'vuex'
import { ref } from 'vue'
import router from '@/router/index'
    
export default {
    setup() {
        const store = useStore();
        let username = ref('');
        let password = ref('');
        let error_message = ref('');

        const login = () => {
            error_message.value = "";
            store.dispatch("login", {
                username: username.value,
                password: password.value,
                success() {
                    router.push({ name: 'home' });
                },
                error() {
                    error_message.value = "用户名或密码错误";
                }
            });
        };

        return {
            username,
            password,
            error_message,
            login
        }
    }
}
</script>
4.2.2 获取 user 信息和退出功能

store.useraction 中实现函数 getinfo,在登录成功之后获取用户信息(在 login 成功后调用 getinfo)。

...resp 作用是解析 key, value 到当前对象。

javascript 复制代码
getinfo(context, data) {
	$.ajax({
		url: "http://localhost:3000/user/account/info/",
		type: "get",
		headers: {
			Authorization: "Bearer " + context.state.token,
		},
		success(resp) {
			if (resp.error_message === "success") {
				context.commit("updateUser", {
					...resp,
					is_login: true,
				});
				data.success(resp);
			} else {
				data.error(resp);
            }
        },
        error(resp) {
            data.error(resp);
        }
	});
}
javascript 复制代码
const login = () => {
	error_message.value = "";
	store.dispatch("login", {
		username: username.value,
		password: password.value,
		success() {
			store.dispatch("getinfo", {
				success() {
					console.log(store.state.user);
					router.push({ name: 'home' });
				},
				error(resp) {
					console.log(resp);
				}
			});
		},
		error() {
			error_message.value = "用户名或密码错误";
		}
	});
}

之后可以更改导航栏信息 NavBar,如果已登录则显示用户名,否则显示登录和注册按钮。

html 复制代码
<ul class="navbar-nav" v-if = "$store.state.user.is_login">
  <li class="nav-item dropdown">
    <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" 
       aria-expanded="false">
 	  {{ $store.state.user.username }}
	</a>
      
    <ul class="dropdown-menu">
	  <router-link class="dropdown-item" :to = "{name: 'user_bot_index'}">Bots</router-link>

	  <li><hr class="dropdown-divider"></li>
                      
      <li><a class="dropdown-item" href="#">退出</a></li>
	</ul>
  </li>
</ul>

<ul class="navbar-nav" v-else>
  <li class="nav-item">
	<router-link class = "nav-link" :to = "{name: 'user_account_login'}" role="button">
	  登录
	</router-link>
  </li>

  <li class="nav-item">
    <router-link class = "nav-link" :to = "{name: 'user_account_register'}" role="button">
	  注册
 	</router-link>
  </li>
</ul>        

之后实现一下 logout 退出功能,和之前的流程一样,只需要把所有 state 清空即可,请自行实现,验证之前的内容是否理解掌握。注册页面的实现同样也不介绍了,都是一样的流程,请自行实现。

4.3 前端页面授权

当用户未登录时,如果访问授权页面则重定向到登录页面,强制要求用户登录,在 router 中实现该功能。

beforeEach 是进到每个页面之前要执行的函数。

javascript 复制代码
import store from "../store/index"

const routes = [
  {
    path: '/',
    name: 'home',
    redirect: '/pk/',
    meta: {
      requestAuth: true,
    }
  },
  /* ... */
  {
    path: '/:catchAll(.*)',
    redirect: "/404/"
  },
]

router.beforeEach((to, from, next) => {
  if (to.meta.requestAuth && !store.state.user.is_login) {
    next({ name: "user_account_login" });
  } else {
    next();
  }
});

4.4 登录状态持久化及小问题处理

每次刷新的时候登录状态都会重置为"未登录状态",这是因为 jwt_token 是存储在内存中,因此要把 token 存储在浏览器的 localstore 硬盘中。

首先修改 store.user 中的登录和退出函数,分别将 jwt_token 在本地保存和删除。

javascript 复制代码
login(context, data) {
	/* ... */
	success(resp) {
		if (resp.error_message === "success") {
			localStorage.setItem("jwt_token", resp.token);
			context.commit("updateToken", resp.token);
			data.success(resp);
			} else {
				data.error(resp);
			}
		},
	});
},
logout(context) {
	localStorage.removeItem("jwt_token");
	context.commit("logout");
}

之后每次进入到登录页面时,先判断本地是否存在 jwt_token,如果存在再判断 token 是否已经过期(调用 state.dispatch("getInfo"),如果成功则说明未过期),如果未过期则跳转到首页,不需要重新登录。

至此,就实现了登录状态的持久化,每次刷新都会跳转到首页,并且保持登录状态。细心的朋友会发现,每次刷新都会闪一下登录页面,视觉效果不太好,因此 user 中再维护一个全局变量 is_pulling_info,通过这个变量设置默认先不让登录页面的组件显示 v-if = "$store.state.user.is_pulling_info",如果未登录再显示。

登录页面添加以下代码:

javascript 复制代码
const jwt_token = localStorage.getItem("jwt_token");
if (jwt_token) {
	store.commit("updateToken", jwt_token);
	store.dispatch("getinfo", {
		success() {
			router.push({ name: "home" });
			store.commit("updatePullingInfo", false);
		},
		error() {
			store.commit("updatePullingInfo", false);
		},
	});
} else {
	store.commit("updatePullingInfo", false);
	console.log(store.state.user.is_pulling_info);
}

5 配置MySQL

5.1 MySQL的基本结构

MySQL 中包含多个数据库 databases,每个 database 含有多个表 tables (每张表 table 可以理解成 Java 中的 class 类)。

MySQL 级别相关操作:

启动 MySQL (需要管理员权限): net start mysql80

链接 Mysql: mysql -u root -p

以下操作仅做简单介绍,在实际写项目的时候由于是使用 MyBatis-plus,因此不会用到这些 SQL 语句。

database 级别相关操作:

创建数据库 create database kob_db; 删除数据库 drop database kob_db; 进入数据库 use kob_db;

查看 MySQL 中所有数据库 show databases;

table 级别相关操作: (要先进入需要的 database)

创建表 create table user(id int, username varchar(100), password varchar(100));

删除表 drop table user; 查看当前数据库中所有表 show tables;

row 级别相关操作:

增: insert into user values(1, 'yukiii', '123'); 删: delete from user where id = 1;

查: select * from user; select password from user where id = 1;

5.2 IDEA中配置MySQL

IDEA 中配置 MySQL 数据库 kob_db 的步骤如下:

  1. MySQL 中选择要连接的数据库
  1. 填写用户,密码,选择要连接到 kob_db 数据库,架构选择默认架构
  1. 测试连接,成功后则 IDEA 中配置成功

5.3 Spring使用MySQL需要的依赖

打开Maven仓库地址,在搜索栏搜索要装的依赖,再将依赖复制到 pom.xml<dependencies>

需要安装的依赖: Spring Boot Starter JDBC, Project Lombok, MySQL Connector/J, mybatis-plus-boot-starter, mybatis-plus-generator,依赖解析的会比较久

pom.xml 添加完后,会发现报红,需要在右侧的 Maven 选项中重新加载所有依赖项。

SpringBoot 在访问 MySQL 时也需要用户名和密码,在 resources/application.properties 添加以下代码:

用户名和密码填写自己的 MySQL 用户,url 中换成 MySQL 端口和数据库名。

java 复制代码
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/kob_db?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

5.4 图形化使用MySQL

对某个表中记录 CRUD 操作都在图形化界面中提供,每个操作之后都要记得点击上传,更新到数据库中。

进行修改的操作,在右侧右键要修改的表可以跳出相应界面。

相关推荐
苏三说技术3 小时前
xxl-job 和 elastic-job,哪个更好?
后端
xkxnq3 小时前
第五阶段:Vue3核心深度深挖(第74天)(Vue3计算属性进阶)
前端·javascript·vue.js
hlABgYML3 小时前
基于NGSIM数据的Wiedemann99跟驰模型标定
mysql
三小河3 小时前
Agent Skill与Rules的区别——以Cursor为例
前端·javascript·后端
Hilaku3 小时前
不要在简历上写精通 Vue3?来自面试官的真实劝退
前端·javascript·vue.js
三小河3 小时前
前端视角详解 Agent Skill
前端·javascript·后端
牛奔3 小时前
Go 是如何做抢占式调度的?
开发语言·后端·golang
Aniugel3 小时前
单点登录(SSO)系统
前端
颜酱3 小时前
二叉树遍历思维实战
javascript·后端·算法
鹏多多3 小时前
移动端H5项目,还需要react-fastclick解决300ms点击延迟吗?
前端·javascript·react.js