当前项目中使用hibernate框架中的uuid.hex方式自动产生主键,
但出现了一些问题。

基本系统是用java写的,但部分辅助还是用的delphi(C/S结构的),
在delphi写的部分包含大量记录生成,对并发基本无要求,
对速度要求很高,这个时候新记录的主键无法自动产生。

最后是所有delphi系统生成记录用的数据表的主键改用
自动增量了。

因为使用的数据库是oracle,采用的是sequence,相对
sqlserver的要灵活很多,要不是改动太大,很可能整个
项目的数据表都会改。

>写得非常好,主键是肯定要的,有了之后会方便很多。
>有一种Object ID的方法,也就是专门使用sequence包产生ID,不知属于你总结这些类别中哪一个?

这种方法很好,在实际的编程当中有很高的灵活性,数据库操作越繁杂这种方法体现的优点越明显,不过要请教一下怎么实现? 比如在DB2中select nextval form seq_1? 这样似乎不行啊!
而且这种方法在一种场合下无法使用:确保主键的连贯性。

Identity列能确保连贯,而且通过Statement.getGeneratedKeys()又能得到新增加的键值,这样也解决了主表-明细表插入的问题,因此选Identity列用作主键可谓是比较广泛的选择。

我个人觉得要用主键的话以上两种方法最可取,不过彼此都有一个互补的缺点:前者无法确保连贯;后者在导数据的时候要额外花点心思。

我现在的开发就遇到一种需要确保主键连贯的场合,无法使用第二种主键,而且也不知道如何使用简单的方法实现第一种方法中提到的Object ID,现在我的苯办法是通过select nextval for seq_1 from temp_table fetch first 1 rows only得到ID的! 希望不要被知道诀窍的人笑掉大牙哦!

> 我是直接用表示数据类型的字符串+System.currentTimeMilli
> ();大家说怎么样?

这种方法不太好,如果有并发的话,容易重复。

> 当前项目中使用hibernate框架中的uuid.hex方式自动产生主?> ,
> 但出现了一些问题。
>
> 基本系统是用java写的,但部分辅助还是用的delphi(C/S结?> 的),
> 在delphi写的部分包含大量记录生成,对并发基本无要求,
> 对速度要求很高,这个时候新记录的主键无法自动产生。
>
> 最后是所有delphi系统生成记录用的数据表的主键改用
> 自动增量了。
>
> 因为使用的数据库是oracle,采用的是sequence,相对
> sqlserver的要灵活很多,要不是改动太大,很可能整个
> 项目的数据表都会改。

hibernate中的uuid.hex如果类似GUID的话(本人不是很了解),以上问题其实并不难解决,可以用两种方法:

1。客户端生成GUID,用Delphi很容易的:
function GetGUID: String;
var
g : TGUID;
begin
Result:='';
if S_OK=CreateGuid(g) then
Result:=GUIDToString(g);
end;


2。Oracle生成,如:
insert into a_tab (id,name) values (sys_guid(), 'tom');

个人建议,最好用自己的ID生成组件,这样可移植和可定制性好,使用Hibernate等ID生成定制性不强。

自己定制ID组件很方便,现成源码可见JPetstore中的Sequence类,一个类即可。

我有一疑问,有一表,如下字段:
ID(GUID)主键, 名称, 所属类别, 其它

我插入一条记录,然后再插入一条名称,所属类别都相同的记录,系统不会出现异常, 而我不想让业务上完全相同的两条记录能够被重复插入,怎么办?

我觉得还是用自制加一比较好,楼主说的子公司上报数据可能出现的主键重复问题,可以用这个方法生成主键:公司名+最大序列号,这样子公司名和总公司名设不同就可以避免这种情况了!

序列

这个问题我也想过很久了,后来还是觉得hibernate的uuid比较好,我也把hibernate的uuid算法揪出来了,可以在没有hibernate的情况下使用:


public class UUIDGenerator {
private static final int IP;
static {
int ipadd;
try {
ipadd = BytesHelper.toInt(InetAddress.getLocalHost().getAddress());
} catch (Exception e) {
ipadd = 0;
}
IP = ipadd;
}

private static short counter = (short) 0;

private static final int JVM = (int) (System.currentTimeMillis() >>> 8);

public UUIDGenerator() {
}

/**
* Unique across JVMs on this machine (unless they load this class in the
* same quater second - very unlikely)
*/
protected int getJVM() {
return JVM;
}

/**
* Unique in a millisecond for this JVM instance (unless there are >
* Short.MAX_VALUE instances created in a millisecond)
*/

protected short getCount() {
synchronized (UUIDGenerator.class) {
if (counter < 0)
counter = 0;
return counter++;
}
}

/**
* Unique in a local network
*/

protected int getIP() {
return IP;
}

/**
* Unique down to millisecond
*/

protected short getHiTime() {
return (short) (System.currentTimeMillis() >>> 32);
}

protected int getLoTime() {
return (int) System.currentTimeMillis();
}

private static final String SEPERATOR =
"";

protected String format(int intval) {
String formatted = Integer.toHexString(intval);
StringBuffer buf = new StringBuffer(
"00000000");
buf.replace(8 - formatted.length(), 8, formatted);
return buf.toString();
}

protected String format(short shortval) {
String formatted = Integer.toHexString(shortval);
StringBuffer buf = new StringBuffer(
"0000");
buf.replace(4 - formatted.length(), 4, formatted);
return buf.toString();
}

public Serializable generate() {
return new StringBuffer(36).append(format(getIP())).append(
UUIDGenerator.SEPERATOR).append(format(getJVM())).append(
UUIDGenerator.SEPERATOR).append(format(getHiTime())).append(
UUIDGenerator.SEPERATOR).append(format(getLoTime())).append(
UUIDGenerator.SEPERATOR).append(format(getCount())).toString();
}

public static void main(String[] args) throws Exception {
for(int i=0;i<100;i++)
System.out.println(new UUIDGenerator().generate());
}

}

引用的Helper类:

public class BytesHelper {

private BytesHelper() {}

public static int toInt( byte[] bytes ) {
int result = 0;
for (int i=0; i<4; i++) {
result = ( result << 8 ) - Byte.MIN_VALUE + (int) bytes[i];
}
return result;
}

public static short toShort( byte[] bytes ) {
return (short) ( ( ( - (short) Byte.MIN_VALUE + (short) bytes[0] ) << 8 ) - (short) Byte.MIN_VALUE + (short) bytes[1] );
}

public static byte[] toBytes(int value) {
byte[] result = new byte[4];
for (int i=3; i>=0; i--) {
result[i] = (byte) ( ( 0xFFl & value ) + Byte.MIN_VALUE );
value >>>= 8;
}
return result;
}

public static byte[] toBytes(short value) {
byte[] result = new byte[2];
for (int i=1; i>=0; i--) {
result[i] = (byte) ( ( 0xFFl & value ) + Byte.MIN_VALUE );
value >>>= 8;
}
return result;
}
}

而且实测效果非常好,在我个人的电脑上每秒可以生成5000个id

我喜欢用系统程序生成,而不是让数据库自己生成。

起码,当我保存一个Bean到数据库时,直接就可以返回系统生成的主键了,而不用再select一次,保存后返回主键很多地方是需要的,不是吗?

而且,好象这样也更灵活吧,比如我想实现分布式的主键,或者各表的键在整个数据库都唯一等等。

是的,用程序产生序列比较好,在Jpetstore 源码中也是有一个专门sequenceDao产生序列号的。
http://www.jdon.com/jdonframework/app.htm

将hibernate组件独立出来是比较好的。

另外,序列号产生器从严格意义来说要求比较高,防止并发需要严格事务支持,还要设置不同的起点,这样减少序列号争夺,所以,序列号产生器组件最好使用现成第三方的。

自动编号主键
缺点:其实缺点也就是来自其优点,就是因为自动增长,在手动要插入指定ID的记录时会显得麻烦,尤其是当系统与其他系统集成时,需要数据导入时,很难保证原系统的ID不发生主键冲突(前提是老系统也是数字型的);如果其他系统主键不是数字型那就麻烦更大了,会导致修改主键数据类型了,这也会导致其他相关表的修改,后果同样很严重;就算其他系统也是数字型的,在导入时,为了区分新老数据,可能想在老数据主键前统一加一个“o”(old)来表示这是老数据,那么自动增长的数字型又面临一个挑战。
--------
这个问题在access 和 MSSQL 存在
但是,至少在MySQL 里不存在,MySQL的自动增量类型,是默认值的形式,所以,你也可以填其他的值

使用数值行搜索比字符型搜索,数据量大时快的多的。
至于序列,还是+1,只是生成方式的问题,各数据库的生成方式有些区别