• 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
    代码执行过程中一直报错,后面在网上查找答案才发现,我引用错依赖了,应该引用下面两个:
    在这里插入图片描述
    添加完正确的引用后,写一个工具接口类就可以了:
    在这里插入图片描述

    代码下载地址

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

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

  • 相关阅读:
    git 提交
    Git提交出现 Everything up-to-date
    vscode 记住git用户密码
    git 将本地项目关联到远程仓库
    ubuntu19.04 安装workbench
    修改vscode终端样式
    python3练习
    C#实现MJPEG服务器
    在WPF中使用AForge控件
    FtpWebRequest.UsePassive属性:设置FTP工作模式
  • 原文地址:https://www.cnblogs.com/kaifaxiaoliu/p/11980116.html
Copyright © 2020-2023  润新知