在中国做外国网站怎么收钱,昆山设计网站的公司,南阳公司网站制作,江苏省建设厅官方网站资质查询五、拓展篇
5.1逻辑删除 在电商网站中#xff0c;我们会上架很多商品#xff0c;这些商品下架以后#xff0c;我们如果将这些商品从数据库中删除#xff0c;那么在年底统计商品的时候#xff0c;这个商品要统计的#xff0c;所以这个商品信息我们是不能删除的。 如果商城…五、拓展篇
5.1逻辑删除 在电商网站中我们会上架很多商品这些商品下架以后我们如果将这些商品从数据库中删除那么在年底统计商品的时候这个商品要统计的所以这个商品信息我们是不能删除的。 如果商城的商品下架了这时候我们将商品从数据库删掉那么到年终总结的时候我们要总结这一年的销售额发现少了20000这肯定是不合理的。所以我们是不能将数据真实删除的。 这里我们就采取逻辑删除的方案逻辑删除的操作就是增加一个字段表示这个数据的状态如果一条数据需要删除我们通过改变这条数据的状态来实现这样既可以表示这条数据是删除的状态又保留了数据以便以后统计。
1.修改字段
在表中增加一列字段status表示是否删除的状态这里我们使用的字段类型为int类型通过1表示该条数据可用0表示该条数据不可用。
2.修改实体类
实体类添加一个字段为Integer用于对应表中的字段
public class User extends ModelUser {private Long id;TableField(username)private String name;private Integer age;private String email;TableField(desc)private String desc;//逻辑删除字段TableLogic(value 1,delval 0)private Integer status;
}3.测试逻辑删除效果
Test
void LogicDelete() {userMapper.deleteById(7L);
}4.查询t_user表
Test
void logicSelect() {LambdaQueryWrapperUser lambdaQueryWrapper new LambdaQueryWrapper();ListUser users userMapper.selectList(lambdaQueryWrapper);System.out.println(users);
}我们还可以通过全局配置来实现逻辑删除的效果
#配置逻辑删除字段
mybatis-plus.global-config.db-config.logic-delete-fieldstatus
#未删除为1
mybatis-plus.global-config.db-config.logic-not-delete-value1
#删除为0
mybatis-plus.global-config.db-config.logic-delete-value05.2通用枚举
当我们想要表示一组信息这组信息只能从一些固定的值中进行选择不能随意写在这种场景下枚举就非常的合适。假如我们想要表示性别性别只有两个值要么是男性要么是女性那我们就可以使用枚举来描述性别。
1.添加字段
我们先在表中添加一个字段gender表示性别这里一般使用int来描述因为int类型可以通过0和1这两个值来表示两个不同的性别
2.编写枚举类
public enum GenderEnum {MAN(0,男),WOMEN(1,女);EnumValueprivate Integer gender;private String genderName;GenderEnum(Integer gender, String genderName) {this.gender gender;this.genderName genderName;}
}3.实体类添加相关字段
Data
AllArgsConstructor
NoArgsConstructor
public class User extends ModelUser {private Long id;private String name;private Integer age;private String email;private GenderEnum gender;private Integer status;
}4.添加数据
Test
void enumTest() {User user new User();user.setName(王五);user.setAge(28);user.setEmail(wangwuqq.com);user.setStatus(1);user.setGender(GenderEnum.WOMEN);userMapper.insert(user);
}5.3字段类型处理器
在某些场景下我们在实体类中是使用Map集合作为属性接收前端传递过来的数据的但是这些数据存储在数据库时我们使用的是json格式的数据进行存储json本质是一个字符串就是varchar类型。那怎么做到实体类的Map类型和数据库的varchar类型的互相转换这里就需要使用字段类型处理器来完成。
1.在实体类中添加一个字段Map类型
Data
NoArgsConstructor
AllArgsConstructor
//查询时将json字符串封装为Map集合
TableName(autoResultMap true)
public class User extends ModelUser {private Long id;TableField(username)private String name;private Integer age;private String email;TableField(desc)private String desc;TableField(exist false)private Integer online;private Integer status;private GenderEnum gender;//指定字段类型处理器TableField(typeHandler FastjsonTypeHandler.class)private MapString,String contact;//联系方式
}2.在数据库中添加一个字段contact为varchar类型
3.添加依赖
字段类型处理器依赖Fastjson这个Json处理器所以我们需要引入对应的依赖
dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.7/version
/dependency4.测试添加操作
Test
void typeHandler() {User user new User();user.setName(Li);user.setAge(28);user.setEmail(liqq.com);user.setGender(GenderEnum.MAN);user.setStatus(1);HashMapString, String map new HashMap();map.put(tel, 15896637666);map.put(phone, 0371-60283366);user.setContact(map);userMapper.insert(user);
}5.测试查询操作
Test
void typeHandlerSelect() {ListUser users userMapper.selectList(null);System.out.println(users);
}5.4自动填充功能
在项目中有一些属性如果我们不希望每次都填充的话我们可以设置为自动填充比如常见的时间。
1.在数据库的表中添加两个字段
注意设置驼峰命名
2.添加字段
在实体类中添加对应字段并为需要自动填充的属性指定填充时机
//插入时自动填充createTime字段
TableField(fill FieldFill.INSERT)
private Date createTime;
//插入或更新时自动填充updateTime字段
TableField(fill FieldFill.INSERT_UPDATE)
private Date updateTime;3.编写自动填充处理器指定填充策略
Component
public class MyMetaHandler implements MetaObjectHandler {Overridepublic void insertFill(MetaObject metaObject) {setFieldValByName(createTime,new Date(),metaObject);setFieldValByName(updateTime,new Date(),metaObject);}Overridepublic void updateFill(MetaObject metaObject) {setFieldValByName(updateTime,new Date(),metaObject);}
}4.测试插入操作
Test
void testFillInsert(){User user new User();user.setName(wang);user.setAge(35);user.setEmail(wangpowernode.com);user.setGender(GenderEnum.MAN);user.setStatus(1);HashMapString, String contact new HashMap();contact.put(phone,010-1234567);contact.put(tel,13388889999);user.setContact(contact);userMapper.insert(user);
}5.测试更新操作
Test
void testFillUpdate(){User user new User();user.setId(18L);user.setName(wangwu);user.setAge(39);user.setEmail(wangpowernode.com);user.setGender(GenderEnum.MAN);user.setStatus(1);HashMapString, String contact new HashMap();contact.put(phone,010-1234567);contact.put(tel,13388889999);userMapper.updateById(user);
}5.5防全表更新与删除插件
在实际开发中全表更新和删除是非常危险的操作在MybatisPlus中提供了插件和防止这种危险操作的发生。
1.注入MybatisPlusInterceptor类并配置BlockAttackInnerInterceptor拦截器
Configuration
public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();//防止全表更新interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());return interceptor;}
}2.测试全表更新
Test
public void updateAll(){User user new User();user.setGender(GenderEnum.MAN);userService.saveOrUpdate(user,null);
}5.6MybatisX快速开发插件
MybatisX是一款IDEA提供的插件目的是为了我们简化Mybatis以及MybatisPlus框架而生。
5.6.1安装 5.6.2功能
1.点击小鸟Mapper接口和映射文件可以互相跳转
2.逆向工程
逆向工程就是通过数据库表结构逆向产生Java工程的结构包括以下几点 实体类Mapper接口Mapper映射文件Service接口Service实现类
1.引入依赖编写对应的配置文件信息
dependenciesdependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.3.1/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.2.8/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.31/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency
/dependencies#配置数据源
spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver
spring.datasource.urljdbc:mysql://localhost:3306/mybatisplus?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8autoReconnecttrueuseSSLfalse
spring.datasource.usernameroot
spring.datasource.password0305222.使用IDEA连接mysql 3.使用逆向工程
4.编写逆向工程配置信息和生成信息 5.在Mapper接口上添加Mapper注解
Mapper
public interface UserMapper extends BaseMapperUser {}6.测试
SpringBootTest
class Mp03ApplicationTests {Autowiredprivate UserMapper userMapper;Resourceprivate UserService userService;Testvoid testUserMapper() {ListUser users userMapper.selectList(null);users.forEach(user - System.out.println(user));}Testvoid testUserService() {User byId userService.getById(10L);System.out.println(byId);}
}3.常见需求代码生成
虽然Mapper接口中提供了一些常见方法我们可以直接使用这些方法来完成sql操作但是对于实际场景中各种复杂的操作需求来说依然是不够用的所以MybatisX提供了更多的方法以及可以根据这些方法直接生成对应的sql语句这样使得开发变得更加简单。 5.7乐观锁
首先我们先要了解开发中的一个常见场景叫做并发请求。并发请求就是在同一时刻有多个请求同时请求服务器资源如果是获取信息没什么问题但是如果是对于信息做修改操作就会出现问题。 比如目前商品的库存只剩余1件了这个时候有多个用户都想要购买这件商品都发起了购买商品的请求不能让多个用户都购买到因为多个用户都买到了这件商品那么就会出现超卖问题库存不够是没法发货的。所以在开发中就要解决这种超卖的问题。 这类场景的核心问题就是一个请求在执行的过程中其他请求不能改变数据如果是一次完整的请求在该请求的过程中其他请求没有对于这个数据产生修改操作那么这个请求是能够正常修改数据的。如果该请求在改变数据的过程中已经有其他请求改变了数据那该请求就不能改变这条数据了。想要解决这类问题最常见的就是加锁的思想锁可以用验证在请求的执行过程中是否有数据发生改变。常见的数据库锁类型有两种悲观锁、乐观锁。一次完成的修改操作是先查询数据然后修改数据。悲观锁悲观锁是在查询的时候就锁定数据在这次请求未完成之前不会释放锁等到这次请求完毕之后再释放锁释放了锁之后其他请求才可以对于这条数据完成读写。例子华为商店抢手机排队中…这样做的操作能够保证读取到的信息就是当前的信息保证了信息的正确性但是并发效率很低在实际开发中使用悲观锁的场景很少因为在并发时我们是要保证效率的。乐观锁乐观锁是通过表字段完成设计的他的核心思想是在读取的时候不加锁其他请求依然可以读取到这个数据在修改的时候判断一个数据是否有被修改过如果有被修改过那么本次请求的修改操作失效。例子大麦抢票 具体的SQL语句 Update 表 set 字段 新值version version 1 where version 1 这样做的操作是不会对于数据读取产生影响的并发效率高但是可能眼前看到的数据并不是真实信息数据是被修改之前的在很多场景下是可以容忍的并不是产生很大影响例如很多时候我们看到的是有库存或者都加入到购物车了但是点进去以后库存没有了。
乐观锁的使用
1.在数据库表中添加一个字段version表示版本默认值是1
2.找到实体类添加对应的属性并使用Version标注为这是一个乐观锁字段信息
Version
private Integer version;3.配置拦截器
因为要对每条修改语句完成语句的增强这里我们通过拦截器的配置让每条修改的sql语句在执行的时候都加上版本控制的功能。
Configuration
public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();//实现乐观锁的控制interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}4.测试效果
先模拟查询再修改
Test
void updateTest() {User user userMapper.selectById(18L);System.out.println(user);user.setName(赵六);userMapper.updateById(user);
}当我们完成修改时会自动将版本号1
5.模拟多个修改请求
乐观锁的效果是一个请求在修改的过程中是允许另一个请求查询的但是修改时会通过版本号是否改变来决定是否修改如果版本号变了证明已经有请求修改过数据了那这次修改不生效如果版本号没有发生变化那就完成修改。
Test
void updateTest2() {//模拟操作1的查询操作User user1 userMapper.selectById(18L);System.out.println(user1);//模拟操作2的查询操作User user2 userMapper.selectById(18L);System.out.println(user2);user2.setName(zhao);userMapper.updateById(user2);user1.setName(liu);userMapper.updateById(user1);
}这段代码其实是两次操作只不过操作1在执行的过程中有操作2完成了对于数据的修改这时操作1就无法再次进行修改了。
5.8代码生成器
代码生成器和逆向工程的区别在于代码生成器可以生成更多的结构更多的内容允许我们能够配置生成的选项更多。
1.引入依赖
!--代码生成器依赖--
dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-generator/artifactIdversion3.5.3/version
/dependency!--freemarker模板依赖--
dependencygroupIdorg.freemarker/groupIdartifactIdfreemarker/artifactIdversion2.3.31/version
/dependency2.编写代码生成器代码
SpringBootTest
public class GeneratorApplicationTests {public static void main(String[] args) {FastAutoGenerator.create(jdbc:mysql://localhost:3306/mybatisplus?serverTimezoneUTCcharacterEncodingutf8useUnicodetrueuseSSLfalse, root, 030522).globalConfig(builder - {builder.author(hhb) // 设置作者//.enableSwagger() // 开启 swagger 模式.fileOverride() // 覆盖已生成文件.outputDir(D://); // 指定输出目录}).packageConfig(builder - {builder.parent(com.hhb.mp02) // 设置父包名.moduleName(mybatisplus) // 设置父包模块名.pathInfo(Collections.singletonMap(OutputFile.xml, D://)); // 设置mapperXml生成路径}).strategyConfig(builder - {builder.addInclude(t_user) // 设置需要生成的表名.addTablePrefix(t); // 设置过滤表前缀}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板默认的是Velocity引擎模板.execute();}
}3.执行查看生成效果
5.9执行SQL分析打印
在我们日常开发工作中避免不了查看当前程序所执行的SQL语句以及了解它的执行时间方便分析是否出现了慢SQL问题。我们可以使用MybatisPlus提供的SQL分析打印的功能来获取SQL语句执行的时间。
1.引入依赖
dependencygroupIdp6spy/groupIdartifactIdp6spy/artifactIdversion3.9.1/version
/dependency2.修改application.properties配置文件
#配置数据源
spring.datasource.driver-class-namecom.p6spy.engine.spy.P6SpyDriver
spring.datasource.urljdbc:p6spy:mysql://localhost:3306/mybatisplus?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8autoReconnecttrueuseSSLfalse
spring.datasource.usernameroot
spring.datasource.password0305223.在resources下创建spy.properties配置文件
#3.2.1以上使用modulelistcom.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory# 自定义日志打印
logMessageFormatcom.baomidou.mybatisplus.extension.p6spy.P6SpyLogger#日志输出到控制台
appendercom.baomidou.mybatisplus.extension.p6spy.StdoutLogger# 使用日志系统记录 sql
#appendercom.p6spy.engine.spy.appender.Slf4JLogger# 设置 p6spy driver 代理
deregisterdriverstrue# 取消JDBC URL前缀
useprefixtrue# 配置记录 Log 例外,可去掉的结果集error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategoriesinfo,debug,result,commit,resultset# 日期格式
dateformatyyyy-MM-dd HH:mm:ss# 实际驱动可多个
#driverlistorg.h2.Driver# 是否开启慢SQL记录
outagedetectiontrue# 慢SQL记录标准 2 秒
outagedetectioninterval24.测试
执行查询所有的操作可以看到sql语句的执行时间。 5.10多数据源
当一个项目的数据库的数据十分庞大时在完成SQL操作的时候需要检索的数据就会更多我们会遇到性能问题会出现SQL执行效率低的问题。针对这个问题我们的解决方案是将一个数据库中的数据拆分到多个数据库中从而减少单个数据库的数据量从分摊访问请求的压力和减少单个数据库数据量这两个方面都提升了效率。
数据源切换
1.引入依赖
dependencygroupIdcom.baomidou/groupIdartifactIddynamic-datasource-spring-boot-starter/artifactIdversion3.1.0/version
/dependency2.创建新的数据库提供多数据源环境 3.编写配置文件指定多数据源信息
spring:datasource:dynamic:primary: masterstrict: falsedatasource:master:username: rootpassword: rooturl: jdbc:mysql://localhost:3306/mybatisplus?serverTimezoneUTCcharacterEncodingutf8useUnicodetrueuseSSLfalsedriver-class-name: com.mysql.cj.jdbc.Driverslave_1:username: rootpassword: rooturl: jdbc:mysql://localhost:3306/mybatisplus2?serverTimezoneUTCcharacterEncodingutf8useUnicodetrueuseSSLfalsedriver-class-name: com.mysql.cj.jdbc.Driver4.创建多个Service分别使用Ds注解描述不同的数据源信息
Service
DS(master)
public class UserServiceImpl extends ServiceImplUserMapper,User implements UserService {
}Service
DS(slave_1)
public class UserServiceImpl2 extends ServiceImplUserMapper, User implements UserService{
}5.测试service多数据源环境执行结果
SpringBootTest
class Mp03ApplicationTests {Autowiredprivate UserServiceImpl userServiceImpl;Autowiredprivate UserServiceImpl2 userServiceImpl2;Testpublic void select(){User user userServiceImpl.getById(1L);System.out.println(user);}Testpublic void select2(){User user userServiceImpl2.getById(1L);System.out.println(user);}
}测试发现结果可以从两个数据源中获取。