优化知识:
Ø 映射对象实体可以序列化,可以通过实现Serializable接口。序列化意味着我们可以在HttpSession中保存实体对象,或者将其通过RMI传输。
Ø 对于实体类最好不要用final修饰,这将导致Hibernate代理机制难以进行,而代理机制则是Hibernate提高性能的主要途径之一。
一、 一对一引用外键设置
1. 主表(StockWrhBusinessBillDO)
<one-to-one name="stockPutWrhBillDO" class="relate.model.StockPutWrhBillDO" cascade="all" property-ref="stockWrhBusinessBillDO"/>
说明:
property-ref="stockWrhBusinessBillDO" :property-ref(可选)被关联从表类中的
对应属性的名字,若没指定,使用被关联类的主键.
Cascade:只在父方设置,有all,delete,update,none;表示父方做了操作,子方跟着做同样的操作
所谓父子关系,是指由父方子方的持久化生命周期,子方对象必须和一个父方对象关联;如果删除父方对象,应该级联删除所有的关联的子方对象。如果子方对象不再和一个父方对象关联,应该把这个子方对象删除。
2.从表(StockPutWrhBillDO)
<many-to-one name="stockWrhBusinessBillDO"
class="relate.model.StockWrhBusinessBillDO"
column="WRH_BUSINESS_BILL_ID"
insert="true"
update="true"/>
说明:
Column:引用父表的主键字段
Insert、update:如你更新数据(xx,yy),正好数据库有条记录(xx,zz).如果你设置update=true,她就会忽略xx,只更新yy,否则,一并更新,因为做为从表,一般都根据主表的根据主表的更新而一道更新,一般将update设置为true。
注意:
因为many-to-one设置了WRH_BUSINESS_BILL_ID的映射,如果在从表映射文件(.hbm.xml文件)中有个字段设置了WRH_BUSINESS_BILL_ID属性,应该把他注释掉或者去掉。如下:
<!-- property name="WRH_BUSINESS_BILL_ID" type="integer">
<column name="WRH_BUSINESS_BILL_ID">
<comment></comment>
</column>
</property-->
二、一对多关联
1.主表(StockWrhBusinessBillDO)
<set name="stockWrhBusinessBillDetailDOs"
table="stock_wrh_business_bill_detail"
cascade="all"
inverse="true"
lazy="false">
<key column="WRH_BUSINESS_BILL_ID"></key>
<one-to-many class="relate.model.StockWrhBusinessBillDetailDO"/>
</set>
说明:
Inverse:用于控制反转,将从表被控方转为主控表,当inverse设置为false时,当执行完插入语句,需要对从表附加操作update操作。
Key:key元素的column属性对应从表中,关联的字段
One-to-many:对应从表映射的类
2.从表(StockWrhBusinessBillDetailDO)
<many-to-one name="stockWrhBusinessBillDO"
class="relate.model.StockWrhBusinessBillDO"
column="WRH_BUSINESS_BILL_ID"
insert="true"
update="true"/>
说明:
Column:引用父表的主键字段
三、hibernate设置 dynamic-update、dynamic-insert
Hibernate允许我们在映射文件里控制insert和update语句的内容.比如在映射文件中<property 元素中的update属性设置成为false,那么这个字段,将不被包括在基本的update语句中,修改的时候,将不包括这个字段了.insert同理.dynamic动态SQL语句的配置也是很常用的.下面介绍配置SQL语句的具体属性:
1)<property>元素 insert属性:设置为false,在insert语句中不包含这个字段,表示永远不会被插入,默认true
2)<property>元素 update属性:设置为false,在update语句中不包含这个字段,表示永远不会被修改,默认true
3)<class>元素 mutable属性:设置为false就是把所有的<property>元素的update属性设置为了false,说明这个对象不会被更新,默认true
4)<property>元素 dynamic-insert属性:设置为true,表示insert对象的时候,生成动态的insert语句,如果这个字段的值是null就不会加入到insert语句当中.默认false
5)<property>元素 dynamic-update属性,设置为true,表示update对象的时候,生成动态的update语句,如果这个字段的值是null就不会被加入到update语句中,默认false
6)<class>元素 dynamic-insert属性:设置为true,表示把所有的<property>元素的dynamic-insert属性设置为true,默认false
7)<class>元素 dynamic-update属性:设置为true,表示把所有的<property>元素的dynamic-update属性设置为true,默认false
说明: Hibernate生成动态SQL语句的消耗的系统资源(比如CPU,内存等)是很小的,所以不会影响到系统的性能,如果表中包含N多字段,建议把dynamic-update属性和insert属性设置为true,这样在插入和修改数据的时候,语句中只包括要插入或者修改的字段.可以节省SQL语句的执行时间,提高程序的运行效率.
注意:今天做了一个测试,发现Hibernate的dynamic-update只在两种条件下生效:
1. 同一session内,对已经persisit的对象进行update,这里的“已经persist”是指update之前先进行了create或者load调用。代码示例:
Session session = openSession();
User user = (User)session.load(User.class,new Long(12));
user.setAddress(null);
session.update(user);
session.flush();
将hibernate配置成show_sql=true,可以看到update产生的sql语句。
2. 不同session之间,update传入的对象是另一个session中的persist对象(对该对象调用了create或者load方法)。代码示例:
Session session1 = openSession();
User user = (User)session1.load(User.class,new Long(12));
Session session2 = openSession();
user.setAddress(null);
session2.merge(user);
session2.flush();
如果将session2.merge(..)改成update,则会更新所有可更新的属性。
四、复合主键
1.映射文件(.hbm.xml文件)中复合主键配置:
2. 建立主键类(TUserPK)
说明:复合主键的主键类必须实现Serializable接口,实现其两个方法equals(),hashCode(),如下所示:
3. Java 类中调用:
五、继承 + 一对多关联
(参见工程:ConnectExtends
配置文件:ConnectExtends/resource/modules/Animal/hibernate-conf/Animal.hbm.xml)
类Cat,Dog继承类Animal,对应三个数据表cat,dog,animal。表cat中的id字段既是主键也是外键,对应于animal表的id字段。类Animaldetail对应数据表animaldetail,其中animalid是外键,映射animal 表的id 字段。
映射文件配置如下:
1. Animal.hbm.xml配置如下:
<set name="animaldetailDOs"
table="animaldetail"
inverse="true"
cascade="all">
<key column="animalid"/>
<one-to-many class="animaldetail.model.AnimaldetailDO"/>
</set>
<joined-subclass name="animal.model.CatDO" table="cat">
<key column="id" />
<property name="catagory" column="catagory" type="java.lang.String"/>
</joined-subclass>
<joined-subclass name="animal.model.DogDO" table="dog">
<key column="id" />
<property name="furcolor" column="furcolor" type="java.lang.String"/>
</joined-subclass>
注意:<set>,<joined-subclass>前后数序不能颠倒
Animaldetail.hbm.xml配置如下:
<many-to-one name="animalDO"
class="animal.model.AnimalDO"
column="animalid"/>
调用操作如下:
CatDO catDO = new CatDO();
catDO.setName("CatA");
catDO.setCatagory("BoSi");
catDO.setRun("用爬的,BaseAnimal属性");
AnimaldetailDO animaldetialDO = new AnimaldetailDO();
animaldetialDO.setDetailname("动物细节一");
catDO.getAnimaldetailDOs().add(animaldetialDO);
animaldetialDO.setAnimalDO(catDO);
catDao.doSave(catDO);
六、分层结构一张表 + 子类另外一张表混合继承实现
(参见工程:ConnectExtends中food包
配置文件:ConnectExtends/resource/modules/Food/hibernate-conf/Food.hbm.xml)
FoodDO有两个子类ProduceFoodDO和ColorFoodDO,ProduceFoodDO有一个子类PricedetailDO
(该例的另外一种实现方式如下例“继承 + 一对以混合使用”所示)
1. .hbm.xml文件配置如下:
<class name="food.model.FoodDO" table="food" lazy="true">
<comment></comment>
<id name="id" column="id" type="integer">
<generator class="increment"></generator>
</id>
<discriminator column="type" type="java.lang.String"/>
……
<subclass name="food.model.ProduceFoodDO" discriminator-value="produce" lazy="false">
<property name="producingarea" column="producingarea" type="java.lang.String" />
<!-- discriminator-value 是数据的分组条件,查询时作为过滤条件使用,一旦设置了,就不要修改值 -->
<subclass name="food.model.PricedetailDO" discriminator-value="produce-detail" lazy="false">
<join table="pricedetail">
<key column="id"></key>
<property name="detaila" column="detaila" type="java.lang.String"></property>
</join>
</subclass>
</subclass>
<subclass name="food.model.ColorFoodDO" discriminator-value="color" lazy="false">
<property name="color" column="color" type="java.lang.String"/>
</subclass>
</class>
2. 调用如下:
PricedetailDO pricedetailDO = new PricedetailDO();
pricedetailDO.setDetaila("粳米uu");
pricedetailDO.setName("大米uu");
pricedetailDO.setStyle("细长uu");
pricedetailDO.setProducingarea("江西uu");
foodService.save(pricedetailDO);
注意: foodservice 提供的方法参数类型是父类类型,参数值为子类对象 (saveFoodDO(FoodDO DO)),在相当大程度提供操作的灵活性,这样你也可以做如下操作:
ColorFoodDO colorFoodDO = new ColorFoodDO();
colorFoodDO.setColor("红色");
colorFoodDO.setName("胡箩卜");
colorFoodDO.setStyle("长条");
foodService.save(colorFoodDO);
七、继承 + 一对一混合应用
(参见工程:Subclass&one2one)
FoodDO有两个子类ColorFoodDO和ProduceFoodDO,子类ProduceFoodDO与PricedtailDO是一对一关联关系
(本例和上例“ 分层结构一张表 + 子类另外一张表混合继承实现”是同一业务的不同实现方式)
1. 配置文件 Food.hbm.xml如下:
<class name="food.model.FoodDO" table="food" lazy="true">
<comment></comment>
<id name="id" column="id" type="integer">
<generator class="increment"></generator>
</id>
<discriminator column="type" type="java.lang.String"/>
……
<subclass name="food.model.ProduceFoodDO" discriminator-value="produce">
<property name="producingarea" column="producingarea" type="java.lang.String"/>
<one-to-one name="pricedetailDO"
class="food.model.PricedetailDO"
cascade="all"/>
</subclass>
<subclass name="food.model.ColorFoodDO" discriminator-value="color">
<property name="color" column="color" type="java.lang.String"></property>
</subclass>
</class>
2. 配置文件Pricedetail.hbm.xml如下:
<class name="food.model.PricedetailDO" table="pricedetail" lazy="true">
<comment></comment>
<id name="id" column="id">
<generator class="foreign">
<param name="property">produceFoodDO</param>
</generator>
</id>
……
<one-to-one name="produceFoodDO" class="food.model.ProduceFoodDO" constrained="true"/>
</class>
3. 应用操作如下:
ProduceFoodDO produceFoodDO = new ProduceFoodDO();
PricedetailDO pricedetailDO = new PricedetailDO();
produceFoodDO.setName("one-to-one 名称");
produceFoodDO.setProducingarea("中企动力");
pricedetailDO.setDetaila("one-to-one 细节");
produceFoodDO.setPricedetailDO(pricedetailDO);
pricedetailDO.setProduceFoodDO(produceFoodDO);
foodService.saveFood(produceFoodDO);
八、独表继承(union-subclass)
(参见工程:unionclass)
BaseUserDO有两个子类UserDO和UserHistoryDO;UserDO对应数据表是users,UserhistoryDO对应数据表userhistory。
表users:id,name,address,lastlogin
表userhistory:id,name,address,lastmodified
两个表前面三个字段都一样,各自拥有一个特性字段。
1.BaseUser.hbm.xml配置文件如下:
<class name="subclass.model.BaseUserDO">
<comment></comment>
<id name="id" column="id" type="java.lang.Integer">
<!—-这里使用了hilo标识生成器,则在数据库建立对应数据表my_unique_key 表中字段为next_hi ,数据表必须赋予一段数据-->
<generator class=" hilo ">
<param name="table">my_unique_key</param>
<param name="column">next_hi</param>
</generator>
</id>
<property name="name" type="java.lang.String">
<column name="name">
<comment></comment>
</column>
</property>
<property name="address" type="java.lang.String">
<column name="address">
<comment></comment>
</column>
</property>
<union-subclass name="subclass.model.UserDO" table="users">
<property name="lastlogin" column="lastlogin" type="java.lang.String"/>
</union-subclass>
<union-subclass name="subclass.model.UserhistoryDO" table="userhistory">
<property name="lastmodified" column="lastmodified" type="java.lang.String"/>
</union-subclass>
</class>
2. 应用操作如下:
UserDO userDO = new UserDO();
userDO.setName("admin");
userDO.setAddress("广州大道");
userDO.setLastlogin(" 2008-07-17 ");
userService.saveBaseUser(userDO);
九、自定义类型(继承UserType)
(对应工程:UserEmails)
Ø 实现目标:有一个用户,对应拥有多个email地址,多个email之间用“;”隔开组装成字符串类型保存在数据表中email字段,在java代码中以List类型展示。
1. UseremailsDO email字段设置如下:
private List emails = null;
public List getEmails() {
return emails;
}
public void setEmails(List emails) {
this.emails = emails;
}
说明:email 类型为 List
2. .hbm.xml映射文件如下:
<class name="emails.model.UseremailsDO" table="useremails">
……
<property name="emails" type="emails.model.UEmailList">
<column name="emails">
<comment></comment>
</column>
</property>
</class>
说明:<property name="emails" type="emails.model.UEmailList">
Type为自定义的hibernate类型,它实现了
org.hibernate.usertype.UserType,在下面第四点,我们将介绍它的实现方式。
3. 应用操作如下:
a)保存操作
List<String> emailList = new ArrayList<String>();
UseremailsDO userEmailDO = new UseremailsDO();
emailList.add("d@myce.net");
emailList.add("e@myce.net");
emailList.add("f@myce.net");
userEmailDO.setUsername("wang liu ");
userEmailDO.setEmails(emailList);
useremailService.saveEmails(userEmailDO);
b) 查询操作
List list = useremailService.selectEmails();
for(int i=0;i<list.size();i++){
UseremailsDO userEmailDO = (UseremailsDO)list.get(i);
System.out.println("UserName:" + userEmailDO.getUsername());
System.out.println("======================================");
for(int j=0;j<userEmailDO.getEmails().size();j++){
System.out.println("Email[" + j + "]:" + (String)(userEmailDO.getEmails().get(j)));
}
System.out.println("");
}
4. 自定义Hibernate类型(实现UserType接口)
public class UEmailList implements UserType{
private List emailList = null;
private static final char SPLITTER = ';';
private static final int[] TYPES = new int[]{Types.VARCHAR};
……
//工程UserEmails包含详细实现方式
}
十、懒加载
懒加载简单说就是对数据的延迟加载,比如数据的级联操作,下面将介绍如何进行懒加载应用配置。(下面是一个一对多的例子)
1.映射文件配置如下(将属性lazy设置为true)
a) animal.hbm.xml映射配置文件(主要配置部分)
<set name="animaldetailDOs"
table="animaldetail"
inverse="true"
cascade="all"
lazy="true">
<key column="animalid"/>
<one-to-many class="animaldetail.model.AnimaldetailDO"/>
</set>
b) animaldetail.hbm.xml映射配置文件(主要配置部分)
<many-to-one name="animalDO"
class="animal.model.AnimalDO"
column="animalid"/>
2.操作应用如下:
CatDO catDO = new CatDO();
catDO = (CatDO)getSession().load(CatDO.class,id);
3. web.xml 设置过滤器
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.
support.OpenSessionInViewFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
说明:因为过滤器是对url过滤映射处理,所有在测试(TestCase)过程中,懒加载机制不起左右
十一、附件资料
一) 继承
<subclass>:继承的双方子类、父类都在同一张数据表
<joined-subclass>:……子类、父类在不同数据表
<union-subclass>:每个具体类一张表,独表中的某些属性是所有子表所共有
1. <subclass>配置如下:
a) 每个类分层结构一张表
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator olumn="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<property name="creditCardType" column="CCTYPE"/>
...
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>
采用这种策略只需要一张表即可。它有一个很大的限制:要求那些由子类定义的字段, 如CCTYPE,不能有非空(NOT NULL)约束。
b) 每个子类一张表,使用辨别标志(Discriminator)
对“每个子类一张表”的映射策略,你可以结合使用<subclass> 与<join>,如下所示:
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<join table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</join>
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
<join table="CASH_PAYMENT">
...
</join>
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
<join table="CHEQUE_PAYMENT" fetch="select">
...
</join>
</subclass>
</class>
可选的声明fetch="select",是用来告诉Hibernate,在查询超类时, 不要使用外部连接(outer join)来抓取子类ChequePayment的数据。
C) 混合使用“每个类分层结构一张表”和“每个子类一张表”
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<join table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
</join>
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
……
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
………
</subclass>
</class>
2. <joined-subclass>配置如下:
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
</class>
需要四张表。三个子类表通过主键关联到超类表(因而关系模型实际上是一对一关联)。
3. <union-subclass>配置如下:
<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class>
这里涉及三张表。每张表为对应类的所有属性(包括从超类继承的属性)定义相应字段。 这种方式的局限在于,如果一个属性在超类中做了映射,其字段名必须与所有子类 表中定义的相同。(我们可能会在Hibernate的后续发布版本中放宽此限制。) 不允许在联合子类(union subclass)的继承层次中使用标识生成器策略(identity generator strategy), 实际上, 主键的种子(primary key seed)不得不为同一继承层次中的全部被联合子类所共用.
4. 隐式多态如下(在同一个映射文件中,映射多个class):
<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CREDIT_AMOUNT"/>
...
</class>
<class name="CashPayment" table="CASH_PAYMENT">
<id name="id" type="long" column="CASH_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CASH_AMOUNT"/>
...
</class>
<class name="ChequePayment" table="CHEQUE_PAYMENT">
<id name="id" type="long" column="CHEQUE_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</class>
说明:我们没有在任何地方明确的提及接口Payment。同时注意 Payment的属性在每个子类中都进行了映射。如果你想避免重复, 可以考虑使用XML实体(例如:位于DOCTYPE声明内的 [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] 和映射中的&allproperties;)。
这种方法的缺陷在于,在Hibernate执行多态查询时(polymorphic queries)无法生成带 UNION的SQL语句。
对于这种映射策略而言,通常用<any>来实现到 Payment的多态关联映射。
<any name="payment" meta-type="string" id-type="long">
<meta-value value="CREDIT" class="CreditCardPayment"/>
<meta-value value="CASH" class="CashPayment"/>
<meta-value value="CHEQUE" class="ChequePayment"/>
<column name="PAYMENT_CLASS"/>
<column name="PAYMENT_ID"/>
</any>
对<any>元素中子元素和属性补充说明:
name: 是持久类中属性名称。
meta-type: 是下面meta-value元素中value的类型,如"string","character"等。
id-type: 是引用类的主键类型。
meta-value元素中value: 该值将保存到数据库表中,用来标识其后的class,即引用的持久类。请参考下面的数据。
meta-value元素中class: 引用持久类的类全称。
第一个column: 保存上面value值的字段。
第二个column: 保存引用持久类的主键值的字段,他的类型是id-type对应到数据库中的字段类型。
二) 一对多
1. 单向一对多关联