MyBatis 实战篇
MyBatis 使用 Redis 缓存前言在MyBatis 缓存一节中,我们介绍了 MyBatis 的多级缓存。MyBatis 的二级缓存可在多个会话中共享缓存,但是这也加大了内存的使用空间,如果二级缓存空间占有量过多势必会导致程序运行空间的不足,因此我们需要将二级缓存转移到专业的缓存服务器上。
Redis 是一个高性能的 kv 数据库,被广泛的使用在缓存服务上,MyBatis 项目开发者提供了 Redis 缓存的实现。本小节我们将一起来学习如何在 MyBatis 中集成 Redis 缓存。
准备添加依赖在项目的 pom.xml 文件中添加上 mybatis-redis 依赖:
12345<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-redis</artifactId> <version>1.0.0-beta2</version></dependency>
mybatis-redis 目前 ...
MyBatis 高级篇
MyBatis 缓存前言频繁地查询必然会给数据库带来巨大的压力,为此 MyBatis 提供了丰富的缓存功能。缓存可以有效的提升查询效率、缓解数据库压力,提高应用的稳健性。
MyBatis 的缓存有两层,默认情况下会开启一级缓存,并提供了开启二级缓存的配置。本小节我们将一起学习 MyBatis 的缓存,充分地了解和使用它。
一级缓存MyBatis 一级缓存是默认开启的,缓存的有效范围是一个会话内。一个会话内的 select 查询语句的结果会被缓存起来,当在该会话内调用 update、delete 和 insert 时,会话缓存会被刷新,以前的缓存会失效。
使用一级缓存下面,我们以一个简单的例子来看看 MyBatis 的一级缓存是如何工作的。
1234567891011121314151617181920212223242526272829303132package com.ahao.mybatis.cache;import com.ahao.mybatis.mapper.UserMapper;import com.ahao.mybatis.model.User;import org.ap ...
MyBatis 初级篇
MyBatis 实验环境搭建前言MyBatis 是一个理论少,实践性强的框架;它没有太多的概念,最好的学习方式就是实践。本小节,我们将一起搭建 MyBatis 的实践环境,方便后续章节的学习。
新建项目考虑到工程的维护性,我们选择 IDE 来新建一个 Maven 项目来使用 MyBatis。当然如果你更倾向了 Gradle,那么没有关系,你只需要更改添加依赖的方式即可。
在 IDE 上,你可以选择 Eclipse 或者 IDEA,当然我们更推荐你使用 IDEA,因为它的社区版已经足够我们学习 MyBatis 了,而且它也是免费的,本小节我们以 IDEA 作为默认的开发环境。
打开 IDEA,选择 New Project,点击左侧的Maven项,然后 Next 新建项目,如下图:
进入下一页后,输入对应的 GroupId 和 ArtifactId,如下图,你也可以选择自己心仪的 id,但是我们推荐你跟我们保持一致,这样在后面的学习中,你的配置和代码才能跟我们完全一致。
填完以后,点击 Next 直到出现 Finish,点击完成即可。
添加依赖项目新建后,在项目根目录下找到 pom ...
JVM 底层原理知识总结
JVM 内存结构Java 虚拟机的内存空间分为 5 个部分:
程序计数器
Java 虚拟机栈
本地方法栈
堆
方法区
JDK 1.8 同 JDK 1.7 比,最大的差别就是:元数据区取代了永久代。元空间的本质和永久代类似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中,而是使用本地内存。
程序计数器(PC 寄存器)程序计数器的定义程序计数器是一块较小的内存空间,是当前线程正在执行的那条字节码指令的地址。若当前线程正在执行的是一个本地方法,那么此时程序计数器为Undefined。
程序计数器的作用
字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制。
在多线程情况下,程序计数器记录的是当前线程执行的位置,从而当线程切换回来时,就知道上次线程执行到哪了。
程序计数器的特点
是一块较小的内存空间。
线程私有,每条线程都有自己的程序计数器。
生命周期:随着线程的创建而创建,随着线程的结束而销毁。
是唯一一个不会出现OutOfMemoryError的内存区域。
Java 虚拟机栈(Java 栈)Java 虚拟机栈的定 ...
ConcurrentHashMap源码+底层数据结构分析
ConcurrentHashMap 1.7存储结构
Java 7 中 ConcurrentHashMap 的存储结构如上图,ConcurrnetHashMap 由很多个 Segment 组合,而每一个 Segment 是一个类似于 HashMap 的结构,所以每一个 HashMap 的内部可以进行扩容。但是 Segment 的个数一旦初始化就不能改变,默认 Segment 的个数是 16 个,你也可以认为 ConcurrentHashMap 默认支持最多 16 个线程并发。
初始化通过 ConcurrentHashMap 的无参构造探寻 ConcurrentHashMap 的初始化流程。
12345678/** * Creates a new, empty map with a default initial capacity (16), * load factor (0.75) and concurrencyLevel (16). */public ConcurrentHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAUL ...
HashMap源码+底层数据结构分析
HashMap 简介HashMap 主要用来存放键值对,它基于哈希表的 Map 接口实现,是常用的 Java 集合之一,是非线程安全的。
HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个
JDK1.8 之前 HashMap 由 数组+链表 组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。 JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。并且, HashMap 总是使用 2 的幂作为哈希表的大小。
底层数据结构分析DK1.8 之前JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。
HashMap 通过 key 的 hashCode 经过扰动函数处理过 ...
CopyOnWriteArrayList 源码解析和设计思路
在 ArrayList 的类注释上,JDK 就提醒了我们,如果要把 ArrayList 作为共享变量的话,是线程不安全的,推荐我们自己加锁或者使用 Collections.synchronizedList 方法,其实 JDK 还提供了另外一种线程安全的 List,叫做 CopyOnWriteArrayList,这个 List 具有以下特征:
1.线程安全的,多线程环境下可以直接使用,无需加锁;
2.通过锁 + 数组拷贝 + volatile 关键字保证了线程安全;
3.每次数组操作,都会把数组拷贝一份出来,在新数组上进行操作,操作成功之后再赋值回去。
整体架构从整体架构上来说,CopyOnWriteArrayList 数据结构和 ArrayList 是一致的,底层是个数组,只不过 CopyOnWriteArrayList 在对数组进行操作的时候,基本会分四步走:
加锁;
从原数组中拷贝出新数组;
在新数组上进行操作,并把新数组赋值给数组容器;
解锁。
除了加锁之外,CopyOnWriteArrayList 的底层数组还被 volatile 关键字修饰,意思是一旦数组被修改,其它线程 ...
ArrayList源码+扩容机制分析
ArrayList 简介ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
ArrayList继承于 AbstractList ,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。
12345public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ }
RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
ArrayList 实现了 Clonea ...
反射详解
什么是反射?反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
正常情况下,如果我们要调用一个对象的方法,或者访问一个对象的字段,通常会传入对象实例:
12345678// Main.javaimport com.Person;public class Main { String getFullName(Person p) { return p.getFirstName() + " " + p.getLastName(); }}
但是,如果不能获得Person类,只有一个Object实例,比如这样:
123String getFullName(Object obj) { return ???}
怎么办?有童鞋会说:强制转型啊!
1234String getFullName(Object obj) { Person p = (Person) obj; return p.getFirstName() + " ...
Java集合使用注意事项
集合判空《阿里巴巴 Java 开发手册》的描述如下:
判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size()==0 的方式。
这是因为 isEmpty() 方法的可读性更好,并且时间复杂度为 O(1)。
绝大部分我们使用的集合的 size() 方法的时间复杂度也是 O(1),不过,也有很多复杂度不是 O(1) 的,比如 java.util.concurrent 包下的某些集合(ConcurrentLinkedQueue 、ConcurrentHashMap…)。
下面是 ConcurrentHashMap 的 size() 方法和 isEmpty() 方法的源码。
123456789101112131415161718192021public int size() { long n = sumCount(); return ((n < 0L) ? 0 : (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)n);& ...