现在的位置: 首页 > 综合 > 正文

Hibernate实战_笔记24(主键生成策略、类映射选项)

2014年09月05日 ⁄ 综合 ⁄ 共 7057字 ⁄ 字号 评论关闭
文章目录

数据库主键

Hibernate需要知道你生成主键的首选策略。不过,先要定义主键。

1、选择主键

备选的键是能够用来识别表中一个特定行的一列或者一组列。要变成主键,备选键必须满足下列属性:
1)它的值(对于备选键的任意列而言)永远不为空。
2)每一行都有唯一的值。
3)一个特定行的值永远不变。

2、选择键生成器

Hibernate有几个内建的标识符生成策略。
生成器名称:native
选项:无
生成器描述:native同一性生成器挑选其他同一性生成器,如identity、sequence或者hilo,取决于底层数据库的能力。使用这个生成器保证映射元数据可以移植到不同的数据库管理系统。
生成器名称:identity
选项:无
生成器描述:这个生成器支持DB2、MySQL、MS SQL Server、Sybase和HypersonicSQL中的同一性列。返回的标识符类型为long、short或者int
生成器名称:sequence
选项:sequence,parameters
生成器描述:这个生成器在DB2、PostgreSQL、Oracle、SAPDB或者Mckoi中创建一个序列;或者使用InterBase中的一个生成器。返回的标识符类型为long、short或者int。如果创建序列的其他设置要被添加到DDL,就使用sequence选项给序列定义一个目录名称(默认是hibernate_sequence)和parameters。
生成器名称:increment
选项:无
生成器描述:Hibernate启动时,这个生成器读取表的最大(数字)主键列值,并且每次插入一个新行时值就增加1。生成的标识符类型为long、short或者int。如果单服务器的Hibernate应用程序对数据库具有排它性访问时,这个生成器特别有效,但不应该被用于任何其他场合。
生成器名称:hilo
选项:table,column,max_lo
生成器描述:高/低位算法是生成long类型标识符的一种高效的方法,给定表和列(默认时,分别为hibernate_unique_key和next)作为高值源。高/低位算法生成仅对特定数据库唯一的标识符。这个生成器每次获取高值时总要使用一个单独的数据库连接,因此它不受用户提供的数据库连接支持。换句话说。别把它与sessionFactory.openSession(myConnection)共用。
生成器名称:seqhilo
选项:sequence,parameters,max_lo
生成器描述:这个生成器的作用就像一般的hilo生成器,除了它使用一个具名的数据库序列来生成高值之外。
生成器名称:uuid
选项:separator
生成器描述:这个生成器是一个128位的UUID(生成String类型的标识符的一种算法,在网络内部唯一)。IP地址与一个唯一的时间戳结合使用。
生成器名称:guid
选项:无
生成器描述:这个生成器在MySQL和SQL服务器中提供一个数据库生成的全局唯一的标识符字符串。
生成器名称:select
选项:key
生成器描述:这个生成器获取由数据库触发器分配的一个主键,它按某些唯一键选择行并获取主键值。这个策略需要另一个唯一的备选键列,且key选项必须被设置成唯一键列的名称。

有些内建的标识符生成器可以通过选项得到配置。在原生的Hibernate XML映射中,把选项定义为键/值对:

<id name="id" column="MY_ID">
    <generator class="sequence">
        <param name="sequence">MY_SEQUENCE</param>
        <param name="parameters">
            START WITH 1 INCREMENT BY 1
        </param>
    </generator>
</id>

可以通过注解使用Hibernate标识符生成器,即使没有直接的注解可用:

@Entity
@Table(name = "TBL_MYENTITY")
@org.hibernate.annotations.GenericGenerator(name = "hibernate-uuid", strategy = "uuid")
public class MyEntity {

	@Id
	@GeneratedValue(generator = "hibernate-uuid")
	@Column(name = "My_ID")
	private String id;
	
	private String name;

	public String getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

      @GenericGenerator Hibernate扩展可以用来给Hibernate标识符生成器提供一个名称,在这个例子中为hibernate-uuid。然后这个名称被标准的generator属性引用。
      生成器的这个声明和它按名称的分配,也必须通过注解应用到基于序列或基于表的标识符生成中。想象你要在所有的实体类中使用一个定制的序列生成器。因为这个标识符生成必须是全局的,所以在orm.xml中声明:

<!--全局标识符生成器-->
<!--这声明了名称为MY_SEQUENCE的数据库序列,带有初始值123,可以用作数据库标识符生成的来源,并且每当持久化引擎需要标识符时,它就应该获得20个值。-->
<sequence-generator name="mySequenceGenerator"
    sequence-name="MY_SEQUENCE"
    initial-value="123"
    allocation-size="20" />

为了把这个标识符生成器应用到特定的实体,就利用它的名称:

@Entity
@Table(name = "TBL_MYENTITY")
public class MyEntity {

	@Id
	@GeneratedValue(generator = "mySequenceGenerator")
	@Column(name = "My_ID")
	private Integer id;
	
	private String name;

	public Integer getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

如果在实体级声明了另一个同名的生成器,且在class关键字之前,它就会覆盖全局的标识符生成器。
你不受限于内建的策略,还可以通过实现Hibernate的IdentifierGenerator接口创建自己的标识符生成器。
甚至可能在单个领域模型中给持久化类混合标识符生成器,但是对于非遗留的数据,建议给所有实体使用相同的标识符生成策略。

类映射选项

1)CRUD SQL语句的动态生成
2)实体易变性控制
3)给查询命名实体
3)映射包实体
4)引用关键字和被保存的数据库标识符

5)实现数据库命名约定

动态的SQL生成

      默认情况下,Hibernate在启动时给每个持久化类创建SQL语句。这些语句是用来读取单个行、删除一行等的简单创建、读取、更新和删除。
Hibernate在启动时如何创建UPDATE语句?毕竟,这时候还不知道要更新的列。答案是生成的SQL语句更新所有的列,并且如果特定列的值没有被修改,这个语句就会把它设置为它的旧值。
      在有些情况下,例如包含几百列的一个遗留表,在该表中,即使最简单的操作(假设只有一个列需要更新)的SQL语句也将很大,必须关闭这个启动时的SQL生成。如果必须为数千个实体高速缓存许多语句的话,这个查询语句高速缓存的内存消耗也很高(这通常不成问题)。
<class>映射元素中有两个属性可以禁用启动时CRUD SQL的生成:

<class name="Item"
    dynamic-insert="true"
    dynamic-insert="true">
</class>

      注意:dynamic-insert="true"支持动态SQL哦哦(说错了,今天试了一下,是相反的,请注意,当时没有试)
      dynamic-insert属性告诉Hibernate是否在SQL INSERT中包括空的属性值,dynamic-update属性告诉Hibernate是否在SQL UPDATE中包括未被修改的属性。
如果使用JDK5.0注解映射,就需要一个原生的Hibernate注解来启用动态的SQL生成:

@Entity
@org.hibernate.annotations.Entity(
	dynamicInsert = true,
	dynamicUpdate = true
)
public class Item {
}

来自Hibernate包的第二个@Entity注解使用额外的选项扩展了JPA注解,包括dynamicInsert和dynamicUpdate。
有时候可以避免生成任何UPDATE语句,如果持久化类被映射为不可变的话。

使实体不可变

      一个特定类的实例可以是不可变的。例如,在CaveatEmptor中,对货品所做的Bid是不可变的。因此,在BID表中永远不需要执行UPDATE语句。Hibernate也可以进行一些其他优化,例如,如果通过把mutable属性设置为false来映射一个不可变的类,就可以避免脏检查:

<hibernate-mapping default-access="field">
    <class name="Bid" mutable="false">
    </class>
</hibernate-mapping>

      如果没有公开类的任何属性的公有设置方法,POJO就是不可变的——所有值都在构造函数中设置。不用私有设置方法,而是经常更喜欢通过Hibernate对不可变的持久化进行直接的字段访问,因此不必编写没用的访问方法。可以用注解映射一个不可变的实体:

@Entity
@org.hibernate.annotations.Immutable
@org.hibernate.annotations.AccessType("field")
public class Bid {
}

1)在class中声明mutable="false"或@Immutable
这意味着对该的更新将会被忽略,不过不会抛出异常,只允许有增加和删除操作。
在class中声明mutable="false":insert=允许,delete=允许,update=不允许
2)在集合中声明mutable="false"或@Immutable
这意味着在这个集合中插入记录或者删除孤行是不允许的,否则会抛出异常。只允许更新操作。不过,如果启用级联删除的话,当父类被删除时,其所有子类也将被删除,即使它是mutable的。
在集合中声明mutable="false":insert=不允许,孤行删除=不允许,delete=允许,update=允许

给查询命名实体

      默认情况下,所有类名都自动地“导入”到Hibernate查询语言(HQL)的命名空间。换句话说,可以在HQL中使用没有包前缀的短类名,这很方便。然而,如果给定的SessionFactory存在两个同名的类,这两个类可能在领域模型的不同包中,这个自动导入就可以关闭。
      如果存在这种冲突,而又改变默认的设置,Hibernate将不知道你正在HQL中引用哪个类。可以在<hibernate-mapping>根元素中设置autoimport="false",对特定的映射文件把名称的自动导入关闭到HQL命名空间。
      实体名称也可以被显式地导入到HQL命名空间。甚至可以导入非显式映射的类和接口,因此短名称可以被用在多态的HQL查询中:

<hibernate-mapping>
    <import class="auction.model.Auditable" rename="IAuditable"/>
</hibernate-mapping>

现在可以用HQL查询(如from IAuditable)来获取实现auction.model.Auditable接口的类的所有持久化实例。
可以利用注解给实体一个显式的名称,如果短名称会在JPA QL或者HQL命名空间中导致冲突的话:

@Entity(name="AuctionItem")
public class Item{
}

声明包名称

      CaveatEmptor应用程序的所有持久化类都在Java包auction.model中声明。然而,你并不想每当在关联、子类或者组件映射中命名这个类或者任何其他类时都重复整个包名称。替换做法是,指定一个package属性:

<hibernate-mapping package="auction.model">
    <class name="Item" table="TBL_ITEM">
    </class>
</hibernate-mapping>

用引号把SQL标识符括起来

      如果在遗留数据库中,你遇到包含奇怪字符或者空格的标识符,或者你希望强制区分大小写。或者,如果依赖Hibernate的默认设置,Java中的类名称或者属性名称可能被自动转化为数据库管理系统中不允许的表名称或者列名称。例如,User类被映射到USER表,而它通常是SQL数据库中保留的一个关键字。Hibernate不认为任何DBMS产品的SQL关键字,因此数据库系统在启动或者运行时抛出异常。
      如果在映射文档中用反引号(backtick)把表名或者列名括起来,Hibernate就会始终在生成的SQL中把这个标识符用引号括起来。下列属性声明强制Hibernate用括起来的列名"DESCRIPTION"生成SQL。Hiberante也知道Microsft SQL Server需要变量[DESCRIPTION],以及MySQL需要`DESCRIPTION`。

<property name="description"
    column="`DESCRIPTION`"/>

实现命名约定

我们经常遇到对数据库表名和列名有严格约定的组织。Hibernate提供一种允许自动强制执行命名标准的特性。
假设CaveatEmptor中的所有表名都应该遵循模式CE_<tablename>。一种解决方案是手工在映射文件中的所有<class>和集合元素上指定table属性。然而,这种方法既费时又容易遗忘。替代做法是,实现Hibernate的NamingStrategy接口

public class CENamingStrategy extends ImprovedNamingStrategy {

	private static final long serialVersionUID = -5538665442917666989L;

	public String classToTableName(String className) {
		return "CE_" + StringHelper.unqualify(className);
	}

	public String propertyToColumnName(String propertyName) {
		return propertyName;
	}

	public String tableName(String tableName) {
		return "CE_" + tableName;
	}

	public String columnName(String columnName) {
		return columnName;
	}
}

      仅当<class>映射没有指定显式的table名称时才调用classToTableName()方法。如果属性没有显式的column名称,就调用propertyToColumnName()方法。当声明了显式的名称时,则调用tableName()和columnName()方法。
如果启用这个CENamingStrategy,类映射声明

<class name="BankAccount">

将导致CE_BANKACCOUNT作为表的名称。
然而,如果指定了表名,像这样:

<class name="BankAccount" table="BANK_ACCOUNT">

那么CE_BANK_ACCOUNT就是表的名称。此时,BANK_ACCOUNT就被传递到tableName()方法。
NamingStrategy接口的最好特性是动态运行的潜能。为了启用一个特定的命名策略,可以在启动时把一个实例传递到Hibernate的Configuration:

Configuration cfg = new Configuration();
cfg.setNamingStrategy(new CENamingStrategy());
SessionFactory sessionFactory = cfg.configuration().buildSessionFactory();

这允许你有多个基于相同映射文件的SessionFactory实例,每一个都使用不同的NamingStrategy。

抱歉!评论已关闭.