1. 项目分析
在开发整个项目之前,应该先分析所需要开发的项目!
建议:有时间的时候,学习一下《软件工程》。
首先,应该分析该项目中需要处理哪些类型的数据,例如,本项目中应该有:商品、商品类别、收藏、订单、购物车、用户、收货地址;
接下来,就需要确定一下以上数据的开发顺序,通常,应该先开发基础数据,先开发简单的或熟悉的数据!所以,以上数据的开发顺序应该是:用户 > 收货地址 > 商品类别 > 商品 > 收藏 > 购物车 > 订单。
如果数据之间存在1对多的关系,应该先处理“1”的数据,再处理“多”的数据。
然后,应该分析首个处理的数据的管理功能,也就是“用户”数据涉及哪些功能!包括:注册、登录、修改密码、修改资料、上传头像!
并规划以上功能的开发顺序,应该先开发基础功能,先开发简单的功能,通常还遵循增、查、删、改的顺序!所以,以上用户数据的管理功能的开发顺序应该是:注册 > 登录 > 修改密码 > 修改资料 > 上传头像。
最后,在处理每种数据之前,还应该先创建这种数据在数据库中的数据表,并在项目中创建这个数据对应的实体类!
并且,在处理每个功能时,应该遵循的开发顺序是:持久层 > 业务层 > 控制器层 > 前端页面。
2. 用户-创建数据表
首先,创建数据库:
CREATE DATABASE tedu_store;
并使用该数据库:
USE tedu_store;
然后创建数据表:
CREATE TABLE t_user (
uid INT AUTO_INCREMENT COMMENT '用户id',
username VARCHAR(20) NOT NULL UNIQUE COMMENT '用户名',
password CHAR(32) NOT NULL COMMENT '密码',
salt CHAR(36) COMMENT '盐值',
gender INT(1) COMMENT '性别:0-女,1-男',
phone VARCHAR(20) COMMENT '手机',
email VARCHAR(30) COMMENT '邮箱',
avatar VARCHAR(100) COMMENT '头像',
is_delete INT(1) COMMENT '是否删除,0-未删除,1-已删除',
created_user VARCHAR(20) COMMENT '创建人',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '最后修改人',
modified_time DATETIME COMMENT '最后修改时间',
PRIMARY KEY (uid)
) DEFAULT CHARSET=utf8mb4;
如果当前数据库并不支持utf8mb4
,就直接使用utf8
。
3. 用户-创建实体类
先在cn.tedu.store
下创建子级entity
包,并在这个包中先创建实体类的基类:
/**
* 实体类的基类
*/
abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = -3122958702938259476L;
private String createdUser;
private Date createdTime;
private String modifiedUser;
private Date modifiedTime;
// GET/SET/toString
}
然后,还是在entity
包中,创建用户数据的实体类:
/**
* 用户数据的实体类
*/
public class User extends BaseEntity {
private static final long serialVersionUID = -3302907460554699349L;
private Integer uid;
private String username;
private String password;
private String salt;
private Integer gender;
private String phone;
private String email;
private String avatar;
private Integer isDelete;
// SET/GET/基于uid的equals()和hashCode()/toString()
}
设计原则:
-
尽量使用更加严格的访问权限;
-
如果某个类的作用就是被继承,这个类应该是抽象类;
-
只用于封装属性的类,应该实现
Serializable
接口; -
实体类中都应该生成基于id(唯一标识)的
hashCode()
和equals()
方法。
4. 用户-注册-持久层
首先,应该在application.properties中配置数据库连接的信息:
spring.datasource.url=jdbc:mysql://localhost:3306/tedu_store?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
在src/main/resources下创建mappers文件夹,用于存放配置SQL语句的XML文件。
并在application.properties中配置即将使用的XML文件的位置:
mybatis.mapper-locations=classpath:mappers/*.xml
然后,在启动类的声明之前配置@MapperScan("cn.tedu.store.mapper")
,用于指令即将使用的MyBatis的接口文件的位置:
@SpringBootApplication
@MapperScan("cn.tedu.store.mapper")
public class StoreApplication {
public static void main(String[] args) {
SpringApplication.run(StoreApplication.class, args);
}
}
注意:如果没有正确的配置接口文件的位置,后续在自动装配接口对象(例如后续会使用到的userMapper
对象)时失败!
在正式编写代码之前,应该测试当前环境是否正常,所以,先启动整个项目,观察是否可以正常启动!然后,打开自带的测试类StoreApplicationTests
,执行其中的空测试方法contextLoads()
,观察是否可以正常通过单元测试!最后,在这个测试类,尝试获取数据库连接:
@Autowired
private DataSource dataSource;
@Test
public void getConnection() throws SQLException {
System.err.println(dataSource.getConnection());
}
接下来,应该完成“注册”时持久层的开发任务!首先,在cn.tedu.store.mapper
(刚才配置在启动类的@MapperScan
注解中的包名)包中创建UserMapper
接口,并在这个接口中添加抽象方法:
/**
* 处理用户数据的持久层接口
*/
public interface UserMapper {
/**
* 插入用户数据
* @param user 用户数据
* @return 受影响的行数,当插入成功时,可以从参数对象中获取自动递增的uid值
*/
Integer insert(User user);
/**
* 根据用户名查询用户数据详情
* @param username 用户名
* @return 匹配的用户数据,如果没有匹配的数据,则返回null
*/
User findByUsername(String username);
}
然后,在src/main/resources/mappers下复制粘贴得到UserMapper.xml文件,并在该文件中配置以上抽象方法对应的SQL语句:
<mapper namespace="cn.tedu.store.mapper.UserMapper">
<resultMap id="UserEntityMap"
type="cn.tedu.store.entity.User">
<id column="uid" property="uid" />
<result column="is_delete" property="isDelete" />
<result column="created_user" property="createdUser" />
<result column="created_time" property="createdTime" />
<result column="modified_user" property="modifiedUser" />
<result column="modified_time" property="modifiedTime" />
</resultMap>
<!-- 插入用户数据 -->
<!-- Integer insert(User user) -->
<insert id="insert"
useGeneratedKeys="true"
keyProperty="uid">
INSERT INTO t_user (
username, password,
salt, gender,
phone, email,
avatar, is_delete,
created_user, created_time,
modified_user, modified_time
) VALUES (
#{username}, #{password},
#{salt}, #{gender},
#{phone}, #{email},
#{avatar}, #{isDelete},
#{createdUser}, #{createdTime},
#{modifiedUser}, #{modifiedTime}
)
</insert>
<!-- 根据用户名查询用户数据详情 -->
<!-- User findByUsername(String username) -->
<select id="findByUsername"
resultMap="UserEntityMap">
SELECT
*
FROM
t_user
WHERE
username=#{username}
</select>
</mapper>
5. 用户-注册-业务层
6. 用户-注册-控制器层
7. 用户-注册-前端页面
8. 用户-登录-持久层
9. 用户-登录-业务层
10. 用户-登录-控制器层
11. 用户-登录-前端页面
-----------------------------
附1:关于VARCHAR与CHAR类型
VARCHAR和CHAR都是用于存储字符串的字段类型!并且,在设计数据表时,都必须显式的指定长度!
VARCHAR是变长的,即存储的字符串的长度不是固定的,只要不超出设置值,都是正确的;而CHAR是定长的,存储的字符串的长度也不允许超出设置值,但是,如果长度小于设置值,则会自动补上空格,以达到设置值!
假设某个字段设计为VARCHAR(20)
,如果存入的是Hello
,实际存储的也就是Hello
,如果字段是CHAR(20)
,也存Hello
,由于这个字符串只有5个字符,则会自动补上15个空格,以达到设计的20
的长度!
在数据库中,处理VARCHAR
时,默认会额外使用1个字节记录实际存入的字符数量!例如在VARCHAR(20)
中存入了Hello
,其实,数据库还另使用1个字节记录了5
(字符的数量),由于默认只使用1个字节记录字符数量,所以,默认情况下,只能存入255个字符,如果使用VARCHAR
时设计值及实际存入的字符串更长,数据库会自动改为使用2个字节来记录,则最多就可以表示65535,所以,最多存入的就是65535个字符!并且,不会使用更多的字节数来做记录,所以,最多也只能存65535个字符!
而使用CHAR(20)
时,是不会使用额外的字节去记录实际存入的字符数量的,因为设计值就是这个字符串的长度!
src/test/java下中类内容:
package cn.tedu.store;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.DigestUtils;
import cn.tedu.store.entity.User;
import cn.tedu.store.mapper.UserMapper;
@RunWith(SpringRunner.class)
@SpringBootTest
public class StoreApplicationTests {
@Test
public void contextLoads() {
}
@Autowired
private DataSource dataSource;
@Autowired
private UserMapper userMapper;
@Test
public void getConnection() throws SQLException {
System.err.println(dataSource.getConnection());
}
@Test
public void insert() {
User user=new User();
user.setUsername("2j2");
user.setPassword("123456");
user.setSalt("1151");
user.setGender(151);
user.setPhone("515151");
user.setEmail("151515@fbvt");
user.setAvatar("25615");
user.setIsDelete(115);
Integer row=userMapper.insert(user);
System.out.println(row);
}
@Test
public void findByUsername() {
String username="nlh";
User users=userMapper.findByUsername(username);
System.out.println(users);
}
@Test
public void md5() {
String password="1234";
String md5Password=DigestUtils.md5DigestAsHex(password.getBytes());
System.out.println(md5Password);
}
}