在b站听了袁老师的开发课,做了一点笔记。
基于springboot框架的电脑商城项目(一)_springboot商城项目_失重外太空.的博客-CSDN博客
1.项目功能:登录、注册、热销商品、用户管理(密码、个人信息、头像、收货地址)、购物车(展示、增加、删除) 、订单模块。
2.开发顺序:注册、登录、用户管理、购物车、商品、订单模块。
3.某一个模块的开发
1.项目名称:store
2.结构:com.cy.store
java web
mybatis
mysql driver
3.资源文件:resource文件夹下(static放静态资源、templates模板)
4.单元测试:test.com.cy.store
5.在properties文件中配置数据的连接源信息
- spring.datasource.url=jdbc:mysql://localhost:3306/store?useUnicode=true&characterEncoding=utf-8
- spring.datasource.username=root
- spring.datasource.password=root
6.创建一个store数据库
create database store character set utf8;
7.测试连接:
数据库连接池:
1.DBCP
2.C3P0
3.Hikari: 管理数据库的连接对象
8.访问项目的静态资源是否可以正常的加载。所有的静态资源复制static目录下。
注意:idea对于JS代码的兼容性较差,编写了js代码可能不能正常加载。五种解决:在项目的maven下clear清理项目、install重新部署;在项目的file选项下 cash清理缓存;重新构建项目:build选项下 rebuild选项;重启idea;重启电脑。
- 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 '日志-创建人',
- created_time DATETIME COMMENT '日志-创建时间',
- modified_user VARCHAR(20) COMMENT '日志-最后修改执行人',
- modified_time DATETIME COMMENT '日志-最后修改时间',
- PRIMARY KEY (uid)
- ) ENGINE=INNODB DEFAULT CHARSET=utf8;
-
- public class baseEntity implements Serializable {
- private String createdUser;
- private Date createdTime; //import java.util.Data
- private String modifiedUser;
- private Date modifiedTime;
-
- //再手动声明get、set方法,equals() and hashCode();toString();
-
- }
为什么要用implements Serializable:实体类User因为要在网络中以流的形式传输,所以需要serialize序列化(为什么要用implements Serializable_IT_wjj的博客-CSDN博客)
- 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;
-
- //再手动声明get、set方法,equals() and hashCode();toString();
- }
任何实体类都要:get和set方法、equals()和hashCode()、toString()
通过MyBatis来操作数据库。在做mybatis开发的流程。
1.用户的注册功能,相当于在做数据的插入操作。
insert into t_user (username, password) values (值列表)
2.在用户注册时首先查询当前用户名是否存在,如果存在则不能注册,相当于一条查询语句。
select * from t_user where username=?
1.定义Mapper接口。在项目的目录结构下创建一个mapper包,在这个包下根据不同的功能模块创建mapper接口。创建UserMapper的接口(interface)。要在接口中定义这两个SQL语句的抽象方法。
- package com.cy.store.mapper;
- import com.cy.store.entity.User;
-
- /** 处理用户数据操作的持久层接口 */
- public interface UserMapper {
- /**
- * 插入用户数据
- * @param user 用户数据
- * @return 受影响的行数(在增删改都有受影响的行数作为返回值,可以根据返回值来判断是否执行成功)
- */
- Integer insert(User user);
-
- /**
- * 根据用户名查询用户数据
- * @param username 用户名
- * @return 找到对应的用户数据则返回用户的数据,如果没有找到的数据则返回null
- */
- User findByUsername(String username);
- }
2.在启动类配置mapper接口文件的位置
//MapperScan注解指定当前项目中的Mapper接口路径的位置,在项目启动的时候会自动加载所有的接口。
@MapperScan("com.cy.store.mapper")
- package com.cy.store;
-
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.boot.web.servlet.MultipartConfigFactory;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.util.unit.DataSize;
- import org.springframework.util.unit.DataUnit;
-
- import javax.servlet.MultipartConfigElement;
-
- @Configuration
- @SpringBootApplication
- @MapperScan("com.cy.store.mapper")
- public class StoreApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(StoreApplication.class, args);
- }
-
- @Bean
- public MultipartConfigElement getMultipartConfigElement() {
- MultipartConfigFactory factory = new MultipartConfigFactory();
- // DataSize dataSize = DataSize.ofMegabytes(10);
- // 设置文件最大10M,DataUnit提供5中类型B,KB,MB,GB,TB
- factory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES));
- factory.setMaxRequestSize(DataSize.of(10, DataUnit.MEGABYTES));
- // 设置总上传数据总大小10M
- return factory.createMultipartConfig();
- }
- }
1.定义xml映射文件,与对应的接口进行关联。所有映射文件属于资源文件,需要放在resource目录结构下,在这个目录下创建一个mapper文件夹(Directory),然后在这个文件夹下存放mapper的映射文件。
2.创建接口对应的映射文件,遵循和接口的名称保持一致即可。创建一个UserMapper.xml文件(file)
namespace的属性:用于指定当前的映射文件和那个接口进行映射,需要指定接口的文件路径,需要标注包的完整路径结构。
- "1.0" encoding="UTF-8" ?>
- mapper
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.cy.store.mapper.UserMapper">
- <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>
-
- mapper>
3.配置接口中的方法对应上SQL语句。需要借助标签完成,insert/update/delete/selete,对应SQL语句的增删改查操作。
insert
select在执行的时候,查询的结果是一个对象、多个对象。
4.将mapper文件的位置注册到properties对应的配置文件中。(固定写法)
mybatis.mapper-locations=classpath:mapper/*.xml
5.单元测试:每个独立的层编写完毕后需要编写单元测试方法,来测试当前的功能。在test包结构下创建一个mapper包,在这个包下在创建持久层的功能测试。
@SpringBootTest:表示标注当前的类是一个测试类,不会随同项目一块打包发送。
@RunWith(SpringRunner.class):表示启动这个单元测试类(如果不写-单元测试类是不能运行的),需要传递一个固定的参数,必须是SpringRunner的实例类型。
单元测试方法(可以单独的独立运行,不用启动整个项目,可以做单元测试,提升了代码的测试效率)必须被@Test注解所修饰,返回值类型必须是void,方法的参数列表不能指定任何类型,方法的访问修饰符必须是public。
- package com.cy.store.mapper;
-
- import com.cy.store.entity.User;
- import com.cy.store.mapper.UserMapper;
- 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 java.text.SimpleDateFormat;
- import java.util.Date;
-
- // @RunWith(SpringRunner.class)注解是一个测试启动器,可以加载Springboot测试注解
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class UserMapperTests {
- //idea有检测的功能,接口是不能直接创建Bean的(动态代理技术来解决)
- @Autowired
- private UserMapper userMapper;
-
- @Test
- public void insert() {
- User user = new User();
- user.setUsername("user05");
- user.setPhone("12345678910");
- Integer rows = userMapper.insert(user);
- System.out.println("rows=" + rows);
- }
-
- @Test
- public void findByUsername() {
- String username = "user02";
- User result = userMapper.findByUsername(username);
- System.out.println(result);
- }
- }
1.RuntimeException异常,作为这个异常的子类,再去定义具体的异常类型来继承这个异常。业务层异常的基类ServiceException,这个异常继承RuntimeException异常。异常机制就建立起来了。
Alt+insert --override methods
- 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。
2.用户在进行注册的时候可能会产生用户名被占用的错误,抛出一个异常:UsernameDuplicatedException
3.正在执行数据插入操作的时候,服务器宕机、数据库宕机。处于正在执行插入的过程中所产生的异常:InsertException异常。
1.在service包下创建一个IUserService接口(interface)。
- package com.cy.store.service;
- import com.cy.store.entity.User;
-
- /** 处理用户数据的业务层接口 */
- public interface IUserService {
- /**
- * 用户注册
- * @param user 用户数据的对象
- */
- void reg(User user);
- }
@Service注解:将当前类的对象交给Spring来管理,自动创建对象以及对象的维护。
2.创建一个实现类UserServiceImpl类,需要实现这个接口,并且实现抽象的方法。
- package com.cy.store.service.impl;
-
- import java.util.UUID;
- import com.cy.store.entity.User;
- import com.cy.store.mapper.UserMapper;
- import com.cy.store.service.IUserService;
- import com.cy.store.service.ex.*;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.util.DigestUtils;
-
- import java.util.Date;
-
- /** 处理用户数据的业务层实现类 */
- @Service
- public class UserServiceImpl implements IUserService {
- @Autowired
- private UserMapper userMapper;
-
- @Override
- public void reg(User user) {
- // 根据参数user对象获取注册的用户名
- String username = user.getUsername();
- // 调用持久层的User findByUsername(String username)方法,根据用户名查询用户数据
- User result = userMapper.findByUsername(username);
- // 判断查询结果是否不为null
- if (result != null) {
- // 是:表示用户名已被占用,则抛出UsernameDuplicateException异常
- throw new UsernameDuplicateException("尝试注册的用户名[" + username + "]已经被占用");
- }
-
- // 创建当前时间对象
- Date now = new Date();
- // 补全数据:加密后的密码。盐值是一个随机的字符串。
- String salt = UUID.randomUUID().toString().toUpperCase();
- String md5Password = getMd5Password(user.getPassword(), salt);
- user.setPassword(md5Password);
- // 补全数据:盐值
- user.setSalt(salt);
- // 补全数据:isDelete(0)
- user.setIsDelete(0);
- // 补全数据:4项日志属性
- user.setCreatedUser(username);
- user.setCreatedTime(now);
- user.setModifiedUser(username);
- user.setModifiedTime(now);
-
- // 表示用户名没有被占用,则允许注册
- // 调用持久层Integer insert(User user)方法,执行注册并获取返回值(受影响的行数)
- Integer rows = userMapper.insert(user);
- // 判断受影响的行数是否不为1
- if (rows != 1) {
- // 是:插入数据时出现某种错误,则抛出InsertException异常
- throw new InsertException("添加用户数据出现未知错误,请联系系统管理员");
- }
- }
-
- /**
- * 执行密码加密
- * @param password 原始密码
- * @param salt 盐值
- * @return 加密后的密文
- */
- private String getMd5Password(String password, String salt) {
- /*
- * 加密规则:
- * 1、无视原始密码的强度
- * 2、使用UUID作为盐值,在原始密码的左右两侧拼接
- * 3、循环加密3次
- */
- for (int i = 0; i < 3; i++) {
- password = DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
- }
- return password;
- }
-
- }
3.在单元测试包下创建一个UserServiceTests类,在这个类中添加单元测试功能。
- package com.cy.store.service;
-
- import com.cy.store.entity.User;
- import com.cy.store.service.ex.ServiceException;
- 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;
-
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class UserServiceTests {
- @Autowired
- private IUserService iUserService;
-
- @Test
- public void reg() {
- try {
- User user = new User();
- user.setUsername("lower");
- user.setPassword("123456");
-
- iUserService.reg(user);
- System.out.println("注册成功!");
- } catch (ServiceException e) {
- //获取类的对象,再获取类的名称
- System.out.println("注册失败!" + e.getClass().getSimpleName());
- //获取异常的具体描述信息
- System.out.println(e.getMessage());
- }
- }
- }
接收请求和处理响应。
状态码、状态描述信息、数据。这部分功能封装在一个类中,将这个类作为方法的返回值,返回给前端浏览器。
- package com.cy.store.util;
- import java.io.Serializable;
-
- /**
- * 响应结果类
- * Json格式的数据进行相应
- * @param
响应数据的类型 - */
- public class JsonResult
implements Serializable { - /** 状态码 */
- private Integer state;
- /** 状态描述信息 */
- private String message;
- /** 数据 */
- private E data;
-
- public JsonResult() {
- super();
- }
-
- public JsonResult(Integer state) {
- super();
- this.state = state;
- }
-
- /** 出现异常时调用 */
- public JsonResult(Throwable e) {
- super();
- // 获取异常对象中的异常信息
- this.message = e.getMessage();
- }
-
- public JsonResult(Integer state, E data) {
- super();
- 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;
- }
- }
依据当前的业务功能模块进行请求的设计。
创建一个控制层对应的UserController类,依赖于业务层的接口。
- /** 处理用户相关请求的控制器类 */
- @RestController
- @RequestMapping("users")
- public class UserController extends BaseController {
- @Autowired
- private IUserService userService;
-
- /*
- @RequestMapping("reg")
- public JsonResult
reg(User user) { - // 调用业务对象执行注册
- userService.reg(user);
- // 返回
- return new JsonResult
(OK); - }
- */
-
- @RequestMapping("reg")
- public JsonResult
reg(User user) { - // 创建返回值(创建响应结果对象)
- JsonResult
result = new JsonResult(); - try {
- // 调用业务对象执行注册
- userService.reg(user);
- // 响应成功
- result.setState(200);
- } catch (UsernameDuplicateException e) {
- // 用户名被占用
- result.setState(4000);
- result.setMessage("用户名已经被占用");
- } catch (InsertException e) {
- // 插入数据异常
- result.setState(5000);
- result.setMessage("注册失败,请联系系统管理员");
- }
- return result;
- }
- }
@RestController
@RestController 详解_换个角度看代码的博客-CSDN博客
@RestController是@controller和@ResponseBody 的结合
@Controller 将当前修饰的类注入SpringBoot IOC容器,使得从该类所在的项目跑起来的过程中,这个类就被实例化。
@ResponseBody 它的作用简短截说就是指该类中所有的API接口返回的数据,甭管你对应的方法返回Map或是其他Object,它会以Json字符串的形式进行数据的响应给到前端。
@RequestMapping注解能将请求和处理请求的控制器方法关联起来,建立映射关系。
SpringMvc---@RequestMapping注解和它的属性_@requestmapping 定义数组_妙Lin的博客-CSDN博客
在控制层抽离一个父类,在这个父类中统一的处理关于异常的操作。编写BaseController类统一处理异常。
@ExceptionHandler 用于统一处理抛出的异常
- /** 控制器类的基类 */
- public class BaseController {
- /** 操作成功的状态码 */
- public static final int OK = 200;
-
-
- /** @ExceptionHandler用于统一处理方法抛出的异常 */
- //请求处理方法,这个方法的返回值就是需要传递给前端的数据
- //自动将异常对象传递给此方法的参数列表上
- //当项目中产生了异常,会被统一拦截到此方法中,这个方法此时充当请求处理方法,方法的返回值直接给到前端
- @ExceptionHandler({ServiceException.class, FileUploadException.class})
- public JsonResult
handleException(Throwable e) { - JsonResult
result = new JsonResult(e); - if (e instanceof UsernameDuplicateException) {
- result.setState(4000);
- }
- } else if (e instanceof InsertException) {
- result.setState(5000);
- }
- return result;
- }
- }
重新构建了reg()方法。
- @RequestMapping("reg")
- public JsonResult
reg(User user) { - // 调用业务对象执行注册
- userService.reg(user);
- // 返回
- return new JsonResult
(OK); - }
1.在\store\src\main\resources\static\web\register.html页面编写发送请求的方法,点击事件来完成,先选中对应的按钮($(“选择器”)),再去添加点击事件,$.ajax()函数发送异步请求。
2.JQuery封装了一个函数,称之为$.ajax()函数,通过对象调用ajax()函数,可以异步加载相关的请求。依靠的是JavaScript提供的一个对象XHR(XmlHttpResponse),封装了这个对象。
3.ajax()使用方式。需要传递一个方法体作为方法的参数来使用,一对大括号称之为方法体,ajax接收多个参数,参数与参数之间要求使用英文逗号分割,每一组参数之间使用英文冒号分割,参数的组成部分是参数名称(不能随意定义)和参数的值(要求使用字符串来表示"双引号引起来"),参数的声明顺序没有要求。语法结构:
$.ajax(fun());
function fun(){
//TODO
}
- $.ajax({
- url:"",
- type:"",
- data:"",
- dataType:"",
- success:function(){
-
- },
- error:function(){
-
- },
- });
4. ajax()函数参数的含义:
参数 | 功能描述 |
url | 标识请求的地址(url地址),不能包含参数列表部分的内容。例如:url:"localhost:8080/users/reg" |
type | 请求的类型(GET、POST)。例如:type:"POST" |
data | 向指定请求url地址提交的数据。例如:data:"username=tom&pwd=123" |
dataType | 提交数据的类型。数据类型一般指定为json类型。dataType:"json" |
success | 当服务器正常响应客户端是,会自动调用success参数的方法,并将服务器返回的数据以参数的形式传递给这个方法的参数上 |
error | 当服务器未正常响应客户端是,会自动调用error参数的方法,并将服务器返回的数据以参数的形式传递给这个方法的参数上 |
5.js代码可以独立存放在一个后缀为js的文件里,或者声明在一个script标签中。
6.js代码无法正常被服务器解析执行,体现在点击页面中的按钮没有任何响应,解决方案:
PostMapping和GetMapping区别