一、参考文档

官网:http://mp.baomidou.com/

参考教程:http://mp.baomidou.com/guide/

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化 开发、提高效率而生。


快速开始参考:http://mp.baomidou.com/guide/quick-start.html

测试项目: mybatis_plus

数据库:mybatis_plus

二、数据库准备工作

先创建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, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

结果:

三、流程:

导入依赖

<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

数据源配置

# 数据源
# 数据库驱动在mysql8之前使用com.mysql.cj.jdbc.Driver,之后使用com.mysql.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Spring Boot 2.1 集成了 8.0版本的jdbc驱动,这个版本的 jdbc 驱动需要添加这个后缀
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456

创建pojo类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}

创建包 mapper 编写Mapper 接口

@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> { }

测试

@SpringBootTest
class DemoApplicationTests {
@Autowired
UserMapper userMapper; @Test
void contextLoads() {
System.out.println(userMapper.selectList(null));
} }

结果:

四、添加日志操作

配置信息

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

出现日志

五、insert操作

@SpringBootTest
class DemoApplicationTests {
@Autowired
UserMapper userMapper; @Test
void contextLoads() {
User hell = new User();
hell.setName("hell");
hell.setAge(23);
hell.setEmail("22@qq,com");
int result = userMapper.insert(hell);
//result是代码影响的函数
System.out.println(result);
//可以看到id值自动填入,这是mp帮我们做到的
//注意:数据库插入id值默认为:全局唯一id
System.out.println(hell);
} }

结果:

六、主键策略

1、全局唯一ID策略

MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID 参考资料:分布式系统唯一ID生成方案汇总:https://www.cnblogs.com/haoxinyue/p/5208136.html

2、自增策略

流程:

创建数据库时设置id自增

创建pojo类时

@TableId(type = IdType.AUTO)
private Long id;

结果:

其他主键策略

/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型
* 用户自己输入
*/
NONE(1),
/**
* 用户输入ID
* 用户自己输入
*/
INPUT(2),
/**
* 全局唯一ID (idWorker)
* 一般对应Long数字类型
*/
ID_WORKER(3),
/**
* 全局唯一ID (UUID)
*/
UUID(4),
/**
* 字符串全局唯一ID (idWorker 的字符串表示)
*/
ID_WORKER_STR(5);

七、更新数据操作

流程:

@SpringBootTest
class DemoApplicationTests {
@Autowired
UserMapper userMapper; @Test
void contextLoads() {
User hell = new User();
hell.setId(2L);
hell.setName("hell");
hell.setAge(23);
hell.setEmail("22@qq,com");
int result = userMapper.updateById(hell);
//result是代码影响的函数
System.out.println(result);
//可以看到id值自动填入,这是mp帮我们做到的
System.out.println(hell);
} }

mp实现乐观锁

什么是乐观锁:

是一种解决方案,主要解决多人并发操作时更新丢失问题,对应mysql事务的隔离性

悲观锁:

与乐观锁对应都是解决多人并发操作时的更新丢失问题,但时悲观锁是串行的,简单来说就是我操作数据库的时候别人不能操作数据库

主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新

乐观锁实现方法:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

流程:

在数据库中添加版本字段

pojo类中

@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;

需要在字段自动填充中初始化版本号

@Component
public class MyMetaObjectHandle implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
/*metaObject是元数据对象,就比如在jdbc中query一个数据之后也会得到一个元数据
*这个元数据内包含了查询到的数据在数据库中存储的x和y信息
*/
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
/*给版本号在创建时提供一个初始值1*/
this.setFieldValByName("version",1,metaObject);
} @Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}

然后就直接测试了

但注意,一定要遵守先查后改的原则,在查的时候得到包含了version信息的user对象,在更新的时候就会去找到这个版本号,确认是否对应,如果一致然后才更新,否则更新失败,而且再更新user时会对version进行+1操作

可见mp的乐观锁主要解决从查到数据到更新数据途中是否有其他人或其他线程修改了数据

/*乐观锁成功*/
@Test
public void testOptimisticLockerSuccess(){
/*查询出用户*/
HashMap<String, Object> map = new HashMap<>();
map.put("name","asdfasdf");
List<User> users = userMapper.selectByMap(map);
System.out.println(users); /*修改用户*/
for (User user:users){
System.out.println("修改前"+user);
user.setAge(20);
int i = userMapper.updateById(user);
System.out.println(i);
System.out.println("修改后"+user);
} } /*乐观锁失败*/
@Test
public void testOptimisticLockerFailures(){
/*查询出用户*/
HashMap<String, Object> map = new HashMap<>();
map.put("name","asdfasdf");
List<User> users = userMapper.selectByMap(map);
System.out.println(users); /*修改用户*/
for (User user:users){
System.out.println("修改前"+user);
user.setAge(20);
user.setVersion(user.getVersion()-1);
int i = userMapper.updateById(user);
System.out.println(i);
System.out.println("修改后"+user);
} }

八、自动填充

比如创建时间和更新时间需要自动填充就会很方便

流程:

创建数据库时添加两个字段

在pojo类中

@TableField(fill = FieldFill.INSERT)
private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;

创建handle包下面创建一个组件

@Component
public class MyMetaObjectHandle implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
/*metaObject是元数据对象,就比如在jdbc中query一个数据之后也会得到一个元数据
*这个元数据内包含了查询到的数据在数据库中存储的x和y位置信息
*/
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
} @Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}

九、查询操作

1、通过id查询

//查询单个用户
User user = userMapper.selectById(1);
System.out.println(user);

2、通过多个id查询

//查询多个用户
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
System.out.println(users);

3、条件查询

//条件查询
HashMap<String, Object> map = new HashMap<>();
map.put("name","Tom");
map.put("age",28);
List<User> users1 = userMapper.selectByMap(map);
System.out.println(users1);

4、分页查询

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

流程:

需要添加一个bean到IOC容器中去

//mybatisplus的分页插件
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}

再分页查询

//分页查询
Page<User> page = new Page<>(1,5);
//selectPage方法后一个参数是wrapper,包装条件
userMapper.selectPage(page, null); //通过page获取全部的分页数据
List<User> records = page.getRecords();
System.out.println(records);
System.out.println(page.getCurrent()); //当前页
System.out.println(page.getPages()); //有多少页
System.out.println(page.getSize()); //每页多少条记录
System.out.println(page.getTotal()); //总共有多少条记录
System.out.println(page.hasNext()); //是否有下一页
System.out.println(page.hasPrevious()); //是否有上一页

十、删除操作

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据

  • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍 旧能看到此条数据记录

1、物理删除:

通过id删除

/*通过id删除*/
@Test
public void deleteById(){ int i = userMapper.deleteById(2L);
System.out.println(i);
}

通过多个id批量删除

/*通过多个id批量删除*/
@Test
public void deleteBatchIds(){ int i = userMapper.deleteBatchIds(Arrays.asList(1L, 3L));
System.out.println(i);
}

条件删除

/*条件删除*/
@Test
public void deleteByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("id",1418944135407554564L); int i = userMapper.deleteByMap(map);
System.out.println(i);
}

2、逻辑删除

流程:

在数据库中添加deleted字段

pojo中操作

@TableLogic
@TableField(fill = FieldFill.INSERT)
public Integer deleted;

用自动填充来初始化deleted值

@Component
public class MyMetaObjectHandle implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
/*metaObject是元数据对象,就比如在jdbc中query一个数据之后也会得到一个元数据
*这个元数据内包含了查询到的数据在数据库中存储的x和y信息
*/
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
/*给版本号在创建时提供一个初始值1*/
this.setFieldValByName("version",1,metaObject);
/*给被删除状态设置一个初始值;
* 默认0为没删除,1为删除,
* 可以在配置参数文件中中修改*/
this.setFieldValByName("deleted",0,metaObject);
} @Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}

配置Bean组件

/*mybatisplus的逻辑删除插件*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}

测试:

/*通过id删除*/
@Test
public void deleteById(){ int i = userMapper.deleteById(2L);
System.out.println(i);
}

注意:内部的sql语句包含了where deleted = 0,所以没有初始化deleted为0的话是删除不了的,会找不到这个数据

测试删除之后的查询:

MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断

/**
* 测试 逻辑删除后的查询:
* 不包括被逻辑删除的记录
*/
@Test
public void testLogicDeleteSelect() {
User user = new User();
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}

逻辑层的删除数据也查不到了

十一、性能分析插件

性能分析拦截器,用于输出每条 SQL 语句及其执行时间 SQL 性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题

主要用于开发环境

流程:

加一个配置bean即可

/*sql性能分析插件*/
@Bean
@Profile({"dev"}) //只有开发环境为dev时才会开启开启这个bean配置
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100); //超过此设置的ms的sql不执行
performanceInterceptor.setFormat(true); //sql语句是否格式化,默认为false
return performanceInterceptor;
}

测试:

十二、wrapper实现复杂查询

  • Wrapper : 条件构造抽象类,最顶端父类

    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件

      • QueryWrapper : Entity 对象封装操作类,不是用lambda语法
      • UpdateWrapper : Update 条件封装,用于Entity对象更新操作
    • AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
      • LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
      • LambdaUpdateWrapper : Lambda 更新封装Wrapper

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);
}

对应sql:

UPDATE user SET deleted=1 WHERE deleted=0 AND name IS NULL AND age >= ? AND email IS NOT NULL

2、eq、ne

等于、不等于

@Test
public void testSelectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}

对应sql:

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name = ?

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);
}

对应sql:

SELECT COUNT(1) FROM user WHERE deleted=0 AND age BETWEEN ? AND ?

4、allEq

所有条件全部一致

@Test
public void testSelectList() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("name", "Jack");
map.put("age", 20);
queryWrapper.allEq(map);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

对应sql:

SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 AND name = ? AND id = ? AND age = ?

5、like、notLike、likeLeft、likeRight

@Test
public void testSelectMaps() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.notLike("name", "e")
.likeRight("email", "t");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表
maps.forEach(System.out::println);
}

对应sql:

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name NOT LIKE ? AND email LIKE ?

6、in、notIn、inSql、notinSql、exists、notExists

in、notIn:

  • notIn("age",{1,2,3})--->age not in (1,2,3)
  • notIn("age", 1, 2, 3)--->age not in (1,2,3)

inSql、notinSql:可以实现子查询

  • 例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
  • 例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)
@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);
}

对应sql:

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND id IN (select id from user where id < 3)

7、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则默认为使用 and 连
.or()
.between("age", 20, 30);
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}

对应sql:

UPDATE user SET name=?, age=?, update_time=? WHERE deleted=0 AND name LIKE ? OR age
BETWEEN ? AND ?

8、嵌套or、嵌套and

这里使用了lambda表达式,or中的表达式最后翻译成sql时会被加上圆括号

@Test
public void testUpdate2() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or(i -> i.eq("name", "李白").ne("age", 20));
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}

9、orderBy、orderByDesc、orderByAsc

@Test
public void testSelectListOrderBy() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

对应sql:

SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 ORDER BY id DESC

10、last

直接拼接到 sql 的最后

只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

@Test
public void testSelectListLast() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.last("limit 1");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

对应sql:

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 limit 1

11、指定要查询的列

@Test
public void testSelectListColumn() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "name", "age");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

对应sql:

SELECT id,name,age FROM user WHERE deleted=0

12、set、setSql

最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段

@Test
public void testUpdateSet() {
//修改值
User user = new User();
user.setAge(99);
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.set("name", "老李头")//除了可以查询还可以使用set设置修改的字段
.setSql(" email = '123@qq.com'");//可以有子查询
int result = userMapper.update(user, userUpdateWrapper);
}

对应sql:

UPDATE user SET age=?, update_time=?, name=?, email = '123@qq.com' WHERE deleted=0 AND name LIKE ?

谷粒学院-2-mybatisplus的更多相关文章

  1. 前端知识(二)08-Vue.js的路由-谷粒学院

    目录 一.锚点的概念 二.路由的作用 三.路由实例 1.复制js资源 2.创建 路由.html 3.引入js 4.编写html 5.编写js 一.锚点的概念 案例:百度百科 特点:单页Web应用,预先 ...

  2. 前端知识(二)05-Eslint语法规范检查-谷粒学院

    目录 一.ESLint简介 二.启用ESLint 1.ESLint插件安装 2.插件的扩展设置 3.确认开启语法检查 三.ESLint规则说明 1.规则说明 2.语法规则 一.ESLint简介 ESL ...

  3. 前端知识(二)01-NPM包管理器-谷粒学院

    目录 一.简介 二.使用npm管理项目 1.项目初始化 2.修改npm镜像 3.npm install命令的使用 4.其它命令 一.简介 什么是NPM NPM全称Node Package Manage ...

  4. 前端知识(一)04 Vue.js入门-谷粒学院

    目录 一.介绍 1.Vue.js 是什么 2.初识Vue.js 二.基本语法 1.基本数据渲染和指令 2.双向数据绑定 3.事件 4.修饰符 5.条件渲染 6.列表渲染 7.实例生命周期 一.介绍 1 ...

  5. 一、Spring Boot 入门

    1.Spring Boot 简介 简化Spring应用开发的一个框架: 整个Spring技术栈的一个大整合: J2EE开发的一站式解决方案: 2.微服务 2014,martin fowler 微服务: ...

  6. java基础语法-内部类与匿名内部类

    1.成员内部类(声明在类内部&&方法之外) class Person{ String name = "韩梅梅"; int age; class Bird{ Stri ...

  7. IDEA 对比eclipse环境调节

    小子刚刚接触Intellij IDEA,以前用的都是eclipse.鉴于ieda的火热,开始学习之旅.本文会随时更新,记载idea中的一些调节方法,尽量在环境的配置上跟eclipse接近些. 在此感谢 ...

  8. 尚硅谷《全套Java、Android、HTML5前端视频》

    尚硅谷<全套Java.Android.HTML5前端视频> (百万谷粉推荐:史上最牛.最适合自学的全套视频.资料及源码) [尚硅谷官网资料导航] 谷粒学院在线学习:http://www.g ...

  9. IDEA使用及优化

    1.修改IntelliJ IDEA\bin\idea64.exe.vmoptions文件的内容 2.Setting配置 2.1 设置主题 2.2 设置编辑区主题 如果想要更多的主题效果的话,可以到如下 ...

随机推荐

  1. CUDA刷新器:CUDA编程模型

    CUDA刷新器:CUDA编程模型 CUDA Refresher: The CUDA Programming Model CUDA,CUDA刷新器,并行编程 这是CUDA更新系列的第四篇文章,它的目标是 ...

  2. JVM_ 动态链接

    虚拟机栈: -> 栈帧---对应每个方法----> 包含: 局部变量表, 本地方法栈, 动态链接, 方法出口, 动态链接: 每个栈帧都保存了 一个 可以指向当前方法所在类的 运行时常量池, ...

  3. 【NX二次开发】用户出口函数介绍

    用户出口(User Exit)是NX Open 中的一个重要概念.NX在运行过程中某些特定的位置存在规定的出口,当进程执行到这些出口时,NX会自动检查用户是否在此处已定义了指向内部程序位置的环境变量: ...

  4. docker 自定义部署Springboot——依赖与代码分离部署

    第一步:执行mvn package 命令打出jar包,然后解压jar包,把lib放到服务器合适的目录下面 第二步:打出不带jar包的SpringBoot工程 首先配置pom.xml文件 <bui ...

  5. Mysql8关于hashjoin的代码处理方式

    Mysql8关于hashjoin的代码处理方式 目录 Mysql8关于hashjoin的代码处理方式 1 表的Schema如下所示: 2 HashJoin代码实现 3 总结 1 表的Schema如下所 ...

  6. Kubernetes ConfigMap详解,多种方式创建、多种方式使用

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 配置是程序绕不开的话题,在Kubernetes中使用ConfigMap来配置,它本质其实就是键值对.本文讲解如何 ...

  7. 浅析uniapp

     前端跨平台框架 之uniapp入门浅析 技术的发展总日新月异,处在风口,前端技术的发展尤为迅速,跨平台的概念也在前端流行起来.从最早期PhoneGap.lonic.Cordova,到近年来的Reac ...

  8. 试着给VuePress添加渐进式Web应用(PWA)支持,基于vuepress/plugin-pwa,点亮离线访问

    背景 有时候,我们也希望VuePress构建的文档中心能支持离线访问,这时候我们需要给他添加渐进式Web应用(PWA,Progressive Web App)的支持,根据官方文档指引,我们可以借助插件 ...

  9. Sherlock and His Girlfriend题解

    题目描述 Sherlock 有了一个新女友(这太不像他了!).情人节到了,他想送给女友一些珠宝当做礼物. 他买了 n件珠宝.第i 件的价值是i+1.那就是说,珠宝的价值分别为2,3,4,-,n+1 . ...

  10. Jquery 插件 chosen_v1.8.7 下拉复选框带搜索功能

    地址:https://harvesthq.github.io/chosen/ 效果: 因为只需要这个功能,就只研究这个功能了,代码: <!doctype html> <html la ...