1.项目分析
- 项目功能:登录、注册、热销商品、用户管理(密码、个人信息、头像、收货地址)、购物车(展示、增加、删除)、订单模块。
- 开发顺序:注册、登录、用户管理、购物车、商品、订单模块。
- 某一个模块的开发:
- 持久层开发:依据前端页面的设计规划相关的SQL语句,以及进行配置
- 业务层开发:核心功能的控制、业务操作以及异常的处理
- 控制层开发:接受请求、处理响应
- 前端开发:JS、Query、AJAX这些技术来连接后台
2.项目开发环境
- jdk:1.8版本及以上
- maven:配置到idea,3.6.1版本+
- 数据库:
MariaDB 、Mysql,5.1+ - 开发的平台:IDEA
3.搭建项目
- 项目名称:store,表示商场
- 结构:com.cy.store
java web
mybatis
mysql driver
对应init project
- 资源文件:resources文件夹下(static、templates)
- 单元测试:test.com.cy.store
- 在properties文件中配置数据库连接
spring.datasource.url = jdbc:mysql://localhost:3306/stores?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
server.port=8080
- 创建一个store数据库
create database store
- 测试连接
- 启动SpringBoot主类,是否有对应的Spring图形输出
- 在单元测试类中测试数据库的连接是否可以正常的加载
- 访问项目的静态资源是否可以正常的加载。所有的静态资源复制static下
注意:idea对应JS代码的兼容性较差,编写了js代码但是有时候不能正常去加载。
- idea缓存清除
- clear-install
- rebuild重新构建
- 重启idea和操作系统
对应测试数据库 对应static
init
访问静态资源
对应sql
用户注册
1 创建数据表
1.选中数据库
use store
2.创建t_user表:
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'盐值',
phone VARCHAR(20)COMMENT'电话号码',
email VARCHAR(30)COMMENT '电子邮箱',
gender INT COMMENT '性别:0-女,1-男',
avatar VARCHAR(50) COMMENT'头像',
is_delete INT COMMENT'是否删除:0-未删除,1-已删除',
created_user VARCHAR(20) COMMENT'日志-创建人',
modified_user VARCHAR(20) COMMENT'日志-最后修改执行人',
created_time DATETIME COMMENT'日志-创建时间',
modified_time DATETIME COMMENT'日志-最后修改时间',
PRIMARY KEY (uid)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
对应t_user.sql
2.创建用户实体类
1.通过表的结构提取出表的公共字段,放在一个实体类的基类中,起名BaseEntity基类中。
package com.cy.store.entity;
import jdk.nashorn.internal.objects.annotations.Constructor;
import lombok.*;
import org.springframework.context.annotation.Configuration;
import java.io.Serializable;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class BaseEntity implements Serializable {
private String createdUser;
private String modifiedUser;
private Date createdTime ;
private Date modifiedTime ;
}
Lombok使用详解
对应 基类
2.创建用户的实体类。需要继承BaseEntity基类
package com.cy.store.entity;
import lombok.*;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User extends BaseEntity implements Serializable {
private Integer uid;
private String username;
private String password;
private String salt;
private String phone;
private String email;
private Integer gender;
private String avatar;
private Integer isDelete;
}
对应User
3.用户注册-持久层
通过mybatis来操作数据库。在做mybatis开发的流程
3.1规划SQL语句
1.用户的注册功能,相当于做数据的插入操作。
insert into t_user(username,password)values(值列表)
2.在用户的注册时首先去查询当前的用户名是否存在,如果存在则不能进行注册。相当于是一条查询语句。
select * from t_user where username=?
3.2创建接口与抽象方法
1.定义Mapper接口。在项目的目录结构下首先创建一个mapper文件夹,在这个包下在根据不同的功能模块来创建mapper接口。创建一个UserMapper的接口。要在接口中定义这两个sql语句抽象方法。
package com.cy.store.mapper;
import com.cy.store.entity.User;
public interface UserMapper {
Integer insert(User user);
User findByUsername(String username);
}
2.在启动类配置mapper接口文件的位置
@MapperScan("com.cy.store.mapper")
3.3编写映射
1.定义xml映射文件,与对应的接口进行关联。所有的映射文件需要放置在resources目录下,在这个目录下创建一个mapper文件夹,然后在这个文件夹下存放mapper映射文件。
2.创建接口的映射文件,遵循和接口名称保持一致即可。创建一个UserMapper.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.cy.store.mapper.UserMapper">
</mapper>
3.配置接口中的方法对应上SQL语句。需要借助标签来完成。insert\update\delete\select,对应sql语句的增删改查操作。
<resultMap id="UserEntityMap" type="com.cy.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>
<insert id="insert" useGeneratedKeys="true" keyProperty="uid">
INSERT INTO
t_user (username, password, salt, phone, email, gender, avatar, is_delete, created_user, created_time, modified_user, modified_time)
VALUES
(#{username}, #{password}, #{salt}, #{phone}, #{email}, #{gender}, #{avatar}, #{isDelete}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})
</insert>
<select id="findByUsername" resultMap="UserEntityMap">
SELECT * FROM t_user WHERE username = #{username}
</select>
对应UserMapper
3.将mapper文件的位置注册到properties对应的配置文件中。
mybatis.mapper-locations=classpath:mapper/*.xml
3.单元测试:每一个独立的层编写完毕后需要编写单元测试方法,来测试当前的功能。在test包结构下创建一个mapper包,在这个包下在创建对持久层的测试。
package com.cy.store.mapper;
import com.cy.store.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
public class UserMapperTests {
@Autowired
private UserMapper userMapper;
@Test
public void insert(){
User user=new User();
user.setUsername("tim");
user.setPassword("123");
Integer row = userMapper.insert(user);
System.out.println(row);
}
@Test
public void findByUsername(){
User user = userMapper.findByUsername("tim");
System.out.println(user);
}
}
UsermapperTest
修改权限
//File | Settings | Editor | Inspections //Spring | Spring Core | Code | Autowiring for bean class //error–>warning
前 后
测试
4.用户注册业务层
4.1 规划异常
1.RuntimeException异常,作为这异常的子类,然后再去定义具体的异常类型来继承这个异常业务层异常的基类,ServiceException异常。这个异常继承RuntimeException 异常。异常机制的建立。
service|ex|ServiceException
package com.cy.store.service.ex;
public class ServiceException extends RuntimeException{
public ServiceException() {
super();
}
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(Throwable cause) {
super(cause);
}
protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
ServiceException
根据业务层不同的功能来详细定义具体的异常的类型,统一的去继承ServiceException异常类。
2.用户在进行注册时可能会产生用户名被占用的错误,抛出一个异常:UsernameDuplicatedException异常。
package com.cy.store.service.ex;
public class UsernameDuplicatedException extends ServiceException{
public UsernameDuplicatedException() {
super();
}
public UsernameDuplicatedException(String message) {
super(message);
}
public UsernameDuplicatedException(String message, Throwable cause) {
super(message, cause);
}
public UsernameDuplicatedException(Throwable cause) {
super(cause);
}
protected UsernameDuplicatedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
UsernameDuplicatedException
3.正在执行数据插入操作的时候,服务器、数据库宕机。处于正在插入的过程中所产生的异常:InsertException异常
package com.cy.store.service.ex;
public class InsertException extends ServiceException{
public InsertException() {
super();
}
public InsertException(String message) {
super(message);
}
public InsertException(String message, Throwable cause) {
super(message, cause);
}
public InsertException(Throwable cause) {
super(cause);
}
protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
InsertException
4.2 设计接口抽象与方法
在service包下创建一个IUserService接口。
package com.cy.store.service;
import com.cy.store.entity.User;
public interface IUserService {
void reg(User user);
}
2.创建一个实现类UserServiceImpl类,需要实现这个接口,并且实现抽象的方法。 实现抽象方法:UserServiceImpl,进行获取用户名,查询,创建,异常处理,数据加密,抛出异常
package com.cy.store.service.impl;
import com.cy.store.entity.User;
import com.cy.store.mapper.UserMapper;
import com.cy.store.service.IUserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UsernameDuplicatedException;
import org.springframework.beans.factory.annotation.Autowired;
import javax.xml.crypto.Data;
import java.util.Date;
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public void reg(User user) {
String username = user.getUsername();
User result = userMapper.findByUsername(username);
if(result!=null){
throw new UsernameDuplicatedException("用户名被占用");
}
user.setIsDelete(0);
user.setCreatedUser(username);
user.setModifiedUser(username);
Date date=new Date();
user.setCreatedTime(date);
user.setModifiedTime(date);
Integer rows = userMapper.insert(user);
if (rows!=1){
throw new InsertException("在用户注册过程中产生未知的异常");
}
}
}
UserService
3.在单元测试包下创建一个UserServiceTests类,在这个类中添加单元测试的功能。
UserServiceTests
测试
加密
package com.cy.store.service.impl;
import com.cy.store.entity.User;
import com.cy.store.mapper.UserMapper;
import com.cy.store.service.IUserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UsernameDuplicatedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import javax.xml.crypto.Data;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public void reg(User user) {
String username = user.getUsername();
User result = userMapper.findByUsername(username);
if(result!=null){
throw new UsernameDuplicatedException("用户名被占用");
}
String oldPassword = user.getPassword();
String salt = UUID.randomUUID().toString().toUpperCase();
user.setSalt(salt);
String md5Password = getMd5Password(oldPassword, salt);
user.setPassword(md5Password);
user.setIsDelete(0);
user.setCreatedUser(username);
user.setModifiedUser(username);
Date date=new Date();
user.setCreatedTime(date);
user.setModifiedTime(date);
Integer rows = userMapper.insert(user);
if (rows!=1){
throw new InsertException("在用户注册过程中产生未知的异常");
}
}
private String getMd5Password(String password,String salt){
for(int i=0;i<3;i++){
password= DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
}
return password;
}
}
UserServiceImpl --加密
测试
5.注册-控制器
5.1 创建响应
状态码、状态描述信息、数据。这部分功能封装一个类中,将这个类作为方法返回值,返回给前端浏览器。 util|JsonResult
package com.cy.store.util;
import java.io.Serializable;
public class JsonResult<E> implements Serializable {
private Integer state;
private String message;
private E data;
public JsonResult() {
}
public JsonResult(Integer state) {
super();
this.state = state;
}
public JsonResult(Throwable e) {
this.message = e.getMessage();
}
public JsonResult(Integer state, E data) {
this.state = state;
this.data = data;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public E getData() {
return data;
}
public void setData(E data) {
this.data = data;
}
}
5.2 设计请求
依据当前的业务功能模块进行请求的设计。
请求路口:/user/reg
请求参数:User user
请求类型:PUSH
验证结果:JsonResult<Void>
5.3 处理请求
1.创建一个控制层对应的类UserController类。依赖于业务层的接口。
package com.cy.store.controller;
import com.cy.store.entity.User;
import com.cy.store.service.IUserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UsernameDuplicatedException;
import com.cy.store.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("users")
public class UserController {
@Autowired
private IUserService userService;
@RequestMapping("reg")
public JsonResult<Void> reg(User user){
JsonResult<Void> result=new JsonResult<>();
try {
userService.reg(user);
result.setState(2000);
result.setMessage("用户注册成功");
} catch (UsernameDuplicatedException e) {
result.setState(4000);
result.setMessage("用户名被占用");
e.printStackTrace();
}catch (InsertException e) {
result.setState(5000);
result.setMessage("注册时产生未知的异常");
e.printStackTrace();
}
return result;
}
}
测试
http://localhost:8080/users/reg?username=Tom&password=123456
5.4 控制层优化设计
在控制层抽离一个父类,在这个父类中统一的取处理关于异常相关操作。编写BaseController。统一的处理异常。 **BaseController **
package com.cy.store.controller;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.ServiceException;
import com.cy.store.service.ex.UsernameDuplicatedException;
import com.cy.store.util.JsonResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
public class BaseController {
public static final int OK=200;
@ExceptionHandler(ServiceException.class)
public JsonResult<Void> handleException(Throwable e){
JsonResult<Void> result=new JsonResult<>(e);
if (e instanceof UsernameDuplicatedException){
result.setState(4000);
result.setMessage("用户名已经被占用");
}else if (e instanceof InsertException){
result.setState(5000);
result.setMessage("注册时产生未知的异常");
}
return result;
}
}
**BaseController **
UserController
重新构建了reg()
package com.cy.store.controller;
import com.cy.store.entity.User;
import com.cy.store.service.IUserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UsernameDuplicatedException;
import com.cy.store.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("users")
public class UserController extends BaseController{
@Autowired
private IUserService userService;
@RequestMapping("reg")
public JsonResult<Void> reg(User user){
userService.reg(user);
return new JsonResult<Void>(OK);
}
}
UserController–优化
测试
http://localhost:8080/users/reg?username=Tom02&password=123456
6 注册-前端页面
1.在register页面中编写发送请求的方法,点击事件来完成。选中对应的按钮($(“选择器”)),再去添加点击的事件, $.ajax()函数发送异步请求。 2.JQuery封装了一个函数,称之为 $.ajax()函数,可以异步加载相关的请求。依靠的是JavaScript提供的一个对象XHR(XmlHttpResponse),封装了这个对象。 3.ajax()使用方法。需要传递一个方法体作为方法的参数来使用。一对大括号称之为方法体。AJAX接受多个参数,参数与参数直接要求使用“,”分割,每一组参数直接使用“:”分割,参数的组成部分一个是参数的名称(不能随意的定义),是参数的值,要求使用字符串来标识。参数的声明顺序没有要求。语法结构:
$.ajax({
url: "",
type: "",
data: "",
dataType: ""
success: function(){
},
error: function(){
}
});
4.ajax()函数参数的含义
参数 | 功能描述 | 例如 |
---|
url | 表示请求的地址(url地址),不能包含参数列表部分的内容。 | 例如: url: “localhost:8080/user/reg” | type | 请求类型(GET和POST请求的类型)。 | 例如: type: “POST” | data | 向指定的请求url地址提交的数据。 | 例如:data: “username=tom&password=123” | dataType | 提交数据的类型。数据的类型一般指定为json类型 | dataType: “json” | success | 当服务器正常响应客户端,会自动调用success参数的方法,并且将服务器返回的数据以参数的形式传递给这个方法的参数上 | \ | error | 当服务器未正常响应客户端,会自动调用error参数的方法,并且将服务器返回的数据以参数的形式传递给这个方法的参数上 | \ | 5.js代码可以独立声明在一个js的文件里或者声明在一个script标签中。 | | |
register.html
<script type="text/javascript">
$("#btn-reg").click(function() {
console.log($("#form-reg").serialize());
$.ajax({
url: "/users/reg",
type: "POST",
data: $("#form-reg").serialize(),
dataType: "json",
success: function(json) {
if (json.state == 200) {
alert("注册成功!");
location.href = "login.html";
} else {
alert("注册失败!" + json.message);
}
},error:function (xhr){
alert("注册时产生未知错误" + xhr.status)
}
});
});
</script>
6.js代码无法正常被服务器执行,体现在点击页面中的按钮没有任何的响应。解决方案:
- 在项目的maven下clear清理项目-install重新部署
- 在项目的file选项下-cash清理缓存
- 重新的构建项目:build选项下-rebuild选项
- 重启idea
- 重启电脑
regist.html
测试
README–用户注册
|