天天看点

工厂方法模式解决流水号生成问题

工厂方法模式

    以可移植的、可扩展的方式来生成流水号EJB应用中的一个难点。 现在比较成熟的流水号生成策略有全局唯一标识(即UUID)和使用数据库内置流水号生成策略。全局唯一标识有单件模式、根据网络标识(Mac地址+IP+JVM唯一对象标识)等策略。不同的数据库也有不同的流水号生成策略:例如Oracle采用内置流水号产生机制,SQL Server则采用Identity机制。这给我们带来方便的同时也使得应用程序在不同系统之间移植变得很麻烦。我采用工厂方法模式解决这个问题。

  结构图

工厂方法模式解决流水号生成问题

我们首先定义一个基类接口,它定义了各种唯一序列生成器的共同的方法。

abstract public interface SequenceCreator {

  abstract public String getSequenceId(String aId);

  abstract public Integer getSequenceAsInt(String aId);

}

  两个函数的参数aId是不同流水号生成标识。getSequenceId是产生以字符串形式返回的流水号,getSequenceAsInt是以×××形式返回流水号。为了防止无谓的重复,下面的实例中我们将只写各个方法的getSequenceId实现。

(1)我们首先看SQLSequenceCreator的实现代码

    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

    Connection con = java.sql.DriverManager.getConnection("jdbc:odbc:DNSEJB");

    CallableStatement cs = con.prepareCall("{call SetIndex(?,?,?)}");

    cs.registerOutParameter(2,Types.VARCHAR);

    cs.setString(1,aId);

    cs.setInt(3,10);

    cs.executeUpdate();

    String str= cs.getString(2);

    return str.substring(aId.length(), str.length());

我们是调用我们自定义的存储过程来生成流水号的,存储过程的代码请参看代码。

(2)Oracle的实现代码

String strSQL = "select " + sequence_name + ".nextval from DUAL";

    Statement  stmt = conn.createStatement();

    ResultSet rs = stmt.executeQuery(strSQL);

    rs.next();

return rs.getString(1);

Oracle对流水号生成提供了比较好的支持,而且Oracle的生成策略也比SQLServer更高效,消耗更少的资源,资源锁定情况也比SQLServer少。

(3)

UUID的实现代码

InetAddress inet = InetAddress .getLocalHost();

Byte[] bytes = inet.getAddress();

String hexInetAddress = hexFormat(getInt(bytes),8);

String thisHashCode=hexFormat(System.identityHashCode(this),8);

MideValue = hexInetAddress+thisHashCode;

Seeder = new SecureRandom();

In node = seeder.nextInt();

Long timeNow = System.currentTimeMillis();

Int timeLow = (int)timeNow&oxFFFFFFF;

Int  node = seeder.nextInt();

Return (hexFormat(timeLow,8)+mid+hexFormat(node,8));

    UUID是一个基于字符串的主键,他有一下字符串组合而成:利用System.currentTimeMillis()精确道毫秒的唯一、IP地址的十六进制标识、利用System.identityHashCode(this)得到的一个JVM内部的唯一地址标识和利用随机数生成器生成随机数。

还有很多不同的流水号生成策略,我们不准备一一罗列。我们的主要问题是要解决在采用不同的序列生成策略时将代码的修改减到最小。

我们定义的SequenceCreator 类定义了所有流水号生成策略公共的方法,并且把这些方法定义为虚方法,在不同的流水号生成策略代码中只要覆盖这些方法即可。序列号生成器工厂类SequenceCreatorFactory的 getSquenceCreator()并不返回具体的流水号生成类,而是返回SequenceCreator,这样当采用不同策略时只要修改getSquenceCreator方法即可。

继续阅读