我们可以看见,一个儿童可以具有很多玩具,这是一个明显的一对多关系,在Hibernate里面有这样的标签与之对应<one-to-many>,然后,我们可以为这两个类写mapping.xml文件了,在hibernate里面,每个要持久化的类都要求有相应的mapping文件,这样hibernate才会知道如何把java class property对应到table columns里面了,有的时候我们不用自己指明java某个属性的类型,hibernate有reflection机制,它可以在运行时动态的察觉某个属性的类型,然后对应到相应的数据库类型中。
1、 配置hibernate.properties文件
你可以拷贝一份例子,然后稍加改动便可以使用了,关键在于以下几处:
配置数据库,如下
## Oracle
hibernate.dialect net.sf.hibernate.dialect.OracleDialect
hibernate.connection.driver_class oracle.jdbc.driver.OracleDriver
hibernate.connection.url jdbc:oracle:thin:@locahost:1521:oracle8i
hibernate.connection.username test
hibernate.connection.password test
设置开发模式
## print all generated SQL to the console
hibernate.show_sql true
这样,开发的时候可以看见实际运行的sql语句了。
2、 我们先建立一个名为Hibernate.cfg.xml的文件,这样,Hibernate就知道数据库的属性以及要加载的mapping文件的位置了。
文件内容如下:
<hibernate-configuration>
<session-factory >
<property name="dialect">net.sf.hibernate.dialect.OracleDialect</property>
<mapping resource="hibernate/Child.hbm.xml"/>
<mapping resource="hibernate/Toy.hbm.xml"/>
</session-factory>
</hibernate-configuration>
我来解释一下,hibernate-configuration为顶级元素,然后的property dialect告诉了hibernate底层的数据库名称,对于不同的数据库hibernated都内建了相应的类来对应,这些类都在net.sf.hibernate.dialet包中,为什么这样哪?因为每种数据库都有不同的o/r mapping机制,况且处理主键的机制也很不相同,所以有必要这样做。其实切换数据库对于hibernate来说也非常的方便,只要更换这个属性就可以了。当然,如果你在其他的配置文件里面用到了数据库特定的配置,那么也必须做相应的修改。
接下来的mapping resource=””就表示了每个持久化的类对应的mapping文件,当然,你也可以不用为每一个类写一个mapping 文件,如果可以的话,我建议为逻辑上相关的类或者一个包写一个mapping文件就可以了。这样显得比较紧凑,不会看见你的源代码里面到处都是xml文件了。当然,目前也有一个工具叫做xdoclet,可以自动帮助你产生mapping文件,但是我想那样的自动化处理毕竟不是十分的灵活,大部分使用hibernate的人都是采用自己手工写mapping文件的。所有我也推荐大家在学习hibernate的时候,不要偷懒,一开始就使用xdoclet来产生mapping文件,这样你的基础会很不牢靠。
还有一点,就是这个resource=value里面的value是和package的名字相对应的。一般情况下,都把类和类的mapping文件放在一起。
3、 撰写mapping文件
大家或许比较关心到底hibernate是如何把java class对应到table column的哪?其实在hibernate的文档里面,这一点介绍的比较清楚,里面专门有一章来介绍mapping机制,因为这是应用hibernate的基础,以后你会发现,你的大部分时间都将花在mapping上面,而不是写sql语句了。
先看Child类的mapping文件
<hibernate-mapping>
<class name="hibernate.Child" table="CHILD"> //把java class对应到database table中
<id name="id" unsaved-value="0">//主键描述
<generator class="sequence">//采用oracle的sequence技术自动自增主键
<param name="sequence">SEQ_CHILD</param>
</generator>
</id>
<array name="toys" table="TOY" cascade="all">//描述和toy的一对多关系
<key column="CHILD_ID"/>
<index column="POSN"/>//之所以要写index,因为有array
<one-to-many class="hibernate.Toy"/>
</array>
</class>
</hibernate-mapping>
里面需要注意的有以下几点:
1) 如果你采用了one-to-many的关系,并且使用了session.saveOrUpdate()方法,那么你最好让<id>属性有unsaved-value=”0”属性,否则会抛出违背外键的异常。
2) 注意,child-toy之间的one-to-many关系是通过外键关联上去的。这个外键就是toy表里面的child_id字段。
3) 由于我们采用array来实现one-to-many的关系,所以我们必须制定一个额外的字段来存储这个array的索引,这样你存进去的序号和你取出来的序号是一致的,当然,如果你不想让hibernate记住序号就不用这个字段了,可以使用<set><bag>等来替换<array>
4) 注意<array>里面的cascade属性,这个属性很重要,它表示,如果这个属性为all,那么当save,update,delete一个Child类的时候,其属性toys也会相应的得到save,update,或者delete,否则不会。当然你也可以使用实现hibernate自带的接口LifeCycle来实现这个功能。但是没有必要。 注意,这个功能有一点要注意,就是hibernate没有垃圾收集功能,当你删除了一个Child类里面的toys里面的某个toy的时候,实际上数据库中并没有删除这个记录,你必须手动的删除它。这是目前hibernate开发者们正在思考改进的一个问题。他们或许会采用JDO的机制<delete-orphan>来实现垃圾收集。
好了,再看Toy.hbm.xml文件
<hibernate-mapping>
<class name="hibernate.Toy" table="TOY">
<id name="toyId" column="TOY_ID" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SEQ_TOY</param>
</generator>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
相似的,Toy类制定了主键产生的方式,以及一个简单的属性name,由于数据库的字段名称和Java类的属性名称一致,所以不用制定property 里面的column属性。