2008年12月14日星期日

MVC设计模式之歌

    这里有首歌实在很不错,听这首歌能更清楚MVC设计模式的精髓啊,哈哈.大家都来听听啊.


作者:James Dempsey.

中文歌词:


MVC是一种范型
它构造代码成为功能段,免得你脑袋淤阻
为达到复用,你必须让边界干净
这边是模型,那边是视图,控制器在中间
模型 视图,和夹心饼干一样有三层
模型 视图 控制器
模型 视图 模型 视图 模型 视图 控制器
模型对象正是你的应用系统存在的理由
你设计的对象,包含了数据、逻辑,之类的
在你的应用问题域,你创建定制的类
你可以选择复用所有视图
但模型对象无须改变
你可以建模一部机器,随便什么机器。
建模一个三岁小孩
建模一瓶白葡萄酒
建模人们的窃窃私语
建模一些水煮蛋
建模Hexley的蹒跚步履
模型 视图
你可以建模GQ时尚杂志中的模特儿。
模型 视图 控制器
视图对象通常是控件,用来显示和编辑
Cocoa也是这样,设计良好,赢得好评
Java也是
把任何老的Unicode字符串交给NSTextView
用户可以和它交互,它几乎可以包含任何东西
但视图不知道模型
字符串可以是一个电话号码,或者亚里士多德的文学作品
保持松耦合
达到最高的复用。
模型 视图,一切都是水波荡漾的蓝色
模型 视图 控制器
你可能怀疑现在做吗?
你可能怀疑怎么做到?
模型和视图之间的数据流动
是由控制器居间协调进行
两者之间状态的改变,数据的同步
都是由控制器控制的。
控制器负责将每个改变的状态送进送出
模型 视图,对于Smalltalk的人来说
这是最大的支柱
模型 视图 控制器
模型 视图,读做「噢噢」,不是「呜呜」
模型 视图 控制器
旅程尚未结束
前方还有道路
编写控制器的人
似乎没有得到掌声
模型的使命很重要
视图的外观很美妙
我或许很懒,但有时却是疯了
我写了多少代码,只是为了黏着两者
其实并不是惨痛
说穿了代码并没有神奇之处
只是用来搬动值罢了
我无意出言恐吓
但这是有声誉的
对于控制器你照做就是了
我真希望能得到一个铜板的奖赏
每次将字符串
送给TextField时
模型 视图
我们要如何丢弃黏结
模型 视图 控制器
控制器相当熟悉模型和视图
所以常常硬编码会妨碍复用
你可以将模型的键连结到任何视图的属性
一旦开始绑定
你会发现源码变少了。
是的,这一切自动又免费,让我感到洋洋得意
我知道一旦你使用接口设计工具
许多代码都可以自动产生
省下许多功夫
模型 视图,好处多多
模型 视图 控制器
模型 视图,但是我的应用已经交付
来不及采用MVC了
模型 视图 控制器



译注1 Cocoa是MacOS X的面向对象API。
译注2 这是Cocoa中的一个Class。
译注3 这是MacOS X的用户界面Aqua预定的颜色。

怎么成为优秀的软件模型设计者?

    我们期待自己成为一个优秀的软件模型设计者,但是,要怎样做,又从哪里开始呢?
    将下列原则应用到你的软件工程中,你会获得立杆见影的成果。

1. 人远比技术重要

    你开发软件是为了供别人使用,没有人使用的软件只是没有意义的数据的集合而已。许多在软件方面很有成就的行家在他们事业的初期却表现平平,因为他们那时侯将主要精力都集中在技术上。显然,构件(components),EJB(Enterprise Java Beans)和代理(agent)是很有趣的东西。但是对于用户来说,如果你设计的软件很难使用或者不能满足他们的需求,后台用再好的技术也于事无补。多花点时间到软件需求和设计一个使用户能很容易理解的界面上。

2. 理解你要实现的东西

    好的软件设计人员把大多数时间花费在建立系统模型上,偶尔写一些源代码,但那只不过是为了验证设计过程中所遇到的问题。这将使他们的设计方案更加可行。


3. 谦虚是必须的品格

    你不可能知道一切,你甚至要很努力才能获得足够用的知识。软件开发是一项复杂而艰巨的工作,因为软件开发所用到的工具和技术是在不断更新的。而且,一个人也不可能了解软件开发的所有过程。在日常生活中你每天接触到的新鲜事物可能不会太多。但是对于从事软件开发的人来说,每天可以学习很多新东西(如果愿意的话)。

4. 需求就是需求

    如果你没有任何需求,你就不要动手开发任何软件。成功的软件取决于时间(在用户要求的时间内完成)、预算和是否满足用户的需求。如果你不能确切知道用户需要的是什么,或者软件的需求定义,那么你的工程注定会失败。

5. 需求其实很少改变,改变的是你对需求的理解

    Object ToolSmiths公司(www.objecttoolsmiths.com)的Doug Smith常喜欢说:“分析是一门科学,设计是一门艺术”。他的意思是说在众多的“正确”分析模型中只存在一个最“正确”分析模型可以完全满足解决某个具体问题的需要(我理解的意思是需求分析需要一丝不苟、精确的完成,而设计的时候反而可以发挥创造力和想象力 - 译者注)。

    如果需求经常改动,很可能是你没有作好需求分析,并不是需求真的改变了。

    你可以抱怨用户不能告诉你他们想得到什么,但是不要忘记,收集需求信息是你工作。

    你可以说是新来的开发人员把事情搞得一团糟,但是,你应该确定在工程的第一天就告诉他们应该做什么和怎样去做。

    如果你觉得公司不让你与用户充分接触,那只能说明公司的管理层并不是真正支持你的项目。

    你可以抱怨公司有关软件工程的管理制度不合理,但你必须了解大多同行公司是怎么做的。

    你可以借口说你们的竞争对手的成功是因为他们有了一个新的理念,但是为什么你没先想到呢?

    需求真正改变的情况很少,但是没有做好需求分析工作的理由却很多。

6. 经常阅读

    在这个每日都在发生变化的产业中,你不可能在已取得的成就上陶醉太久。

    每个月至少读2、3本专业杂志或者1本专业书籍。保持不落伍需要付出很多的时间和金钱,但会使你成为一个很有实力的竞争者。

7. 降低软件模块间的耦合度

    高耦合度的系统是很难维护的。一处的修改引起另一处甚至更多处的变动。

    你可以通过以下方法降低程序的耦合度:隐藏实现细节,强制构件接口定义,不使用公用数据结构,不让应用程序直接操作数据库(我的经验法则是:当应用程序员在写SQL代码的时候,你的程序的耦合度就已经很高了)。

    耦合度低的软件可以很容易被重用、维护和扩充。

8. 提高软件的内聚性

    如果一个软件的模块只实现一个功能,那么该模块具有高内聚性。高内聚性的软件更容易维护和改进。

    判断一个模块是否有高的内聚性,看一看你是否能够用一个简单的句子描述它的功能就行了。如果你用了一段话或者你需要使用类似“和”、“或”等连词,则说明你需要将该模块细化。

    只有高内聚性的模块才可能被重用。

9. 考虑软件的移植性

    移植是软件开发中一项具体而又实际的工作,不要相信某些软件工具的广告宣传(比如java 的宣传口号write once run many ? 译者注)。

    即使仅仅对软件进行常规升级,也要把这看得和向另一个操作系统或数据库移植一样重要。

    记得从16位Windows移植到32位windows的“乐趣”吗 ?当你使用了某个操作系统的特性,如它的进程间通信(IPC)策略,或用某数据库专有语言写了存储过程。你的软件和那个特定的产品结合度就已经很高了。

    好的软件设计者把那些特有的实现细节打包隐藏起来,所以,当那些特性该变的时候,你的仅仅需要更新那个包就可以了。

10. 接受变化

    这是一句老话了:唯一不变的只有变化。

    你应该将所有系统将可能发生的变化以及潜在需求记录下来,以便将来能够实现(参见“Architecting for Change”,Thinking Objectively, May 1999)

    通过在建模期间考虑这些假设的情况,你就有可能开发出足够强壮且容易维护的软件。设计强壮的软件是你最基本的目标。

11. 不要低估对软件规模的需求

    Internet 带给我们的最大的教训是你必须在软件开发的最初阶段就考虑软件规模的可扩充性。

    今天只有100人的部门使用的应用程序,明天可能会被有好几万人的组织使用,下月,通过因特网可能会有几百万人使用它。

    在软件设计的初期,根据在用例模型中定义的必须支持的基本事务处理,确定软件的基本功能。然后,在建造系统的时候再逐步加入比较常用的功能。

    在设计的开始考虑软件的规模需求,避免在用户群突然增大的情况下,重写软件。

12. 性能仅仅是很多设计因素之一

    关注软件设计中的一个重要因素--性能,这好象也是用户最关心的事情。一个性能不佳的软件将不可避免被重写。

    但是你的设计还必须具有可靠性,可用性,便携性和可扩展性。你应该在工程开始就应该定义并区分好这些因素,以便在工作中恰当使用。性能可以是,也可以不是优先级最高的因素,我的观点是,给每个设计因素应有的考虑。

13. 管理接口

    “UML User Guide”(Grady Booch,Ivar Jacobson和Jim Rumbaugh ,Addison Wesley, 1999)中指出,你应该在开发阶段的早期就定义软件模块之间的接口。

    这有助于你的开发人员全面理解软件的设计结构并取得一致意见,让各模块开发小组相对独立的工作。一旦模块的接口确定之后,模块怎样实现就不是很重要了。

    从根本上说,如果你不能够定义你的模块“从外部看上去会是什么样子”,你肯定也不清楚模块内要实现什么。

14. 走近路需要更长的时间

    在软件开发中没有捷径可以走。

    缩短你的在需求分析上花的时间,结果只能是开发出来的软件不能满足用户的需求,必须被重写。

    在软件建模上每节省一周,在将来的编码阶段可能会多花几周时间,因为你在全面思考之前就动手写程序。

    你为了节省一天的测试时间而漏掉了一个bug,在将来的维护阶段,可能需要花几周甚至几个月的时间去修复。与其如此,还不如重新安排一下项目计划。

    避免走捷径,只做一次但要做对(do it once by doing it right)。

15. 别信赖任何人

    产品和服务销售公司不是你的朋友,你的大部分员工和高层管理人员也不是。

    大部分产品供应商希望把你牢牢绑在他们的产品上,可能是操作系统,数据库或者某个开发工具。

    大部分的顾问和承包商只关心你的钱并不是你的工程(停止向他们付款,看一看他们会在周围呆多长时间)。

    大部分程序员认为他们自己比其他人更优秀,他们可能抛弃你设计的模型而用自己认为更好的。

    只有良好的沟通才能解决这些问题。

    要明确的是,不要只依靠一家产品或服务提供商,即使你的公司(或组织)已经在建模、文档和过程等方面向那个公司投入了很多钱。

16. 证明你的设计在实践中可行

    在设计的时候应当先建立一个技术原型, 或者称为“端到端”原型。以证明你的设计是能够工作的。

    你应该在开发工作的早期做这些事情,因为,如果软件的设计方案是不可行的,在编码实现阶段无论采取什么措施都于事无补。技术原型将证明你的设计的可行性,从而,你的设计将更容易获得支持。

17. 应用已知的模式

    目前,我们有大量现成的分析和设计模式以及问题的解决方案可以使用。

    一般来说,好的模型设计和开发人员,都会避免重新设计已经成熟的并被广泛应用的东西。http://www.ambysoft.com/processPatternsPage.html收藏了许多开发模式的信息。

18. 研究每个模型的长处和弱点

    目前有很多种类的模型可以使用,如下图所示。用例捕获的是系统行为需求,数据模型则描述支持一个系统运行所需要的数据构成。你可能会试图在用例中加入实际数据描述,但是,这对开发者不是非常有用。同样,数据模型对描述软件需求来说是无用的。每个模型在你建模过程中有其相应的位置,但是,你需要明白在什么地方,什么时候使用它们。


19. 在现有任务中应用多个模型

    当你收集需求的时候,考虑使用用例模型,用户界面模型和领域级的类模型。

    当你设计软件的时候,应该考虑制作类模型,顺序图、状态图、协作图和最终的软件实际物理模型。

    程序设计人员应该慢慢意识到,仅仅使用一个模型而实现的软件要么不能够很好地满足用户的需求,要么很难扩展。

20. 教育你的听众

    你花了很大力气建立一个很成熟的系统模型,而你的听众却不能理解它们,甚至更糟-连为什么要先建立模型都不知道。那么你的工作是毫无意义的。

    教给你开发人员基本的建模知识;否则,他们会只看看你画的漂亮图表,然后继续编写不规范的程序。

    另外, 你还需要告诉你的用户一些需求建模的基础知识。给他们解释你的用例(uses case)和用户界面模型,以使他们能够明白你要表达地东西。当每个人都能使用一个通用的设计语言的时候(比如UML-译者注),你的团队才能实现真正的合作。

21. 带工具的傻瓜还是傻瓜

    你给我CAD/CAM工具,请我设计一座桥。但是,如果那座桥建成的话,我肯定不想当第一个从桥上过的人,因为我对建筑一窍不通。

    使用一个很优秀的CASE工具并不能使你成为一个建模专家,只能使你成为一个优秀CASE工具的使用者。成为一个优秀的建模专家需要多年的积累,不会是一周针对某个价值几千美元工具的培训。一个优秀的CASE工具是很重要,但你必须学习使用它,并能够使用它设计它支持的模型。

22. 理解完整的过程

    好的设计人员应该理解整个软件过程,尽管他们可能不是精通全部实现细节。

    软件开发是一个很复杂的过程,还记得《object-oriented software process》第36页的内容吗?除了编程、建模、测试等你擅长工作外,还有很多工作要做。

    好的设计者需要考虑全局。必须从长远考虑如何使软件满足用户需要,如何提供维护和技术支持等。

23. 常做测试,早做测试

    如果测试对你的软件来说是无所谓的,那么你的软件多半也没什么必要被开发出来。

    建立一个技术原型供技术评审使用,以检验你的软件模型。

    在软件生命周期中,越晚发现的错误越难修改,修改成本越昂贵。尽可能早的做测试是很值得的。

24. 把你的工作归档

    不值得归档的工作往往也不值得做。归档你的设想,以及根据设想做出的决定;归档软件模型中很重要但不很明显的部分。 给每个模型一些概要描述以使别人很快明白模型所表达的内容。

25. 技术会变,基本原理不会

    如果有人说“使用某种开发语言、某个工具或某某技术,我们就不需要再做需求分析,建模,编码或测试”。不要相信,这只说明他还缺乏经验。抛开技术和人的因素,实际上软件开发的基本原理自20世纪70年代以来就没有改变过。你必须还定义需求,建模,编码,测试,配置,面对风险,发布产品,管理工作人员等等。

    软件建模技术是需要多年的实际工作才能完全掌握的。好在你可以从我的建议开始,完善你们自己的软件开发经验。

    以鸡汤开始,加入自己的蔬菜。然后,开始享受你自己的丰盛晚餐吧。

使用PowerDesigner进行数据库概念模型建模(一)

      为什么要进行建模呢?
      想一想,做事情先是不是需要事先计划一下?想要去做什么,怎么去做,如何做到?
      很多同学在做项目实战的时候,得到需求都不知道该做些什么,这就是因为大脑里没有对要做的事情有规划.还有的同学东想想,西想想,脑袋越想越乱,昏昏欲睡,目光呆泻....
      我曾多次告诉学员,你要开发一个项目的时候,首先要画一张数据关系图(ER图),从需求里找出关键的数据对象,理清它们之间的关系,并且在图上体现出来.这样,项目的基础--数据的设计就出来了.那么针对项目的设计,再画一张图,主要是根据这些数据对象之间的关系,以及你想要获取什么数据对象,来定义新的对象和其方法,并且一步步的在图上描绘出来它们之间的联系,并且相应的标上注释.简单的来说,大体就是这样,当然这其中牵扯了很多内容都被我们忽略了.有了这两份图,起码你知道你应该做什么,怎么去做.接下来的工作就是你用代码实现这两份图的思想了.虽然简单,但有了针对性,目标才更加的明确,实现起来才更加容易.
      罗马不是一天建成的,我们需要慢慢的去锻炼自己的设计能力,之所以要慢慢,是因为对需求的设计锻炼多了,编码多了,你才能从中有所收获.设计是门艺术,形你可以学,但神韵需要你自己去体会,去悟,去修炼....
      以前我们学习过用visio2003画ER图,但要学习设计,我觉得还是PowerDesigner比较好,因为它是一站式的解决方案,从概念到物理到基本实现.
      我们今天呢,就从学习用PowerDesigner来画和ER图差不多的东西,比较专业的术语是概念模型.
      要求基础:<<SQL Server数据库设计和高级查询>>(s2学期)
      我们打开PowerDesigner12.5后看到的工作界面如下:

      接下来呢,我们要新建一个概念模型,文件->新建,选择如下:

      当然你可以修改模型名字为你所喜欢的,点击确定就可以了.接下来,你会看到工作区的变化:

      你会看到左边出现了Diagram_1,这就是你要建模的地方,当然,你可以新建多个.为什么要建多个呢?如果你的项目是分模块的,那么把相关数据对象创建在不同的Diagram中,这样划分是十分清晰的.(当然还有好的解决方案,下祥)那么如何新建Diagram呢?看下图:

      我们接着来了解一下设计工具:

      理论说的够多了,现在我们来一个实际的需求,学员管理,对每个班的学员进行管理,超级简单吧,可以分析出有两个实体,学员和班级,并且,它们之间是多对一的关系. 先拖两个实体到图(Diagram)上:

      只是拖拽了两个实体,我们还需要设置这两个实体,让它们代表各自的真实含义.双击实体可以打开属性设置界面,其中name是在外面显示这个实体的名字,code是实际编码中用到的.

      打开Attribute选项卡来设置此表的字段,点击Data Type表格可以弹出字段类型的设置窗口,后面的M代表不能为null,P为主健(这里术语是标识符),D代表显示.

      当然了,你可能觉得设置属性,该处提供的设置太少了.没关系,双击属性可打开更详细的设置页面:

      可以在下面添加检查约束:

      不满足?想要更多的约束??满足你!!点击左下角的More按钮:

      全部设置好实体后如下,没有外健吗?问的好,概念模型只需要定义关系就好了:

      接下来,我们要建立关系了,拖动Relationship图标,按对应关系指向实体,结果如下:

      点击关系名可以设置更具体的内容:

      接下来,我们可以深入设置对应关系:

      相信大家看图已经一目了然了,班级和学员的关系是一对多的关系,也就是一个班级有多名学员.Mandatory这个选项是什么意思呢?如果选择了强制关系,以班级表to学生表为例,可以看到后面的映射基数是0,n.也就是说一个班级可以没有学生,也可以有多个学生.在勾了Mandatory后,映射基数变为1,n.也就是一个班级必须最少有一个学生.
      最后,我们完成了这个很简单的概念模型,如下:

      是不是美中不足的是映射基数没有显示出来,有点不直观啊,没关系,设置工具->显示参数选择,然后勾选Cardinality即可,看图示:

      最后的概念模型图:

      这只是部分,同学们要学会组合,这样才完整,学会工具使用没什么,重要的是要把思想和工具结合起来才敏捷啊.下讲是关于关系(Association)设计问题的探讨.其余未尽事宜,老规矩:一办找我交流!!(本文用工具:PowerDesigner12.5+汉化包)

2008年12月12日星期五

在MyEclipse中部署Weblogic9.2中文版j2ee服务器

    长久以来,我们都习惯了使用tomcat作为我们j2ee开发的服务器,虽然tomcat作为免费开源的服务器非常好用,但是它最多能支持到servlet而已,像分布式应用,集群等等高级领域统统不支持,毕竟它是免费的...我们今天要介绍的weblogic,为企业提供一个完整的商务应用解决方案,而且不论性能,功能,稳定性均比Tomcat高了不止一个档次.举个形象的例子,tomcat就像是夏利,而weblogic就像是宝马.
    今天呢,我们来学习一下如何安装Weblogic服务器,如何在Myeclipse中进行部署.
    要求基础:<<使用JSP开发Web应用系统>>(s2)
    首先呢,我们需要获得Weblogic9.2中文版服务器(直接下载使用,5个ip限制):http://download2.bea.com/pub/platform/92/server920_zh_CN_win32.exe
    以下是安装步骤:
1.双击exe开始安装:

2.安装程序开始配置环境:

3.许可协议:

4.选择安装目录:

5.选择安装类型:

6.这里的可选工具Mercury是测试在服务器上部署的工程性能的:

7.Mercury测试工具的许可:

8.选择weblogic产品的主目录,包括server和workshop,(我们暂时只用到server):

9.选择快捷方式的位置:

10.开始安装了....

11.安装完成了.

12.安装完成后,我们需要配置一下服务器的域(在域下部署各应用,它相当于IIS里的虚拟目录),以及管理员用户名以及密码:

13.新建域,相当于一个虚拟主机一样:

14. 默认即可:

15.配置管理员用户名和密码:

16.配置JDK,这里要看左边它的建议,你不能选择其他jdk,否则启动服务器后要报错!

17.这里默认即可,高级选项以后可以再配:

18.最后一步,创建域的名字和位置:

19.完成....

20.启动服务器后看到此画面说明没问题:

21.默认的端口是7001,也就是http://localhost:7001/console可以进入管理界面,如下:

22.我们可以看到亲切的中文管理界面,你们是不是流口水啦,哈哈.

23.打开myeclipse,然后选设置:

24.设置weblogic9参数:

25.下面还要设置此weblogic使用的jdk,还记得刚才我们安装的时候选择的jdk吗?对,要新建一个,使用weblogic自带的jdk:

26.设置完后,可以启动weblogic服务器:


27.使用weblogic需要注意的地方,你的工程的JRE环境要设置成使用配置weblogic时新建的JRE环境,用它来编译你的项目代码,这样你部署到weblogic时,才能正常访问:


全部安装,配置,应用过程如上,各位同学快下载一个weblogic9.2中文服务器回去体验一下吧!

2008年12月9日星期二

彻底掌握hibernate对象关联映射

    有很长时间没写博客了,前些天主要忙孩子的事情,我终于当爹啦,心情难以平复啊.他是个很可爱的小胖小子,很可惜,现在他并不在我身边,真是让我感觉寂寞难耐啊,做什么事情都没什么心情,心思都在我那可爱的儿子那里呢,真盼望元旦快些到来啊,我好回去亲亲我的好儿子.
    回到正题来,今天我们的目的是要掌握hibernate对象关联映射,设计对象关联映射是hibernate开发过程中的主要内容,老是有许多同学对这块理解、实践不够。那么我这次正好再详细的讲解一次,图文并茂,希望大家认真练习,把它彻底掌握。

要求基础:《Struts/Spring/Hibernate/Ajax的网上信息发布平台》(y2学期)


首先,我们来看一下此次设计的数据库概念模型图(使用PowerDesigner12.5建立的CDM):














    下面我来描述一下这个概念模型,一共4个表,雇员表,雇员薪水表,部门表,项目表。其中雇员表和雇员薪水表是一对一的关系,雇员表与部门表是多对一的关系,部门表和项目表是多对多的关系。
下面是物理模型图(使用PowerDesigner12.5建立的PDM):



以下是生成的数据库脚本:

/*==============================================================*/
/* DBMS name: Microsoft SQL Server 2005 */
/* Created on: 2008-12-11 13:43:14 */
/*==============================================================*/


if exists (select 1
from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F')
where r.fkeyid = object_id('Employees') and o.name = 'FK_EMPLOYEE_EMPLOYEES_SECTOR')
alter table Employees
drop constraint FK_EMPLOYEE_EMPLOYEES_SECTOR
go

if exists (select 1
from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F')
where r.fkeyid = object_id('Salary') and o.name = 'FK_SALARY_EMPLOYEE__EMPLOYEE')
alter table Salary
drop constraint FK_SALARY_EMPLOYEE__EMPLOYEE
go

if exists (select 1
from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F')
where r.fkeyid = object_id('Sector_Project') and o.name = 'FK_SECTOR_P_M_PROJECT')
alter table Sector_Project
drop constraint FK_SECTOR_P_M_PROJECT
go

if exists (select 1
from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F')
where r.fkeyid = object_id('Sector_Project') and o.name = 'FK_SECTOR_P_N_SECTOR')
alter table Sector_Project
drop constraint FK_SECTOR_P_N_SECTOR
go

if exists (select 1
from sysindexes
where id = object_id('Employees')
and name = 'Employees_Sector_FK'
and indid > 0
and indid < 255)
drop index Employees.Employees_Sector_FK
go

if exists (select 1
from sysobjects
where id = object_id('Employees')
and type = 'U')
drop table Employees
go

if exists (select 1
from sysindexes
where id = object_id('Salary')
and name = 'Employee_Salary_FK'
and indid > 0
and indid < 255)
drop index Salary.Employee_Salary_FK
go

if exists (select 1
from sysobjects
where id = object_id('Salary')
and type = 'U')
drop table Salary
go

if exists (select 1
from sysobjects
where id = object_id('Sector')
and type = 'U')
drop table Sector
go

if exists (select 1
from sysindexes
where id = object_id('Sector_Project')
and name = 'm_FK'
and indid > 0
and indid < 255)
drop index Sector_Project.m_FK
go

if exists (select 1
from sysindexes
where id = object_id('Sector_Project')
and name = 'n_FK'
and indid > 0
and indid < 255)
drop index Sector_Project.n_FK
go

if exists (select 1
from sysobjects
where id = object_id('Sector_Project')
and type = 'U')
drop table Sector_Project
go

if exists (select 1
from sysobjects
where id = object_id('project')
and type = 'U')
drop table project
go

/*==============================================================*/
/* Table: Employees */
/*==============================================================*/
create table Employees (
employeeId int identity,
sectorId int not null,
employeeName varchar(30) null,
regTime datetime null,
constraint PK_EMPLOYEES primary key nonclustered (employeeId)
)
go

declare @CurrentUser sysname
select @CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'雇员表,雇员的基本信息',
'user', @CurrentUser, 'table', 'Employees'
go

/*==============================================================*/
/* Index: Employees_Sector_FK */
/*==============================================================*/
create index Employees_Sector_FK on Employees (
sectorId ASC
)
go

/*==============================================================*/
/* Table: Salary */
/*==============================================================*/
create table Salary (
salaryId int identity,
employeeId int not null,
money money null,
constraint PK_SALARY primary key nonclustered (salaryId)
)
go

/*==============================================================*/
/* Index: Employee_Salary_FK */
/*==============================================================*/
create index Employee_Salary_FK on Salary (
employeeId ASC
)
go

/*==============================================================*/
/* Table: Sector */
/*==============================================================*/
create table Sector (
sectorId int identity,
sectorName varchar(30) null,
constraint PK_SECTOR primary key nonclustered (sectorId)
)
go

declare @CurrentUser sysname
select @CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'部门表',
'user', @CurrentUser, 'table', 'Sector'
go

/*==============================================================*/
/* Table: Sector_Project */
/*==============================================================*/
create table Sector_Project (
sectorId int not null,
projectId int not null,
constraint PK_SECTOR_PROJECT primary key (sectorId, projectId)
)
go

/*==============================================================*/
/* Index: n_FK */
/*==============================================================*/
create index n_FK on Sector_Project (
sectorId ASC
)
go

/*==============================================================*/
/* Index: m_FK */
/*==============================================================*/
create index m_FK on Sector_Project (
projectId ASC
)
go

/*==============================================================*/
/* Table: project */
/*==============================================================*/
create table project (
projectId int identity,
projectName varchar(30) null,
constraint PK_PROJECT primary key nonclustered (projectId)
)
go

alter table Employees
add constraint FK_EMPLOYEE_EMPLOYEES_SECTOR foreign key (sectorId)
references Sector (sectorId)
go

alter table Salary
add constraint FK_SALARY_EMPLOYEE__EMPLOYEE foreign key (employeeId)
references Employees (employeeId)
go

alter table Sector_Project
add constraint FK_SECTOR_P_M_PROJECT foreign key (projectId)
references project (projectId)
go

alter table Sector_Project
add constraint FK_SECTOR_P_N_SECTOR foreign key (sectorId)
references Sector (sectorId)
go



下面我们看看sql2005里的数据库关系图:





好了,现在我们建立一个hibernate工程。(步骤省略,这里实在太简单了,我们还是直接切入正题)



下面,我们进行数据库的反向工程,这样类和映射文件,以及dao(数据访问对象)都可以被创建好。我们根据映射文件来学习对象关联映射。



反向工程步骤:



1.先建立个数据库连接,使用sqlserver2005摸板,别忘了加数据库驱动的jar包,设置完后点击test driver按钮测试一下连接能否连通。




2.找到相应的库表:



3.开始反向工程:








这里Java src folder是你项目的源文件夹,Java package是你要生成实体和映射文件的地方。
然后点击下一步:




我们有多对多的表,所以这里要钩上Enable many-to-many detection ,然后直接点击完成即可。


然后回到我们的项目下,看看都生成了哪些东西呢?





我们可以看到,在com包生成了hibernateSessionFactory工具类,entity包里生成了实体类以及相应的配置文件,而且我们看到右边hibernate.cfg.xml配置文件已经加入了新建的实体映射文件。这个工具真的是很方便,减少了不少麻烦,至少目前为止,你一行代码都没写过呢!!!

在这里,我们还要在配置文件里新建个属性,以便于一会我们进行对象关联持久化的时候可以看到hibernate生成的语句。

我们在下面点击Add按钮新建个属性:

1.


2.


加入show_sql属性,并且赋值为true就可以了。


准备工作都做好了,下面我们开始学习对象关联映射:

一、一对一映射:

我们知道原先设计的时候,雇员和薪水表是一对一的关系,那么我们看看反向回来的Employees与Salary

首先是Employees的映射文件:Employees.hbm.xml




〈hibernate-mapping〉
〈class catalog="hibernate" schema="dbo" table="Employees" name="entity.Employees"〉
〈id name="employeeId" type="java.lang.Integer"〉
〈column name="employeeId"〉
〈generator class="native"〉
〈/id〉
〈many-to-one class="entity.Sector" name="sector" fetch="select"〉
〈column name="sectorId"/〉
〈/MANY-TO-ONE〉
〈property name="employeeName" type="java.lang.String"〉
〈column name="employeeName" length="30"〉
〈/property〉
〈property name="regTime" type="java.util.Date"〉
〈column name="regTime" length="23"〉
〈/property〉
〈set name="salaries" inverse="true"〉
〈key〉
〈column name="employeeId" not-null="true"〉
〈/key〉
〈one-to-many class="entity.Salary"〉
〈/set〉
〈/class〉
〈/HIBERNATE-MAPPING〉

恩,这里出现了2个不寻常的标签,一个是many-to-one,一个是set。many-to-one从字面上我们就可以理解为是多对一,那么雇员表和哪个表是多对一个关系呢?是的,与部门表是多对一个的关系。现在我们要研究的是一对一的关系,那么先不理它。看下面的set的标签,这又是为什么呢?因为set是一对多的一的那一端,我们想要一对一,所以这个标签也不符合。(因为一对一在表里表示出来的物理结构和一对多很像,所以工具就直接生成为一对多了,我们需要在这里改成一对一)那么,我们把set去掉,换成one-to-one标签。(改完后的样子如下:
〈one-to-one name="salaries" property-ref="employees"〉
)把set去掉了,我们不仅仅要改映射文件,我们还需要改实体类文件。原实体类文件如下:

package entity;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
* Employees entity.
*
* @author MyEclipse Persistence Tools
*/

public class Employees implements java.io.Serializable {

// Fields

private Integer employeeId;
private Sector sector;
private String employeeName;
private Date regTime;
private Set salaries = new HashSet(0);

// Constructors

/** default constructor */
public Employees() {
}

/** minimal constructor */
public Employees(Integer employeeId, Sector sector) {
this.employeeId = employeeId;
this.sector = sector;
}

/** full constructor */
public Employees(Integer employeeId, Sector sector, String employeeName,
Date regTime, Set salaries) {
this.employeeId = employeeId;
this.sector = sector;
this.employeeName = employeeName;
this.regTime = regTime;
this.salaries = salaries;
}

// Property accessors

public Integer getEmployeeId() {
return this.employeeId;
}

public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}

public Sector getSector() {
return this.sector;
}

public void setSector(Sector sector) {
this.sector = sector;
}

public String getEmployeeName() {
return this.employeeName;
}

public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}

public Date getRegTime() {
return this.regTime;
}

public void setRegTime(Date regTime) {
this.regTime = regTime;
}

public Set getSalaries() {
return this.salaries;
}

public void setSalaries(Set salaries) {
this.salaries = salaries;
}

}

因为去掉了set标签,又因为我们与薪水表是一对一的关系,薪水表的实体类是Salary,所以改之后的Employees实体类如下:

package entity;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
* Employees entity.
*
* @author MyEclipse Persistence Tools
*/

public class Employees implements java.io.Serializable {

// Fields

private Integer employeeId;
private Sector sector;
private String employeeName;
private Date regTime;
private Salary salaries;

// Constructors

/** default constructor */
public Employees() {
}

/** minimal constructor */
public Employees(Integer employeeId, Sector sector) {
this.employeeId = employeeId;
this.sector = sector;
}

/** full constructor */
public Employees(Integer employeeId, Sector sector, String employeeName,
Date regTime, Salary salaries) {
this.employeeId = employeeId;
this.sector = sector;
this.employeeName = employeeName;
this.regTime = regTime;
this.salaries = salaries;
}

// Property accessors

public Integer getEmployeeId() {
return this.employeeId;
}

public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}

public Sector getSector() {
return this.sector;
}

public void setSector(Sector sector) {
this.sector = sector;
}

public String getEmployeeName() {
return this.employeeName;
}

public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}

public Date getRegTime() {
return this.regTime;
}

public void setRegTime(Date regTime) {
this.regTime = regTime;
}

public Salary getSalaries() {
return this.salaries;
}

public void setSalaries(Salary salaries) {
this.salaries = salaries;
}

}

那么映射文件one-to-one标签里的属性都是什么意思呢?name是Employees类属性salaries的名字,说明对应的另一端类型是Salary,而property-ref属性的意思是另一端类属性(也就是Employees类型的属性,因为是一对一,那么薪水类Salary必然有一个人员类Employees的一个引用,这里是双向关联,那么property-ref填写的就是这个Salary类Employees的一个引用属性的名字)。

现在Employees类以及映射文件已经改好了,Salary不用改。
Salary的映射文件如下:

〈xml version="1.0" encoding="utf-8"?〉
〈!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"〉
〈!--
Mapping file autogenerated by MyEclipse Persistence Tools
--〉
〈hibernate-mapping〉
〈class name="entity.Salary" table="Salary" schema="dbo" catalog="hibernate"〉
〈id name="salaryId" type="java.lang.Integer"〉
〈column name="salaryId" /〉
〈generator class="native" /〉
〈/id〉
〈many-to-one name="employees" class="entity.Employees" fetch="select"〉
〈column name="employeeId" not-null="true" /〉
〈/many-to-one〉
〈property name="money" type="java.lang.Double"〉
〈column name="money" scale="4" /〉
〈/property〉
〈/class〉
〈/hibernate-mapping〉

Salary的实体类如下:

package entity;

/**
* Salary entity.
*
* @author MyEclipse Persistence Tools
*/

public class Salary implements java.io.Serializable {

// Fields

private Integer salaryId;
private Employees employees;
private Double money;

// Constructors

/** default constructor */
public Salary() {
}

/** minimal constructor */
public Salary(Integer salaryId, Employees employees) {
this.salaryId = salaryId;
this.employees = employees;
}

/** full constructor */
public Salary(Integer salaryId, Employees employees, Double money) {
this.salaryId = salaryId;
this.employees = employees;
this.money = money;
}

// Property accessors

public Integer getSalaryId() {
return this.salaryId;
}

public void setSalaryId(Integer salaryId) {
this.salaryId = salaryId;
}

public Employees getEmployees() {
return this.employees;
}

public void setEmployees(Employees employees) {
this.employees = employees;
}

public Double getMoney() {
return this.money;
}

public void setMoney(Double money) {
this.money = money;
}

}

在这里解释一下〈many-to-one name="employees" class="entity.Employees" fetch="select"〉
〈column name="employeeId" not-null="true" /〉
〈/many-to-one〉

不是一对一吗,怎么这里出现了多对一的标签呢?这是因为表结构里反映出来的,Employees和Salary的表逻辑上是一对一,但物理上Salary有一个外健直接指向Employees的主健,这跟逻辑上的一对多的物理表现形式是一样的,所以这里外表Salary是〈many-to-one〉而主表Employees是〈one-to-many〉(主健表维持一对一的关系)〈many-to-one〉的name属性是Salary类中引用Employees的属性名字,那么它对应的是表中的外建,所以下面的column就不用解释了。

我们下面就进行一对一的测试,我们写一个测试类:

import org.hibernate.Session;
import org.hibernate.Transaction;

import entity.Employees;
import entity.Salary;
public class test {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Session s=HibernateSessionFactory.getSession();
Transaction tx=s.beginTransaction();
try
{
Employees em=new Employees();
em.setEmployeeName("yangchao");
em.setRegTime(new java.util.Date());

Salary money=new Salary();
money.setMoney(10000.00);
money.setEmployees(em);

em.setSalaries(money);

s.save(em);
s.save(money);

tx.commit();


}
catch(Exception e)
{
tx.rollback();
e.printStackTrace();
}
finally
{
s.close();
}
}

}

运行后生成的hibernate语句为:

Hibernate: insert into hibernate.dbo.Employees (sectorId, employeeName, regTime) values (?, ?, ?)

Hibernate: insert into hibernate.dbo.Salary (employeeId, money) values (?, ?)

看看数据库里的结果:

Employees表:


为什么这里sectorId为null呢?这里说明一下,sectorId是部门表的主健,是本表的外健(本表与部门表是多对一的关系),由于我们只针对雇员表和薪水表进行一对一的数据插入,没有操作部门表,所以这里为null。


Salary表:

好了,一对一讲到这里相信大家都了解了吧!写了这么多,我BS一下google的博客,贴代码实在是太费劲了,它解析〈〉标签的,一旦有配置文件N多〈〉的,还得手改。。。贴图也不人性化。BS啊,BS!!

接下来,我们开始讲解一对多是如何实现的,这里用雇员表和部门表举实例,那么雇员表的映射文件如上,这里把针对一对多的片段配置公布如下(Sector与Employees是一对多关系):

Employees映射片段:

〈many-to-one name="sector" class="entity.Sector" fetch="select"〉

〈column name="sectorId" /〉

〈/many-to-one〉

Employees实体类片段:

private Sector sector;

这里解释一下,Sector部门与Employees雇员是一对多的关系,那么雇员类里必须有一个部门的引用,代表了这个雇员所属的部门。那么上面的映射文件代表了什么意思呢?因为是一对多关系,那么在Employees表里一定有一个外健引用Sector的主健,并且一对多的关系是由代表一的那一端,也就是Sector来维持的。上面配置里的name属性的值sector正好是Employees类引用Sector类的属性的名字。column标签里的name属性直接是Employees表中的外健名字。

清楚了Employees的实体类和映射文件后,我们来看一下一的这端Sector的映射文件和实体类:

Sector的映射文件:


〈?xml version="1.0" encoding="utf-8"?〉
〈!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"〉
〈!--
Mapping file autogenerated by MyEclipse Persistence Tools
--〉
〈hibernate-mapping〉
〈class name="entity.Sector" table="Sector" schema="dbo" catalog="hibernate"〉
〈id name="sectorId" type="java.lang.Integer"〉
〈column name="sectorId" /〉
〈generator class="native" /〉
〈/id〉
〈property name="sectorName" type="java.lang.String"〉
〈column name="sectorName" length="30" /〉
〈/property〉
〈set name="employeeses" inverse="true"〉
〈key〉
〈column name="sectorId" /〉
〈/key〉
〈one-to-many class="entity.Employees" /〉
〈/set〉
〈set name="projects" inverse="true" table="Sector_Project" schema="dbo" catalog="hibernate"〉
〈key〉
〈column name="sectorId" /〉
〈/key〉
〈many-to-many entity-name="entity.Project"〉
〈column name="projectId" /〉
〈/many-to-many〉
〈/set〉
〈/class〉
〈/hibernate-mapping〉

Sector的实体类:

package entity;

import java.util.HashSet;
import java.util.Set;

/**
* Sector entity.
*
* @author MyEclipse Persistence Tools
*/

public class Sector implements java.io.Serializable {

// Fields

private Integer sectorId;
private String sectorName;
private Set employeeses = new HashSet(0);
private Set projects = new HashSet(0);

// Constructors

/** default constructor */
public Sector() {
}

/** minimal constructor */
public Sector(Integer sectorId) {
this.sectorId = sectorId;
}

/** full constructor */
public Sector(Integer sectorId, String sectorName, Set employeeses,
Set projects) {
this.sectorId = sectorId;
this.sectorName = sectorName;
this.employeeses = employeeses;
this.projects = projects;
}

// Property accessors

public Integer getSectorId() {
return this.sectorId;
}

public void setSectorId(Integer sectorId) {
this.sectorId = sectorId;
}

public String getSectorName() {
return this.sectorName;
}

public void setSectorName(String sectorName) {
this.sectorName = sectorName;
}

public Set getEmployeeses() {
return this.employeeses;
}

public void setEmployeeses(Set employeeses) {
this.employeeses = employeeses;
}

public Set getProjects() {
return this.projects;
}

public void setProjects(Set projects) {
this.projects = projects;
}

}
我们来看这一段映射文件配置:

〈set name="employeeses" inverse="true"〉
〈key〉
〈column name="sectorId" /〉
〈/key〉
〈one-to-many class="entity.Employees" /〉
〈/set〉

set是什么呢?我们以前学过集合吧,恩,是的,这里代表集合。他对应的是Employees的集合,因为Sector与Employees是一对多的关系嘛,那么在一的这端Sector肯定有个集合来保存多个Employees。〈key〉标签表示了Employees表引用的是Sector表里sectorId主健。下面的〈one-to-many〉就很好理解了,说明了对应多的那一端类是什么。(后面还有个set,那是多对多的配置,现在先不管)

我们再写个测试类来针对Sector与Employees进行一对多的数据插入。

测试类片段如下:

Session s=HibernateSessionFactory.getSession();
Transaction tx=s.beginTransaction();
try
{
Sector se=new Sector(); //新建部门
se.setSectorName("北大青鸟学术部");

Employees em=new Employees();
em.setEmployeeName("coco");
em.setRegTime(new java.util.Date());

em.setSector(se); //设置引用的部门

Employees em1=new Employees();
em1.setEmployeeName("yoyo");
em1.setRegTime(new java.util.Date());

em1.setSector(se);//设置引用的部门

se.getEmployeeses().add(em);
se.getEmployeeses().add(em1);
s.save(se);
s.save(em);
s.save(em1);
tx.commit();
}
catch(Exception e)
{
tx.rollback();
e.printStackTrace();
}
finally
{
s.close();
}

我们再来看一下Employees表中的内容:




再看一下Sector表中的内容:


hibernate生成的sql语句:

Hibernate: insert into hibernate.dbo.Sector (sectorName) values (?)
Hibernate: insert into hibernate.dbo.Employees (sectorId, employeeName, regTime) values (?, ?, ?)
Hibernate: insert into hibernate.dbo.Employees (sectorId, employeeName, regTime) values (?, ?, ?)


最后一个内容,虽然理解上复杂点,但实际配置起来也挺容易的。我们再加把劲,把这最后的多对多映射给搞定。

我们通过概念模型了解到了Sector部门表与Project项目表是多对多的关系,这是逻辑上的,实际上我们在数据库里表示多对多关系的时候,都是以两个一对多的物理结构来体现的。还记得刚才讲一对多时Sector映射文件里被忽略掉的最后一个〈set〉标签吗?现在该它登场了!!

Sector的映射文件片段:

〈set name="projects" inverse="true" table="Sector_Project" schema="dbo" catalog="hibernate"〉
〈key〉
〈column name="sectorId" /〉
〈/key〉
〈many-to-many entity-name="entity.Project"〉
〈column name="projectId" /〉
〈/many-to-many〉
〈/set〉

这个就是多对多的一端的配置。我们刚才已经知道了多对多关系在数据库里表现为两个一对多。那么Sector_Project表就是Sector和Project的中间表,我们来看一下Sector_Project表的结构:


这里体现出来什么?两个一对多,也就是Sector表与Sector_Project表是一对多,Project表与Sector_Project表是一对多,Sector_Project的2个字段各是Sector表,Project表主健的外健。
这样同学们就应该理解了吧。在理解的基础之上看上面的配置,是不是一目了然了呢?大家可以再参考一下我们教材<>hibernate关系映射章节的图例以及解释.我就不在这里阐述过多的概念了,我们还是以实例做导向.

那么下面看一下Sector类的片段代码:

private Set projects = new HashSet(0);

这里大家就清楚了吧,这个集合是保存对应的多个Project,那么聪明的你们也一定会想到,既然是多对多,那么Project类里也有个集合是保存多个Sector的,是的,你们真是太聪明了,的确如此啊!!

趁热打铁,我们来先看一下Project实体代码:

package entity;

import java.util.HashSet;
import java.util.Set;

/**
* Project entity.
*
* @author MyEclipse Persistence Tools
*/

public class Project implements java.io.Serializable {

// Fields

private Integer projectId;
private String projectName;
private Set sectors = new HashSet(0);

// Constructors

/** default constructor */
public Project() {
}

/** minimal constructor */
public Project(Integer projectId) {
this.projectId = projectId;
}

/** full constructor */
public Project(Integer projectId, String projectName, Set sectors) {
this.projectId = projectId;
this.projectName = projectName;
this.sectors = sectors;
}

// Property accessors

public Integer getProjectId() {
return this.projectId;
}

public void setProjectId(Integer projectId) {
this.projectId = projectId;
}

public String getProjectName() {
return this.projectName;
}

public void setProjectName(String projectName) {
this.projectName = projectName;
}

public Set getSectors() {
return this.sectors;
}

public void setSectors(Set sectors) {
this.sectors = sectors;
}

}

果然有吧,呵呵.

那么接下来看看Project的映射文件:

〈?xml version="1.0" encoding="utf-8"?〉
〈!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"〉
〈!--
Mapping file autogenerated by MyEclipse Persistence Tools
--〉
〈hibernate-mapping〉
〈class name="entity.Project" table="project" schema="dbo" catalog="hibernate"〉
〈id name="projectId" type="java.lang.Integer"〉
〈column name="projectId" /〉
〈generator class="native" /〉
〈/id〉
〈property name="projectName" type="java.lang.String"〉
〈column name="projectName" length="30" /〉
〈/property〉
〈set name="sectors" table="Sector_Project" schema="dbo" catalog="hibernate"〉
〈key〉
〈column name="projectId" /〉
〈/key〉
〈many-to-many entity-name="entity.Sector"〉
〈column name="sectorId" /〉
〈/many-to-many〉
〈/set〉
〈/class〉
〈/hibernate-mapping〉

聪明的你们一定可以对照Sector类映射文件中的多对多配置来理解Project类映射文件中的多对多配置,好的,我满足你们的愿望,把机会留给你们。我们还是来写个测试类来插入多对多数据:

Session s=HibernateSessionFactory.getSession();
Transaction tx=s.beginTransaction();
try
{
Sector se=new Sector(); //新建部门
se.setSectorName("北大青鸟教学质量部");

Sector se1=new Sector();
se1.setSectorName("北大青鸟研发部");

Project pro=new Project();
pro.setProjectName("S1课程");

Project pro2=new Project(); //新建项目
pro2.setProjectName("S2课程");

Project pro3=new Project();
pro3.setProjectName("Y2课程");

/**
* 假想:北大青鸟教学质量部负责s1,s2,y2课程,北
* 大青鸟研发部负责s2,y2课程
*/
se.getProjects().add(pro);
se.getProjects().add(pro2);
se.getProjects().add(pro3);


se1.getProjects().add(pro2);
se1.getProjects().add(pro3);

/**
* 因为互相引用的关系,所以单方面设置的话,中间表中无数据
*/

pro.getSectors().add(se);
pro2.getSectors().add(se);
pro2.getSectors().add(se1);
pro3.getSectors().add(se);
pro3.getSectors().add(se1);



s.save(pro);
s.save(pro2);
s.save(pro3);
s.save(se);
s.save(se1);

tx.commit();
}
catch(Exception e)
{
tx.rollback();
e.printStackTrace();
}
finally
{
s.close();
}
}


Sector表的结果:



Project表的结果:



中间表Sector_Project的结果:



hibernate生成的sql语句:
Hibernate: insert into hibernate.dbo.project (projectName) values (?)
Hibernate: insert into hibernate.dbo.project (projectName) values (?)
Hibernate: insert into hibernate.dbo.project (projectName) values (?)
Hibernate: insert into hibernate.dbo.Sector (sectorName) values (?)
Hibernate: insert into hibernate.dbo.Sector (sectorName) values (?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)


到此,一对一,一对多,多对多的双向关联映射都全部讲完了。同学们,我希望你们回家按照此文内容多多练习,掌握它其实真的非常简单。如果你们遇到任何问题,到一办找我问就可以。因为此博客超烂,所以不排除有某些代码贴遗漏了,你们可以找我要脚本和示例代码。最后,还是希望大家有效的利用时间来学习,自律才能成功!