Posted on 2005-07-21 16:50
风太少 阅读(189)
评论(0) 编辑 收藏
由于最近在把以前的一个设计移到hibernate上来,所以需要用到one-to-one,因为在以前的设计中需要用到在一个主表中对于多个子表的主键关联,所以一开始就想到了one-to-one的应用,觉得这样解决不但不会引起以前数据设计的改变,也能够很好的利用hibernate所带来的OR优势,可是当实际使用的时候发现,在插入数据的时候可以有选择的在任意子表中进行插入,所有的结果都在原来的预期之中,但是在查询的时候,比如说只查询主表中的内容
From tableMain仅仅执行看起来十分简单的一条语句,你所期望的是他紧紧查询T_MAIN这张主表,可是结果确实hibernate通过多个外连接将所有的子表一口气的全部查询出来
select * from t_main main outer join t_sub1 sub1 on main.id = sub1.id outer join t_sub2 sub2 on main.id = sub2.id... 如此的效率绝对让你头痛不已,不仅如此,如果你通过首先获得子表t_sub1的某个主键ID,然后通过这个主键查询出子表对象,在关联至住表,同样的情况又会发生,又会生成类似的SQL语句,这样一来看来对于这个设计应用one-to-one本身就是一种错误,是这样吗?
或许有人认为我们在每个one-to-one中加入lazy="true"这个属性会杜绝上述情况的发生,经过笔者的证实即便你加入了lazy="true",也不会带来任何的改变;又或者在hibernate.config中加入fetch depth属性以及在每个关联中设置outer-join="false",这些都不会引起本质上的变化,加入outer-join="false"其实结果只是将原有的outer join语句改变成多条sql语句而已,并没发生什么本质变化,反而效率更低了。
该怎么办呢?我们先仔细研究一下one-to-one的概念,one to one代表一对一,在一般的模型中很少会遇到one-to-one这种概念,因为他十分强调一对一的概念,就好比一个人他只有一个身体和一个头而已,头和身体是十分好的例子,因为有身体必定只有一个头,而且说到了身体必定要说头,就好像看了某个女孩的身材必定想知道她的长相如何(-_-),所以在这时我们使用one-to-one,因为这种一对一的关系是很强的,而且从对象中取得body必定会取得他所关联的head,这样的情况下使用outer-join是十分方便和有效率的,因为它使用了outer join查询从而避免了两条到数据库的查询语句,而且在这种情况下也只需要在body_hbm.xml中设置一个one-to-one即可,所以在这种确实是一对一
而且在主表中一对一的关联个数(即主表中one-to-one标签)十分少的情况下,使用one-to-one是一种很不错的解决办法。
如果一个主表会对多个子表都进行one-to-one关联呢,就像我们一开始遇到的这种情况,比如你不仅仅只想了解那个你中意的女孩的身材和脸蛋,而且还想知道他的学历,身世等等一切,在这种情况下,如果我们都是用多个one-to-one在主表中的话,那情况正如我们一开始看见的,是十分可怕的,该怎么做呢?不妨考虑一下使用one-to-many,什么,many?一开始听到many这个词的时候,我也觉得挺惊讶的这明明是多个一对一的关联为什么要用到many呢?其实many并没有一定要说是大于一的,你就只在它的many中存在一个关联它有能乃你何呢?如果用到many的话,我们就需要改动数据表的设计了,在每个有关连的子表中加入一列main_id代表主表中该记录的主键子段值,只需要这样子改动就可以了,这样所带来的效果绝对是值得你这样做的,然后我们就按照以往的one-to-many来设计就好了
在body.hbm.xml加入(一到head的关联举例,其他的关联按照这样的格式添加即可)
<set name="head" inverse="true" lazy="true" cascade="all-delete-orphan">
<key column="ID0000"/>
<one-to-many class="com.xx.Head"/>
</set>
在head.hbm.xml加入
<many-to-one name="body" column="ID0000" class="com.xx.Body" not-null="true"/>
行了,经过上面的改动我们就摆脱了查询时多个outer-join的困扰,只在需要的时候才对子表进行查询,因为设置了lazy="true",所以一切的一切都在我们的预料之中,我们如果希望获得body的话hibernate绝对不会把它的head 也查询出来,节省了查询是所需要的负担,除非到了我们十分需要head的情况才会进行关联查询,获得所需要的head结果。
所以由此看来
在one-to-one这种一对一的关系不是很强的情况下,或者是在一张表中存在多个one-to-one的情况下,使用one-to-many来代替one-to-one不失为一种不错的做法,当然更重要的良好的数据库设计,hibernate毕竟只是末,
千万不要本末倒置。
posted @
2005-03-30 13:26 一个人的日子,我独来独往 阅读(217) |
评论 (2) |
编辑 收藏
one-to-one在hibernate中可以用来作为两张表之间的主键关联,这也是hibernate中主键关联的一种用法,这样在一张表中的ID,在生成另外一张表的同时回自动插入到相应的ID字段中去,相应的XML文件设置比较简单,举例如下:
<!-- 建立一对一的到Address的映射,这个是写在User的XML配置文件中的 -->
<!-- 相应的User bean(PO)中也要添加属性 com.xx.Address address-->
<one-to-one name="address" cascade="all" class="com.xx.Address"/>
<!-- cascade的属性设置不再重复了,可以查看hibernate文档 -->
<!-- 建立一对一的到User的映射,这个是写在Address的XML配置文件中的 -->
<!-- 相应的Address bean(PO)中也要添加属性 com.xx.User user--> -->
<one-to-one name="user" class="com.xx.User" constrained="true"/>
为了在Address中使用User中的主键ID值,我们需要设置Address中的主键生成规则,如下所示,采用foreign关键字
<id column="ID" name="id" type="long" unsaved-value="0">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
这里需要注意的是property的属性值必须与上面到User的映射所填写的name属性值一致,这样就完成了one-to-one的映射关系。
上面的过程都很简单,下面我来说说这里需要注意的地方:
1. 在设置属性ID的时候必须注意字段的长度,如笔者这样使用oracle的sequence来生成ID,其长度有14位之长,则应选择hibernate类型long,对应的实体中应选择Long,这样不会出现溢出的情况。
2. 在测试的时候必须要注意这两张表之间因为已经存在了一对一的关系,所以我们不能只写
user.setAddress(address);
而忽略了
address.setUser(user);
这样在做插入的时候会报出attempted to assign id from null one-to-one property: address的错误,这一点初学者会经常犯,笔者也是其中之一。
3. 如果不写cascade="all"或者写成cascade="none"的话,即使你写了
user.setAddress(address);
address.setUser(user);
也不会发生任何事情,只有user会被存储。
以上是一些笔者经历的小经验,如果有不对的地方欢迎指正。
posted @
2005-03-23 17:27 一个人的日子,我独来独往 阅读(437) |
评论 (5) |
编辑 收藏
在很多情况下,我们使用Hibernate在已经建立好数据库的基础上。在oracle中,如果已经建立好的数据库中使用了sequence,则可以按照下面的步骤把它引入到Hibernate中:
1、在oracle 首先创建sequence
create sequence seq_id
minvalue 1
start with 1
increment by 1
cache 20;
2.在你的hbm.xml中的配置
<id column="ID0000" name="id" type="integer">
<generator class="sequence">
<param name="sequence">seq_id</param>
</generator>
</id>
这样再插入数据的时候,Hibernate回自动生成如下语句:
hibernate: select seq_id.nextval from dual
hibernate: insert into YXJK.T_YXJK_WHRYTXL (XM0000, ZW0000, LXDH00, SJHM00, DZYJ00,
IP0000, ID0000) values (?, ?, ?, ?, ?, ?, ?)
自动生成下一个序列值,然后将对象插入表中。
在使用的时候需要注意,Hibernate对于sequence的主键的要求是一定要是shor,long,或者integer
posted @
2005-03-23 10:30 一个人的日子,我独来独往 阅读(313) |
评论 (0) |
编辑 收藏
在做具有MVC结构的B/S程序时,怎样将这三层隔离开是十分关键的,一般用DAO封装Hibernate来获得对数据库的具体操作,在这里我们可以为每一个需要建立O-R MAPPING的对象(通过Hibernate实现OR映射)实现一个DAO,然后通过这个DAO来获得具体的数据库操作,用DAO的好处是我们可以把对一个对象的操作集中在同一个DAO中,便于管理,另外向上层只提供了接口屏蔽了底层对数据库的操作,通过hibernate,我们向上层直接提供建立了O-R MAPPING的OBJECT;同时在领域模型这一层,也就是M这一层,我们将一些业务逻辑(business logic)封装进来,这里所指的M这一层通常也就是我们在Hibernate中所用到的plain objectS,就是用来建立O-R MAPPING所需要用到的与表对应的OBJECTs,一般的领域模型都是由这些plain objectS构成;这样我们在控制层也就是C这一层只需要初始化DAO打开到持久层的通路,然后调用一些简单的方法执行业务逻辑,请注意这时候我们的业务逻辑已经被封装在领域模型这一层中了,这样我们每一层都是相互独立的,控制层C和展现层V都不和持久层所提供的接口有关系
posted @
2005-03-17 23:14 一个人的日子,我独来独往 阅读(123) |
评论 (0) |
编辑 收藏
今天的主要收获是发现通过在servlet的Filter中实现session.begin(),session.close(),session.beginTransaction()以及transaction.commit()是一个不错的选择
如上图这样,在从服务器端返回到客户端的时候,也就是在转向到最终页面的时候,由Filter实现关闭session和transaction,是一个很好的实现方法