07年5月29日
(1)为什么不把表关系在数据库里建,非要建在hibernate的hbm.xml文件里?
今天基于SQLSERVER2000利用spring+hibernate+struts做了高级搜索, 在sqlserver2000里的表并没有建表与表之间的关系,而是把表之间的关系写在hbm.xml配置文件里,我不知道这是为什么,问了经理,得到的回答是hbm配置文件建立表关系,数据库里不建是完全可行的,这样程序中也好处理,假设在数据库里建立了这种表与表的关系,那么程序实现时,就必须按照这样的关系进行增删改查的操作,这种情况往往解决不了实际需求,所以最佳的办法就是把表关系写在hbm.xml的配置文件里,这样做程序里是不会出错的,完全可行的最佳方案!而且,sqlserver数据库是面向对象的,不像mysql是关系型数据库,我终于明白了为什么书上讲hiberntae的时候大多数都用mysql为例子,就因为mysql是关系型数据库,可以把hibernate的O/R映射关系体现的完美,有助于对O/R关系映射的理解.
(2)为什么搜索时超过3张表(包括3张表)要建视图而不是通过hibernate的级联关系进行搜索?
因为hibernate查询时,只要涉及的表大于等于3张的时候,数据量一上来,搜索的速度会相当的慢,和牛一样!这样就必须在SQLServer里建视图了,提到了视图,有必要说一下什么是视图了.
什么是视图,怎样建视图,什么情况要用到视图,怎么给视图加主键?
先说怎么样建视图吧,打开"企业管理器"---->选择你要用的数据库,双击打开------>点"视图"------>右键"新建视图"
------->出现页面的中上部分有一个空白的区域----->右键选择"添加表",把你要建立视图所设计的表都选进来(当然,如果你SQL语句牛比,而且思路清晰时,也可以手写创建视图的sql语句),然后选择表主键和其它表字段的关系,双击选中的主键,然后拖到其他表相应的字段就OK了,注意,每个字段前面都有一个复选框,这个东西选中了表示你要基于这个视图显示的字段!,设计完成以后,起个名字保存一下就OK了,一般以V字母开头,这样规范一点!
视图建好了,这是个什么东西有什么作用啊?
以前从来没接触过视图,我个人的理解,就是基于几张表,并且把这几张表建立了关系,形成了一张新表,这个新表的名字就是刚才建视图的名字,完全可以当成一张表来看(其实也就是一张表,只不过是数据库自动把几张表合在一起新形成的表),视图一般情况下,搜索的时候会用到(前提条件是当搜索时的表>=3张),这样可以提升搜索的速度,很有必要!
视图建好了,但是没有主键,在没有主键的情况下,hibernate映射的时候会自动加上一个复合主键,并且单独生成一个PO,这样就生成了两个PO和一个对应的hbm.xml配置文件,这样感觉很不爽!既然sqlserver里不能给视图加主键,那么就在hbm.xml配置文件里加好了!可能会有人有这样的迷惑,我查询的东西零零散散的,主键没办法加.其实不是这样的,举个例子,我要根据一堆条件来查询文件(上传文件)的信息,不管几张表,不管根据什么条件查,其实得到fileId(主键)是最重要的,不是吗?这样,我就可以把fileId设为此视图的主键了.再来说这个问题,有人会说我"简直在胡扯,我的视图就是没有这样的主键,你只不过是幸运!"那么好,这也可以解决,在视图的hbm.xml配置文件里加上
<id name="Id" type="integer">
<column name="Id" />
<generator class="native" />
</id>
这样也是OK的,我只是听同事这样给我说的,我没试过,如果失败,那我就是在胡扯了:)我以fileId这个字段当视图的主键就把上面粉色代码的Id都改成fileId就行了.
其实给视图加主键是有技巧的,当你搜索的时候,前提是大于等于3张表才建视图!无论你基于多少张表,根据多少个条件来查询,无非就是想得到你最终想要的目的信息,而这个目的信息一定是有它自己的主键的,那么完全就可以把这个目的信息的主键当作此视图的主键就OK了!
(3)关于spring+struts+hibernate架构的分页问题
我的项目里有这样一个分页方法:
int count = fm.getTfileInforDAO().lunfileCount(lunceFileForm);
int page = PageController.initPage(request);// 当前页
int pageSize = 15;
PageController pc = new PageController(request, "pageController",count, pageSize, page);
lunfileList = fm.getTfileInforDAO().lunceFileList(lunceFileForm ,pageSize, page);
count是根据搜索条件计算总条数,page是当前页,pageSize是每页显示的信息条数,当我做到这里的时候,觉得很不爽,因为一个相同的搜索条件查了两次数据库,就相当于查询的时候同一个搜索条件判断了两次!这是高级搜索,搜索的条件是很多的,这样不是比较影响系统的性能吗?于是我改写了这个分页,把总条数count也在调用lunceFileList(lunceFileForm ,pageSize, page);这个方法的时候加进去一起查了出来,改完以后麻烦也就跟着来了,当我查询个人信息,部门信息,小组信息......的时候,都要改写我的分页方法,内容大同小异,简直就是在干体力活,还没办法把这个想法作个抽象或者接口,于是只能按着原来的分页去做,这样我又发现了它的好处!就是代码层次更清晰,分离的更好,重用性相当的高!恩,问问写这个分页的同事吧,才知道为了代码层次清晰,重用性高,而且在程序里判断这些搜索条件是很快的,不用担心性能了!
(4)基于视图的搜索的优化问题
当我基于视图查出来的数据页面里一定显示几个重要的字段,以列表的形式显示在页面上,然后每条信息后面有个"查看"的链接,直接走struts的action,在action里根据每条记录的Id查询一次它相应的表,把它的整个信息查出来.我当时就想,这样会查询两次,一次是基于搜索条件和视图的查询,一次是基于Id相应表的查询,这样做性能多差啊,我把基于搜索条件和视图查出来的信息放在session里多好啊!问了经理,终于搞清楚为什么分两步查了,因为基于搜索条件和视图查出来的数据量会非常的大,谁也不知道session能开辟多大的内存空间,如果存在session里,内存空间会占用的特别大,这种代价比分两步查找的代价要大得多了去了!所以还是分两步查找吧!
今天学到很多,今天真好!
07年5月30日
(5)关于建立视图出现的
com.yliso.hibernate.po.Vluncefile column: fileId (should be mapped with insert="false"
update="false")映射的错误:
原因很简单,先看看下面的hbm.xml配置文件
<hibernate-mapping>
<class name="com.yliso.hibernate.po.Vluncefile" table="Vluncefile" schema="dbo"
catalog="yliso">
<id name="fileId" type="integer">
<column name="fileId" />
</id>
<property name="fileId" type="integer">
<column name="fileId" />
</property>
<property name="stepId" type="integer">
<column name="stepId" />
</property>
<property name="taskId" type="integer">
<column name="taskId" />
</property>
<property name="filePrincipal" type="string">
<column name="filePrincipal" length="30" />
</property>
</class>
</hibernate-mapping>
上面的配置文件中,把fileId映射了两次,这样就会提示上面的错误,去掉一个fileId的映射就OK了!
还有就是上面配置文件中的主键问题,很容易发现没有主键的生成方式,即<generator class="native" />
这句话,因为视图是查找用的,不涉及增删改的操作,所以也就不涉及主键是怎么生成的了,而且在视图里
不写主键生成方式是完全没有什么问题的!
注意:使用视图时,一定要仔细看看里面的字段是否有重复的映射,如果出现上面的错误提示信息,基本上
就是配置文件里的字段映射重复了!
(6)视图里建立多个left join的SQL语句
关于left join / right join /inner join 都很好理解,我的博客的数据库分类里面引用了两篇这样的文章,说得
很透彻了.(另外加上一句,left outer join 和left join有什么区别?我查了一下,都说没有区别,只不过在SQLServer
建视图运行的时候,你写的left join 会自动变成left outer join.得出的结论就是left outer join是标准的,仅此而已.)
这两篇文章都是基于两张表操作做的说明,要是多张表怎么办?实际工作中要是两张表的话也不用建视图了,一定会
有很多张表的,我就有9张表要建个视图需要写一个left join语句.
下面是我的解决办法:
先说一下这9张表的关系
(1)TfileInfor文件信息表
fileId 字段(此表的主键) taskId 字段 (任务表的主键)
stepId 字段 (步骤表的主键)
(2)TfileInfor2TsysDepartInf 文件信息和部门的关联表
fileId 字段(文件信息表的主键) departId 字段 (部门表的主键)
(3)TsysDepartInf(部门表)
departId 字段 (部门表的主键)
(4)TfileInfor2TsysGroupInf 文件信息和小组的关联表
groupId 字段 (小组表的主键) fileId 字段 (文件信息表的主键)
(5)TsysGroupInf小组表
groupId 字段 (小组表的主键)
(6)TfileInfor2TsysUserInf 文件信息和用户的关联表
fileId 字段(文件信息表的主键) userId 字段(用户表的主键)
(7)TsysUserInf 用户表
userId 字段(用户表的主键)
(8)Tprjstep 步骤表
stepId 字段 (步骤表的主键)
(9)Tprjtask 任务表
taskId 字段 (任务表的主键)
好了,现在基于TfileInfor文件信息表用left join关联这9张表进行高级搜索了,建立视图的sql语句如下:
SELECT TfileInfor.taskId, TfileInfor.filePrincipal, TsysUserInf.userName,
Tprjstep.stepName, Tprjtask.taskName, TsysGroupInf.groupName,
TsysDepartInf.departName, TfileInfor.upLoadDate, TfileInfor.fileName,
TsysDepartInf.departId, TfileInfor.stepId, TfileInfor.fileId
FROM TfileInfor
left join TfileInfor2TsysDepartInf on TfileInfor.fileId = TfileInfor2TsysDepartInf.fileId
left join TsysDepartInf on TfileInfor2TsysDepartInf.departId = TsysDepartInf.departId
left join TfileInfor2TsysGroupInf on TfileInfor.fileId = TfileInfor2TsysGroupInf.fileId
left join TsysGroupInf on TfileInfor2TsysGroupInf.groupId = TsysGroupInf.groupId
left join TfileInfor2TsysUserInf on TfileInfor.fileId = TfileInfor2TsysUserInf.fileId
left join TsysUserInf on TfileInfor2TsysUserInf.userId = TfileInfor2TsysUserInf.userId
left join Tprjstep on TfileInfor.stepId = Tprjstep.stepId
left join Tprjtask on TfileInfor.taskId = Tprjtask.taskId
恩,完成了,其实通过表关系可以看出来,第8,第9张表也可以分别和文件信息表各建一张中间表,但是时间太紧,先这样了,而且这个视图我总觉得有不优化的地方,就是关联表之间好像可以用inner join,但是我还是为了确保搜索的时候不出错,用了left join.以前从来没做过搜索,所以这块等到按着这个视图实现了功能,优化的时候再试试就知道了:),每个表明前面都有一个dbo.表名/dbo.表名.字段名,我觉得那个dbo.很不爽,把它干掉了