谷粒学院-2-mybatisplus
一、参考文档
参考教程: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
 
 
 - AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
 
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的更多相关文章
- 前端知识(二)08-Vue.js的路由-谷粒学院
		
目录 一.锚点的概念 二.路由的作用 三.路由实例 1.复制js资源 2.创建 路由.html 3.引入js 4.编写html 5.编写js 一.锚点的概念 案例:百度百科 特点:单页Web应用,预先 ...
 - 前端知识(二)05-Eslint语法规范检查-谷粒学院
		
目录 一.ESLint简介 二.启用ESLint 1.ESLint插件安装 2.插件的扩展设置 3.确认开启语法检查 三.ESLint规则说明 1.规则说明 2.语法规则 一.ESLint简介 ESL ...
 - 前端知识(二)01-NPM包管理器-谷粒学院
		
目录 一.简介 二.使用npm管理项目 1.项目初始化 2.修改npm镜像 3.npm install命令的使用 4.其它命令 一.简介 什么是NPM NPM全称Node Package Manage ...
 - 前端知识(一)04 Vue.js入门-谷粒学院
		
目录 一.介绍 1.Vue.js 是什么 2.初识Vue.js 二.基本语法 1.基本数据渲染和指令 2.双向数据绑定 3.事件 4.修饰符 5.条件渲染 6.列表渲染 7.实例生命周期 一.介绍 1 ...
 - 一、Spring Boot 入门
		
1.Spring Boot 简介 简化Spring应用开发的一个框架: 整个Spring技术栈的一个大整合: J2EE开发的一站式解决方案: 2.微服务 2014,martin fowler 微服务: ...
 - java基础语法-内部类与匿名内部类
		
1.成员内部类(声明在类内部&&方法之外) class Person{ String name = "韩梅梅"; int age; class Bird{ Stri ...
 - IDEA 对比eclipse环境调节
		
小子刚刚接触Intellij IDEA,以前用的都是eclipse.鉴于ieda的火热,开始学习之旅.本文会随时更新,记载idea中的一些调节方法,尽量在环境的配置上跟eclipse接近些. 在此感谢 ...
 - 尚硅谷《全套Java、Android、HTML5前端视频》
		
尚硅谷<全套Java.Android.HTML5前端视频> (百万谷粉推荐:史上最牛.最适合自学的全套视频.资料及源码) [尚硅谷官网资料导航] 谷粒学院在线学习:http://www.g ...
 - IDEA使用及优化
		
1.修改IntelliJ IDEA\bin\idea64.exe.vmoptions文件的内容 2.Setting配置 2.1 设置主题 2.2 设置编辑区主题 如果想要更多的主题效果的话,可以到如下 ...
 
随机推荐
- C# & JS 判断字符串是否为日期格式
			
在C#中,对格式的判断有一类专门函数,那就是TryParse.TryParse在各个不同的类型类(如int,string,DateTime)中,都是存在的.在TryParse中一般有两个参数,一个是待 ...
 - SpringBoot原理深入及源码剖析(一) 依赖管理及自动配置
			
前言 传统的Spring框架实现一个Web服务需要导入各种依赖jar包,然后编写对应的XML配置文件等,相较而言,SpringBoot显得更加方便.快捷和高效.那么,SpringBoot究竟是如何做到 ...
 - 137. 只出现一次的数字 II
			
2021-04-30 LeetCode每日一题 链接:https://leetcode-cn.com/problems/single-number-ii/ 方法1:使用map记录每个数出现的次数,再找 ...
 - .Net Core with 微服务 - Consul 注册中心
			
上一次我们介绍了 Ocelot 网关的基本用法.这次我们开始介绍服务注册发现组件 Consul 的简单使用方法. 服务注册发现 首先先让我们回顾下服务注册发现的概念. 在实施微服务之后,我们的调用都变 ...
 - Mysql_SQLyog 数据库的创建
			
1.创建数据库 CREATE DATABASE [IF NOT EXISTS] westos; 2.删除数据库 DROP DATABASE [IF EXISTS] westos; 3.使用数据库 -- ...
 - 【曹工杂谈】Mysql客户端上,时间为啥和本地差了整整13个小时,就离谱
			
瞎扯一点非技术 本来今天上午就打算写的,结果中途被别的事吸引了注意力,公司和某保险公司合作推了一个医疗保险,让我们给父母买,然后我研究了半天条款:又想起来之前买的支付宝那个好医保,也买了两年多了,但是 ...
 - java并发编程JUC第十二篇:AtomicInteger原子整型
			
AtomicInteger 类底层存储一个int值,并提供方法对该int值进行原子操作.AtomicInteger 作为java.util.concurrent.atomic包的一部分,从Java 1 ...
 - 复习Spring第四课---Spring对国际化的支持
			
其实国际化这东西很少使用,之前也就是粗略的学了下,趁今天有空,拿出来稍微写写.以前学android开发的时候,类似于多语言的版本.差别就是一个是手机打开,一个是浏览器打开,本质是一样的. 在Sprin ...
 - 在 NUC980 上运行 RT-Thread
			
NUC980 & RT-Thread (1) NUC980 nuc980 是新塘推出的基于 ARM926EJ-S,集成 64 MB 或 128 MB DDR-II 的处理器,主频可以达到300 ...
 - 通过浏览器运行cmd命令、启动steam
			
我们先来看看实现起来的效果,我们在浏览器中输入ping so.com 试试打开计算器.启动steam 要实现这个效果其实用到了浏览器自定义协议,我们可以通过自定义协议打开wechat.扣扣.emali ...