使用递归唯一性验证的方式生成主键
- - CSDN博客数据库推荐文章JadePool作为简化JDBC编程工具,提供 主键生成方法是必须的. 在JadePool中,ProcessVO用于事务型数据库的DML操作,Access用于非事务型数据库的DML操作,Access参照ProcessVO实现. 目前,JadePool只提供了单主键的键值生成方法,没有提供复合主键的生成方法.
JadePool作为简化JDBC编程工具,提供 主键生成方法是必须的。在JadePool中,ProcessVO用于事务型数据库的DML操作,Access用于非事务型数据库的DML操作,Access参照ProcessVO实现。
目前,JadePool只提供了单主键的键值生成方法,没有提供复合主键的生成方法。这里以ProcessVO为例。ProcessVO提供了三个插入主键的方法,分别是:
public Long insertKey(String tableName); public Object insertAutoKey(String tableName); public String insertKey(String tableName, String keyValue);
参数tableName是表的名称,虽然没有提供XML映射、注入映射,但创建ProcessVO对象时,通过实例化DbCenter的某一个实例,数据库的结构信息已经被保存在该实例的相关属性中。在tableName表中,哪一个字段是主键,该字段有哪些属性,这些信息均可以通过访问DbCenter的实例得到。
一、长整形主键键值生成策略
调用insertKey(String tableName)插入长整形键值。
insertKey在多例模式的某一个实例中实现,取值范围是系统时间*10000+[0~9999]。在这个取值范围内,按照从小到大的方向,通过递归唯一性验证的方式获取第一个符合条件的值。用户在使用JadePool时,应当注意,一个数据库只使用DbCenter的一个实例。
一个数据库只使用DbCenter的一个实例,这同使用单例模式的效果是相同的。
插入主键的过程是这样的,ProcessVO的对象向DbCenter的实例请求插入主键键值,DbCenter的实例通过调用自身管理的Table对象的方法makeLongKeyValue()计算唯一性的主键值, 将结果返回给DbCenter的实例,逐步返回给ProcessVO对象。
在Table对象中,makeLongKeyValue()方法调用了一个递归方法onlyLongValue(),用于确保返回的键值是唯一的。
ProcessVO DbCenter | | | | | | insertKey------------->| | | | getTable(tableName).makeLongKeyValue() | | |<------------------| | | | |
具体的实现,参阅源代码
/** * Make long KeyValue * * @return System.currentTimeMillis*10000+Num, the Num is between 0 and * 9999;for example :2012-11-01 HH:mm:ss as * 1351749144390,getting:13517491443897256 */ synchronized public long makeLongKeyValue() { long t = System.currentTimeMillis(); return onlyLongValue(t, 0);//only value//递归,直到不等 } private long onlyLongValue(long time, long add) { long v; long len = 10000; long k = time * len + add; if (k > lastMakeLongKeyValue) { lastMakeLongKeyValue = k; v = k; } else { add++; if (add > 9999) { v = onlyLongValue(time + 1, 0); } else { v = onlyLongValue(time, add); } } return v; }
有关主键生成策略的话题很多,较有代表性的提法是用系统时间加随机数作为键值,这种方式产生的结果会导致键值不能按照时间的先后排列,如果按照主键键值的大小顺序排队买火车票,这种方式就不合理了。
二、通用键值生成器
调用insertAutoKey(String tableName)可以插入Long、Integer、Short、Double、Float以及String类型的主键键值。除了String类型有一定的条件限制外,其它类型的主键键值均是在原表中最大主键值得基础上加1或更大的数所得。生成过程与长整形主键生成的过程相同,同样经过递归唯一性验证。
具体的实现,参阅源代码
/** * @param field * @param fieldValue 从数据库中查询的最大值 生成各种类型的第一个主键值 */ synchronized public Object makeObjectKeyValue(Field field, Object fieldValue) throws SQLException { String className = field.getTypeClassName(); Object nextValue = null; if (fieldValue != null && !"".equals(fieldValue.toString())) { if (className.equals("java.lang.Long")) { if (((Long) fieldValue).longValue() == Long.MAX_VALUE) { throw new SQLException("Long value must less than Long.MAX_VALUE " + Long.MAX_VALUE); } nextValue = new Long(((Long) fieldValue).longValue() + 1); } if (className.equals("java.lang.Integer")) { if (((Integer) fieldValue).intValue() == Integer.MAX_VALUE) { throw new SQLException("Integer value must less than Integer.MAX_VALUE " + Integer.MAX_VALUE); } nextValue = new Integer(((Integer) fieldValue).intValue() + 1); } if (className.equals("java.lang.Short")) { if (((Short) fieldValue).shortValue() == Short.MAX_VALUE) { throw new SQLException("Short value must less than Short.MAX_VALUE " + Short.MAX_VALUE); } short v = ((Short) fieldValue).shortValue(); v++; nextValue = new Short(v); } if (className.equals("java.lang.Float")) { if (((Float) fieldValue).floatValue() == Float.MAX_VALUE) { throw new SQLException("Float value must less than Float.MAX_VALUE " + Float.MAX_VALUE); } Float v = new Float(((Float) fieldValue).floatValue() + 1); long l = (long) v.floatValue(); nextValue = new Float(l); } if (className.equals("java.lang.Double")) { if (((Double) fieldValue).doubleValue() == Double.MAX_VALUE) { throw new SQLException("Double value must less than Double.MAX_VALUE " + Double.MAX_VALUE); } Double v = new Double(((Double) fieldValue).doubleValue() + 1); long l = (long) v.floatValue(); nextValue = new Double(l); } } else { if (className.equals("java.lang.Long")) { nextValue = new Long(0); } if (className.equals("java.lang.Integer")) { nextValue = new Integer(0); } if (className.equals("java.lang.Short")) { short v = 0; nextValue = new Short(v); } if (className.equals("java.lang.Float")) { nextValue = new Float(0); } if (className.equals("java.lang.Double")) { nextValue = new Double(0); } } return onlyObjectValue(field, nextValue);//唯一性验证 } private Object onlyObjectValue(Field field, Object keyValue) throws SQLException { Object v; if (!keyValue.equals(lastMakeObjectKeyValue)) { lastMakeObjectKeyValue = keyValue; v = keyValue; } else { v = onlyObjectValue(field, makeObjectKeyValue(field, keyValue));//递归,直到不等 } return v; }
三、插入用户指定的主键键值
调用insertKey(String tableName, String keyValue)方法,用来将用户指定的键值keyValue插入到tableName表中。调用Table中的makeStringKeyValue(keyField, keyValue)方法验证主键键值的唯一性、合法性。
具体的实现,参阅源代码
/** * Make String KeyValue 检查用户输入的字段是否存在、其值是否合法和是否唯一 * * @param keyValue 给定键值 * @return 字段存在,且合法唯一,则返回keyValue本身;否则,返回null */ synchronized public String makeStringKeyValue(String keyField, String keyValue) throws SQLException { JadeTool tool = new JadeTool(); if (keyValue == null) { throw new SQLException("the key field value must not be null. "); } String v = null; if (tool.isInFields(fields, keyField) && !keyValue.equals(lastStringKeyValue)) { lastStringKeyValue = keyValue; v = keyValue; } if (v == null) { throw new SQLException("the key field value already being existence. "); } return v;//可能返回null值,返回null时,用户应当重新输入新值 }