Oracle序列和主键映射

前言

每张表都有主键,可分别简单主键和组合主键,简单主键为表中的一列,组合主键为表中的几列。主键的生成策略有许多种,其中,序列是oracle常见的主键生成策略之一。本文主要讲解JPA映射主键的技术细节。其中,主键生成策略是oracle序列,JPA实现是Hibernate。

oracle序列

创建序列

drop sequence testsequence;
Create sequence testsequence Increment by 1 Start with 1 NOMAXVALUE NOMINVALUE Nocycle nocache;

序列testsequence,从1开始递增,每次递增1。不指定最大值、最小值、不循环、不缓存。

创建表

create table TEST
(
  id   NUMBER not null,
  name VARCHAR2(30)
)
;
alter table TEST
  add constraint PK_ID primary key (ID);

create table TEST2
(
  id   NUMBER not null,
  name VARCHAR2(30)
)
;
alter table TEST2
  add constraint PK_TEST2_ID primary key (ID);

创建了两张表,TEST、TEST2。表结构完全一致,简单主键ID,名字name。

创建触发器

CREATE OR REPLACE TRIGGER "TG_TEST"
BEFORE INSERT ON TEST FOR EACH ROW  WHEN (new.id is null) begin
select testsequence.nextval into:new.id from dual;
end;

/
ALTER TRIGGER "TG_TEST" ENABLE;

CREATE OR REPLACE TRIGGER "TG_TEST2"
BEFORE INSERT ON TEST2 FOR EACH ROW  WHEN (new.id is null) begin
select testsequence.nextval into:new.id from dual;
end;

/
ALTER TRIGGER "TG_TEST" ENABLE;

定义两张表TEST、TEST2的主键生成策略是序列testsequence。

JPA主键映射

注解

使用JPA映射主键,若主键的生成策略是序列的话,需要使用到四个注解,分别是@Id、@Column、@SequenceGenerator、@GeneratedValue。注解@Id表示该实体属性对应数据库的主键字段,注解@Column表示该实体属性对应数据的字段的名称,@SequenceGenerator定义一个序列, @GeneratedValue应用一个序列。

private long id;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
    @SequenceGenerator(name="sequence", sequenceName="testSequence", initialValue=1, allocationSize=1)
    @Column(name = "ID")
    public long getId()
    {
        return this.id;
    }

    public void setId(long id)
    {
        this.id = id;
    }

@SequenceGenerator,属性name,表示序列定义的名称,属性sequenceName,表示序列的名称。这两个属性的解释很像,但并非一回事。属性name指的是序列定义本身的名称,属性sequenceName表示该定义涉及到的序列的名称。属性initialValue,表示序列初始值,属性allocationSize,表示序列递增或递减的幅度。

@GeneratedValue,属性strategy,表示主键生成策略,GenerationType.SEQUENCE表示主键生成策略是序列,属性generator,表示应用序列定义的名称。@GeneratedValue的属性generator和@SequenceGenerator的属性name保持一致。现在,执行持久化动作。

 Test test = new Test();
 test.setName("John Wiley & Sons");
 manager.persist(test);

查询数据库,数据的情况如下:

根据源代码,注解@SequenceGenerator的属性initialValue、 allocationSize为可选项,默认值分别是1、50。

 /**
     * (Optional) The value from which the sequence object
     * is to start generating.
     */
    int initialValue() default 1;

    /**
     * (Optional) The amount to increment by when allocating
     * sequence numbers from the sequence.
     */
    int allocationSize() default 50;

但是,根据测试,结果并非如源代码展示的那样。以上序列重新建立、清空表TEST的数据,把@SequenceGenerator的属性initialValue、 allocationSize去掉,保持默认。执行持久化实体的动作。

查询数据库,数据的情况如下:

若是再一次重新建立以上序列,清空表TEST的数据,重启web server,执行实体持久化实体的工作,数据库的ID并不一定从100开始。也就是说initiaValue的值是随机的,allocationSize也并非是50。而且,从插入表中的主键数值看来,已经脱离了序列的运作。从上图可以看出,序列testSequence的LAST_NUMBER 是3,但是程序插入的主键数值却是100,101。因此,建议在写注解@SequenceGenerator时,虽然属性 initialValue、 allocationSize是可选的,但要明确为这两个属性指定数值,并保持和数据库对序列的定义完全一致。

应用范围

根据源代码,在持久化单元内,序列定义是全局。在一个实体内定义的序列定义,可以应用于持久化单元内的其他实体。

/**
 * Defines a primary key generator that may be referenced by name when
 * a generator element is specified for the {@link GeneratedValue}
 * annotation. A sequence generator may be specified on the entity
 * class or on the primary key field or property. The scope of the
 * generator name is global to the persistence unit (across all
 * generator types).
 *
 * <pre>
 *   Example:
 *
 *   @SequenceGenerator(name="EMP_SEQ", allocationSize=25)
 * </pre>
 *
 * @since Java Persistence 1.0
 */

但是,根据测试,在持久化单元内,序列定义只针对定义它的实体有效。在实体TEST内定义了一个序列定义,名称为sequence。现在在实体TEST2应用这个序列定义。

  private long id;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
    @Column(name = "ID")
    public long getId()
    {
        return this.id;
    }

    public void setId(long id)
    {
        this.id = id;
    }

启动web server,报出错误Unknown Id.generator: sequence。

private long id;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
    @SequenceGenerator(name="sequence", sequenceName="testSequence", initialValue=1, allocationSize=1)
    @Column(name = "ID")
    public long getId()
    {
        return this.id;
    }

    public void setId(long id)
    {
        this.id = id;
    }

以上代码,在实体TEST2内,针对序列testSequence,重新定义了序列定义,持久化一切正常。因此,序列定义只针对定义它的实体有效。在生产环境中,不同的表应该使用不同的序列。这样才能最大限度保证序列产生的数值在特定的表中,其主键数值具有连续性。比如说,一个序列产生的数值类似1、2、3、4、5......在该序列同时应用在表TEST,TEST2,有可能数值1、2分配给了TEST,而把3、4、5分配给了TEST2,这样看起来,表中的主键数值就常常没有连续性。当然,序列只分配给某个表,也不能保证该表的主键数值就一定会有连续性。当遇到事务回滚时,序列产生的数值同样没有插入到表的主键,造成主键数值的不连续。

时间: 2024-10-12 02:27:07

Oracle序列和主键映射的相关文章

Oracle与Mysql主键、索引及分页的区别小结

Oracle与Mysql主键.索引及分页的区别,学习oracle的朋友可以参考下 区别: 1.主键,Oracle不可以实现自增,mysql可以实现自增. oracle新建序列,SEQ_USER_Id.nextval 2.索引: mysql索引从0开始,Oracle从1开始. 3.分页, mysql: select * from user order by desc limit n ,m. 表示,从第n条数据开始查找,一共查找m条数据. Oracle:select * from user sele

Hibernate 中 联合主键映射 组合关系映射 大对象映射(或者说文本大对象,二进制数据大对象)

Clob:文本大对象,最长4G Blob:二进制数据大对象,最长4G util: public class HibUtil { private static SessionFactory sessionFactory; static{ //获取配置信息 hibernate.cfg.xml Configuration configuration = new Configuration().configure(); //创建一个 ServiceRegistry的实例 //首先获得其标准建造器,此处用

oracle 触发器实现主键自增

drop table book; --创建表 create table book( bookId varchar2(4) primary key, name varchar2(20) ); --创建序列 create sequence book_seq start with 1 increment by 1; --创建触发器 create or replace trigger book_trigger before insert on book for each row begin select

MySQL主键自动生成和生成器表以及JPA主键映射

MySQL主键自动生成 表设计 MySQL有许多主键生成策略,其中很常见的一种是自动生成.一般情况下,主键类型是BIGINT UNSIGNED,自动生成主键的关键词是AUTO_INCREMENT. CREATE TABLE Stock ( id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, NO VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, price DECIMAL(6,2) N

hibernate 复合主键映射

第一次写博客,只想把工作中遇到的问题记下来. 最近做了一个二次开发的改造,改完之后被告知数据表主键根据需求需要用复合主键,因为本人菜鸟一枚,复合主键从未用过,只能靠度娘 网上给出的复合主键映射大致就两种 第一种:将复合主键对应的属性与实体其他普通属性放在一起 /*实体类.复合主键必须实现Serialzable接口*/ public class User implements Serialzable { private static final long serialVersionUID=1L;/

MyBatista主键映射,执行inset返回主键

有点时候,我们的业务需要执行了insert语句后再返回主键,这时可以使用主键映射 首先,定义sql,以MySQL为例 1 <insert id="addUser" useGeneratedKeys="true" keyProperty="user_id" parameterType="com.entity.User"> 2 insert into user(name,xes,age)values('jack','男

hibernate中基于主键映射1-1关联关系和基于外键映射1-1关联关系的不同

基于主键映射1-1关联关系和基于外键映射1-1关联关系的不同,主要区别是在配置映射文件上会有区别 两个持久化类为Manager和Department 1:基于主键映射1-1关联关系 1)使用其他持久化类的主键生成主键的实体的映射文件 首先需要指定主键生成方式为foreigner 格式为: <id name="departmentId" type="java.lang.Integer"> <column name="department_i

Hibernate 查询排序与联合主键映射

1.查询排序 (1)数据库排序(推荐) <map order-by="name ase" > <!--name的升序,降序desc--> session.createQuery(" ").uniqueResult() //返回唯一的对象,前台对象只有一个 <set order-by="name asc"> (2)内存排序 <set sort="natural" > sort属性值

【Hibernate步步为营】--复合主键映射详解

本题是最基本的分段树操作了.或者一般叫线段树,不过好像和线段没什么关系,只是分段了. 不使用lazy标志,更新只是更新单点. 如果不使用分段树,那么更新时间效率只需要O(1),使用分段树更新效率就需要O(lgn)了. 但是不是用分段树,那么查询的时间效率是O(n),而分段树查询效率是O(lgn) 这就是amortize分摊了时间,而且lgn真的很快,数据不是非常巨大的时候,接近常数了. 故此本题需要使用分段树. #include <cstdio> class EnemyInfo { stati