Hibernate实战(第2版)学习笔记一

1.ORM就是利用描述对象和数据库之间映射的元数据,自动(且透明)地把Java应用程序中的对象持久化到关系数据库中的表。ORM本质上是把数据从一种表示法(可逆)转换为另一种表示法进行工作。
2.使用连接池的原因有3:
(1)获得性的连接很昂贵。有些数据库管理系统甚至给每个连接启动一个全新的服务器进程。
(2)为数据库管理系统维护许多闲置的连接很昂贵,并且连接池可以最优化闲置连接的使用(或者没有请求时就断开连接)。
(3)给某些驱动程序创建预编译的语句也很昂贵,且连接池可以对跨请求的连接高速缓存语句。
3.Hibernate定义了一个插件架构,允许与任何连接池软件整合,然而,对C3P0的支持的内建的,并且这个软件与Hibernate捆绑在一起。
4.可以提供自己的连接吗?实现org.hibernate.connection.ConnectionProvider接口,用hibernate.connection.provider_class配置选项命名你的实现。现在,如果Hibernate需要数据库连接,它将依赖于你定制的提供程序(provider)。
5.hibernate.use_sql_comments——它导致Hibernate把注释放在所有生成的SQL语句内部,来提示他们的出处。
6.如果想要通过配置来启用统计集合,并且不用编程,就设置hibernate.generate_statistics配置属性为true.
7.统计接口中,Statistics用于全局的信息,EntityStatistics用于有关一个特定实体的信息,CollectionStatistics用于一个特定的集合任务,QueryStatistics用于SQL和HQL查询,SecondLevelCacheStatistics用于详细的关于可选的二级高速缓存中的一个特定区域的运行时信息。一种方便的方法是logSummary(),它通过单个调用把一个完整的摘要打印到控制台。
8.JPA的编程接口
(1)javax.persistence.Persistence——给EntityManagerFactory的创建提供一种静态方法的一个启动类
(2)javax.persistence.EntityManagerFactory——等同于Hibernate SessionFactory。这个运行时对象表示一个特定的持久化单元。它是线程安全的,通常被当作一个单例(singleton)处理,并给EntityMangaer实例的创建提供方法。
(3)javax.persistence.EntityManager——等同于Hibernate Session。这个单线程、非共享的对象标示数据访问的一个特定工作单元。它提供方法去管理实体实例的生命周期并创建Query实例。
(4)javax.persistence.Query——等同于Hibernate Query。一个对象是一种特定的JPA查询语言或者原生的SQL查询表示法,它允许参数的安全绑定,并给查询的执行提供各种各样的方法。
(5)javax.persistence.EntityTransaction——等同于Hibernate Transaction,在Java SE环境中用于RESOURCE_LOCAL事务的划分。在Java EE中,依赖JTA标准的javax.transaction.UserTransaction接口进行编程式的事务划分。
9.JTA允许Hibernate参与托管资源中的事务。Hibernate可以通过JNDI查找托管资源(数据库连接),并且能够把自身当做一项服务绑定到JNDI上。最后,Hibernate可以通过JMX被部署,然后通过JMX容器被当作一项服务来管理,并且使用标准的JMX客户端程序在运行时被监控。
10.JTA是Java企业应用中事务控制的标准服务接口。它公开了几个接口,例如用于事务划分的UserTransaction API和用于参与列事务生命周期中的TransactionManager API。事务管理器能够协调跨资源的单个事务——想象在单个事务的两个数据库中的两个Hibernate Session中进行。
11.Java命名和目录接口API(JNDI)允许对象向(从)一个层次结构(目录树)中存储(获取)。JNDI实现注册(registry)模式。基础的对象(事务上下文、数据源等)、配置设置(环境设置、用户注册等),甚至应用对象(EJB引用、对象工厂等)都可以被绑定到JNDI。
12.如果hibernate.session_factory_name属性设置为JNDI节点的名称,Hibernate的SessionFactory就会自动把自身绑定到JNDI。如果运行时环境不提供默认的JNDI上下文(或者如果默认的JNDI实现不支持Referenceable的实例),就需要使用hibernate.jndi.uirl和hibernate.jndi.class属性指定一个JNDI的初始上下文。
13.Java管理扩展(Java Management Extension,JMX)。JMX是关于系统组件管理或者系统服务管理的。
JMX规范定义了下列组件:
(1)JMX MBean——可以重用(通常是构成基础)的一个组件,给管理公开接口。
(2)JMX容器(container)——调解对MBean的一般访问(本地的或者远程的)。
(3)JMX客户端程序(client)——可能通过JMX容器用来管理人和MBean。
14.Hibernate不要求持久化类实现Serializable(可序列化)。然而,当对象被存储在一个HttpSession中,或者用RMI按值传递时,就需要序列化。
不同于JavaBeans规范,它不需要特定的构造函数,而Hibernate(和JPA)则要求每个持久化类都有个无参构造函数。Hibernate在这个构造函数上使用Java Reflection API调用持久化类来实例化对象。构造函数可以是非公共的,但必须至少是包可见(package-visible)的,如果运行时生成的代理要用于性能优化的话。代理生成也要求这个类不作final声明(也没有final方法)!
15.Hibernate自动侦测对象状态的改变,以便使更新过的状态与数据库同步。从获取方法返回一个不同的对象,通常比由Hibernate传递到设置方法的对象来的安全。Hibernate按值比较对象——不是按对象同一性——来确定一个属性的持久化状态是否需要被更新。这里有个重要的例外:集合是按同一性比较的!对于一个被映射为持久化集合的属性,你应该从获取方法中返回与Hibernate传递到设置方法中完全相同的集合实例。如果没有,Hibernate将更新数据库,即使不需要更新,保存在内存中的状态每次也都会与数据库同步。
16.如果你在加载和存储实例时配置Hibernate来使用这些方法的话,如果抛出RuntimeException。当前的事务就被回滚,你要自己处理这个异常。如果抛出已检查应用异常,Hibernate就会把这个异常包在一个RuntimeException里面。
17.ORM工具要求元数据指定类何彪、属性和列、关联和外键、Java类型和SQL类型等之间的映射。这种信息称作ORM元数据。元数据是关于数据的数据,映射元数据定义和支配在面向对象和SQL系统中不同的类型系统和关系表示法之间的转化。
18.如果正使用JDK5.0,就考虑JPA/Hibernate注解作为首选。如果想把一个特定的类映射具体化,或者利用一个不能作为注解使用的的Hibernate扩展,就回到原生的Hibernate XML映射文件。如果不打算使用任何供应商扩展,或者如果只想覆盖几个注解,或者如果需要设置包括部署描述符的完整可移植性,就考虑JPA XML描述符。
19.dynamic-insert属性告诉Hibernate是否在SQL INSERT中包括空的属性值,dynamic-update属性告诉Hibernate是否在SQL UPDATE中包括未被修改的属性。
20.一旦创建了SessionFactory,它的映射就是不可变的。SessionFactory内部使用一种与配置时所用的不同的元模型。没有办法从SessionFactory或者Session中退回到原始的Configuration。(注意,你可以从Session中获得SessionFactory,如果希望访问一个全局设置的话)。然而,应用程序可以通过调用getClassMetadata()或者getCollectionMetadata()来读取SessionFactory的元模型。
21.Hibernate中有3中内建的实体模式:
(1)POJO——基于POJO、持久化类的一种领域模型实现。这是默认的实体模式。
(2)MAP——不需要Java类;用HashMap在Java应用程序中表示实体。这个模式允许完全动态应用程序的快速原型。
(3)DOM4J——不需要Java类;实体被表示为XML元素,基于dom4j API。这种模式对于导出或者导入数据,或者通过XSLT处理来渲染和转换数据时特别有用。
22.现在你有3中识别对象的方法:
(1)如果对象在JVM中占据着相同的内存位置,它们就是同一的。这可以通过使用==操作符进行检查。这个概念称作对象同一性。
(2)如果对象有着相同的值,它们就是相等的,如equals(object o)方法定义的一样。不显式覆盖这个方法的类,继承了由java.lang.Object定义的实现,它必将对象同一性。这个概念称作等同性。
(3)如果存储在一个关系数据库中的对象标示相同的行,或者它们共享相同的表和主键值,它们就是同一个的。这个概念称作数据库同一性。
23.Hibernate以两种方式把数据库同一性公开给应用程序:
(1)持久化实例的标示符属性值。
(2)Session.getIdentifier(Object entity)返回的值。
标示符属性很特殊——它的值是由持久化实例标示的数据库行的主键值。
Hibernate不允许持久化实例的标识符值第一次分配之后对她进行改变。主键值永远不变——否则该属性将不是个适当的主键备选对象!
24.选择主键
备选的键是能够用来识别表中一个特定的行的一列或一组列。要变成主键,备选键必须满足下列属性:
(1)它的值(对于备选键的任意列而言)永远不为空。
(2)每一行都有唯一的值
(3)一个特定的值永远不变。
25.如果在实体级声明了另一个同名的生成器,且在class关键字之前,它就会覆盖全局的标识符生成器。可以用相同的方法声明和应用@TableGenerator。
你不受限于内建的策略,还可以通过实现Hibernate的IdentifierGenerator接口创建自己的标识符生成器。
甚至可能在单个领域模型中给持久化类混合标识符生成器,但是对于非遗留的数据,建议给所有实体使用相同的标识符生成策略。
26.默认情况下,Hibernate在启动时给每个持久化类创建SQL语句。这些语句是用来读取单个行、删除一行等的简单创建、读取、更新和删除。
27.<class>映射元素中有两个属性可以禁用启动时CRUD SQL的生成:
<class name="Item" dynamic-insert="true" dynamic-update="true"></class>
dynamic-insert属性告诉Hibernate是否在SQL INSERT中包括空的属性值。
dynamic-update属性告诉Hibernate是否在SQL UPDATE中包括未被修改的属性。
28.如果把mutable属性设置为false来映射一个不可变的类,就可以避免脏检查:
<hibernate-mapping default-access="field">
    <class name="Bid" mutable="false">
    ...
    </class>
</hibernate-mapping>
29.默认情况下,所有类名都自动地“导入”到Hibernate查询语言(HQL)的命名空间。换句话说,可以在HQL中使用没有包前缀的短类名,这很方便。然而,如果给定的SessionFactory存在两个同名的类,这两个类可能在领域模型的不同包中,这个自动导入就可以关闭。
    如果存在这种冲突,而又不改变默认的设置,Hibernate将不知道你正在HQL中引用哪个类。可以在<hibernate-mapping>根元素中设置autoimport="false",对特定的映射文件把名称的自动导入关闭到HQL命名空间。
实体名称也可以被显式地导入到HQL命名空间。甚至可以导入非显式映射的类和接口,因此短名称可以被用在多态的HQL查询中。
<hibernate-mapping>
    <import class="auction.model.Aduitable" rename="IAuditable"/>
</hibernate-mapping>
30.如果在映射文档中用反引号(backtick)把表名或列名括起来,Hibernate就会始终在生成的SQL中把这个标识符用引号括起来。
除了把所有表名和列名用反引号括起来之外,再没有其他办法可以强制Hibernate在任何地方使用括起来的标识符了。
31.如果映射持久化类,无论它是实体还是值类型,所有持久化属性都必须在XML映射文件中被显示地映射。另一方面,如果用注解映射类,它的所有属性都被默认为是持久化的。可以用@java.persistence.Transient注解给属性进行标识来把它们排除,或者使用transient的Java关键字(通常只给Java序列化排除字段)。
32.JPA是基于异常模型的一个配置,因此可以依赖默认。如果持久化类的一个属性没有被注解,就应用下列规则:
(1)如果属性是JDK类型,它自动就是持久化的。换句话说,它在Hibernate XML映射文件中像<property name="propertyName"/>一样处理。
(2)如果属性的类被注解为@Embeddable,它就被映射为自己的类的组件。
(3)如果属性的类型时Serializable,它的值以序列化的形式保存。
如果不想依赖这些默认,就在一个特定的属性上应用@Basic注解。@Column注解相当于XML的<column>元素。以下是如何按要求声明一个属性值的例子:
@Basic(optional=false)
@Column(nullable=false)
public BigDecimal getInitialPrice{return initialPrice;}
@Basic注解把属性标识为在Java对象级上不可选。第二个设置,列映射上的nullable=false,只负责NOT NULL(非空)数据库约束的生成。Hibernate JPA实现在任何情况下对待这两个选项都一视同仁,因此你可以只用其中一个注解达到这一目的。
33.Hibernate Annotation包包括一个更高级且更负责的数据验证框架,不仅可以用它在DDL中定义数据库Schema约束,还可以用于运行时的数据验证。
34.Hibernate允许利用@org.hibernate.annotations.AccessType(<strategy>)注解灵活地定制访问策略:
(1)如果在类/实体级中设置AccessType,类的所有属性都根据选中的策略访问。属性级注解是在字段还是在获取方法上,这取决于策略。这个设置覆盖来自标准的@Id注解位置的任何默认值。
(2)如果给字段访问默认或者显示地设置一个实体,字段中的AccessType("property")注解就把这个特定的属性转换为通过属性获取方法/设置方法的运行时访问。AccessType注解的位置仍然为字段。
(3)如果给属性访问默认或者显式地设置一个实体,获取方法中的AccessType("field")注解就把这个特定的属性转换为通过同名字段的运行时访问。AccessType注解的位置仍然为获取方法。
(4)任何@Embedded类都继承默认的或者显式声明的自己根实体类的访问策略
(5)任何@MappedSuperclass属性都通过被映射实体类的默认或者显示声明的访问策略而被访问。
除了字段和属性访问之外,另一个可能有用的策略是noop。它映射Java持久化类中不存在的属性。这听起来有点奇怪,但是他让你在HQL查询中指向这个“虚拟属性”(换句话说,只在HQL查询中使用数据库列)。
如果没有合适的内建访问策略,也可以通过实现接口org.hibernate.property.PropertyAccessor定义自己的定制属性访问策略。在access映射属性或者@AccessType注解中设置(完全匹配的)类名。
35.衍生属性的值在运行时计算,通过对利用formula属性定义的表达式求值。例如,可以把totalIncludingTax属性映射到SQL表达式:
<property name="totalIncludingTax" formula="TOTAL+TAX_RATE*TOTAL" type="big_decimal"/>
这个给定的SQL公式在每次从数据库获取实体时求值(并且在任何其他时间不求值,因此如果其他的属性被修改,这个结果就可能过时)。属性没有列属性(或者子元素),并且永远不会出现在SQL的INSERT或者UPDATE中,而只在SELECT中。公式可能指向数据库表的列,它们可以调用SQL函数,甚至包括SQL子查询。SQL表达式实际上被传递到底层的数据库;
36.典型的Hibernate应用程序需要刷新包含由数据库为其生成值的任何属性的对象。然而,标识属性为已生成(generated),让应用程序把这个责任委托给了Hibernate。本质上,每当Hibernate给定义了已生成属性的实体执行SQL INSERT或者UPDATE时,它在获取已生成的值之后立即执行SELECT。使用property映射中的generated开关启用这个自动刷新:
<property name="lastModified" column="LAST_MODIFIED" update="false" insert="false" generated="always"/>
标记为数据库生成的属性还必须是非可插入和非可更新的,用insert和update属性进行控制它们。如果两者都设置为false,属性的列就永远不会出现在INSERT或者UPDATE语句中——属性值是制度的。而且,通常不在类中给不可变的属性添加公有的设置方法(并切换到字段访问)。
利用注解,通过@Generated的Hibernate注解声明不可变性(和自动刷新)。
37.Java没有复合的概念——类或者属性无法被标记为组件或者复合。唯一的区别是对象标识符:组件没有独立的同一性,因此持久化组件类不需要标识符属性或者标识符映射。
38.Hibernate对用户自定义的类使用术语component(组件),该类被持久化到与自己的实体相同的表。(此处单词component的用法,与建筑级的概念没有任何关系,而是同软件组件中的概念。)。
Hibernate组件可以拥有其他组件,甚至关联到其他实体。这种灵活性是Hibernate支持细粒度的对象模型的基础。
39.Java Persistence规范把组件称为被嵌入的类。为了用注解映射被嵌入的类,可以在自己的实体中声明一个特定的属性为@Embedded。
40.对于类被映射为组件有2条重要的限制。第一,就像对于所有的值类型一样,共享引用是不可能的。第二,没有优雅的方式表示对Address的空引用。作为任何优雅的方法的替代,Hibernate在组件的所有被映射列中都把空组件表示为空值。这意味着如果保存一个包含全部空属性值的组件对象,Hibernate就会在从数据库中获取自己的实体对象时返回一个空组件。
41.表示一个继承层次结构有4中不同的方法:
(1)每个带有隐式多态的具体类一张表——使用非显式的继承映射和默认的运行时多态行为。
(2)每个具体类一张表——完全放弃来自SQL模式的多态和继承关系。
(3)每个类层次结构一张表——通过反规范化SQL模式启用多态,并利用保存类型信息的一个类型辨别标志列。
(4)每个子类一张表——把is a(继承)关系表示为has a(外键)关系
42.Java Persistence接口也不支持完全的多态查询;只有被映射的实体(@Entity)可以正式成为Java Persistence查询的一部分(注意Hibernate查询接口是多态的,即使你用注解映射)。
    如果依赖这个隐式多态,就像通常一样用@Entity映射具体的类。但是也必须复制基类的属性,把他们映射到所有具体的类表。默认情况下,基类的属性被忽略并且不是持久化的!你要在具体的子类表中注解超类来启用它属性的嵌入。
    可以用@AttributeOverride注解在一个子类中覆盖来自超类的列映射。数据库标识符也可以在超类中声明,给所有的子类使用一个共用的列名和生成器策略。
43.Hibernate可以用一个UNION查询把单张表模拟成为关联映射的目标。
44.如果没有再超类中指定辨别标志列,它的名称就默认为DTYPE,且它的类型默认为字符串。继承层次结构中所有具体的类都可以有辨别标志值;没有显式的辨别标志值时,如果使用Hibernate XML文件,Hibernate就默认为完全限定的类名;如果使用注解或者JPA XML文件,则默认为实体名称。注意,在Java Persistence中,没有给非字符串辨别标志类型指定默认值,每个持久化提供程序都可以有不同的默认值。
45.第四种方案是吧继承关系表示为相关的外键关联。声明持久化属性的每个类/子类(包括抽象类甚至接口)都有它自己的表。不同于我们最先映射的每个具体类一张表的策略,此处的表仅仅包含了每个非继承的属性(由子类本身声明的每个属性)以及也是超类表的外键的主键的列。这一策略的主要好处在于,SQL Schema被标准化了,Schema演变和完整性约束定义很简单。对一个特定子类的多态管理可能被表示为引用这个特定子类的表的一个外键。在Hibernate中,用<joined-subclass>元素给每个子类映射创建一张表。
46.可以把所有的映射策略应用到抽象类和接口。接口可能没有状态,但是可能包含访问方法声明,因此可以像抽象类一样地处理它们。可以用<class>、<union-subclass>、<subclass>或者<joined-subclass>映射接口,并可以用<property>映射任何被声明或者被继承的属性。Hibernate不会视图实例化抽象类,即使你查询或者加载了它。
47.(1)如果你不需要多态关联或者查询,就倾向于每个具体类一张表,基于UNION的显式映射应该是首选,因为随后(最优化的)多态查询和关联将成为可能。隐式多态对于利用非持久化相关的接口的查询最有用。
(2)如果一定要多态关联(对超类的关联,及由此在运行时通过具体类的动态解析对层次结构中的所有类关联)或者查询,并且子类相对地声明几种属性(尤其当子类之间的主要区别在于他们的行为中)时,则倾向于每个类层次结构一张表。你的目标是把可为空的列数减到最少,并让你自己确信反规范化的Schema在长期运行中不会产生问题。
(3)如果你一定需要多态的关联或者查询,并且子类声明多个属性(子类的不同主要在于它们所持有的数据),则倾向于每个子类一张表。或者,根据继承层次结构的宽度和深度,以及相对于联合的可能联结成本,使用每个具体类一张表。
48.最后,也可以在单个的映射文件中使用<union-subclass>、<subclass>和<joined-subclass>映射元素(作为一级元素,而不是<class>)。然后必须声明被扩展的类,且必须在子类映射文件之前程序化地加载基类映射(在XML配置文件中列出映射资源清单时,不必担心这个顺序)。这种技术运行扩展类层次结构,而不用修改超类的映射文件。
49.实体是指其实例具有自己的持久化同一性的任何类。值类型是指没有定义某种持久化同一性的类。在实际应用中,这意味着实体类型是包含标识符属性的类,值类型的类则取决于实体。
运行时,你有一个实体实例的网络与值类型实例交错。实体实例可能处于这三种持久化生命周期状态之一:瞬时(transient)、脱管(detached)或者持久化(persistent)。
实体有自己的生命周期。Hibernate Session接口的Save()和delete()方法应用到实体类的实例,而从不应用到值类型的实例。值类型实例的持久化生命周期完全由自己的实体实例的生命周期所决定的。
在Hibernate中,值类型可以定义关联;可能从一个值类型实例导航到一些其他的实体。但是永远不可能从其他实体导航回到值类型实例。关联始终指向实体。这意味着,当从数据库中获取值类型实例时,它完全为一个实体所有,从不共享。
在数据库级中,任何表都被当作实体。然而Hibernate提供某些构造,从Java代码中隐藏数据库级实体的存在。
50.Hibernate内建的映射类型通常共享它们映射的Java类型的名称。然而,一个特定的Java类型可能有不止一个Hibernate映射类型。
内建的类型不能用于执行任意的转化,例如把一个VARCHAR数据库值映射到一个Java的Integer属性值。可以给这种转化定义自己的定制值类型。
JDBC驱动程序提供特定于供应商的SQL数据类型的部分抽象,允许Hibernate在执行DML时使用ANSI标准类型。对于数据库专用的DDL生成,Hibernate利用对特定的SQL方言的内建支持,把ANSI标准类型转变为适当的特定于供应商的类型。(这意味着如果你正在使用Hibernate访问数据和定义SQL Schema,通常不必担心SQL数据类型)
此外,Hibernate的类型系统很聪明,可以根据值定义的长度(length)转换SQL数据类型。
警告:如果用timestamp(最常见的情况)映射java.util.Date属性,Hibernate就在从数据库中加载属性时返回java.sql.Timestamp。Hibernate必须使用JDBC子类,因为它包括可能出现在数据库中的十亿分之一秒的信息。Hibernate无法只切断这个信息。如果试图吧java.util.Date属性与equals()方法进行比较,就会出现问题,因为它与java.sql.Timestamp子类equals()方法不同步。
如果持久化的Java类中的属性是byte[]类型,Hibernate就可以用二进制映射类型把它映射到一个VARBINARY列。(注意真正的SQL类型取决于方言。)如果持久化的Java类中的属性是java.lang.String类型,Hibernate就会用text映射类型把它映射到SQL CLOB列。
注意在这两个案例中,当加载保存着属性变量的实体实例时,Hibernate立即就初始化属性值,当你必须处理潜在的大值时,这样很不方便。
一种捷径方案是按需要通过字段访问的拦截延迟加载。但是对于额外代码的诸如,这种方法需要持久化类的字节码基础设施(ByteCode Instrumentation,BCI)。第二种解决方案是Java类中一种不同的属性。JDBC直接支持定位器对象(Locator Object,LOB)。如果你的Java属性是java.sql.Clob或者java.sql.Blob类型,就可以用clob或者blob映射类型映射它,以便不通过BCI而获得大值的延迟加载。当加载属性的所有者时,属性值就是一个定位对象——实际上,它是一个指向了还没有被物化的真实值的指针。一旦访问了属性,值就被物化了。这种按需加载只在数据库事务打开时才起作用,因此当自己的实体实例处于持久化和事务状态,而不是处于托管状态时,就可以访问这种类型的任何属性。现在领域模型也被绑定到JDBC了,因为需要导入java.sql包。虽然领域模型类在单独的单元测试中是可执行的,但是没有数据库连接则无法访问LOB。
如果领域模型中有这些属性类型,为了创建和设置java.sql.Blob或者java.sql.Clob值,就用静态的Hibernate。createBlob()和Hibernate.createClob()方法,并提供字节数组、输入流或者字符串。
最后,注意Hibernate和JPA都对Serializable(可序列化)的任何属性类型提供序列回滚。这种映射类型把属性的值转换为随后保存在VARBINARY(或者相当的)列中的字节流。当属性所有者被加载时,属性值是反序列化的。

猜你喜欢

转载自bsr1983.iteye.com/blog/2093834
今日推荐