Java 持久化技术规范(JPA )中的主键生成策略

Table 策略 (Table strategy)

这种策略中,持久化引擎 (persistence engine) 使用关系型数据库中的一个表 (Table) 来生成主键。这种策略可移植性比较好,因为所有的关系型数据库都支持这种策略。不同的 J2EE 应用服务器使用不同的持久化引擎。

下面用一个例子来说明这种表生成策略的使用:


清单 1.Table 生成策略
@Entity    public  class  PrimaryKey_Table    @TableGenerator(name  "PK_SEQ",    table  "SEQUENCE_TABLE",                                  pkColumnName    "SEQUENCE_NAME",                                  valueColumnName    "SEQUENCE_COUNT")    @Id          @GeneratedValue(strategy  =GenerationType.TABLE,generator="PK_SEQ")          private  Long  id;    //Getters  and  Setters  //为了方便,类里面除了一个必需的主键列,没有任何其他列,以后类似 

首先,清单 中使用 @javax.persistence.TableGenerator 这个注解来指定一个用来生成主键的表 (Table),这个注解可以使用在实体类上,也可以像这个例子一样使用在主键字段上。

其中,在这个例子中,name 属性“PK_SEQ” 标示了这个生成器,也就是说这个生成器的名字是 PK_SEQ。这个 Table 属性标示了用哪个表来存贮生成的主键,在这个例子中,用“ SEQUENCE_TABLE” 来存储主键,数据库中有对应的 SEQUENCE_TABLE 表。其中 pkColumnName 属性用来指定的是生成器那个表中的主键,也就是 SEQUENCE_TABLE 这个表的主键的名字。属性 valueColumnName 指定列是用来存储最后生成的那个主键的值。

也可以使用持久化引擎提供的缺省得 Table,例如:


清单 2. 使用确省的表生成器
public  class  PK  implements  Serializable          private  static  final  long  serialVersionUID  1L;              @Id          @GeneratedValue(strategy  GenerationType.TABLE)                private  Long  id;    //  Getters  and  Setters   

不同的持久化引擎有不同的缺省值,在 glass fish 中,Table 属性的缺省值是 SEQUENCE, pkColumnName 属性缺省值是 SEQ_NAME,,valueColumnName 属性的缺省值是 SEQ_COUNT

回页首

Sequence 策略

一些数据库,比如 Oralce,有一种内置的叫做“序列” (sequence)的机制来生成主键。为了调用这个序列,需要使用 @javax.persistence.SequenceGenerator 这个注解。

例如


清单 3.sequence 策略生成主键
@Entity    public  class  PK_Sequence  implements  Serializable          private  static  final  long  serialVersionUID  1L;          @SequenceGenerator(name="PK_SEQ_TBL",sequenceName="PK_SEQ_NAME")          @Id          @GeneratedValue(strategy  GenerationType.SEQUENCE,generator="PK_SEQ_TBL")          private  Long  id;    //  Getters  and  Setters   

其中的 @javax.persistence.SequenceGenerator 定义如下:


清单 4.@SequenceGenerator 注解的定义
@Target(value  {ElementType.TYPE,  ElementType.METHOD,  ElementType.FIELD})    @Retention(value  RetentionPolicy.RUNTIME)    public  @interface  SequenceGenerator          public  String  name();          public  String  sequenceName()  default  "";          public  String  catalog()  default  "";          public  String  schema()  default  "";          public  int  initialValue()  default  1;          public  int  allocationSize()  default  50;   

从定义中可以看出这个注解可以用在类上,也可以用在方法和字段上,其中 name 属性指定的是所使用的生成器;sequenceName 指定的是数据库中的序列;initialValue 指定的是序列的初始值,和 @TableGenerator 不同是它的缺省值 1;allocationSize 指定的是持久化引擎 (persistence engine) 从序列 (sequence) 中读取值时的缓存大小,它的缺省值是 50。

回页首

Identity 策略

一些数据库,用一个 Identity 列来生成主键,使用这个策略生成主键的时候,只需要在 @GeneratedValue 中用 strategy 属性指定即可。如下所示:


清单 5.strategy 策略生成主键
@Entity    public  class  PK_Identity  implements  Serializable          private  static  final  long  serialVersionUID  1L;          @Id          @GeneratedValue(strategy  GenerationType.IDENTITY)          private  Long  id;    //  Getters  and  Setters   

回页首

Auto 策略

使用 AUTO 策略就是将主键生成的策略交给持久化引擎 (persistence engine) 来决定,由它自己选择从 Table 策略,Sequence 策略和 Identity 策略三种策略中选择合适的主键生成策略。不同的持久化引擎 (persistence engine) 使用不同的策略,在 galss fish 中使用的是 Table 策略。

使用 AUTO 策略时,我们可以显示使用,如:


清单 6.Auto 策略生成主键
@Entity    public  class  PK_Auto  implements  Serializable          private  static  final  long  serialVersionUID  1L;          @Id          @GeneratedValue(strategy  GenerationType.AUTO)          private  Long  id;        //  Getters  and  Setters         

或则只使用:

@Generated Value

或者干脆什么都不写,因为缺省得主键生成策略就是 AUTO。

回页首

复合主键

在对象关系映射模型中,使用单独的一个字段作为主键是一种非常好的做法,但是在实际应用中,经常会遇到复合主键的问题,就是使用两个或两个以上的字段作为主键。比如,在一些历史遗留的数据库表中,经常出现复合主键的问题,为了解决这种问题,JPA2.0 中采用的 @EmbeddedId 和 @IdClass 两种方法解决这种问题。它们都需要将用于主键的字段单独放在一个主键类 (primary key class) 里面,并且该主键类必须重写 equals () 和 hashCode () 方法,必须实现 Serializable 接口,必须拥有无参构造函数。

@EmbeddedId 复合主键

清单 中的 NewsId 类被用做主键类,它用 @Embeddable 注解进行了注释,说明这个类可以嵌入到其他类中。之外这个类中还重写了 hashCode () 和 equals () 方法, 因为这个类中的两个属性要用作主键,必须有一种判定它们是否相等并且唯一的途径。


清单 7.@EmbeddedId 中的主键类
@Embeddable    public  class  NewsId  implements  Serializable          private  static  final  long  serialVersionUID  1L;          private  String  title;          private  String  language;          public  String  getLanguage()                  return  language;                  public  void  setLanguage(String  language)                  this.language  language;                  public  String  getTitle()                  return  title;                  public  void  setTitle(String  title)                  this.title  title;                  @Override          public  boolean  equals(Object  obj)                  if  (obj  ==  null)                          return  false;                                  if  (getClass()  !=  obj.getClass())                          return  false;                                  final  NewsId  other  (NewsId)  obj;  if  ((this.title  ==  null)  (other.title  !=  null)  !this.title.equals(other.title))                          return  false;                  if  ((this.language  ==  null)  (other.language  !=  null)  !this.language.equals(        other.language))                          return  false;                                  return  true;                  @Override          public  int  hashCode()                  int  hash  5;                  hash  41  hash  (this.title  !=  null  this.title.hashCode()  0);                  hash  41  hash  (this.language  !=  null  this.language.hashCode()  0);                  return  hash;               

清单 中的 News 类使用了清单 中定义的主键类,可以看倒非常简单,只需要使用 @EmbeddedId 指定主键类即可。


清单 8.News 实体类使用定义好的主键类
@Entity    public  class  News  implements  Serializable          private  static  final  long  serialVersionUID  1L;          @EmbeddedId          private  NewsId  id;          private  String  content;        //  Getters  and  Setters   

清单 是持久化后生成的数据库表的结构,可以看出来这个表如我们预想的一样是 Title 和 Language 的联合主键。


清单 9. 使用主键类生成的表结构
CREATE  TABLE  `news`      `CONTENT`  varchar(255)  default  NULL,      `TITLE`  varchar(255)  NOT  NULL,      `LANGUAGE`  varchar(255)  NOT  NULL,      PRIMARY  KEY    (`TITLE`,`LANGUAGE`)    ENGINE=InnoDB  DEFAULT  CHARSET=latin1; 

回页首

IdClass 复合主键

IdClass 这种复合主键策略,在主键类上和 Embeddable 这种复合主键策略稍有不同。如清单 10,这个策略中的主键类不需要使用任何注解 (annotation),但是仍然必须重写 hashCode() 和 equals() 两个方法。其实也就是将 Embeddable 这种复合主键策略中的主键类的 @Embeddable 注解去掉就可以了。


清单 10. IdClass 复合主键策略中的主键类
public  class  NewsId  implements  Serializable          private  static  final  long  serialVersionUID  1L;          private  String  title;          private  String  language;          public  String  getLanguage()                  return  language;                  public  void  setLanguage(String  language)                  this.language  language;                  public  String  getTitle()                  return  title;                  public  void  setTitle(String  title)                  this.title  title;                  @Override          public  boolean  equals(Object  obj)                  if  (obj  ==  null)                          return  false;                                  if  (getClass()  !=  obj.getClass())                          return  false;                                  final  NewsId  other  (NewsId)  obj;  if  ((this.title  ==  null)  (other.title  !=  null)  !this.title.equals(other.title))                          return  false;                  if  ((this.language  ==  null)  (other.language  !=  null)  !this.language.equals(        other.language))                          return  false;                                  return  true;                  @Override          public  int  hashCode()                  int  hash  5;                  hash  41  hash  (this.title  !=  null  this.title.hashCode()  0);                  hash  41  hash  (this.language  !=  null  this.language.hashCode()  0);                  return  hash;               

从清单 11 中可以看出这个 News 实体类首先使用 @IdClass (NewsId.class) 注解指定了主键类。同时在类中也复写了主键类中的两个字段,并分别用 @Id 进行了注解。


清单 11. IdClass 策略中使用复合主键的 News 
@Entity    @IdClass(NewsId.class)    public  class  News  implements  Serializable          private  static  final  long  serialVersionUID  1L;          @Id          private  String  title;          @Id          private  String  language;    private  String  content;    //  Getters  and  Setters   

从清单 12 中可以看出,两种复合主键的映射策略,持久化后映射到数据库中的表的结构是相同的,一个明显的区别就是在查询的时候稍有不同。如在使用 @EmbeddableId 策略的时候,要使用如下查询语句:

Select  n.newsId.title  from  news 

而使用 @IdClass 策略的时候,要使用如下查询语句:

Select  n.title  from  news 

另外一点就是使用 @IdClass 这种策略的时候,在复写主键类中的字段的时候务必要保证和主键类中的定义完全一样。


清单 12. @IdClass 策略生成的表结构
CREATE  TABLE  `news`      `CONTENT`  varchar(255)  default  NULL,      `TITLE`  varchar(255)  NOT  NULL,      `LANGUAGE`  varchar(255)  NOT  NULL,      PRIMARY  KEY    (`TITLE`,`LANGUAGE`)    ENGINE=InnoDB  DEFAULT  CHARSET=latin1; 

回页首

总结

Java EE 项目开发中的持久层,虽然具体的实现方式,也就是持久化引擎会随着你选择的 Java EE 服务器的不同而有所不同,但是在 JPA(java persistence API) 这个规范之下,每个实体的主键生成策略却只有上面几种,也就是说我们主要掌握了上面几种主键生成策略,就可以在以后 Java EE 项目持久层开发中以不变应万变的姿态来面对纷繁复杂的具体情况了。



zhuanzai:http://www.ibm.com/developerworks/cn/java/j-lo-jpaprimarykey/ 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值