常见商业模式
B2C模式:
两个角色:
商家到用户,自己制作大量自有版权的视频,放在自有平台上,让用户付费。
这是这个项目使用的模式
核心模块:课程模块
B2B2C模式:
商家到商家到用户,平台连接第三方教育机构,平台一般不直接提供课程内容。
比如京东,很多的电商平台都是这种模式。
分成两个系统进行开发
系统后台
管理员使用
系统前台
普通用户使用
采用前后端分离开发
后端技术
SpringBoot
SpringCloud
MybatisPlus
Spring Security
前端技术
vue + element ui + axios + node.js …
其他技术
阿里云oss
阿里云视频点播服务
阿里云短信服务
微信支付和登录
docker
git
jenkins
创建数据库,创建数据库表
添加数据用于MP操作
DROP TABLE IF EXISTS user;CREATE TABLE USER(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id)
);
DELETE FROM user;INSERT INTO user (id,name,age,email) VALUES
(1, 'Jone', 18, 'test1@test.com'),
(2, 'Jack', 20, 'test2@test.com'),
(3, 'Tom', 28, 'test3@test.com'),
(4, 'Sandy', 21, 'test4@test.com'),
(5, 'Billie', 24, 'test5@test.com');
使用Idea 创建一个Spring Boot工程
引入相关依赖
springboot和mp
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine com.baomidou mybatis-plus-boot-starter 3.0.5 mysql mysql-connector-java org.projectlombok lombok
在application.properties里面配置mysql配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=xxxxxxx
编写代码测试
查看sql输出日志
# mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
@Test
void addUser() {User user = new User();user.setName("lucy");user.setAge(30);user.setEmail("lucy@qq.com");int insert = userMapper.insert(user);System.out.println("insert: "+insert);
}
在这里没有手动设置id,但是MP会自动生成。

自动增长 AUTO INCREMENT
分表时,会像下面这样分配每张表的id,所以创建新的表的时候需要知道上一张表最后一个项目的id

UUID 一个生成的随机的值
不好进行排序
用Redis实现

MP自带策略,19位,使用雪花算法生成的

AUTO: 自动增长
ID_WORKER: MP自带的策略,生成19位的值,数字类型使用这种策略
ID_WORKER_STR:也是MP自带的策略,生成19位值,字符串类型使用这种策略
INPUT: 需要我们自己设置id
NONE: 没有策略,也需要自己设置
UUID: 每次生成一个UUID
@TableId(type=IdType.AUTO)
private Long id
mp实现修改操作
@Test
void updateUser() {User user = new User();user.setId(2L);user.setAge(120);int i = userMapper.updateById(user);System.out.println(i);
}
addUser()的时候手动传值
在实体类里面需要自动填充的属性上添加注解
@TableField(fill = FieldFill.INSERT)
private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
FieldFill.INSERT表示在执行插入的时候自动填充这个字段
自己创建类,实现METAObjectHandler接口,实现接口里面的方法
@Component // 一定注意交给Spring容器管理
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) { // 填充操作时会执行this.setFieldValByName("createTime", new Date(), metaObject);this.setFieldValByName("updateTime", new Date(), metaObject);}@Overridepublic void updateFill(MetaObject metaObject) { // 更新操作时会执行this.setFieldValByName("updateTime", new Date(), metaObject);}
}
主要解决:丢失更新问题
如果不考虑事务隔离性,会产生读问题?
不考虑隔离性,产生写问题:丢失更新问题。
丢失更新问题
上面的过程中,mary原本是想修改数据500,可最后修改了数据8000,这就导致了lucy的数据更新被覆盖了。
多人同时修改一条记录,最后一个提交的人会把前面提交的更新覆盖。
解决方案:
在数据库表中添加version字段
对应的实体类添加版本号属性
在实体类的属性上添加注解
@Version
private Integer version; // 版本号
配置乐观锁插件 (建议写到配置类里面)
@Configuration // 注解表示这是一个配置类
public class MPConfig {/** 乐观锁插件,这个插件是固定的,直接复制就可以了* */@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() {return new OptimisticLockerInterceptor();}
}
编写代码测试
@Test
void testLock() { // 因为乐观锁的存在,所以一定要先查再改// 通过id查询数据User user = userMapper.selectById(1633278798692634625L);// 1633278798692634625user.setAge(200);userMapper.updateById(user);
}
根据id查询
// 根据id查询数据
User user = userMapper.selectById();
多个id的批量查询
@Test
void testSelectBench() {List integers = Arrays.asList(1, 2, 3);List users = userMapper.selectBatchIds(integers);users.forEach(System.out::println);
}
简单的条件查询
@Test
void testSelectByMap() { // 这种不经常使用HashMap map = new HashMap<>();map.put("name", "mary");map.put("age", 30);List users = userMapper.selectByMap(map);for (User user : users) {System.out.println(user);}
}
分页查询 (和PageHelper差不多)
配置分页插件
@Bean // 也是在配置类里面进行的
public PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();
}
编写分页代码
直接new page对象,传入两个参数,当前页和每页显示的记录数
@Test
void testPage() {// 创建page对象,传入参数: 当前页 和 每页小时的记录数Page userPage = new Page<>(1, 3);// 调用MP中分页查询的方法// 调用MP分页查询过程中,会把查出来的数据封装到page对象里面userMapper.selectPage(userPage, null);System.out.println(userPage.getCurrent()); // 获取当前页System.out.println(userPage.getRecords()); // 每页数据list集合System.out.println(userPage.getSize()); // 每页的记录数System.out.println(userPage.getTotal()); // 表中总记录数System.out.println(userPage.getPages()); // 得到总页数System.out.println(userPage.hasNext()); // 是否有下一页System.out.println(userPage.hasPrevious()); // 是否有上一页
}
通过id删除
@Test
void testDeleteByID() {int row = userMapper.deleteById(1L);System.out.println(row);
}
批量删除
@Test
void testDeleteBatchIds() {int row = userMapper.deleteBatchIds(Arrays.asList(2, 3));System.out.println(row);
}
简单条件删除
@Test
void testDeleteByMap() {HashMap map = new HashMap<>();map.put("age", 18);int i = userMapper.deleteByMap(map);System.out.println(i);
}
在数据库中添加 deleted 字段
实体类添加 deleted 字段
加上注解 @TableLogic 和 TableField(fill = FieldFill.INSERT)
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
配置类中配置逻辑删除插件
@Bean
public ISqlInjector sqlInjector() {return new LogicSqlInjector();
}
mybatis-plus.global-config.db-config.logic-delete-value=1 # 也可以在这里配置删除后是什么1,不删除是什么值
mybatis-plus.global-config.db-config.logic-not-delete-value=0
要删除的时候还是执行之前的删除方法,但是效果就变成了把 deleted 字段变成1,使用MP查询的时候我们也不需要加额外的条件,MP会自动加上 deleted 不为1这个条件。
如果想要查询被删除的记录的话,MP没有办法做到,只能通过新建 xml 文件,手写slq来实现。
在配置类中配置性能分析插件
/*
* SQL执行性能分析插件
* 开发环境使用,线上不推荐。MaxTime是指sql最大运行时长
*
* 环境一般有三种:
* dev: 开发环境
* test: 测试环境
* prod: 生产环境, 用户真正去使用的
* */
@Bean
@Profile({"dev", "test"})
public PerformanceInterceptor performanceInterceptor() {PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();performanceInterceptor.setMaxTime(100); // ms, 超过这个时间的sql不执行performanceInterceptor.setFormat(true);return performanceInterceptor;
}
在配置文件中设置 SpringBoot当前环境
运行代码的时候会在输出窗口看到sql的运行时长。

如果sql运行时间超过MaxTime的话,就会抛出一个异常。


一般使用 QueryWrapper 来构件条件
@Test
void testSelectQuery() {// 创建QueryWrapper对象QueryWrapper wrapper = new QueryWrapper<>();// 通过QueryWrapper设置条件// ge, gt, le, lt// 查询age >= 30 的记录// 第一个参数是列名, 第二个参数是值
// wrapper.ge("age", 30);// eq, ne
// wrapper.eq("name", "东方不败");
// wrapper.ne("name", "东方不败");// between
// wrapper.between("age", 20, 30);// like 模糊查询
// wrapper.like("name", "东");// orderByDesc, 排序
// wrapper.orderByDesc("id");// last, 拼接sql语句
// wrapper.last("limit 1");// 指定要查询的列wrapper.select("id", "name");List users = userMapper.selectList(wrapper);System.out.println(users);}