《java与模式》读书笔记之七----序列键生成器与单例模式及多例模式(二)


         上一章中,我们实现了序列器生成器的功能,并成功运用了单例模式。细心的你可能也发现了一个问题:每次获取键值时都要去查询数据库,这样会带来一个性能的问题,那有没有办法优化列?答案是肯定的。做法就是我们常在处理数据库操作时用到的:缓存。


        思路是这样的:给整个表做一个缓存,在每次取值的时候先去缓存中获取数据,如果获取不以,将去数据库中查询,注意这些我们不会递增,而是一次性出N个值出来,N的大小可以根据系统的吞吐量来决定,就算我们系统在使用中,突然Down掉了,你也不用怕,无非就是浪费了几个序列键而已。在本例中,为了演示效果,我们将N 将为5。


     我们创建一个Bean来保存数据库中的一条记录的相关信息。


package com.pattern.id.generator;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 保存数据库中一条记录的相关信息
 */
public class KeyInfo {

	/**缓存中的最大值*/
	private int keyMax;
	/**缓存中的最小值,也是就是每次从数据库中取值以后的第一个值*/
	private int keyMin;
	private int nextKey;
	/**缓存多少个*/
	private int poolSize;
	/**键名**/
	private String keyName;
	
	
	
	/**
	 * 构造函数
	 * @param poolSize
	 * @param keyName
	 */
	public KeyInfo(int poolSize,String keyName){
		this.poolSize = poolSize;
		this.keyName = keyName;
		retrieveFromDB();
	}
	
	
	/**
	 * 从数据库获取数据
	 */
	private void retrieveFromDB(){
		Connection conn = null;
		try {
			//一次从数据库中取poolSize出来
			Class.forName("com.mysql.jdbc.Driver");
			 conn = DriverManager
					.getConnection("jdbc:mysql://localhost:3306/frameworkdb?user=root&password=123456");
			String updateSql = "update id_gen set GEN_VALUE = GEN_VALUE+ " + this.poolSize + " where keyName = ? ";
			PreparedStatement upateStmt = conn.prepareStatement(updateSql);
			upateStmt.setString(1, this.keyName);
			upateStmt.execute();
			String sql = "select GEN_VALUE from id_gen where GEN_KEY = ? ";
			PreparedStatement stmt = conn.prepareStatement(sql);
			stmt.setString(1, this.keyName);
			ResultSet rs = stmt.executeQuery();
			
			//赋值
			while (rs.next()) {
				this.keyMax =  rs.getInt(1);
				this.keyMin = this.keyMax - this.poolSize+1;
				this.nextKey = this.keyMin;
			}

		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
	/**
	 * 获取下一个键值
	 * @return
	 */
	public int getNextKey() {
		//缓存中使用完以后,再重新获取
		if(nextKey > keyMax){
			retrieveFromDB();
		}
		//返回nextKey,并将nextKey+1
		return nextKey++;
	}
	
	
	public String getKeyName() {
		return keyName;
	}


	public int getKeyMax() {
		return keyMax;
	}
	public int getKeyMin() {
		return keyMin;
	}

	public int getPoolSize() {
		return poolSize;
	}
	
	
}



               接下来,我们看看序列键生成器的代码:

             

package com.pattern.id.generator;

import java.util.HashMap;
import java.util.Map;

public class KeyGenerator {

	private static KeyGenerator keyGen = new KeyGenerator();
	private static final int POOL_SIZE = 5;
	private Map<String,KeyInfo> keyList = new HashMap<String,KeyInfo>();

	private KeyGenerator() {
	};

	/**
	 * 工厂方法
	 * 
	 * @return
	 */
	public static KeyGenerator getInstance() {
		return keyGen;
	}

	/**
	 * 获取下一个value
	 * 
	 * @return
	 */
	public synchronized int getNextKey(String keyName) {
		KeyInfo key = keyList.get(keyName);
		if(key==null){
			key = new KeyInfo(POOL_SIZE,keyName);
		}
		return key.getNextKey();
	}

	

}



        大家可以看到,我们把获取数据库的操作转移到KeyInfo中,每条记录对自己负责,我们在序列键生成器中,只需要调用各自记录的Bean,就能获取到相关信息。当第一次获取某条记录的时候相关信息时,首先我们是去缓存中查询,如果查询不到,我们就会创建该记录的Bean,并缓存起来。如果查询到,我们则直接调查用相关方法,获取我们需要的信息(下一个键值),每条记录对自己负责,而生成器,刚相当于成了一完全的缓存对象,他的所有操作都委托给相应记录的Bean自己处理。


         同时,我还在想,这例可不可以用多例模式来做列?  我们将map 放到KeyInfo类中,让KeyInfo类,变成多例模式,这样KeyGenarator就可以省去,这样做也是行得通。但是我个人觉得: 从逻辑上来讲,一个序列键管理器负责管理多条记录更说通。

       应该说,两种设计都行通,看个人喜好吧。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zl3450341/article/details/6886250
个人分类: 设计模式
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭