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

  本章是个小小的实际应用,也算是对单例/多例模式的一个回顾。在本章开始之前,我们来讲讲序列键生成器。

     我们在做数据库设计的时候,一般都会为数据库设计一个主键,来方便查询,更多的时候这个主键是代理主键,也就是说并没有实际意义。所以通常我们会把这个主键设计为自增的方式。比如Mysql中,我们可以设置为:autoIncresement,  Oracle中可以利用Sequence..... 但是有些数据库是不支持这种自增的方式的,也就是说:自增必须我们自己通过程序来实现,比如sybase。  那么这种我们通过程序来控制自增长的过程,就叫序列键生成。简单的说:用一张表来记录其它表的主键的当前值,进来通过程序来达到自增的目的。


      除了数据库不支持自增方式以后,还有另外一种情况会使用到序列键生成,那就是多张表的代理主键的值不能重复时。比如现在有2张表:T1,T2,他们的主键分别是pk1,pk2.如果说我们的业务要求pk1,pk2两列的值不能出现重复时,那么用我们以往的自增方式显然是办不到的,这个时候就需要生成序列键。


  好了进入正题,我们先建立一张表,SQL语句如下:

CREATE TABLE `id_gen` (
  `GEN_KEY` varchar(20) NOT NULL,
  `GEN_VALUE` int(11) DEFAULT 0,
  PRIMARY KEY (`GEN_KEY`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


   我们要主键的 keyname与它当前的value保存到这个表中,每次我们取值之前,先update一下value,再取出来value。有的人会说:为什么我们每次不先取value再更新列?现在试想一下,如果当你取完值以后,系统被中断了,这时你还没有来得及更新value,那么下次取的到仍就是同一个值,如果上一个值已经被正确使用了,那么就出现了重复值,与我们设计的初衷就不符了。如果先更新,再取值的话,算被中断,顶多是浪费了一个值而已。 从上面的分析,我们不难看出,这个序列键生成器,在整个系统中只需要一个。那么,很快我们就想到了单例模式,那么我们就开始设置我们的生成器。 

 


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 KeyGenerator {

	private static KeyGenerator keyGen = new KeyGenerator();

	private KeyGenerator() {
	};

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

	/**
	 * 获取下一个value
	 * 
	 * @return
	 */
	public synchronized int getNextKey(String keyName) {
		return getNextKeyFromDB(keyName);
	}

	private int getNextKeyFromDB(String keyName) {
		Connection conn = null;
		try {
			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+1 where GEN_KEY = ? ";
			PreparedStatement upateStmt = conn.prepareStatement(updateSql);
			upateStmt.setString(1, keyName);
			upateStmt.execute();
			String sql = "select GEN_VALUE from id_gen where GEN_KEY = ? ";
			PreparedStatement stmt = conn.prepareStatement(sql);
			stmt.setString(1, keyName);
			ResultSet rs = stmt.executeQuery();
			while (rs.next()) {
				return rs.getInt(1);
			}

		} 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 0;
	}

}


关于上面的代码,我不想做过多解释,都是JDBC的基础知识,关于JDBC资源,应该一层层来,我这里写示例代码就直接性关闭Connection。现在我们来写个测试类,来测试一下, 看我们这个类是否能正常工作。

package com.pattern.id.generator;

public class ClientTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		KeyGenerator gen = KeyGenerator.getInstance();
		int value = gen.getNextKey("test");
		System.out.println(value);
	}

}


  多运行几次,可以看结果是从1开始不停的递增上去的,当然你也可以用多线程来测试是否会出现重复值的现象,我可以肯定的说:不会,至于为什么,可以参见我的另一篇博客,关于synchronized的理解。

好啦,这说明,我们的序列键生成器功能已经达到了预期目的。
    
        在这里给大家扩展点知识,用过hibernate的同学可能就知道。其实hibernate对于这种方式是支持,至于在hibernate中怎么实现这一功能,我就不多说了。有兴趣的同学可以在Google里面搜索:@TableGenerator

 


   

你可能感兴趣的:(《java与模式》读书笔记之六----序列键生成器与单例模式及多例模式(一))