两种方法可以设置主键:
- 自增长主键
- 唯一序列
一 自增主键
- 方法一:通过序列以及触发器实现主键自增长
适用于直接使用JDBC连接数据库,这种方式将主键自增长的任务完全交给数据库,无需在代码层面上进行任何控制
--首先建一个表TEST_tab create table TEST_tab( NID int PRIMARY KEY, test1 varchar2(20), test2 varchar2(20), test3 varchar2(20), test4 varchar2(20), test5 varchar2(20) ); -- 再建一个序列SEQ_TEST create sequence SEQ_TEST minvalue 1 -- 最小值 nomaxvalue -- 不设置最大值 start with 1 -- 从1开始计数 increment by 1 -- 每次加1个 nocycle -- 一直累加,不循环 nocache; -- 不建缓冲区 cache n / nocache --其中n代表一个整数,默认值为20 -------------------------------------------- create sequence product_id_seq increment by 1 start with 1; --------------------------------------------
-- 创建一个触发器 CREATE OR REPLACE TRIGGER tg_test /*触发器*/ BEFORE INSERT ON TEST_tab /*表*/ FOR EACH ROW /* WHEN (new.nid is null)*/ begin select /*序列*/seq_test.nextval into:new.nid/*主键*/ from dual; -- select /*序列*/seq_test.nextval into:new.id/*主键*/ from dual; end;
-- 测试 select * from TEST_tab; insert into TEST_tab(/*nid,*/ test1) values(/*6,*/'aaa'); insert into TEST_tab(test1) values('bbb');
序列生成器所生成的数字只能保证在单个实例里是唯一的
这就不适合将它用作并行或者远程环境里的主关键字,因为各自环境里的序列可能会生成相同的数字,从而导致冲突的发生
- 方法二:通过序列以及Hibernate配置实现自增长
适用于通过Hibernate连接数据库的方式
在数据库上创建序列,通过配置在POJO类上的注释,让Hibernate去调用数据库的序列实现自增长
--为product表的主键创建序列 create sequence product_id_seq increment by 1 start with 1;
// POJO类主键上的注释 @Id @SequenceGenerator(name = "prodG",sequenceName="PRODUCT_ID_SEQ",allocationSize=1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "prodG") public int getId() { return id; } //SequenceGenerator,GeneratedValue 声明该主键使用序列来实现自增长,sequenceName指定了序列名(即在上一步骤所创建的序列名) //allocationSize 指定了自增长的大小,这里手动设置为1。 //在Hibernate Annotation版本(大概是3.2版本)之前,如果不设置这个属性,其默认为1 //但在3.2版本之后如果不设置allocationSize,则其自增长大小变成50
- 唯一序列
SYS_GUID() 生成32位的唯一编码。 序列生成器所生成的数字只能保证在单个实例里是唯一的, 这就不适合将它用作并行或者远程环境里的主关键字,因为各自环境里的序列可能会生成相同的数字,从而导致冲突的发生。 SYS_GUID会保证它创建的标识符在每个数据库里都是唯一的。 此外,序列必须是DML陈述式的一部分,因此它需要一个到数据库的往返过程(否则它就不能保证其值是唯一的)。 SYS_GUID 源自不需要对数据库进行访问的时间戳和机器标识符,这就节省了查询的消耗。 很多应用程序都依靠序列生成器来创建数据行的主关键字,这些数据行没有一个明显的主值,这也就是说,在这样的数据集里一条记录的创建就会让数据列发生改变。 因此,管理员可能会对在表格中将SYS_GUID用作主关键字而不使用序列数感兴趣。这在对象在不同机器的不同数据库里生成以及需要在后来合并到一起的情况下很有用。 使用SYS_GUID或者序列会在数据库使用周期里的某些地方造成性能上的消耗; 问题就是在哪里。对于SYS_GUID而言,性能上的影响在查询时间和创建时间上(在表格里要创建更多的块和索引以容纳数据)。 对序列而言,性能上的影响在查询期间,在这个时候,SGA序列的缓冲区被用光。在缺省情况下,一个序列一次会缓冲20个值。 如果数据库没有使用这些值就关闭了,它们就会被丢失。 SYS_GUID生成的值的另一个显著的不足之处是,管理这些值会变得困难得多。你必须(手动)输入它们或者通过脚本来填充它们,或者将它们作为Web参数来传递。 出于这些原因,将SYS_GUID作为一个主关键字不是一个很好主意,除非是在一个并行的环境里或者希望避免使用管理序列生成器的情况下。
-- 测试 create table test( id raw(16) default sys_guid() primary key, filler varchar2(100) );
这两种方式都是通过Oracle的序列实现自增长
第一种通过数据库的触发器在插入的时候自动插入主键。
而后者则由Hibernate自动完成获取主键,插入主键这一操作。
用到Hibernate进行开发的项目中,建议选择第二种进行配置。因为如果通过第一种方式进行配置,则Hibernate无法获取到其主键的值,在插入的时候Hibernate会提示:父类主键未找到(即主键无值)
并且Hibernate的级联增加也会因为无法获取到主键而无法插入数据到关系表中
- Cache与NoCache的区别:
如果指定CACHE值,oracle就可以预先在内存里面放置一些Sequence,这样存取的快些。 cache里面的取完后,Oracle自动再取一组到cache。 使用cache或许会跳号, 比如数据库突然不正常down掉(shutdown abort),cache中的Sequence就会丢失。 举个例子:比如你的sequence中cache 100,那当你sequence取到90时突然断电,那么在你重启数据库后,sequence的值将从101开始。 如果指定NOCACHE值,Oracle就不会预先在内存里面存放Sequence,当然这也就可以避免数据库不正常down掉的sequence丢失。 不过会产生一些问题:创建nocache sequence在高并发访问时,容易导致row cache lock等待事件, 主要原因是每次获取nextval时都需要修改rowcache中的字典信息。使用nocache 爏equence,还会导致如下问题: 由于每次修改字典信息都需要commit,可能导致log file sync等待, nocache sequence在RAC环境下,会对基于sequence生成的列创建的索引造成实例间大量索引块争用 基于以上问题,避免创建nocache sequence。 再来看看sequence相关保护机制: row cache lock:在调用sequence.nextval情况下需要修改数据字典时发生,对应row cache lock事件 SQ lock:在内存缓存(并非rowcache)上获取sequence.nextval时发生,对应enq:SQ-contention事件 SV lock:RAC环境下获取cache+order属性的sequence.nextval时发生,对应DFS lock handle事件 什么情况下使用cache什么时间上使用nocache? 我个人感觉应该尽量使用cache,因为现在的数据库很多都是在高并发的情况下运行的,首先这样可以搞性能, 并且也不会产生row cache lock等待事件。可能有些人会担心数据库不正常的down掉会产生序列号间断,但这也是很少的情况。 当然如果你的业务要求是绝不能产生间断的序列号,那就要使用nochache了。