javaweb,springboot项目生成路径

前置知识

拦截器和过滤器区别:

过滤器

init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。

doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。

destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次

@Component
public class MyFilter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        System.out.println("Filter 前置");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("Filter 处理中");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

        System.out.println("Filter 后置");
    }
}

拦截器 (Interceptor)
链式调用,一个应用中可以同时存在多个拦截器Interceptor, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。

preHandle() :这个方法将在请求处理之前进行调用。注意:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。

postHandle():只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。 有意思的是:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。

afterCompletion():只有在 preHandle() 方法返回值为true 时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。

@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("Interceptor 前置");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        System.out.println("Interceptor 处理中");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        System.out.println("Interceptor 后置");
    }
}

将自定义好的拦截器处理类进行注册,并通过addPathPatterns、excludePathPatterns等属性设置需要拦截或需要排除的 URL。

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
    }
}

拦截器 or 过滤器

在这里插入图片描述

  • SpringBoot
  • SpringSecurity
  • Servlet

Servlet 过滤器

Filter

  • 依靠tomcat容器
  • 是基于函数回调
  • 请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后
  • 容器启动它就存在了

SpringBoot 拦截器

设置拦截器HandlerInterceptor

package net.pro.myvue.common.securtiy.component;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * @author yangpan
 */
@Component
public class MyInterceptor implements HandlerInterceptor {


    private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        String methodName = method.getName();
        logger.info("====拦截到了方法:{},在该方法执行之前执行====", methodName); // 返回true才会继续执行,返回false则取消当前请求
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("执行完方法之后进执行(Controller方法调用之后),但是此时还没进行视图渲染,也就是会返回空白");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("整个请求都处理完咯,DispatcherServlet也渲染了对应的视图咯,此时我可 以做一些清理的工作了");
    }
}

然后在配置拦截器WebMvcConfigurationSupport

package net.pro.myvue.common.securtiy.component;

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.WebMvcConfigurationSupport;


/**
 * @author yangpan
 */
@Configuration
public class InterceptorConf extends WebMvcConfigurationSupport {

    @Autowired
    MyInterceptor myInterceptor;

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).addPathPatterns("/**");
    }
}

OncePerRequestFilter

json的返回值

前言

以往都是SpringMVC+Mybatis+Spring+Thymeleaf的结合通过Controller的界面跳转和数据的传输,以及前端的数据表单的传输以及Ajax请求来实现数据的交互。

现在是Springboot+Vue+Mybatis来实现前后端的分离,后端只需要单一开发业务逻辑返回一个Json的数据即可,和界面的跳转一点也不沾边了,而前端也不需要完全依靠后端,只需要后端给的数据进行渲染就可以了,两者互不干涉。

以下都是为建议,并不是强制的

  1. 命名
  2. 位置
  3. 形式
  4. 代码

命名

              这个最好写成XXXDTO,而且是范型的类,如ResponseDTO,表明它是用于响应的传输数据类型

位置

src                               源码目录
|-- common                       各个项目的通用类库
|-- |-- domain | --ResponseDTO      ?           
|-- config                            项目的配置信息
|-- constant                          全局公共常量
|-- handler                           全局处理器
|-- interceptor                       全局连接器
|-- listener                          全局监听器
|-- module                            各个业务
|-- |--- employee                         员工模块
|-- |--- role                             角色模块
|-- |--- login                            登录模块
|-- third                             三方服务,比如redis, oss,微信sdk等等
|-- util                              全局工具类
|-- Application.java 

形式

代码

/**
 1. 返回状态
 2.  3. @author yangpan
 */
@Setter
@Getter
public class ResponseDTO<T> {
    /**
     * 状态码
     */
    protected Integer code;
    /**
     * 返回消息
     */
    protected String msg;

    /**
     * 是否成功
     */
    protected Boolean success;
    /**
     * 返回数据
     */
    protected T data;

    /**
     * 自定义
     *
     * @param code
     * @param msg
     * @param success
     * @param data
     */
    public ResponseDTO(Integer code, String msg, Boolean success, T data) {
        this.code = code;
        this.msg = msg;
        this.success = success;
        this.data = data;
    }

    /**
     * 错误返回信息
     * 
     * @param msg
     * @param success
     */
    public ResponseDTO(String msg, Boolean success) {
        this.code = 0001;
        this.msg = "用户错误";
        this.success = success;
    }

    /**
     * 没有数据返回成功
     */
    public ResponseDTO() {
        this.code = 0000;
        this.msg = "操作成功!";
    }
    ..............可自行扩展
    
    @Override
    public String toString() {
        return "ResponseDTO{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", success=" + success +
                ", data=" + data +
                '}';
    }
}

SpringSecurity

SpringSecurity入门到精通传送门

SpringSecurity精通传送门

SpringBoot结合流程
在这里插入图片描述

密码加密放入数据库

  @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }
   ====>返回的是一个加密对象

   @Autowired
    PasswordEncoder e;
  
    void encoder(){
        final String pwd = e.encode("所要加密密码");
        final boolean b = e.matches(pwd, "还未加密前的密码");
    }

根据b判断密码是否正确

SpringSecurity本质是一些拦截器的组合

前置知识

AuthenticationEntryPoint(类)

AuthenticationEntryPoint是Spring Security Web一个概念模型接口,顾名思义,他所建模的概念是:“认证入口点”。
它在用户请求处理过程中遇到认证异常时,被ExceptionTranslationFilter用于开启特定认证方案(authentication schema)的认证流程,

通俗一点就是你没认证就想访问资源就会被拦截

@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        StringBuffer requestURL = request.getRequestURL();
        PrintWriter writer = response.getWriter();
        RespBean error = RespBean.error("您暂未登录,请先登录");
        error.setCode(401);
        writer.write(new ObjectMapper().writeValueAsString(error));
        writer.flush();
        writer.close();
    }
}

AccessDeniedHandler(类)

当访问接口没有权限时,自定义返回结果,可以设置SpringSecurity认证失败了要返回的结果

@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter writer = response.getWriter();
        RespBean error = RespBean.error("您的权限不足");
        error.setCode(403);
        writer.write(new ObjectMapper().writeValueAsString(error));
        writer.flush();
        writer.close();
    }
}

正文

  1. 依赖
  2. 位置
  3. 代码

依赖

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
   </dependency>

位置

src                               源码目录
|-- common                       各个项目的通用类库
|-- |-- domain | --ResponseDTO  数据转化类型                       
|-- |--security |-- component   安全主件
                     |-- SecurityConfig  ?
|-- config                            项目的配置信息
|-- constant                          全局公共常量
|-- handler                           全局处理器
|-- interceptor                       全局连接器
|-- listener                          全局监听器
|-- module                            各个业务
|-- |--- employee                         员工模块
|-- |--- role                             角色模块
|-- |--- login                            登录模块
|-- third                             三方服务,比如redis, oss,微信sdk等等
|-- util                              全局工具类
|-- Application.java 

代码

1.1 SecurityConfig
1.2 CustUserDetailsImpl

SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //自定义的数据信息
    @Autowired
    CustUserDetailsImpl userDetails;

    //这个是用于数据密码解密的,这个必须配置不然SpringSecurity会报错
    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

    //http安全
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable();
    }

    //这个是解密的
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetails).passwordEncoder(encoder());
    }

    //直接跳过认证
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/success",
                "/session/login",
                "/swagger-ui.html",            //swagger放行
                "/logout",
                "/session/verificationCode",  //放行验证码
                "/css/**",                 ---|
                "favicon.ico",             ---|--->//静态资源   
                "/js/**",                  ---|
                "/index.html",          
                "/doc.html",
                "/webjars/**",
                "/swagger-resources/**",
                "/v2/api-docs/**",
                "/ws/**");
    }
}

CustUserDetailsImpl

@Component
public class CustUserDetailsImpl  implements UserDetailsService{


    @Autowired
    LoginDao dao;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        assert s != null;

        final UserLoginDTO login = dao.login(s);

        if (login == null || login.equals("")) {
            throw new UsernameNotFoundException("用户名不存在");
        }

        return new User(login.getLoginName(), login.getLoginPwd(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));

    }
}

PostMan

当用这个修饰@RequstBody,只能接受JSON格式

   @PostMapping("/session/login")
    @ApiOperation(value = "创建用户", notes = "创建用户")
    public ResponseDTO<Map<String, Object>> login(@Validated @RequestBody UserLoginDTO userLoginDTO, HttpServletRequest request) {
        final ResponseDTO<Map<String, Object>> login = loginService.login(userLoginDTO, request);
        return login;
    }

如果需要传验证码需要先得到验证码
在这里插入图片描述
得到的就是接下来我们要说的Base64验证码
在这里插入图片描述
将它粘贴到浏览器
在这里插入图片描述
即可得到验证码,把它算出来
在这里插入图片描述
然后咱就访问,记住要符合我的箭头写的

在这里插入图片描述
获得结果
在这里插入图片描述

验证码

依赖

<!--图形验证码-->
       <dependency>
            <groupId>com.github.whvcse</groupId>
            <artifactId>easy-captcha</artifactId>
            <version>1.6.2</version>
      </dependency>

这个是别人写好的,直接用就可以了

java代码:
CaptchaUtil

/**
 * 验证码工具类
 *
 * @author yangpan
 */
public class CaptchaUtil {

    /**
     * 验证验证码
     *
     * @param code
     * @param code2
     * @return
     */
    public static boolean verify(String code, String code2) {
        if (code != null) {
            return code.trim().toLowerCase().equals(code2);
        }
        return false;
    }

    /**
     * 设置相应头
     *
     * @param response HttpServletResponse
     */
    public static void setHeader(HttpServletResponse response) {
        response.setContentType("image/gif");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
    }

    /**
     * 清除验证码的session
     *
     * @param request
     */
    public static void clear(HttpServletRequest request) {
        request.getSession().removeAttribute(CaptchaConstant.CAPTCHA_KEY);
    }

}

获取验证码服务工具

 public String verificationCode(HttpServletRequest request, HttpServletResponse response) {
        //设置返回的响应头
        CaptchaUtil.setHeader(response);
        //这个是生成验证码的api,由以上的依赖包提供,这个是算数的api
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(115, 40);
        //设置算数的位数
        captcha.setLen(2);
        //验证码的答案,返回结果
        String captchaResult = captcha.text();
        //将其存放在Session中,以便验证验证码的时候取出验证
        final HttpSession session = request.getSession();
        session.setAttribute(CaptchaConstant.CAPTCHA_KEY, captchaResult);
        //把验证码转化为Base64数据,直接在前端用<img src="s"/>即可展示
        final String s = captcha.toBase64();
        return s;
    }

获取验证码

  @GetMapping("/session/verificationCode")
    @ApiOperation(value = "获取验证码", notes = "获取验证码")
    public String verificationCode(HttpServletRequest request, HttpServletResponse response) {
        final String s = loginService.verificationCode(request, response);
        return s;
    }

在这里插入图片描述

Jwt

依赖

  <!--jjwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

          application.yaml中配置

############################# jwt ########################################
jwt:
  # JWT存储的请求头
  tokenHeader: Authorization
  # JWT 加解密使用的密钥
  secret: secret
  # JWT的超期限时间 (60*60*24)
  expiration: 20
  # JWT负载拿到开头
  tokenHead: Bearer

        Jwt工具类JWTTokenUtil

package net.pro.myvue.common.securtiy.component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 *
 * @author yangpan
 */
@Component
public class JwtTokenUtil {


    /**
     * 用户名的key
     */
    private static final String CLAIM_KEY_USERNAME = "sub";

    /**
     * jwt的创建时间的key
     */
    private static final String CLAIM_KEY_CREATED = "created";


    /**
     * 盐
     */
    @Value("${jwt.secret}")
    private String secret;

    /**
     * 失效时间
     */
    @Value("${jwt.expiration}")
    private Long expiration;


    /**
     * 根据用户信息生成token
     *
     * @param userDetails
     * @return
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>(20);
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /**
     * 根据荷载生成jwt token
     *
     * @param claims
     * @return
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 从token中获取登陆用户名
     *
     * @param token
     * @return
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getclaimsfromtoken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 从token中获取荷载
     *
     * @param token
     * @return
     */
    private Claims getclaimsfromtoken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return claims;
    }

    /**
     * 生成token失效时间
     *
     * @return
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }


    /**
     * 从token中获取过期时间
     * @param token
     * @return
     */
    private Date getExpiredDateFromToken(String token) {
        Claims claims = getclaimsfromtoken(token);
        return claims.getExpiration();
    }

    /**
     * 判断token是否失效
     * @param token
     * @return
     */
    private boolean isTokenExpired(String token) {
        Date expireDate = getExpiredDateFromToken(token);
        return expireDate.before(new Date());
    }

    /**
     * 验证token是否有效
     * @param token
     * @param userDetails
     * @return
     */
    public boolean validateToken(String token,UserDetails userDetails){
        String username = getUsernameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }
}

jwt配置一个拦截器认证
JwtAuthorizationTokenFilter

package net.pro.myvue.common.securtiy.component;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author yangpan
 */
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Resource
    JwtTokenUtil jwtTokenUtil;

    @Resource
    CustUserDetailsImpl custUserDetails;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        final String authHeader = request.getHeader(tokenHeader);
        if(null != authHeader && authHeader.startsWith(tokenHead)){
            String authToken = authHeader.substring(tokenHead.length());
            String username = jwtTokenUtil.getUsernameFromToken(authToken);
            //token存在用户名但是未登录
            if(null != username && null == SecurityContextHolder.getContext().getAuthentication()){
                //执行登录
                UserDetails userDetails = custUserDetails.loadUserByUsername(username);
                //验证token是否有效,重新给用户对象赋值
                if(jwtTokenUtil.validateToken(authToken,userDetails)){
                    UsernamePasswordAuthenticationToken authenticationToken =
                            new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
    }

Mysql

角色 or 权限

     之前没有分清他们之间的区别,一直认为,只要其中一个就可以了为什么要配置两种~

     一个角色没有权限是毫无用处的,打比方就是老师这个角色如果没有管理学生的权限,那老师这个角色不就废了嘛
  
 ,相反有权限但是没有角色理论上是可以的,你并不是老师但是有管理学生的权限,但是复用性很差,如果一旦这个权

限系统复杂起来了,等到那天你退休了,你所掌握的权限可能需要重新的分配给新的人,使得权限分配过于混乱,所以

赋予角色权限而不是直接用户个人权限可以提高复用性,降低复杂度。    

总结:

用户 => 角色 => 权限


后台管理数据库建立

表的建立

t_employee

   首先建立员工表用于登陆,然后通过t_role_employee查询得到员工的角色

CREATE TABLE `t_employee` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `login_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录帐号',
  `login_pwd` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录密码',
  `actual_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '员工名称',
  `nick_name` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '别名',
  `phone` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '手机号码',
  `id_card` varchar(18) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '身份证',
  `birthday` date DEFAULT NULL COMMENT '出生日期',
  `email` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮箱',
  `department_id` int unsigned NOT NULL COMMENT '部门id',
  `is_leave` int NOT NULL DEFAULT '0' COMMENT '是否离职1是',
  `is_disabled` int NOT NULL DEFAULT '0' COMMENT '是否被禁用 0否1是',
  `remark` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  `create_user` int unsigned NOT NULL COMMENT '创建者id',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `is_delete` int NOT NULL DEFAULT '0' COMMENT '是否删除0否 1是',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工表';

   员工和角色之间的关系表

t_role_employee

CREATE TABLE `t_role_employee` (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_id` int NOT NULL COMMENT '角色id',
  `employee_id` int NOT NULL COMMENT '员工id',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=214 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色员工功能表';

通过sql语句

SELECT re.id FROM t_role_employee re WHERE re.employee_id = #{employee_id};

即可与员工建立关系

t_employee => t_role_employee


真正的角色表,用于员工的角色认证
t_role

CREATE TABLE `t_role` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `role_name` varchar(20) NOT NULL COMMENT '角色名称',
  `remark` varchar(255) DEFAULT NULL COMMENT '角色描述',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8mb3 COMMENT='角色表';

角色权限表对应表
t_role_privilege

CREATE TABLE `t_role_privilege` (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_id` int NOT NULL COMMENT '角色id',
  `privilege_key` varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限key',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11121 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色权限功能表';

权限表
t_privilege

CREATE TABLE `t_privilege` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '功能权限表主键id',
  `type` tinyint NOT NULL COMMENT '1.菜单 2.功能点',
  `name` varchar(50) NOT NULL COMMENT '菜单名称',
  `key` varchar(1000) NOT NULL COMMENT '路由name 英文关键字',
  `url` text COMMENT '路由path/type=3为API接口',
  `sort` int NOT NULL DEFAULT '0' COMMENT '排序',
  `parent_key` varchar(1000) DEFAULT NULL COMMENT '父级key',
  `update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `key` (`key`) USING BTREE,
  KEY `type` (`type`) USING BTREE,
  KEY `parent_key` (`parent_key`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8mb3 COMMENT='权限功能表';

t_position

CREATE TABLE `t_position` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `position_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '岗位名称',
  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '岗位描述',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='岗位表';

关于DTO,VO,DO,QueryParam

DTO(Data Transfer Object): 包命名为dto

可以理解为被包装的传输数据

VO(View Object) :包命名为vo

给前端所展示的数据

mybatis-plus

自动生成代码

mybatis-plus有两种方法生成,一种是基于插件的,一种是基于java代码的

基于插件

将下列的代码填在pom.xml中

       <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <configuration>
                    <configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>
                    <overwrite>true</overwrite>
                    <verbose>true</verbose>
                </configuration>
           </plugin>

当然generatorConfig.xml你可以自己定义名字和位置

然后你就会在项目的右边的maven中看见
在这里插入图片描述
然后在resources下配置generatorConfig.xml

generatorConfig.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- 数据库驱动:选择你的本地硬盘上面的数据库驱动包-->
    <classPathEntry  location="C:\Users\yangpan\.m2\repository\mysql\mysql-connector-java\8.0.18\mysql-connector-java-8.0.18.jar"/>
    <context id="DB2Tables"  targetRuntime="MyBatis3">
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--数据库链接URL,用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost/demo?serverTimezone=UTC" userId="root" password="8874520*">
        </jdbcConnection>
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!-- 生成模型的包名和位置-->
        <javaModelGenerator targetPackage="cn.yp.demo.dao" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- 生成映射文件的包名和位置-->
        <sqlMapGenerator targetPackage="cn.yp.demo.dao2" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!-- 生成DAO的包名和位置-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="cn.yp.demo.dao2" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
<!--  要生成的表 tableName是数据库中的表名或视图名 如果要生成多个表则要多个table标签    domainObjectName是实体类名&ndash;&gt;-->
        <table tableName="student" domainObjectName="Person" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>
    </context>
</generatorConfiguration>

接下来什么都不用管了,直接生成了,什么依赖包都不用引入
缺点:只能单表的简单操作,如果复杂点可以自行在xml中配置

基于Java配置的

依赖

         <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
         <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.4</version>
         </dependency>
            
         <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.0</version>
        </dependency>
            
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>

java代码

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

public class CodeGenerator {
   public static void main(String[] args) {

       AutoGenerator autoGenerator = new AutoGenerator();
       DataSourceConfig dataSourceConfig = new DataSourceConfig();
       dataSourceConfig.setDbType(DbType.MYSQL);
       dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
       dataSourceConfig.setUsername("root");
       dataSourceConfig.setPassword("root");
       dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/auto_office?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");
       autoGenerator.setDataSource(dataSourceConfig);
       GlobalConfig globalConfig = new GlobalConfig();
       globalConfig.setOpen(true); // 代码生成后打开目录
       globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java");
       globalConfig.setAuthor("mjdai");
//        globalConfig.setIdType(IdType.ASSIGN_ID);// id 主键策略
//        globalConfig.setDateType(DateType.ONLY_DATE); // 定义生成的实体类中日期类型
       globalConfig.setSwagger2(true);// 开启Swaggers模式
       globalConfig.setBaseResultMap(true);
       globalConfig.setBaseColumnList(true);
       globalConfig.setServiceName("%sService");
       autoGenerator.setGlobalConfig(globalConfig);
       PackageConfig packageConfig = new PackageConfig();
       packageConfig.setParent("org.mjdai");
       packageConfig.setEntity("pojo");
       packageConfig.setMapper("mapper");
       packageConfig.setController("controller");
       packageConfig.setService("service");
       packageConfig.setServiceImpl("service.impl");
       autoGenerator.setPackageInfo(packageConfig);
       StrategyConfig strategyConfig = new StrategyConfig();

//        strategyConfig.setInclude("t_admin"); // 生成单表写法
        strategyConfig.setInclude("user","product"); // 生成多张表写法。生成所有表,不用配置
       strategyConfig.setTablePrefix("t"+"_"); // 去表前缀 t,根据实际情况填写
       strategyConfig.setEntityLombokModel(true);
       strategyConfig.setNaming(NamingStrategy.underline_to_camel);
       strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);

       autoGenerator.setStrategy(strategyConfig);

       autoGenerator.execute();
   }
}

点击运行即可
第二个方法和其他的方法不一样的是这个不用在mapper的xml中配置,

why?

因为

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author qhling
 * @since 2021-12-06
 */
@Service
public class MsgServiceImpl extends ServiceImpl<MsgMapper, Msg> implements MsgService {

}


这个是服务自动生成的实现类,可以看见它自动的继承了ServiceImpl这个类

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author qhling
 * @since 2021-12-06
 */
public interface MsgService extends IService<Msg> {

}




/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author qhling
 * @since 2021-12-06
 */
public interface MsgMapper extends BaseMapper<Msg> {

}

这个mapper继承了BaseMapper,是实现增删改查的抽象类

最后要在启动器中扫描Mapper

数据库和javaBean的映射

1.类型对比

数据库类型	             Java 类型
CHAR	                  String
VARCHAR	                  String
LONGVARCHAR	              String
NUMERIC	                  java.math.BigDecimal
DECIMAL	                  java.math.BigDecimal
BIT	                      boolean
TINYINT	                  byte
SMALLINT	              short
INTEGER	                  int
BIGINT	                  long
REAL	                  float
FLOAT	                  double
DOUBLE	                  double
BINARY	                  byte[]
VARBINARY	              byte[]
LONGVARBINARY	          byte[]
DATE	                  java.sql.Date
TIME	                  java.sql.Time
TIMESTAMP	              java.sql.Timestamp

Springboot+Mybatis

待写

SpringBoot+SpringMVC

待写

Tips

接口回调


接口回调是指:可以把使用实现了某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称为对象功能的接口回调.

缺省


即系统默认状态,意思与“默认”相同。缺省是一种计算机术语,指在无决策者干预情况下,对于决策或应用软件、计算机程序的系统参数的自动选择。默认选项的设计可以在用户不须决策的状况下就可以基础地使用上述的软件与程序。“缺省”最初来源于计算机英文文档中的单词"default""default”有很多意思:违约、缺省、拖欠、默认,由于当时计算机方面的翻译水平不高,于是就把这个词直译成了“缺省”,其实应该取它的引申意思“默认”。
           只有沉下心,冷静下来才有可能~
  • 16
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值