1、全局的XML映射元数据
如果检查XML映射DTD,会看到<hibernate-mapping>根元素有全局的选项,被应用到它内部的(多个)类映射——这些选项中有一些如下列例子所示:<hibernate-mapping schema="SYSTEM" default-lazy="true" default-access="field" auto-import="false"> </hibernate-mapping>schema:一个数据库Schema前缀SYSTEM能够被Hibernate用给已映射类所生成的所有SQL声明
default-lazy:给有些类关联启用默认的懒加载
default-accesss:通过Hibernate为这个文件中所有被映射的类的所有持久化属性启用直接的字段访问。
auto-import:设置对这个文件中的所有类关闭
提示:没有类声明的映射文件——任何复杂的应用都需要和呈现全局的元数据。例如,可以轻松地导入许多接口,或者具体化几百个查询字符串。在大型的应用程序中,通常创建没有实际类映射的映射文件,并且只有导入、外部查询,或者全局过滤器和类型定义。如果看看DTD,就会看到<class>映射在<hibernate-mapping>根元素内部是可选的。把全局的元数据分开,并把它组织到单独的文件中,并在Hibernate配置中加载它们,就像一般的映射文件一样。
2、全局的注解元数据
注解天生就被织入一个特定类的Java源代码中。虽然可能把全局的注解放在类的源文件中,我们还是喜欢把全局的元数据放在一个单独的文件中。这称作包元数据(package metadata),它被特定包目录中具名package-info.java的文件所启用:@org.hibernate.annotations.TypeDefs({ @org.hibernate.annotations.TypeDef( name="monetary_amount_usd", typeClass=MonetaryAmountType.class, parameters={@org.hibernate.annotations.Parameter(name="convertTo",value="USD")} ), @org.hibernate.annotations.TypeDef( name="monetary_amount_eur", typeClass=MonetaryAmountType.class, parameters={@org.hibernate.annotations.Parameter(name="convertTo",value="EUR")} ) }) @org.hibernate.annotations.NamedNativeQueries({ @org.hibernate.annotations.NamedNativeQuery( name="findItemsOrderByPrice", query="select i from Item i order by i.initialPrice") }) package auction.persistence.types;包元数据文件的这个例子,在包auction.persistence.types中,它声明了两个Hibernate类型转换器。后面会讨论Hibernate类型系统。
这就是前一个代码示例中只包括来自Hibernate包的注解,而没有Java Persistence注解的原因之一。JPA规范(在最后一刻)所做的一个改变是移除了JPA注解的包可见性。结果,没有任何Java Persistence注解可以被放在package-info.java文件中。如果需要可移植的全局的Java Persistence元数据,就把它放在orm.xml文件中。
现在你知道了如何编写本地的和全局的映射元数据。大型应用程序中的另一个问题是元数据的可移植性。
3、使用占位符
XML映射文件被绑定到了一个特定的数据库产品,你损失了Hibernate应用程序的数据库可移植性。处理这个问题的一种方法在你的XML文件中使用一个点位符,在映射文件被复制到目标目录下的构建期间,这个XML文件被替换(Ant支持这个)。只有当你有使用Ant的经验,或者应用程序的其他部分已经需要构建时的替换时,才建议使用这种机制。一种更优雅的变更方法是使用定制的XML实体(与应用的业务实体无关)。
<id name="id" type="integer"> <generator class="&idgenerator;" /> </id>&idgenerator;值称作实体点位符。可以在XML文件的顶部定义它的值为实体声明,作为文档类型定义的一部分:
<id name="id" type="integer"> <generator class="&idgenerator;" /> </id>当映射文件被读取的时候,XML解析器现在将在Hibernate启动时替换点位符。
可以把这个更推进一步,在单个的文件中把这个新增事物具体化到DTD,并在所有其他的映射文件中包括全局的选项:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" [ <!ENTITY % globals SYSTEM "classpath://persistence/globals.dtd"> %globals; ]>persistence包里的globals.dtd文件中:
<!ENTITY idgenerator "native">要从Oracle切换到一个不同的数据库系统,只要部署一个不同的globals.dtd文件。
你经常不仅要替换一个XML元素或者属性值,还要在所有的文件中包括映射元数据的整个块,例如当许多类共享一些常用的属性,并且你无法在单个位置使用继承来捕捉它们的时候。利用XML实体替换,可以一个XML片段具体化到一个单独的文件,并把它包括在其他XML文件中。
假设所有的持久化类都有一个dataModified属性。第一步是把这个映射放在它自己的文件中,比如DateModified.xml:
<property name="dateModified" column="DATE_MOD" type="timestamp"/>这个文件不需要XML首部或者任何其他标签。现在为一个持久化类把它包括在映射文件中:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" [ <!ENTITY dateModified SYSTEM "classpath://model/UserName.xml"> ]> <hibernate-mapping> <class name="Item" table="TBL_ITEM"> <id ...> &datemodified; ... </class> </hibernate-mapping>DateModified.xml的内容将被&datemodified;点位符包括和替换。当然,这也适用于较大的XML片段。
运行时操作元数据
在运行时浏览、操作或者构建新映射,对于应用程序来说有时候有用。XML API(如DOM、dom4j和JDOM)都允许XML文档的直接运行时操作,因此你能够在把XML文档传递给Configuration对象之前,在运行时对它进行创建或者操作。下列代码将一个新的属性motto添加到了User类:
// Get the existing mapping for User from Configuration PersistentClass userMapping = cfg.getClassMapping(User.class.getName()); // Define a new column for the USER table Column column = new Column(); column.setName("ADDRESS");// 列名称 column.setNullable(true);// 列是否可为空 // tbl_user表添加address列 userMapping.getTable().addColumn(column); // SimpleValue包裹Column SimpleValue value = new SimpleValue(null); value.setTable(userMapping.getTable()); value.setTypeName("string"); value.addColumn(column); // 在User类中定义新属性 Property prop = new Property(); prop.setValue(value); prop.setName("address"); prop.setNodeName(prop.getName()); userMapping.addProperty(prop); // 创建一个新的SessionFactory工厂,使用新的映射 // 一旦创建了SessionFactory,它的映射就是不可变的 SessionFactory sf = cfg.buildSessionFactory();PersistentClass对象表示了单个持久类的元模型,你从Configuration对象中获取它。Column、SimpleValue和Property是Hibernate元模型的所有类,可以在org.hibernate.mapping包中使用它们。
提示:记住,把一种属性添加到一个现有的持久化类映射,如此处所示,相当容易,但是给之前未被映射的类编程式地创建一个新的映射就比较麻烦了。
一旦创建了SessionFactory,它的映射就是不可变的。SessionFactory内部使用一种与配置时所用的不同的元模型。没有办法从SessionFactory或者Session中退回到原始的Configuration。(注意,你可以从Session中获得SessionFactory,如果希望访问一个全局设置的话。)然而,应用程序可以通过调用getClassMetadata()或者getCollectionMetadata()来读取SessionFactory的元模型。这里有个例子:
User user = new User(); user.setUsername("admin"); user.setPassword("bdqn"); ClassMetadata meta = sf.getClassMetadata(user.getClass()); String[] metaPropertyNames = meta.getPropertyNames(); for (String s : metaPropertyNames) { System.out.println("属性:" + s); } Object[] propertyValues = getParamsByEntity(sf, user, EntityMode.POJO); for (Object o : propertyValues) { System.out.println("属性值: " + o); }