1. Spring 复习
Spring 主要是创建对象和管理对象的框架.
Spring 通过 DI 实现了 IoC.
Spring 能很大程度的实现解耦.
需要掌握 SET 方式注入属性的值.
需要理解自动装配.
需要掌握 Spring 表达式.
需要掌握 AOP(暂时没学).
2. Spring MVC 复习
Spring MVC 框架是解决了 V-C 交互的问题, 即: 服务器端如何接收客户端的请求, 并如何给予响应.
需要掌握如何接收请求参数.
需要掌握如何转发数据.
需要掌握转发与重定向.
需要掌握响应 JSON 数据.
需要掌握统一处理异常的做法.
需要掌握拦截器的使用.
3. MyBatis 复习
执行增删改的操作的方法应该返回 Integer, 表示受影响的行数;
执行查询方法的 < select > 节点必须配置 resultType 或 resultMap;
执行查询时如果列名与字段名不一致, 在查询时需要自定义别名, 以保证名称统一;
掌握 < resultMap > 的配置.
--------------------------------------
1. 项目开发流程
关于项目的开发, 首先应该确定需要处理的数据有哪些: 商品, 用户, 收藏, 订单, 购物车, 商品分类, 收货地址......
然后, 确定这些数据的开发, 管理的先后顺序, 因为某些数据是必须建立在其它数据基础之上的, 例如必须先有用户数据, 才可以有订单数据或收货地址数据, 另外, 不同的数据功能, 开发的难度也有差异, 应该尽可能的先开发简单的, 熟悉的数据功能, 然后再开发相对较难的数据功能, 所以, 以上数据的开发顺序大概可以是: 用户> 收货地址> 商品分类> 商品> 收藏> 购物车> 订单......
每种类型的数据的处理, 都应该遵循: 增> 查> 删> 改.
每个功能的处理, 应该: 持久层> 业务层> 控制器层> 界面.
核心原则: 一次只解决一个问题!
2. 用户 - 注册 - 持久层
关于持久层, 应该先检查有没有对应的数据库 / 表, 及对应的实体类.
关于数据库:
- CREATE DATABASE tedu_store;
- USE tedu_store;
关于数据表:
- CREATE TABLE t_user (
- uid INT AUTO_INCREMENT COMMENT '用户 id',
- username VARCHAR(20) UNIQUE NOT NULL COMMENT '用户名',
- password CHAR(32) NOT NULL COMMENT '密码',
- salt CHAR(36) COMMENT '盐',
- gender INT COMMENT '性别, 0 - 女, 1 - 男',
- avatar VARCHAR(50) COMMENT '头像',
- phone VARCHAR(20) COMMENT '手机号码',
- email VARCHAR(30) COMMENT '电子邮箱',
- is_delete INT 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=UTF8;
然后, 下载本次项目 store.zip, 解压到 Workspace 中, 通过 Import> Existing Maven Projects 导入项目.(可以在 spring boot 官网自己生成)
由于以上数据表中关于日志的 4 个字段是后续每张表都应该有的, 则后续的每张表对应的实体类中也应该有 4 个对应的属性, 所以, 应该创建实体类的基类来封装这 4 个字段对应的属性, 且, 当前项目中的所有实体类都应该继承自该基类:
- /**
- * 实体类的基类
- */
- public abstract class BaseEntity implements Serializable {
- private static final long serialVersionUID = -6185124879935579311L;
- private String createdUser;
- private Date createdTime;
- private String modifiedUser;
- private Date modifiedTime;
- // SET/GET ...
- }
创建与数据表对应的实体类 cn.tedu.store.entity.User:
- /**
- * 用户数据的实体类
- */
- public class User extends BaseEntity {
- private static final long serialVersionUID = 8777086855777796877L;
- private Integer uid;
- private String username;
- private String password;
- private String salt;
- private Integer gender;
- private String avatar;
- private String phone;
- private String email;
- private Integer isDelete;
- // SET/GET ...
- }
持久层的开发重点应该分为 3 个步骤:
1. 分析当前功能所需要执行的 SQL 语句
当前执行 "注册" 功能, 必然需要执行插入数据操作:
- INSERT INTO t_user (
- username, password ... modified_time
- ) VALUES (
- ?, ?, ... ?
- )
为了保证 "用户名唯一", 还应该有 "根据用户名查询数据" 的操作:
- SELECT
- uid
- FROM
- t_user
- WHERE
- username=?
2. 创建接口(如果必要的话), 并设计抽象方法
创建 cn.tedu.store.mapper.UserMapper 接口文件, 并在其中添加抽象方法:
- /**
- * 处理用户数据的持久层接口
- */
- public interface UserMapper {
- /**
- * 插入用户数据
- * @param user 用户数据
- * @return 受影响的行数
- */
- Integer addnew(User user);
- /**
- * 根据用户名查询用户信息
- * @param username 用户名
- * @return 匹配的用户数据, 如果没有匹配的数据, 则返回 null
- */
- User findByUsername(String username);
- }
3. 在 xml 中配置抽象方法的映射
在 src/main/resources 下创建 mappers 文件夹, 然后复制此前的项目得到 UserMapper.xml, 配置好该文件中根节点的 namespace 对应的接口, 然后, 再配置以上 2 个抽象方法对应的映射:
- <mapper namespace="cn.tedu.store.mapper.UserMapper">
- <!-- 插入用户数据 -->
- <!-- Integer addnew(User user) -->
- <insert id="addnew">
- 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"
- resultType="cn.tedu.store.entity.User">
- SELECT
- uid
- FROM
- t_user
- WHERE
- username=#{username}
- </select>
- </mapper>
注意: 此次并没有在接口文件之前添加 @Mapper 注解, 由于这个注解是添加在接口之前的, 则项目中可能出现的多个持久层接口都需要添加该注解, 管理起来比较麻烦, 所以, 改为在执行程序 StoreApplication 之前添加 @MapperScan("cn.tedu.store.mapper")注解, 以指定持久层接口所在的包, 则后续每个持久层接口都不必再添加 @Mapper 注解.
最后, 在 src/test/java 下, 创建 cn.tedu.store.mapper.UserMapperTestCase 测试类:
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class UserMapperTestCase {
- @Autowired
- private UserMapper mapper;
- @Test
- public void addnew() {
- User user = new User();
- user.setUsername("root");
- user.setPassword("1234");
- Integer rows = mapper.addnew(user);
- System.err.println("rows=" + rows);
- }
- @Test
- public void findByUsername() {
- String username = "root";
- User user = mapper.findByUsername(username);
- System.err.println(user);
- }
- }
3. 用户 - 注册 - 业务层
业务层的开发通常也是 3 个步骤来完成!
1. 规划异常
业务层中的方法的返回值, 仅以操作成功为标准, 判断是否需要返回某种数据.
由于返回值并不体现操作成功与否, 则还需要考虑失败的情况, 并抛出对应的异常, 通常, 建议自定义异常, 针对不同的操作错误 (操作失败的原因) 抛出不同的异常, 并且, 这些异常都应该继承自 RuntimeException(原因后续再讲). 实际做法是自定义 ServiceException, 是继承自 RuntimeException 的, 而其它自定义的异常都继承自 ServiceException:
- /**
- * 业务异常, 当前项目中自定义异常类的基类
- */
- public class ServiceException extends RuntimeException {
- private static final long serialVersionUID = 980104530291206274L;
- public ServiceException() {
- super();
- }
- public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
- public ServiceException(String message, Throwable cause) {
- super(message, cause);
- }
- public ServiceException(String message) {
- super(message);
- }
- public ServiceException(Throwable cause) {
- super(cause);
- }
- }
然后, 本次的 "注册" 功能可能抛出 "用户名被占用" 和 "插入数据失败" 错误, 则需要创建对应的异常 UserConflictException 和 InsertException 以备抛出.
2. 在业务层接口中声明抽象方法
在业务层中声明的方法是被控制器层 (Controller) 调用的, 且在实现业务层功能时, 需要调用持久层对象中的方法, 所以, 在参数方面, 应该是承上启下的, 即: 能满足调用和被调用的需求.
创建业务层接口 cn.tedu.store.service.IUserService 接口, 并添加抽象方法:
- void reg(User user)
- throws UserConflictException, InsertException;
3. 实现接口中的抽象方法
创建 cn.tedu.store.service.impl.UserServiceImpl 类, 实现 IUserService 接口, 在类中声明持久层对象 @Autowired private UserMapper userMapper;, 在类之前添加 @Service 注解:
- @Service
- public class UserServiceImpl
- implements IUserService {
- @Autowired
- private UserMapper userMapper;
- @Override
- public void reg(User user) throws UserConflictException, InsertException {
- // TODO Auto-generated method stub
- }
- }
通常, 应该把持久层中声明的抽象方法复制到业务层的实现类中, 并且通过持久层对象直接调用来实现方法的功能, 这些方法应该是私有的, 如果是查询类的方法, 应该直接返回调用持久层方法的返回结果, 如果是增删改的方法, 应该将方法的返回值修改为 void, 并且, 在方法体中, 判断调用时的返回结果, 如果结果不符合预期, 则抛出异常.
基于以上原则, 应该在业务层的实现类中添加:
- /**
- * 插入用户数据
- * @param user 用户数据
- * @return 受影响的行数
- */
- private void addnew(User user) {
- Integer rows = userMapper.addnew(user);
- if (rows != 1) {
- throw new InsertException("增加用户数据时出现未知错误! 请联系系统管理员!");
- }
- }
- /**
- * 根据用户名查询用户信息
- * @param username 用户名
- * @return 匹配的用户数据, 如果没有匹配的数据, 则返回 null
- */
- private User findByUsername(String username) {
- return userMapper.findByUsername(username);
- }
然后, 重写接口中定义的抽象方法, 在编写时, 应该先分析过程, 然后再编写代码:
- @Override
- public void reg(User user) throws UserConflictException, InsertException {
- // 根据 user.getUsername()获取用户名匹配的数据
- // 检查数据是否为 null
- // 是: 为 null, 用户名未被占用, 则应该补全参数中的属性值
- // - 1. 密码加密, 并封装
- // - 2. 封装 salt
- // - 3. 封装 isDelete, 固定为 0
- // - 4. 封装 4 项日志数据
- // - 执行注册: addnew(user)
- // 否: 非 null, 用户名被占用, 则抛出 UserConflictException
- }
分析完成, 编写可以完成的部分的代码(暂不包括密码加密与 salt 的处理):
- @Override
- public void reg(User user) throws UserConflictException, InsertException {
- // 根据 user.getUsername()获取用户名匹配的数据
- String username = user.getUsername();
- User data = findByUsername(username);
- // 检查数据是否为 null
- if (data == null) {
- // 是: 为 null, 用户名未被占用, 则应该补全参数中的属性值
- // TODO - 1. 密码加密, 并封装
- // TODO - 2. 封装 salt
- // - 3. 封装 isDelete, 固定为 0
- user.setIsDelete(0);
- // - 4. 封装 4 项日志数据
- Date now = new Date();
- user.setCreatedTime(now);
- user.setModifiedTime(now);
- user.setCreatedUser(username);
- user.setModifiedUser(username);
- // - 执行注册: addnew(user)
- addnew(user);
- } else {
- // 否: 非 null, 用户名被占用, 则抛出 UserConflictException
- throw new UserConflictException(
- "注册失败! 您尝试注册的用户名 (" + username + ") 已经被占用!");
- }
- }
完成后, 仍编写对应的单元测试:
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class UserServiceTestCase {
- @Autowired
- private IUserService service;
- @Test
- public void reg() {
- try {
- User user = new User();
- user.setUsername("admin");
- user.setPassword("8888");
- user.setPhone("13800138001");
- user.setEmail("admin@tedu.cn");
- user.setGender(1);
- user.setAvatar("http://www.tedu.cn/logo.png");
- service.reg(user);
- System.err.println("OK.");
- } catch (ServiceException e) {
- System.err.println(e.getMessage());
- }
- }
- }
4. 用户 - 注册 - 控制器层
5. 用户 - 注册 - 界面
来源: https://www.cnblogs.com/topzhao/p/10335580.html