Just Do Java

Java 's Blog


  • 首页

  • 分类

  • 作者

  • 归档

  • 关于

通过几段 Java 代码理解 RPC

发表于 2020-05-13 | 分类于 Java

RPC 远程过程调用可以说是分布式系统的基础,本文将通过 Java 演示一次普通的 rpc 调用到底发生了什么。

阅读全文 »

常用的 JDK 命令,你知道几个?

发表于 2020-05-11 | 分类于 Java

Hello 大家好,我是鸭血粉丝,不知道你有没有过这样的经历,经常在面试的时候被问到 JDK 相关的命令,如何排查线上的问题,线上程序突然崩了要怎么处理,等等类似这种场景。其实并不是每个开发人员都能有这种实战经验,现实工作中往往很多开发人员是接触不到线上环境的。但是作为一个以 Java 谋生的程序员,如果连这些 JDK 自带的一些命令都不知道,那也说不过去。

阿粉以前也是这样,从来没有接触过线上环境,有任何问题都是运维人员去处理,因为相关制度不允许开发人员接触生产服务器。但是作为一个有梦想的开发人员,不能接触不代表就不用学,阿粉还是私下好好学习了一波,万一哪天就用上了呢。

img

阅读全文 »

Java 生态圈中的嵌入式数据库,哪家强?

发表于 2020-05-07 | 分类于 数据库

嵌入式数据库一个很陌生的词汇,以前只是听说,但是没有真正使用过,今天小编和大家一起来揭开它的面纱。

阅读全文 »

明明谢谢参与的大小跟特等奖的大小一样,可是为什么我们大部分情况还是只能中谢谢参与

发表于 2020-05-06 | 分类于 算法

Hello 大家好,我是鸭血粉丝,不知道你有没有过这样的经历,经常在手机上参与各种抽奖,但是明明眼看着就要中特等奖了,但是最后还是中了个谢谢参与,阿粉我就是一个中奖绝缘体,以前总是觉得是自己的运气不好,但是自从知道了背后的暗箱操作过后,阿粉就见怪不怪了。

img

阅读全文 »

深入浅出 synchronized 与锁

发表于 2020-05-06 | 分类于 Java并发

synchronized 关键字

说到锁,都会提 synchronized 。这个英文单词儿啥意思呢?翻译成中文就是「同步」的意思

阅读全文 »

差点儿被妹子难住的我

发表于 2020-05-01 | 分类于 数据结构与算法

今天差点儿被妹子难住了,具体情况容我慢慢说

阅读全文 »

mybatis系列之mybatis一级缓存

发表于 2020-04-27 | 分类于 Java

hello~各位读者好,我是鸭血粉丝(大家可以称呼我为「阿粉」)。今天,阿粉带着大家来了解一下 mybatis 一级缓存的实现原理。

1.上期回顾

首先,我们还是回顾一下上篇文件的类容。毕竟离上次讲 mybatis还是过去了很久,汗~。

还是先看下这个测试类,大家还有印象吗:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MybatisTest {
    @Test
    public void testSelect() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            FruitMapper mapper = session.getMapper(FruitMapper.class);
            Fruit fruit = mapper.findById(1L);
            System.out.println(fruit);
        } finally {
            session.close();
        }
    }
}

上篇源码分析讲了 SqlSession 构建的过程。这次,我们来了解下 mybatis 一级缓存的实现原理。

2.mybatis缓存

2.1 缓存的作用

mybatis 缓存的作用就是提升查询的效率和减少数据库的压力。

2.2 mybatis的缓存类

mybatis缓存相关的类都在cache包里面,有个 Cache 的接口,默认实现是 PerpetualCache 类。当然,还有一些其他缓存类,是通过装饰器模式实现的。我们来看下包结构:

然后看下这些缓存类的作用:

  • PerpetualCache :基本缓存类,默认实现。
  • LruCache :LRU策略的缓存,作用是当缓存到达上限时候,删除最近最少使用的缓存。
  • FifoCache : FIFO 策略的缓存,作用是当缓存到达上限时候,删除最先入队的缓存。
  • SoftCache : 带清理策略的缓存,作用是通过JVM 的软引用来实现缓存,当JVM内存不足时,会自动清理掉这些缓存。
  • WeakCache :带清理策略的缓存,作用是通过JVM 的弱引用来实现缓存,当JVM内存不足时,会自动清理掉这些缓存。
  • LoggingCache : 带日志功能的缓存。
  • SynchronizedCache : 同步缓存,基于synchronized 关键字实现,作用是解决并发问。
  • BlockingCache : 阻塞缓存,通过在get/put 方式中加锁,保证只有一个线程操作缓存,基于Java 重入锁实现
  • SerializedCache : 支持序列化的缓存,将对象序列化以后存到缓存中,取出时反序列化。
  • ScheduledCache :定时调度的缓存,在进行get/put/remove/getSize 等操作前,判断缓存时间是否超过了设置的最长缓存时间(默认是一小时),如果是则清空缓存–即每隔一段时间清 空一次缓存。这个有点像 redis 设置的超时时间。
  • TransactionalCache :事务缓存。

2.3 一级缓存

一级缓存也叫本地缓存,MyBatis 的一级缓存是在会话(SqlSession)层面进行缓存的。MyBatis 的一级缓存是默认开启的,不需要任何的配置。

上面说到缓存的默认实现对象是 PerpetualCache ,那么这个对象是在哪里维护的呢?MyBatis 的一级缓存是在会话共享,那么我们先看下 SqlSession 这个里面有没有维护。SqlSession 是接口,阿粉上一篇源码讲了创建 SqlSession 最后返回的是 DefaultSqlSession 对象。我们看下这个类的成员变量:

1
2
3
4
5
6
7
8
9
10
public class DefaultSqlSession implements SqlSession {

  private final Configuration configuration;
  private final Executor executor;

  private final boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;
    。。。
}

首先,Configuration 对象是全局的,不可能放在这个里面。后面3个也不像,我们看下 Executor 类。

Executor 类也是一个接口,我们看下它的抽象实现 BaseExecutor :

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class BaseExecutor implements Executor {

  private static final Log log = LogFactory.getLog(BaseExecutor.class);

  protected Transaction transaction;
  protected Executor wrapper;

  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;
    。。。
}

看到没,缓存对象是在这个对象里面维护的。Executor 这个类就是执行 sql 的,可以理解为 sql 执行器。

然后再来看下 PerpetualCache 类是怎么实现缓存的。

1
2
3
4
5
6
7
public class PerpetualCache implements Cache {

  private final String id;

  private Map<Object, Object> cache = new HashMap<>();
    。。。
}

很明显,是用 HashMap 实现的。既然是 HashMap ,那么用什么作为 key 呢?我们看下 BaseExecutor 里面有一个 createCacheKey() 的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

然后我们来看下:

  • ms.getId() :ms 是解析 mapper.xml 创建的对象,每个 select/update/delete/insert 标签会创建一个 MappedStatement 对象。id 就是 mapper 的 namespace 加上 4种标签的 id。
  • rowBounds.getOffset() :分页参数。
  • rowBounds.getLimit() : 分页参数。
  • boundSql.getSql() : sql 语句。
  • value :这个是解析的 sql 传入的参数。
  • configuration.getEnvironment().getId() : 这个是配置的数据源 id ,spring 里面,数据源不会在 mybatis里面配置。

这就是缓存 key 的组成。

最后阿粉还是用例子来验证一下一级缓存是否是在 session 中共享的。判断是否命中缓存,可以根据是否打印 sql 来判断,没有缓存就会去数据库查询,所有会打印 sql,否则就是有缓存,不会打印sql。来看下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 @Test
    public void testSelect() throws IOException {

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession session = sqlSessionFactory.openSession();
        try {
            FruitMapper mapper = session.getMapper(FruitMapper.class);
            Fruit fruit = mapper.findById(1L);
            System.out.println("第一次查询:"+ fruit);
            Fruit fruit1 = mapper.findById(1L);
            System.out.println("第二次查询:" + fruit1);
        } finally {
            session.close();
        }
    }

结果为:

1
2
3
4
5
6
7
8
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@42f48531]
==>  Preparing: select id,name from fruit where id =? 
==> Parameters: 1(Long)
<==    Columns: id, name
<==        Row: 1, 苹果
<==      Total: 1
第一次查询:Fruit(id=1, name=苹果)
第二次查询:Fruit(id=1, name=苹果)

说明同一个 session 里面,缓存是共享的。接下来我们在不同的 session 中看下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MybatisTest {

    @Test
    public void testSelect() throws IOException {

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession session = sqlSessionFactory.openSession();
        SqlSession session2 = sqlSessionFactory.openSession();
        try {
            FruitMapper mapper = session.getMapper(FruitMapper.class);
            FruitMapper mapper2 = session2.getMapper(FruitMapper.class);
            Fruit fruit = mapper.findById(1L);
            System.out.println("第一次查询:"+ fruit);
            Fruit fruit1 = mapper2.findById(1L);
            System.out.println("第二次查询:" + fruit1);
        } finally {
            session.close();
        }

    }
}

结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@42f48531]
==>  Preparing: select id,name from fruit where id =? 
==> Parameters: 1(Long)
<==    Columns: id, name
<==        Row: 1, 苹果
<==      Total: 1
第一次查询:Fruit(id=1, name=苹果)
Opening JDBC Connection
Created connection 1358857082.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@50fe837a]
==>  Preparing: select id,name from fruit where id =? 
==> Parameters: 1(Long)
<==    Columns: id, name
<==        Row: 1, 苹果
<==      Total: 1
第二次查询:Fruit(id=1, name=苹果)

说明不同的 session ,缓存是不共享的。

3.总结

今天的 mybatis 一级缓存到这里就结束了。喜欢阿粉的同学记得点个赞哦。我们下次再见。

阅读全文 »

龙岭迷窟真的这么好看?今天我们就用 Java 爬取豆瓣数据好好分析一下!|原创

发表于 2020-04-25 | 分类于 Java

首图来自最近热播的『鬼吹灯之龙岭迷窟』,看过上一部『鬼吹灯之怒晴湘西』同学应该能看懂这个笑点。 潘粤明老师上一部还是卸岭魁首陈玉楼,这一部摇身一变成了胡八一。

阅读全文 »

听说,这些表情包只有程序员才懂

发表于 2020-04-24 | 分类于 程序人生

hello~各位读者好,我是鸭血粉丝(大家可以称呼我为「阿粉」)。最近阿粉下班写作没灵感的时候就会去刷刷知乎,找找灵感,这不就有了今天这篇文章

阅读全文 »

做一个程序媛是一种什么体验?

发表于 2020-04-23 | 分类于 程序人生

是一种什么体验呢?

阅读全文 »

我用两行代码搞定了,结果妹子有更巧妙的办法

发表于 2020-04-23 | 分类于 数据结构与算法

在线认输。

阅读全文 »

分享几款让你事半功倍的装机必备软件

发表于 2020-04-22 | 分类于 工具

Hello 大家好,我是鸭血粉丝,之前给大家分享过 Mac 上的几款好用的软件,没看过的可以去看看Mac 上必备的常用软件,你值得拥有。今天给大家分享几个软件,都是阿粉平时工作和学习中常用的软件。

阅读全文 »

面试官:兄弟,你培训出来才2年,你要30K,你把我当傻子么?

发表于 2020-04-22

hello~各位读者好,我是鸭血粉丝(大家可以称呼我为「阿粉」),今天阿粉和公司面试官沟通了一波,瞬间想到了之前的一个朋友,于是就发生了这样的故事。

阅读全文 »

实体类的属性映射怎么可以少了它?

发表于 2020-04-19 | 分类于 java

我们都知道,随着一个工程的越来越成熟,模块划分会越来越细,其中实体类一般存于 domain 之中,但 domain 工程最好不要被其他工程依赖,所以其他工程想获取实体类数据时就需要在各自工程写 model,自定义 model 可以根据自身业务需要映射相应的实体属性。这样一来,这个映射工程貌似并不简单了。阿粉差点就犯难了……

阅读全文 »

码农三十岁之后过上了怎么的生活?

发表于 2020-04-19

hello~各位读者好,我是鸭血粉丝(大家可以称呼我为「阿粉」)。

阅读全文 »

程序媛实习生 VS 程序员实习生

发表于 2020-04-12 | 分类于 程序人生

中了几条?

阅读全文 »

面试必问的 MySQL 知识点,你还有哪些没准备好,赶紧收藏脑图!

发表于 2020-04-11 | 分类于 MySQL

Hello,大家好,我是鸭血粉丝,虽说今年的大环境不是很好,但是现在毕竟是金三银四,有些公司还是在招聘的。MySQL 作为我们 Java 工程师最常用的数据库,不管是在日常的工作中还是面试中,我们都必须要对 MySQL 常见的一些知识有很好的储备,这样在面试的过程中才可以做到得心应手。下面阿粉从 MySQL 最高频的几个知识点给大家介绍一下。

更详细的脑图大家可以关注公众号,在后台回复【MySQL脑图】获得。

阅读全文 »

我靠这个知识撩到了公司的程序媛

发表于 2020-04-11 | 分类于 java , 基础

进来瞅瞅阿粉的故事

阅读全文 »

别说 Python 会生成二维码,Java也会。

发表于 2020-04-09

前几天看了Python的一个公众号发了一篇文章,说可以生成二维码啥的,然后说多么的简单,这时候阿粉就表示非常不服气了,凭啥说你的那么简单,说的我们 Java 做出的二维码很复杂一样。今天阿粉就教给大家来生成一个小小的二维码,而且也是一样的简单。

阅读全文 »

从雷霄桦到司徒正美,那些因为天赋异禀而被上帝召唤回去的人你们还好吗?

发表于 2020-04-04 | 分类于 杂谈

Hello 大家好,我是鸭血粉丝,大家都叫我阿粉。

清明时节雨纷纷,这个清明跟往年的太不一样,我记得以前最调侃的一句话就是:现在的人把清明节过的就跟情人节一样。但是今年的清明节回归了原本属于它的气氛,4 月 4 日 10 点全国降半旗静默 3 分钟,防空警报鸣响,汽车公交等交通设备鸣笛三分钟,用于哀悼在疫情中牺牲的英雄壮士,各大游戏停服一天,各大论坛停更一天,各大 APP 首页黑白一天。

阅读全文 »
1 … 13 14 15 … 32
Java Geek Tech

Java Geek Tech

一群热爱 Java 的技术人

633 日志
116 分类
24 作者
RSS
GitHub 知乎
Links
  • 纯洁的微笑
  • 沉默王二
  • 子悠
  • 江南一点雨
  • 炸鸡可乐
  • 郑璐璐
  • 程序通事
  • 懿
© 2019 - 2022 Java Geek Tech
由 Jekyll 强力驱动
主题 - NexT.Mist