SpringBoot写一个登陆注册功能,和期间走的坑

前言

   最近在做一个关于SpringBoot的项目,首先从最简单的注册登陆开始,从前端到后端。

1. 首先介绍项目的相关技术和工具:

开发工具使用IDEA,技术使用SpringBoot2.1.3+Mybatis+Jpa+mysql,项目中主要使用Mybatis,jpa只做了demo,实体转换使用的是mapstruct,集成了swagger文档配置,redis缓存demo。

2. 首先创建项目

两种方式:1、直接在IDEA中file–>new–>project,选择spring Initalizr创建一个springBoot项目。
在这里插入图片描述
2、或者直接在spring的官网创建一个springboot项目springBoot官网创建项目

3. 项目的结构

首先项目的结构和普通的spring项目是一样的,采用controller、service、dao三层
项目结构
接下来项目采用逆向介绍:
实体—>Mapper.xml—>Mapper.inteface–>service–>controller—>html
项目中的一些实体和mybatis的代码,采用mybatis generator逆向生成。
如果逆向生成不太懂,请自行百度了解或点击Mybatis-Generator之最完美配置详解

3.1实体类:

@Table(name = "user_info")
@Entity
public class UserInfo extends BaseEntity{

    @Id
    @Column(name = "USER_ID")
    private Long userId;

    @Column(name = "USER_NICK_NAME")
    private String userNickName;

    @Column(name = "USER_SUR_NAME")
    private String userSurName;

    @Column(name = "USER_NAME")
    private String userName;

    @Column(name = "USER_DESC")
    private String userDesc;

    @Column(name = "USER_SEX")
    private Boolean userSex;

    @Column(name = "USER_PHONE")
    private String userPhone;

    @Column(name = "USER_EMAIL")
    private String userEmail;

    @Column(name = "USER_HOME")
    private String userHome;

    @Column(name = "USER_BIRTHDAY")
    private Date userBirthday;

    @Column(name = "USER_REGISTER_DATE")
    private Date userRegisterDate;

    @Column(name = "USER_LEVEL")
    private Long userLevel;

    @Column(name = "PASS_WORD")
    private String passWord;

    @Column(name = "LOGIN_NUM")
    private Long loginNum;
    GetSet方法省略

由于项目中集成了Jpa所以实体类上有表和列的注解。

3.2 Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.mybatis.UserInfoMapper">
  <resultMap id="BaseResultMap" type="com.example.demo.entity.UserInfo">
    <id column="USER_ID" jdbcType="BIGINT" property="userId" />
    <result column="USER_NICK_NAME" jdbcType="VARCHAR" property="userNickName" />
    <result column="USER_SUR_NAME" jdbcType="VARCHAR" property="userSurName" />
    <result column="USER_NAME" jdbcType="VARCHAR" property="userName" />
    <result column="USER_DESC" jdbcType="VARCHAR" property="userDesc" />
    <result column="USER_SEX" jdbcType="BIT" property="userSex" />
    <result column="USER_PHONE" jdbcType="VARCHAR" property="userPhone" />
    <result column="USER_EMAIL" jdbcType="VARCHAR" property="userEmail" />
    <result column="USER_HOME" jdbcType="VARCHAR" property="userHome" />
    <result column="USER_BIRTHDAY" jdbcType="TIMESTAMP" property="userBirthday" />
    <result column="USER_REGISTER_DATE" jdbcType="TIMESTAMP" property="userRegisterDate" />
    <result column="CREATE_DATE" jdbcType="TIMESTAMP" property="createDate" />
    <result column="UPDATE_DATE" jdbcType="TIMESTAMP" property="updateDate" />
    <result column="STATUS_CD" jdbcType="DECIMAL" property="statusCd" />
    <result column="STATUS_DATE" jdbcType="TIMESTAMP" property="statusDate" />
    <result column="REMARK" jdbcType="VARCHAR" property="remark" />
    <result column="USER_LEVEL" jdbcType="BIGINT" property="userLevel" />
    <result column="PASS_WORD" jdbcType="VARCHAR" property="passWord" />
    <result column="LOGIN_NUM" jdbcType="BIGINT" property="loginNum" />
  </resultMap>
  <sql id="Base_Column_List">
    USER_ID, USER_NICK_NAME, USER_SUR_NAME, USER_NAME, USER_DESC, USER_SEX, USER_PHONE, 
    USER_EMAIL, USER_HOME, USER_BIRTHDAY, USER_REGISTER_DATE, CREATE_DATE, UPDATE_DATE, 
    STATUS_CD, STATUS_DATE, REMARK, USER_LEVEL, PASS_WORD, LOGIN_NUM
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from user_info
    where USER_ID = #{userId,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    delete from user_info
    where USER_ID = #{userId,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.example.demo.entity.UserInfo">
    insert into user_info (USER_ID, USER_NICK_NAME, USER_SUR_NAME, 
      USER_NAME, USER_DESC, USER_SEX, 
      USER_PHONE, USER_EMAIL, USER_HOME, 
      USER_BIRTHDAY, USER_REGISTER_DATE, CREATE_DATE, 
      UPDATE_DATE, STATUS_CD, STATUS_DATE, 
      REMARK, USER_LEVEL, PASS_WORD, 
      LOGIN_NUM)
    values (#{userId,jdbcType=BIGINT}, #{userNickName,jdbcType=VARCHAR}, #{userSurName,jdbcType=VARCHAR}, 
      #{userName,jdbcType=VARCHAR}, #{userDesc,jdbcType=VARCHAR}, #{userSex,jdbcType=BIT}, 
      #{userPhone,jdbcType=VARCHAR}, #{userEmail,jdbcType=VARCHAR}, #{userHome,jdbcType=VARCHAR}, 
      #{userBirthday,jdbcType=TIMESTAMP}, #{userRegisterDate,jdbcType=TIMESTAMP}, #{createDate,jdbcType=TIMESTAMP}, 
      #{updateDate,jdbcType=TIMESTAMP}, #{statusCd,jdbcType=DECIMAL}, #{statusDate,jdbcType=TIMESTAMP}, 
      #{remark,jdbcType=VARCHAR}, #{userLevel,jdbcType=BIGINT}, #{passWord,jdbcType=VARCHAR}, 
      #{loginNum,jdbcType=BIGINT})
  </insert>
  <insert id="insertSelective" parameterType="com.example.demo.entity.UserInfo">
    insert into user_info
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        USER_ID,
      </if>
      <if test="userNickName != null">
        USER_NICK_NAME,
      </if>
      <if test="userSurName != null">
        USER_SUR_NAME,
      </if>
      <if test="userName != null">
        USER_NAME,
      </if>
      <if test="userDesc != null">
        USER_DESC,
      </if>
      <if test="userSex != null">
        USER_SEX,
      </if>
      <if test="userPhone != null">
        USER_PHONE,
      </if>
      <if test="userEmail != null">
        USER_EMAIL,
      </if>
      <if test="userHome != null">
        USER_HOME,
      </if>
      <if test="userBirthday != null">
        USER_BIRTHDAY,
      </if>
      <if test="userRegisterDate != null">
        USER_REGISTER_DATE,
      </if>
      <if test="createDate != null">
        CREATE_DATE,
      </if>
      <if test="updateDate != null">
        UPDATE_DATE,
      </if>
      <if test="statusCd != null">
        STATUS_CD,
      </if>
      <if test="statusDate != null">
        STATUS_DATE,
      </if>
      <if test="remark != null">
        REMARK,
      </if>
      <if test="userLevel != null">
        USER_LEVEL,
      </if>
      <if test="passWord != null">
        PASS_WORD,
      </if>
      <if test="loginNum != null">
        LOGIN_NUM,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        #{userId,jdbcType=BIGINT},
      </if>
      <if test="userNickName != null">
        #{userNickName,jdbcType=VARCHAR},
      </if>
      <if test="userSurName != null">
        #{userSurName,jdbcType=VARCHAR},
      </if>
      <if test="userName != null">
        #{userName,jdbcType=VARCHAR},
      </if>
      <if test="userDesc != null">
        #{userDesc,jdbcType=VARCHAR},
      </if>
      <if test="userSex != null">
        #{userSex,jdbcType=BIT},
      </if>
      <if test="userPhone != null">
        #{userPhone,jdbcType=VARCHAR},
      </if>
      <if test="userEmail != null">
        #{userEmail,jdbcType=VARCHAR},
      </if>
      <if test="userHome != null">
        #{userHome,jdbcType=VARCHAR},
      </if>
      <if test="userBirthday != null">
        #{userBirthday,jdbcType=TIMESTAMP},
      </if>
      <if test="userRegisterDate != null">
        #{userRegisterDate,jdbcType=TIMESTAMP},
      </if>
      <if test="createDate != null">
        #{createDate,jdbcType=TIMESTAMP},
      </if>
      <if test="updateDate != null">
        #{updateDate,jdbcType=TIMESTAMP},
      </if>
      <if test="statusCd != null">
        #{statusCd,jdbcType=DECIMAL},
      </if>
      <if test="statusDate != null">
        #{statusDate,jdbcType=TIMESTAMP},
      </if>
      <if test="remark != null">
        #{remark,jdbcType=VARCHAR},
      </if>
      <if test="userLevel != null">
        #{userLevel,jdbcType=BIGINT},
      </if>
      <if test="passWord != null">
        #{passWord,jdbcType=VARCHAR},
      </if>
      <if test="loginNum != null">
        #{loginNum,jdbcType=BIGINT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.example.demo.entity.UserInfo">
    update user_info
    <set>
      <if test="userNickName != null">
        USER_NICK_NAME = #{userNickName,jdbcType=VARCHAR},
      </if>
      <if test="userSurName != null">
        USER_SUR_NAME = #{userSurName,jdbcType=VARCHAR},
      </if>
      <if test="userName != null">
        USER_NAME = #{userName,jdbcType=VARCHAR},
      </if>
      <if test="userDesc != null">
        USER_DESC = #{userDesc,jdbcType=VARCHAR},
      </if>
      <if test="userSex != null">
        USER_SEX = #{userSex,jdbcType=BIT},
      </if>
      <if test="userPhone != null">
        USER_PHONE = #{userPhone,jdbcType=VARCHAR},
      </if>
      <if test="userEmail != null">
        USER_EMAIL = #{userEmail,jdbcType=VARCHAR},
      </if>
      <if test="userHome != null">
        USER_HOME = #{userHome,jdbcType=VARCHAR},
      </if>
      <if test="userBirthday != null">
        USER_BIRTHDAY = #{userBirthday,jdbcType=TIMESTAMP},
      </if>
      <if test="userRegisterDate != null">
        USER_REGISTER_DATE = #{userRegisterDate,jdbcType=TIMESTAMP},
      </if>
      <if test="createDate != null">
        CREATE_DATE = #{createDate,jdbcType=TIMESTAMP},
      </if>
      <if test="updateDate != null">
        UPDATE_DATE = #{updateDate,jdbcType=TIMESTAMP},
      </if>
      <if test="statusCd != null">
        STATUS_CD = #{statusCd,jdbcType=DECIMAL},
      </if>
      <if test="statusDate != null">
        STATUS_DATE = #{statusDate,jdbcType=TIMESTAMP},
      </if>
      <if test="remark != null">
        REMARK = #{remark,jdbcType=VARCHAR},
      </if>
      <if test="userLevel != null">
        USER_LEVEL = #{userLevel,jdbcType=BIGINT},
      </if>
      <if test="passWord != null">
        PASS_WORD = #{passWord,jdbcType=VARCHAR},
      </if>
      <if test="loginNum != null">
        LOGIN_NUM = #{loginNum,jdbcType=BIGINT},
      </if>
    </set>
    where USER_ID = #{userId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.example.demo.entity.UserInfo">
    update user_info
    set USER_NICK_NAME = #{userNickName,jdbcType=VARCHAR},
      USER_SUR_NAME = #{userSurName,jdbcType=VARCHAR},
      USER_NAME = #{userName,jdbcType=VARCHAR},
      USER_DESC = #{userDesc,jdbcType=VARCHAR},
      USER_SEX = #{userSex,jdbcType=BIT},
      USER_PHONE = #{userPhone,jdbcType=VARCHAR},
      USER_EMAIL = #{userEmail,jdbcType=VARCHAR},
      USER_HOME = #{userHome,jdbcType=VARCHAR},
      USER_BIRTHDAY = #{userBirthday,jdbcType=TIMESTAMP},
      USER_REGISTER_DATE = #{userRegisterDate,jdbcType=TIMESTAMP},
      CREATE_DATE = #{createDate,jdbcType=TIMESTAMP},
      UPDATE_DATE = #{updateDate,jdbcType=TIMESTAMP},
      STATUS_CD = #{statusCd,jdbcType=DECIMAL},
      STATUS_DATE = #{statusDate,jdbcType=TIMESTAMP},
      REMARK = #{remark,jdbcType=VARCHAR},
      USER_LEVEL = #{userLevel,jdbcType=BIGINT},
      PASS_WORD = #{passWord,jdbcType=VARCHAR},
      LOGIN_NUM = #{loginNum,jdbcType=BIGINT}
    where USER_ID = #{userId,jdbcType=BIGINT}
  </update>
  <select id="findByExample" parameterType="com.example.demo.entity.UserInfo"
          resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from user_info
    <where>
    <if test="userNickName != null">
      AND USER_NICK_NAME = #{userNickName,jdbcType=VARCHAR}
    </if>
    <if test="userSurName != null">
      AND USER_SUR_NAME = #{userSurName,jdbcType=VARCHAR}
    </if>
    <if test="userName != null">
      AND USER_NAME = #{userName,jdbcType=VARCHAR}
    </if>
    <if test="userDesc != null">
      AND USER_DESC = #{userDesc,jdbcType=VARCHAR}
    </if>
    <if test="userSex != null">
      AND USER_SEX = #{userSex,jdbcType=BIT}
    </if>
    <if test="userPhone != null">
      AND USER_PHONE = #{userPhone,jdbcType=VARCHAR}
    </if>
    <if test="userEmail != null">
      AND USER_EMAIL = #{userEmail,jdbcType=VARCHAR}
    </if>
    <if test="userHome != null">
      AND USER_HOME = #{userHome,jdbcType=VARCHAR}
    </if>
    <if test="userBirthday != null">
      AND USER_BIRTHDAY = #{userBirthday,jdbcType=TIMESTAMP}
    </if>
    <if test="userRegisterDate != null">
      AND USER_REGISTER_DATE = #{userRegisterDate,jdbcType=TIMESTAMP}
    </if>
    <if test="createDate != null">
      AND CREATE_DATE = #{createDate,jdbcType=TIMESTAMP}
    </if>
    <if test="updateDate != null">
      AND UPDATE_DATE = #{updateDate,jdbcType=TIMESTAMP}
    </if>
    <if test="statusCd != null">
      AND STATUS_CD = #{statusCd,jdbcType=DECIMAL}
    </if>
    <if test="statusDate != null">
      AND STATUS_DATE = #{statusDate,jdbcType=TIMESTAMP}
    </if>
    <if test="remark != null">
      AND REMARK = #{remark,jdbcType=VARCHAR}
    </if>
    <if test="userLevel != null">
      AND USER_LEVEL = #{userLevel,jdbcType=BIGINT}
    </if>
    <if test="passWord != null">
      AND PASS_WORD = #{passWord,jdbcType=VARCHAR}
    </if>
    <if test="loginNum != null">
      AND LOGIN_NUM <![CDATA[ > ]]> #{loginNum,jdbcType=BIGINT}
    </if>
    </where>
  </select>
</mapper>

关于这个xml多说一句,在代码运行期间遇到的坑:如果数据库表字段和实体的类型匹配不到,在结果返回的时候使用resultType是不能将结果正常的返回出来的,我遇到的情况是查询出数据条数为1,结果的list的size的确是1,但是里面却是null,这个就是因为字段类型不匹配,导致的数据没有被填充到指定的实体中,这个时候应该使用resultMap对表字段和实体字段进行指定。这样就能正常的返回数据了。

3.3 mapper.inteface

@Mapper
public interface UserInfoMapper{
    int deleteByPrimaryKey(Long userId);

    int insert(UserInfo record);

    int insertSelective(UserInfo record);

    UserInfo selectByPrimaryKey(Long userId);

    int updateByPrimaryKeySelective(UserInfo record);

    int updateByPrimaryKey(UserInfo record);

    List<UserInfo> findByExample(UserInfo userInfo);
}

这里说一句注意点,这个接口是用来和xml文件进行交互的,xml文件你可以理解为是当前接口的实现类。注意点1:当前接口需要加注解@Mapper,将当前类交由spring管理。
注意点2:如果接口中的方法参数是多个(>1)的时候需要对参数加@Param进行名称的指定,这里使用注解指定的名称就是在xml中取值使用的名称而不是参数的名称,eg:String find(@Param(“aa”) int bb),如果想在xml中取到参数bb对应的值,那么就应该获取名称为aa的变量,${aa}或者#{aa}。如果方法的参数是一个或者没有,那么@Param注解可加可不加。

3.4 Service

项目中采用接口+实现类的形式:
实现类ServiceImpl:

@Service("userServiceImpl")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Override
    public UserInfo findByName(String name) {
        return userDao.findByUserNickName(name);
    }

    @Override
    public List<UserInfo> findAll() {
        return userDao.findAll();
    }

    @Override
    public UserInfo findOne(Long id) {
        return userDao.findById(id).isPresent()?userDao.findById(id).get():new UserInfo();
    }

    @Override
    public UserInfo save(UserInfo UserInfo) {
        return userDao.save(UserInfo);
    }

    @Override
    public UserInfo saveAndUpdate(UserInfo UserInfo) {
        return userDao.saveAndFlush(UserInfo);
    }

    @Override
    public void deleteUser(long id) {
        userDao.deleteById(id);
    }

    @Override
    public UserInfo findById(long id){
        return userInfoMapper.selectByPrimaryKey(id);
    }

    @Override
    public int insert(UserInfo userInfo) {

        //用户注册的时候只有用户名和密码,所以为用户设置必填的一些字段,
        //主键
        userInfo.setUserId(KeyUtils.UUID());
        Date nowDate = new Date();
        //注册时间、创建时间
        userInfo.setUserRegisterDate(nowDate);
        userInfo.setCreateDate(nowDate);
        //剩余登陆次数,默认5次
        userInfo.setLoginNum(L_FIVE);
        //性别,若没有则设置为true,true表示男,数据库对应的是1
        if(StringUtils.isEmpty(userInfo.getUserSex())){
            userInfo.setUserSex(true);
        }
        //用户姓氏取用户名的第一个字符
        userInfo.setUserSurName(userInfo.getUserNickName().substring(I_ZERO, I_ONE));
        userInfo.setUserName(userInfo.getUserNickName().substring(I_ONE));

        //用户状态默认有效
        userInfo.setStatusCd(StatusCdEnum.ACTIVE_ENUM.getCode());
        //用户等级
        userInfo.setUserLevel(L_ONE);
        //用户密码默认采用MD5进行加密
        userInfo.setPassWord(EncryptUtil.getInstance().MD5(userInfo.getPassWord()));

        return userInfoMapper.insert(userInfo);
    }

    @Override
    @Transactional
    public UserEnum login(UserInfo userInfo) {
        String userNickName = userInfo.getUserNickName();
        String passWord = userInfo.getPassWord();
        //用户名或密码为空,直接返回
        if(StringUtils.isEmpty(userNickName) || StringUtils.isEmpty(passWord)){
            return LOGIN_USER_PWD_NULL;
        }

        //登陆的步骤:
        //1、首先根据用户名查询数据库有效的数据且次数大于0的是否存在,若用户名不存在则直接返回,若用户名存在则继续下面的操作
        UserInfo ui = new UserInfo();
        ui.setStatusCd(StatusCdEnum.ACTIVE_ENUM.getCode());
        ui.setLoginNum(L_ZERO);
        ui.setUserNickName(userNickName);
        List<UserInfo> userInfos = userInfoMapper.findByExample(ui);
        if(CollectionUtils.isEmpty(userInfos)){
            return LOGIN_USER_NULL;
        }
        //2、对比查询出的密码,密码不正确修改对应的数据的登陆次数
        for (UserInfo info : userInfos) {
            //如果密码不相等,比较次数
            if(!EncryptUtil.getInstance().MD5(passWord).equals(info.getPassWord())){
                //密码不相等,首先将该账号的登陆次数减一
                UserInfo u = new UserInfo();
                u.setUserId(info.getUserId());
                u.setLoginNum(info.getLoginNum()-L_ONE);
                //次数>1表示当前用户此次失败后不会被锁定
                if(info.getLoginNum() > L_ONE){
                    int i = userInfoMapper.updateByPrimaryKeySelective(u);
                    if (i>I_ZERO) {
                        LOGIN_USER_ERROR.setNum(String.valueOf(info.getLoginNum()-L_ONE));
                        return LOGIN_USER_ERROR;
                    }
                }else{
                    //次数<1表示此次失败后,账号将被锁定
                    u.setStatusCd(StatusCdEnum.FROZEN_ENUM.getCode());
                    u.setStatusDate(new Date());
                    int i = userInfoMapper.updateByPrimaryKeySelective(u);
                    if(i>I_ZERO){
                        return LOGIN_USER_NUM;
                    }
                }
            }
        }

        //根据用户名和密码查询数据库
        return UserEnum.SUCCESS;
    }
}

实现类中主要对登陆和注册的相关逻辑操作,这里说下相关逻辑:

注册:界面中只输入用户名和密码,在业务层将必填的字段进行补全,密码进行加密,状态为有效,登陆次数为默认5次,注册时间为当前时间等。
登陆:根据用户名进行数据库的查询(有效的、登陆次数>1),如果查不到,报错返回,如果查到数据则判断查到的密码和输入的密码是否相等,如果相等,通过,如果不相等,则判断登陆次数是否>1,如果大于1则将剩余的次数报错(用户名或密码不正确,剩余次数*次)返回,如果<1则报错返回错误信息(该用户失败次数已达5次,请次日再试)。

接口Service:

public interface UserService {
    /**
     * 根据名称查询user对象
     * @param name 名称
     * @return user对象
     */
    UserInfo findByName(String name);

    List<UserInfo> findAll();

    UserInfo findOne(Long id);

    UserInfo save(UserInfo UserInfo);

    UserInfo saveAndUpdate(UserInfo UserInfo);

    void deleteUser(long id);
    UserInfo findById(long id);

    int insert(UserInfo userInfo);

    UserEnum login(UserInfo userInfo);
}

3.5 Controller

这个是业务的controller控制层,也没有什么复杂的逻辑,只需要注意一点就是,如果想要返回到页面中那么controller的注解不能使用@RestController,应该使用@Controller,如果只是返回数据则无所谓。因为@RestController会将返回的结果转换为json串,所以不能返回到页面中。

@Api("用户表操作控制层")
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 根据名称查询
     * @param name 名称
     * @return 返回User对象
     */
    @GetMapping("/user/name")
    @ApiOperation("根据名称查询user对象,JPA版")
    public UserInfo findByName(@RequestParam("name") String name){
        return userService.findByName(name);
    }

    /**
     * 查询所有的user对象
     * @return user列表
     */
    @GetMapping("/user/all")
    @ApiOperation("查询所有的user对象,JPA版")
    public List<UserInfo> findAll(){
        return userService.findAll();
    }

    /**
     * 新增
     */
    @PostMapping("/user")
    @ApiOperation("新增user对象,JPA版")
    public UserInfo save(@RequestBody UserInfo userInfo){
        return userService.save(userInfo);
    }

    /**
     * 删除
     */
    @DeleteMapping("/TUserInfo/{id}")
    @ApiOperation("删除user对象,JPA版")
    public void delete(@PathVariable("id") long id){
        userService.deleteUser(id);
    }

    /**
     * 更新
     */
    @PutMapping("/user")
    @ApiOperation("更新user对象,JPA版")
    public UserInfo update(UserInfo userInfo){
        return userService.saveAndUpdate(userInfo);
    }

    /**
     * 通过Id查询
     */
    @RequestMapping("/user/{id}")
    @ApiOperation("查询user对象,JPA版")
    public UserInfo findOne(@PathVariable("id") Long id){
        return userService.findOne(id);
    }

    /**
     * 通过Id查询,MyBatis版
     */
    @GetMapping("/user/mybatis/{id}")
    @ApiOperation("根据Id查询UserInfo对象,Mybatis版")
    public UserInfo findById(@PathVariable Long id){
        return userService.findById(id);
    }

    /**
     * 新增,Mybatis版
     */
    @PostMapping("/user/mybatis")
    @ApiOperation("新增user对象,Mybatis版")
    public int insert(@RequestBody UserInfo userInfo){
        return userService.insert(userInfo);
    }

    @PostMapping("/register")
    @ApiOperation("用户注册")
    public ResultDTO userRegister(UserInfoDTO userInfoDTO){
        UserInfo userInfo = EntityToDTO.INIT.toUserInfo(userInfoDTO);
        int result = userService.insert(userInfo);
        if(result > 0){
            return ResultUtils.SUCCESS();
        }
        return ResultUtils.ERROR();
    }

    @PostMapping("/login")
    @ApiOperation("用户登陆")
    public ResultDTO userLogin(UserInfoDTO userInfoDTO){
        UserInfo userInfo = EntityToDTO.INIT.toUserInfo(userInfoDTO);
        UserEnum result = userService.login(userInfo);
        if(result == UserEnum.SUCCESS){
            return ResultUtils.SUCCESS();
        }
        return ResultUtils.ERROR(result.getCode(),result.getMsg(),result.getNum());
    }
}

这个是视图controller,专门用户视图的跳转

@Api("用于视图转换")
@Controller
@RequestMapping("/view")
public class ViewController {

    /**
     * 进入注册界面
     */
    @GetMapping("/register")
    @ApiOperation("进入用户注册")
    public String enterRegister(){
        System.out.println("进入注册界面...");
        return "index";
    }

}

没有什么复杂的逻辑,这里不赘述。

3.6 html页面

html页面 中采用ajax进行登录和注册的交互,具体的页面如下:

注册和登陆的代码相差无几,这里就只贴注册的代码。

$.ajax({
				type:"POST",
				url:"/user/register",
				data:{
					"userNickName": username,
					"passWord": password
				},
				dataType:"json",
				success:function(dataX){
					console.log("返回的code为:"+dataX.code);
				if(dataX.code === "000000"){
					$("#login-username").val(username);
					$("#login-password").val(password);
					//注册成功
					spop({
						template: '<h4 class="spop-title">注册成功</h4>即将于3秒后返回登录',
						position: 'top-center',
						style: 'success',
						autoclose: 3000,
						onOpen : function(){
							var second = 2;
							var showPop = setInterval(function(){
								if(second == 0){
									clearInterval(showPop);
								}
								$('.spop-body').html('<h4 class="spop-title">注册成功</h4>即将于'+second+'秒后返回登录');
								second--;
							},1000);
						},
						onClose : function(){
							goto_login();
						}
					});
				}else{
					alert("注册失败:" + dataX.msg)
				}
			},
			error:function(jqXHR){
				alert("注册异常:"+ jqXHR.statusText);
			}
		});

页面效果如下:
这个是登陆界面
这个是注册界面

3.7 yml文件配置

由于springBoot的结构是在resources目录下有专门的templates文件夹和static文件夹,
templates用于放置一些html页面,static放置一些静态资源,由于SpringBoot会默认访问resources下的这两个文件夹下的html和js、css…文件,关于html和静态资源需要在yml文件中进行路径的配置,否则会访问不到静态资源,这里本人就踩了不少坑。

spring:
  # 配置这个表示访问templates页面路径的前缀
  thymeleaf:
    prefix: classpath:/templates/
    # 访问静态资源的路径,可以是多个,表示请求的静态资源会查找的目录
  resources:
    static-locations:
      classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
  # 访问静态资源的请求方式,就是html请求静态资源的时候需要以static开头
  mvc:
    static-path-pattern: /static/**

3.8 POM文件依赖

这里引用的依赖大多都是直接从maven仓库中找的稳定的新的,需要的可自行去搜索查找,点我

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <swagger.version>2.9.2</swagger.version>
        <mybatis.version>2.0.0</mybatis.version>
        <fastjson.version>1.2.57</fastjson.version>
        <mapstruct.version>1.2.0.Final</mapstruct.version>
    </properties>

    <dependencies>
        <!-- springWeb端的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring JPA的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- mysql数据库的依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- redis依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- swagger依赖 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <!-- mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <!-- fastjson json转换工具类 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- mapstruct 转换类依赖 -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${mapstruct.version}</version>
            <scope>provided</scope>
        </dependency>
        <!--  springBoot的静态资源依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- 实现springBoot的热部署依赖 -->
        <!-- 实现热部署参考https://blog.csdn.net/qq_41700133/article/details/82224390 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <scope>true</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>

<!--        <finalName>xml</finalName>
        <sourceDirectory>src/main/java</sourceDirectory>
        <testSourceDirectory>src/test/java</testSourceDirectory>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>*</include>
                    <include>**/*</include>
                </includes>
                &lt;!&ndash; 去除资源文件 &ndash;&gt;
                <excludes>
                    <exclude>/generatorConfig.xml</exclude>
                </excludes>
            </resource>
        </resources>-->

        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

<!--            <plugin>
                &lt;!&ndash; 指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 &ndash;&gt;
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    &lt;!&ndash; 一般而言,target与source是保持一致的,但是,有时候为了让程序能在其他版本的jdk中运行(对于低版本目标jdk,源代码中不能使用低版本jdk中不支持的语法),会存在target不同于source的情况 &ndash;&gt;
                    <source>${java.version}</source> &lt;!&ndash; 源代码使用的JDK版本 &ndash;&gt;
                    <target>${java.version}</target> &lt;!&ndash; 需要生成的目标class文件的编译版本 &ndash;&gt;
                    <encoding>${project.build.sourceEncoding}}</encoding>&lt;!&ndash; 字符集编码 &ndash;&gt;
                    &lt;!&ndash;如果没有这个属性 springBoot 自动配置不会生效&ndash;&gt;
                    <fork>true</fork>
                </configuration>
            </plugin>-->

            <!-- maven指定不编译的java文件 -->
<!--            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <excludes>
                        <exclude>resources/generatorConfig.xml</exclude>
                    </excludes>
                </configuration>
            </plugin>-->

        </plugins>
    </build>

</project>

**至此从前端到后端使用springboot开发的登陆注册就完成了。**

4. 踩坑秘籍

4.1 xml文件返回结果是list的size为1,但是结果为null

如果数据库表字段和实体的类型匹配不到,在结果返回的时候使用resultType是不能将结果正常的返回出来的,我遇到的情况是查询出数据条数为1,结果的list的size的确是1,但是里面却是null,这个就是因为字段类型不匹配,导致的数据没有被填充到指定的实体中,这个时候应该使用resultMap对表字段和实体字段进行指定。这样就能正常的返回数据了。

4.2 Controller返回到html

如果想要返回到页面中那么controller的注解不能使用@RestController,应该使用@Controller,如果只是返回数据则无所谓。因为@RestController会将返回的结果转换为json串,所以不能返回到页面中。

4.3 关于静态资源的配置

由于springBoot的结构是在resources目录下有专门的templates文件夹和static文件夹,
templates用于放置一些html页面,static放置一些静态资源,由于SpringBoot会默认访问resources下的这两个文件夹下的html和js、css…文件,关于html和静态资源需要在yml文件中进行路径的配置,否则会访问不到静态资源,这里本人就踩了不少坑。

spring:
# 配置这个表示访问templates页面路径的前缀
thymeleaf:
  prefix: classpath:/templates/
  # 访问静态资源的路径,可以是多个,表示请求的静态资源会查找的目录
resources:
  static-locations:
    classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
# 访问静态资源的请求方式,就是html请求静态资源的时候需要以static开头
mvc:
  static-path-pattern: /static/**

4.4 Swagger 配置

swagger需要在pom文件先添加依赖,具体依赖看上面的介绍,然后自定义一个swagger配置类,就可以在Controller中使用了,配置类参考:

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket buildDocket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(buildApiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    public ApiInfo buildApiInfo(){
        return new ApiInfoBuilder()
                .title("SpringBoot配置Swagger文档API")
                .description("简单优雅的RestFun风格")
                .version("1.0")
                .build();
    }
}

4.5 mapstruct的配置和使用

mapstruct的配置我踩坑比较多,最初直接在在maven仓库中搜索找到了依赖直接放进去,写了接口类,发现编译后并不能自动生成实现类。
我在maven仓库下载的是core
core
代码执行过程中一直报错,后面在网上查找答案才发现,我引用错依赖了,应该引用下面两个:
在这里插入图片描述
添加完正确的引用后,写一个工具接口类就可以了:
在这里插入图片描述

代码下载地址

如果文章能够看懂,请自行编码,如实在完成不了再下载源码,编码注重的是自己敲代码的过程。下载地址点我

至此文章已全部结束,文章用来记录自己踩过的坑,同时希望能帮助一些人,文章篇幅过长,有问题希望大家指正。我们一起成长!!

原文地址:https://www.cnblogs.com/kaifaxiaoliu/p/11980116.html