<从零学习javaWeb> 2. 数据表设计和三层架构

数据表创建、Mybatis、三层架构

前置

经过后端项目初始化后,有以下两个步骤:

  1. 我们需要对数据表进行设计
  2. 并且以三层架构的形式整理后端目录
  3. 生成数据表对应的实体对象、mapper和service
  4. 实现一个简单的注册逻辑,包括账号密码的校验、密码加密和写表

数据表设计

方法

  • IEAD中数据库创建
  • sql语句创建

知识点

  • 主键用primary key表示
  • 数据类型一般有int、tinyint、bigint、datatime、varchar
    • 其中tinyint为1字节、int为4字节、bigint为8字节
    • varchar以实际字符大小来存储,相比text节省空间
  • not null 表示非空、default设置缺省值、comment设置字段含义

三层架构

一次后端的请求可以分成三个部分:提取数据、数据处理和响应请求。后端三层架构将其解耦成三层,分别是Dao层(Mapper层)、service层和controller层,方便代码的解耦、复用和维护。

知识点

  • 对应的注解比较多:@RestController、@RequestMapping、@Autowired、@Resource等

实现基本数据库操作

步骤

实现数据库操作,分成几个步骤:

  1. 生成实体对象,即一个表对应一个实体类,表的字段对应类成员
  2. 生成mapper层接口和xml,对应数据库的访问接口,xml中写具体的sql
  3. 生成service层接口和实现,对应增上改查和数据处理

方法:Mybatis

  1. 在IDEA中连接数据库,指定一个表可以用MybatisX-Generator,自动生成对应的代码框架,然后将generator中的文件到包中,如图:

  2. 对Mybatis生成的代码进行测试,在UserService接口用alt+enter自动创建测试:

    java 复制代码
    package com.lzj.usercenter.service;
    import java.util.Date;
    
    import com.lzj.usercenter.domain.User;
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    /**
     * 用户服务测试
     *
     * @author:lzj
     */
    import javax.annotation.Resource;
    
    @SpringBootTest
    class UserServiceTest {
    
        @Resource
        private UserService userService;
    
        @Test
        void testAddUser(){
            User user = new User();
            user.setUsername("lzj");
            user.setUserAccount("lzj");
            user.setAvatarUrl("https://portrait.gitee.com/uploads/avatars/user/3031/9094632_DXdaxia_1620607641.png!avatar30");
            user.setGender(0);
            user.setUserPassword("");
            user.setPhone("111111");
            user.setEmail("222222@qq.com");
            user.setUserStatus(0);
            user.setCreateTime(new Date());
            user.setUpdateTime(new Date());
            user.setIsDelete(0);
            user.setUserRole(0);
            user.setPlanetCode("");
    
            boolean result = userService.save(user);
            System.out.println(user.getId());
            Assertions.assertEquals(true, result);
        }
    }

遇到的坑

  1. 驼峰命名自动转换问题

    java 复制代码
    org.springframework.jdbc.BadSqlGrammarException: 
    ### Error updating database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column 'user_account' in 'field list'
    ### The error may exist in com/lzj/usercenter/mapper/UserMapper.java (best guess)
    ### The error may involve com.lzj.usercenter.mapper.UserMapper.insert-Inline
    ### The error occurred while setting parameters
    ### SQL: INSERT INTO user  ( username, user_account, avatar_url, gender, user_password, phone, email, user_status, create_time, update_time, is_delete, user_role, planet_code )  VALUES  ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
    ### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'user_account' in 'field list'
    ; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Unknown column 'user_account' in 'field list'

    原因:mybatis-plus会自动将驼峰字段转换成下划线,如果字段是驼峰命名,实体类也是驼峰,则需要关闭自动转换,在application.yml加入配置:

    yml 复制代码
    mybatis-plus:
      configuration:
        map-underscore-to-camel-case: false
  2. yml配置问题

    java 复制代码
    java.lang.IllegalStateException: Failed to load ApplicationContext
    
     at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98)
     at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
     at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
     at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
     at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)
     at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
     at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$8(ClassBasedTestDescriptor.java:363)
     at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:368)
     at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$9(ClassBasedTestDescriptor.java:363)
     at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
     at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
     at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
     at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
     at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
     at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313)
     at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
     at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:742)
     at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
     at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:362)
     at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:283)
     at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
     at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:282)
     at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:272)

    原因:这个报错表示依赖注入有问题,产生的原因有好几种,最难找到的就是yml中缩进问题(发现1.中的缩进少了一格),需要检查刚刚修改的yml配置的缩进有没有问题。

实现简单的后端注册校验接口

注册校验在前后端都需要做,后端根据规则去校验,并将密码加密存入数据库。

  1. 在UserService接口加入一个账户校验接口
java 复制代码
public interface UserService extends IService<User> {

    /**
     *
     * @param userAccount 用户账号
     * @param userPassword 用户密码
     * @param checkPassword 校验码
     * @return 新用户id
     */
    long userRegister(String userAccount, String userPassword, String checkPassword);
}
  1. 在UserServiceImpl实现该方法
java 复制代码
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{


    private final String SALT = "lzj";
    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword) {
        //校验
        if(StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)){
            return -1;
        }
        if(userAccount.length() < 4){
            return -1;
        }
        if(userPassword.length() < 8 || checkPassword.length() < 8){
            return -1;
        }
        //账号不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        long count = this.count(queryWrapper);
        if(count > 0 ){
            return -1;
        }
        //账户不能包含特殊字符
        String validRule = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%...... &*()------+|{}【】';:""'。,、?]";
        Matcher matcher = Pattern.compile(validRule).matcher(userAccount);
        if(matcher.find()){
            return -1;
        }
        //密码需要与校验密码相同
        if(!userPassword.equals(checkPassword)){
            return -1;
        }
        // 2.对密码进行加密(密码千万不要直接以明文存储到数据库中)
        String verifyPassword = DigestUtils.md5DigestAsHex((SALT +userPassword).getBytes(StandardCharsets.UTF_8));

        //保存
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(verifyPassword);
        boolean saveResult = this.save(user);
        if(!saveResult){
            return -1;
        }
        return user.getId();
    }
}
  1. 生成该方法的测试样例
java 复制代码
@Test
void userRegister() {
    String userAccount = "lzj33";
    String userPassword = "123456789";
    String checkPassword = "12345";
    long result = userService.userRegister(userAccount, userPassword, checkPassword);
    Assertions.assertEquals(-1,result);
    userAccount = "lzj33";
    userPassword = "123456789";
    checkPassword = "123456789";
    result = userService.userRegister(userAccount, userPassword, checkPassword);
    System.out.println(result);
    User user = userService.getById(result);
    System.out.println(user);

}

Tips

  • 加Apache Common Lang的库,它实现的StringUtils.isAnyBlank可以放入多个参数,方便同时对多个字符串校验
  • 加密可以用md5,加SALT更随机
  • 特殊字符用正则表达式过滤
相关推荐
不能放弃治疗2 小时前
第 29 章 - ES 源码篇 - 网络 IO 模型及其实现概述
后端·elasticsearch
颜淡慕潇4 小时前
【K8S问题系列 | 21 】K8S中如果PV处于Bound状态,如何删除?【已解决】
后端·云原生·容器·kubernetes·pv
SomeB1oody4 小时前
【Rust自学】7.6. 将模块拆分为不同文件
开发语言·后端·rust
赛博末影猫4 小时前
SpringBoot(Ⅱ-2)——,SpringBoot版本控制,自动装配原理补充(源码),自动导包原理补充(源码),run方法
java·spring boot·后端
光岳楼观景4 小时前
Springboot -- JSON
spring boot·后端·json
掘金酱5 小时前
稀土掘金社区2024年度影响力榜单正式公布
android·前端·后端
qq_2518364576 小时前
asp.net 高校学生勤工俭学系统设计与实现
开发语言·数据库·后端·学习·asp.net
孙尚香蕉6 小时前
Spring IOC详解:掌握控制反转的核心原理与实践应用
java·后端·spring
SomeB1oody8 小时前
【Rust自学】8.2. Vector + Enum的应用
开发语言·后端·rust