有时候我们希望生成全局唯一的序列号。可以用于生成主键或者生成全局的序列号用于生成编号或者其他。这时候我们可以用SQL语句自行管理键值。使用一个表来存储所有的键列值。如下表所示:
key
value
PO_NUMBER
105
SE_NUMBER
2555
...
...
1. 存储方式:
预定式键值存储:在预定一个值时首先将值更新为下一个可用值,然后查出来之后提供给客户端使用。这样万一出现中断的话顶多是浪费这几个键值。每次可以预定多个键值(也就是一个键值区间)而不是一个值,也就是更新键值的时候将键值增加大于1的数目。这么做可以避免多次访问数据库。
记录式键值存储:也就是说,键值首先被返还给客户端,然后记录到数据库中取。这样做的缺点是:一旦系统出现中断,就可能出现客户端已经使用了一个键值,而这个键值却没有来得及存储到数据库中。在系统重启之后,系统还会从这个已经被使用过的键值开始,从而导致错误。
2.单例模式与多例模式的应用
单例模式:
可以用单例模式来实现,整个系统只有一个序列键值管理器来管理序列号。
多例模式:
多例类往往持有一个内蕴状态(内蕴状态是存储在享元对象内部并且不会随环境的改变而改变),多例类的每一个实例都有独特的内蕴状态。一个多例类持有一个集合对象,用来登记自身的实例,而其内蕴状态往往就是登记的键值。当客户端通过多例类的静态工厂方法请求多例类的实例时,这个工厂方法都会在集合内查询是否有这样的一个实例。如果有直接返回给客户端;如果没有就创建一个这样的实例,并登记到集合中,然后返回给客户端。如下:
键名为内蕴状态;键名和自身作为map的key和value存入集合中。
packagecn.qlq.singleton;importjava.util.concurrent.ConcurrentHashMap;public classKeyGenerator {//多例模式应用
private static ConcurrentHashMap keyGenerators = new ConcurrentHashMap<>();privateKeyGenerator(String key) {//用key做处理
}/*** 静态工厂方法提供自己的实例
*
*@return
*/
public staticKeyGenerator getInstance(String keyName) {if(keyGenerators.containsKey(keyName)) {returnkeyGenerators.get(keyName);
}
KeyGenerator keyGenerator= newKeyGenerator(keyName);
keyGenerators.put(keyName, keyGenerator);returnkeyGenerator;
}
}
3. 单例模式应用
1.没有数据库的情况
首先不使用数据库,用一个成员属性模拟键值。如下:
packagecn.qlq;importjava.util.concurrent.ConcurrentHashMap;/*** 单例模式的唯一值生成器
*
*@authorQiaoLiQiang
* @time 2019年6月12日下午10:39:42*/
public classKeyGenerator {/*** 存放key、value*/
private ConcurrentHashMap values = new ConcurrentHashMap<>();private static KeyGenerator keyGenerator = newKeyGenerator();privateKeyGenerator() {//防止反射创建实例
if (keyGenerator != null) {throw new RuntimeException("not allowed!");
}
}/*** 静态工厂方法提供自己的实例
*
*@return
*/
public staticKeyGenerator getInstance() {returnkeyGenerator;
}/*** 获取下一个序列制
*
*@paramkey
* 序列的键
*@return自增后的值*/
public synchronized intgetNextKey(String key) {//如果存在就加1且返回
if(values.containsKey(key)) {
Integer nextValue= values.get(key) + 1;
values.put(key, nextValue);returnnextValue;
}//初始化
Integer startValue = 1;
values.put(key, startValue);returnstartValue;
}
}
测试代码:(两个线程使用序列生成器)
packagecn.qlq;importjava.util.concurrent.CountDownLatch;public classMainClass {public static void main(String[] args) throwsInterruptedException {//制造一个闭锁
final CountDownLatch countDownLatch = new CountDownLatch(2);//模拟使用序列生成器
for (int i = 0; i < 2; i++) {new Thread(newRunnable() {
@Overridepublic voidrun() {for (int j = 0; j < 4; j++) {
String key= j % 2 + "";
System.out.println(key+ "\t" +Ke