技术标签: java
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
润物无声
效率至上
丰富功能
1、创建数据库及相应的表
create database mybatis_plus;
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)
);
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
3、创建spring boot工程(maven),并添加相应jar包,安装Lombok插件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!--mysql运行时依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
4、在 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=root
5、Spring Boot 启动类
@SpringBootApplication
@MapperScan("com.atguigu.mybatis_plus.mapper")
public class MybatisPlusApplication {
......
}
7、创建包 entity 编写实体类 User.java
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
8、创建包 mapper 编写Mapper 接口
public interface UserMapper extends BaseMapper<User> {
}
9、测试(添加测试类,进行功能测试)
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void testSelectList() {
//UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper
//所以不填写就是无任何条件
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
@SpringBootTest
public class CRUDTests {
@Autowired
private UserMapper userMapper;
@Test
public void testInsert(){
User user = new User();
user.setName("Helen");
user.setAge(18);
user.setEmail("[email protected]");
int result = userMapper.insert(user);
System.out.println("影响的行数:" + result); //影响的行数
System.out.println("id:" + user); //id自动回填
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CpgCMoGi-1609223758124)(file:///C:/Users/86134/Documents/My%20Knowledge/temp/615201484_files/93ff417f-c9f7-4225-b395-2afe2776183d.jpg)]
1、业务分库
业务分库指的是按照业务模块将数据分散到不同的数据库服务器。例如,一个简单的电商网站,包括用户、商品、订单三个业务模块,我们可以将用户数据、商品数据、订单数据分开放到三台不同的数据库服务器上,而不是将所有数据都放在一台数据库服务器上。这样的就变成了3个数据库同时承担压力,系统的吞吐量自然就提高了。
虽然业务分库能够分散存储和访问压力,但同时也带来了新的问题,接下来我进行详细分析。
join 操作问题
事务问题
成本问题
2、主从复制和读写分离
读写分离的基本原理是将数据库读写操作分散到不同的节点上。读写分离的基本实现是:
需要注意的是,这里用的是“主从集群”,而不是“主备集群”。“从机”的“从”可以理解为“仆从”,仆从是要帮主人干活的,“从机”是需要提供读数据的功能的;而“备机”一般被认为仅仅提供备份功能,不提供访问功能。所以使用“主从”还是“主备”,是要看场景的,这两个词并不是完全等同。
3、数据库分表
将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如,淘宝的几亿用户数据,如果全部存放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进行拆分。
单表数据拆分有两种方式:垂直分表和水平分表。示意图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UKFZK4SH-1609223758125)(file:///C:/Users/86134/Documents/My%20Knowledge/temp/615201484_files/373ba7ef41999b4cc090e5aaee3bc63b_2.png)]
单表进行切分后,是否要将切分后的多个表分散在不同的数据库服务器中,可以根据实际的切分效果来确定。如果性能能够满足业务要求,是可以不拆分到多台数据库服务器的,毕竟我们在上面业务分库的内容看到业务分库也会引入很多复杂性的问题。分表能够有效地分散存储压力和带来性能提升,但和分库一样,也会引入各种复杂性:
垂直分表:
水平分表:
水平分表相比垂直分表,会引入更多的复杂性,例如数据id:
主键自增:
Hash :
雪花算法:分布式ID生成器
雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。
核心思想:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LgxZNXT7-1609223758127)(file:///C:/Users/86134/Documents/My%20Knowledge/temp/615201484_files/665ae40a-443f-4356-bb5d-4742f947c430.jpg)]
MyBatis-Plus默认的主键策略是:ASSIGN_ID (使用了雪花算法)
@TableId(type = IdType.ASSIGN_ID)
private String id;
@TableId(type = IdType.AUTO)
private Long id;
要想影响所有实体的配置,可以设置全局主键配置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
@Test
public void testUpdateById(){
User user = new User();
user.setId(1L);
user.setAge(28);
int result = userMapper.updateById(user);
System.out.println("影响的行数:" + result);
}
自动填充
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作
1、数据库修改
在User表中添加datetime类型的新的字段 create_time、update_time
2、实体类修改
@Data
public class User {
......
@TableField(fill = FieldFill.INSERT)
private Date createTime;
//@TableField(fill = FieldFill.UPDATE)
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
3、实现元对象处理器接口
package com.atguigu.mybatisplus.handler;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
1、场景描述
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就完全被小王的覆盖了。
现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1多万。
2、乐观锁与悲观锁
3、模拟修改冲突
CREATE TABLE product
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id)
);
INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
实体类
package com.atguigu.mybatis_plus.entity;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
Mapper
package com.atguigu.mybatis_plus.mapper;
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}
1、通过多个id批量查询
@Test
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
2、简单的条件查询
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
1、添加分页插件
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
2、测试selectPage分页
@Test
public void testSelectPage() {
Page<User> page = new Page<>(1,5);
Page<User> pageParam = userMapper.selectPage(page, null);
pageParam.getRecords().forEach(System.out::println);
System.out.println(pageParam.getCurrent());
System.out.println(pageParam.getPages());
System.out.println(pageParam.getSize());
System.out.println(pageParam.getTotal());
System.out.println(pageParam.hasNext());
System.out.println(pageParam.hasPrevious());
}
当指定了特定的查询列时,希望分页结果列表只返回被查询的列,而不是很多null值
@Test
public void testSelectMapsPage() {
//返回很多null列
//Page<User> page = new Page<>(1, 5);
//QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper.select("name", "age");
//Page<User> pageParam = userMapper.selectPage(page, queryWrapper);
//pageParam.getRecords().forEach(System.out::println);
//Page不需要泛型
Page<Map<String, Object>> page = new Page<>(1, 5);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age");
Page<Map<String, Object>> pageParam = userMapper.selectMapsPage(page, queryWrapper);
List<Map<String, Object>> records = pageParam.getRecords();
records.forEach(System.out::println);
System.out.println(pageParam.getCurrent());
System.out.println(pageParam.getPages());
System.out.println(pageParam.getSize());
System.out.println(pageParam.getTotal());
System.out.println(pageParam.hasNext());
System.out.println(pageParam.hasPrevious());
}
1、根据id删除
@Test
public void testDeleteById(){
int result = userMapper.deleteById(5L);
System.out.println(result);
}
2、批量删除
@Test
public void testDeleteBatchIds() {
int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10));
System.out.println(result);
}
3、简单条件删除
@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
int result = userMapper.deleteByMap(map);
System.out.println(result);
}
4、逻辑删除
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
逻辑删除的使用场景:
可以进行数据恢复
有关联数据,不便删除
逻辑删除实现流程:
(1)数据库修改
ALTER TABLE `user` ADD COLUMN `deleted` boolean DEFAULT false
(2)实体类修改
@TableLogic
private Integer deleted;
(3)配置(可选)
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
(4)测试
测试后发现,数据并没有被删除,deleted字段的值由0变成了1
测试后分析打印的sql语句,是一条update
**注意:**被删除前,数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
@Test
public void testLogicDelete() {
int result = userMapper.deleteById(1L);
System.out.println(result);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mEiluCqm-1609223758128)(file:///C:/Users/86134/Documents/My%20Knowledge/temp/959191875_files/27b56b5e-39a6-42ba-b7ed-4f109b6ad7bf.png)]
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : 查询条件封装
UpdateWrapper : Update 条件封装
AbstractLambdaWrapper : 使用Lambda 语法
LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper
@SpringBootTest
public class QueryWrapperTests {
@Autowired
private UserMapper userMapper;
}
查询方式 说明
setSqlSelect 设置 SELECT 查询字段
where WHERE 语句,拼接 + WHERE 条件
and AND 语句,拼接 + AND 字段=值
andNew AND 语句,拼接 + AND (字段=值)
or OR 语句,拼接 + OR 字段=值
orNew OR 语句,拼接 + OR (字段=值)
eq 等于=
allEq 基于 map 内容等于=
ne 不等于<>
gt 大于>
ge 大于等于>=
lt 小于<
le 小于等于<=
like 模糊查询 LIKE
notLike 模糊查询 NOT LIKE
in IN 查询
notIn NOT IN 查询
isNull NULL 值查询
isNotNull IS NOT NULL
groupBy 分组 GROUP BY
having HAVING 关键词
orderBy 排序 ORDER BY
orderAsc ASC 排序 ORDER BY
orderDesc DESC 排序 ORDER BY
exists EXISTS 条件语句
notExists NOT EXISTS 条件语句
between BETWEEN 条件语句
notBetween NOT BETWEEN 条件语句
addFilter 自由拼接 SQL
last 拼接在最后,例如:last(“LIMIT 1”)
1、ge、gt、le、lt、isNull、isNotNull
@Test
public void testDelete() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNull("name")
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}
2、eq、ne(seletOne()返回的是一条实体记录,当出现多条时会报错)
@Test
public void testSelectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);//只能返回一条记录,多余一条则抛出异常
System.out.println(user);
}
3、between、notBetween
@Test
public void testSelectCount() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("age", 20, 30);
Integer count = userMapper.selectCount(queryWrapper); //返回数据数量
System.out.println(count);
}
4、like、notLike、likeLeft、likeRight
@Test
public void testSelectMaps() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.select("name", "age")
.like("name", "e")
.likeRight("email", "5");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表
maps.forEach(System.out::println);
}
5、in、notIn、inSql、notinSql、exists、notExists
@Test
public void testSelectObjs() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.in("id", 1, 2, 3);
queryWrapper.inSql("id", "select id from user where id <= 3");
List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表
objects.forEach(System.out::println);
}
6、or、and
@Test
public void testUpdate1() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or()
.between("age", 20, 30);
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}
7、lambda表达式
@Test
public void testUpdate2() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "n")
.or(i -> i.like("name", "a").eq("age", 20));
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}
8、orderBy、orderByDesc、orderByAsc
@Test
public void testSelectListOrderBy() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age", "id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
9、set、setSql
@Test
public void testUpdateSet() {
//修改值
User user = new User();
user.setAge(60);
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.set("name", "Peter")//除了可以查询还可以使用set设置修改的字段
.setSql(" email = '[email protected]'");//可以有子查询
int result = userMapper.update(user, userUpdateWrapper);
}
由于工作中需要用到rtp协议,java暂时没有比较好的开发框架,参考了其他的一些博文,自己摸索着编译了jrtplib库,在此记录一下。编译环境:Ubuntu14.2准备工作:1、安装NDK;2、安装CMake。linux下安装cmake,可执行sudo apt-get install cmake安装。(可参考https://blog.csdn.net/suibianshen2012/...
最近在学习spring集成rabbitMQ,发现一个问题,一直搞了2天也没解决。早上抱着随便试试的态度,不用15672端口,用5672端口就行了,突然就解决了,分享一下。明明本地都已经可以访问了,但启动的话,一直报连接超时。把端口号改成5672,完美解决这个问题....
blog迁移至:http://www.micmiu.com 系统环境:ubuntu10.10 、firefox3.6.10 JDK的安装路径:/opt/jdk1.6.0_20 下面以&lt;JDK dir&gt;表示有关JDK或者JRE的安装这里就不再具体讲述了,不会的可以搜索下有很多这方面的文章。这次还是按照以前的方法给Ubuntu下的firef...
uC/OS-II操作系统移植标准步骤,采用官方源码,保证每个人都能轻松以移植成功
经常在反复的做一件事情,但是没有停下来总结的习惯,而只是处于一个当局者的位置忘了自己的初衷,所以经常停下来想想当初做这件事情的想法,可能会更能理顺我们的思路。从开始接触软件工程巨理论的知识,到云山雾罩的画UML设计图,再到设计模式的深入学习,接着是三层架构的理解,最后是运用这些学到的东西将机房收费系统重构完成,一直在懵懵懂懂中体会着。我想把我的软件工程分为三部曲:雾里看花、衣带渐宽终不悔,...
我们手动编译安装php5.6版本时,与之前安装的老版本不一样,在执行make这一步时会出现错误:make: *** [ext/fileinfo/libmagic/apprentice.lo] Error 1这是由于服务器内存小于1G所导致编译占用资源不足(好吧,我的服务器一共就1G的内存,当然不足)解决办法:在编译参数后面加上一行内容--disable-fileinfoD...
themeleaf高级标签的使用A. url标签的使用使用@{}来表示url,(type=${type})表示路径参数,用来替换路径参数<a th:href="@{http://www.baidu.com/{type}(type=${type})}">link1</a>或者使用@{${}}这样直接传参<div th:style="'backgroun...
百度云登录帐号使用aria2, 帐号会被拉黑名单限速。如果您的下载速度在 100-200 KB/S,很有可能您的百度云账户被限制了,请尝试不登陆百度云帐号导出下载,速度提速显著!最新版本更新:0.96版本 解决选中文件无法导出下载的问题。更新方法:下载并解压:下载地址打开chrome的扩展程序, 选中 ‘开发者模式’finder里面将文件夹拖到扩展程序 页面, 会提示安装由于插件,百度云下载被封,...
AngularJs可以自定义指令。指令一般有4中声明方式:E - 元素名称: A - 属性: C - 类名: M - 注释: 下面直接看实例:新建一个index.htmlAngularJs自定义指令 {{text}} 新建一个css文件ExpanderSimple.css:.expander {
库A引用a.jar,库B引用a.jar,项目引用A和B,编译不行库A引用a.jar,库B引用A,库C引用A,项目引用B和C,可以编译库A引用a.aar(implementation),项目引用A(implementation)和a.aar(implementation),可以编译...
下面这篇文章是我发在《程序员》第1期上的文章。其实是去年10月份为了练笔而写的,一点个人感想而已。大家千万别当真。 这不是你想象中的软件产业 王咏刚,2004年10月----------------------------------- 北京时间早晨6:30,大四学生赵甲子拎着刚从食堂买来的油饼和棒子面粥,站在女生楼下仰头大喊:“下来吃饭了!”楼上娇滴滴地回道:“你送上来!”楼下喘着...
1.cron表达字符含义语法:Seconds Minutes Hours DayofMonth Month DayofWeek YearSeconds Minutes Hours DayofMonth Month DayofWeek比较多的使用的通常是第二条,因为很少任务是按年份去执行的2.常用表达式示范每隔5秒执行一次:*/5 * * * * ?每隔1分钟执行一次:0 */1...