精华内容
下载资源
问答
  • 使用传统的系统分析和设计方法作为框架,以强调在管理大型传统计算机项目和大型专家系统中的异同。 作为非技术性的说明性指南,本文着重于在项目的每个阶段进行定义,要完成的任务,所需的资源,对组织的影响,可能...
  • 基于我们在开发和应用框架方面的经验,我们提供了一个案例研究,以说明创建CTSII时出现的问题以及如何解决这些问题。 该框架显示为具有六列的菜单,每列代表框架的一个维度。 可以将每个维度内的类别与列之间的连接...
  • 采用基于框架表示法的结构化技术表达静态预案,并基于关系模型设计预案库结构和索引。同时将基于案例推理方法和最近邻法相结合,通过计算突发事件与预案的相似度实现最优相似预案的搜索。
  • 研究中建议的框架被证明是评估虚拟大学和机构中的虚拟课程内容的合适工具。 该框架可以从质量、策略、标准和教学设计四个方面评估任何虚拟课程内容。 这项研究有助于虚拟学习服务提供商和电子内容开发商识别内容...
  • 目的:本文的目的是在低偿保险支付... 设计/方法/方法:这是本地的Obs&Gyn当地诊所定性案例研究。 从诊所观察并记录了所有观察现象。 采用平衡计分卡,对蓝海战略的新见解和7的麦肯锡理论为诊所管理制定了动态策略。
  • 文章目录基于《悉尼协议》框架下Java课程案例教学研究一、Java课程教学存在问题(一)Java课程目标定位不足(二)Java课程教学存在的问题1. 教材内容更新滞后2. 学习目标及路径模糊3. 学生学习兴趣不强4. 学生动手...

    基于《悉尼协议》框架下Java课程案例教学研究

    (泸州职业技术学院,信息工程学院 646000)

    摘要:《悉尼协议》对我国高等职业教育有着重要借鉴价值,应用《悉尼协议》框架,对Java课程实施与专业、生活和应用相结合的案例教学,重构教学内容,改革教学模式。实践证明,可以提高学生Java课程学习兴趣,更好地培养应用型与技能型人才。

    关键词: 悉尼协议;Java课程;案例教学;教学模式改革

    一、Java课程教学存在问题

    (一)Java课程目标定位不足

    根据软件专业培养目标确定《Java高级程序设计》课程目标。作为软件技术专业的专业核心课程,本课程涉及Java基本语法、控制结构、面向对象编程、异常处理、输入输出与文件操作,JDBC数据库编程、图形用户界面编程等内容。通过本课程学习,学生能够使用面向对象编程技术与数据库编程技术,独立开发符合Java编码规范的应用程序,遇到错误会进行调试,能够发现、分析问题并解决问题,能够查找、阅读 、理解相关学习资料,并加以应用,进一步培养自学能力,为软件技术相关专业后续课程的学习奠定比较坚实的基础。按照《悉尼协议》课程建设要求,课程目标在“成果为导向、学生为中心、继续改进”方面还体现得不够充分,尚有待改进。

    (二)Java课程教学存在的问题

    课程知识点多,学院目前只安排64课时,为了完成教学任务,教学进度比较快,往往是以知识点讲解为主,学生认为课堂主要在灌输知识,而不是引导他们通过实践掌握编程技能,因此学生学习兴趣与积极性不高;另外,案例或项目与应用、专业和生活的结合不够充分,实用性欠缺直接影响了Java课程教学效果。

    1. 教材内容更新滞后

    Java技术不断更新,新标准与新技术不断涌现,教学内容也必须不断更新,才能跟上Java技术的发展,但是目前教材内容更新慢,往往滞后于Java技术标准的发展。一般来说,教材从构思、编写、审核到出版需要一定时间,因此最新出版的教材与最近的技术之前往往有一年左右的滞后期。

    2. 学习目标及路径模糊

    Java课程并非一门孤立的编程课,以往教学局限在Java知识与技能,没有给学生指明今后进一步的学习方向。由于部分Java授课教师没有Java后续课程的授课经验与实际项目开发的经验,面对Java庞杂的技术体系,无法给学生进一步学习推荐有效的参考书籍和学习资料,甚至无法给学生绘制明确的学习路线图。

    3. 学生学习兴趣不强

    Java课程实践性比较强,但是参考教材都是以知识点为线索,偏重讲解基础理论知识,实验内容大多是验证型任务,与生活实际应用脱节现象比较严重,学生学起来感觉枯燥乏味,从而在学习过程中逐渐失去兴趣。没有兴趣支撑,只是被动的学习,导致最终Java学习效果不理想。

    4. 学生动手能力不强

    目前大多院校,采用课堂讲课方式,以理论知识讲述为主,辅以少量的上机练习,上机时间安排不充裕,学生在有限的上机时间内,主要是参考教师给出的代码,验证课堂教学案例与巩固型习题,以加深对知识点的理解。课后学生没有下功夫独立完成具有挑战性的任务,编程实践量不够,无法形成比较完整的知识体系,动手能力也严重不足。经过实际考察,发现在课程结束后,能够独立完成课堂教学案例之外新任务的学生人数并不多,远远没有达到预期的课程教学效果。

    二、改革Java课程教学举措

    (一)《悉尼协议》内涵

    《悉尼协议》主要针对接受三年制高等教育培养的“工程技术专家”认证,与我国高等职业教育相对应和衔接。《悉尼协议》有一套严格完整的认证标准,包括培养目标、学生、毕业要求、课程体系、师资队伍、持续改进、毕业生跟踪反馈及社会评价等。虽然目前我国还未加入该协议,但是全面加入国际工程教育认证、走国际化道路,是高职教育的发展趋势和必经之路。所以, 《悉尼协议》对于我国高等职业教育具有非常重要的参考意义和借鉴价值。

    “悉尼协议”注重需求达成,注重目标和结果;而现有专业建设大多是不做详细、深入的专业调研,以自我为中心进行专业建设,或照抄照搬别人的经验和模式,或在旧有内容上套一张新奇的皮,新瓶装旧酒,并不注重最终是否达成目标和需求。“悉尼协议”引入多方协商、利益平衡机制,用人单位、合作企业、行业专家、学生、教师等都是参与者;而现有专业建设往往以“岗位”、“技能”、“工作”为重心,没有真正考核利益各方的真实需求。“悉尼协议”遵循教育教学规律,注重循序渐进,注重学生可持续发展,注重专业建设螺旋式上升,注重持续改进;而现有专业建设往以“学科为中心”、“教师为中心”或“学校为中心”,互相孤立,并不能很好地以“学生为中心”进行协调和合作。

    针对Java课程教学存在的问题,为更好适应不断发展的教育理念和高职教育“走出去”的要求,必须打破传统思维,对Java课程进行改革。改进案例教学,创新教学模式,使Java课更多地与应用相结合、与专业相结合、与生活相结合,利用新媒体条件,使学生从“学Java”到“做Java”再到“用Java”。从教学模式、教学内容、教学方法、教学手段甚至教学评价等方面,进行改革创新,这样不仅可以激发学生对Java课程甚至专业课程的兴趣,更好完成Java课程教学任务,而且可以紧跟我校教育理念,与高职教育培养目标和我校人才培养理念相一致,与《悉尼协议》要求相一致,培养出适应社会发展的应用型人才。

    (二)学科交叉达成教学创新

    素质教育的核心是创新,没有创新就没有发展和进步。宇宙是相互联系的统一体,各学科虽然研究某一方面的现象和规律,但毕竟是相互联系的,因此,教学活动也应该注重同类学科或异类学科之间的交叉。

    1. 注重同类学科的交叉

    讲解Java程序设计,应把它放在同类学科里,进行横向延伸和纵向延伸。横向延伸是指通过Java与C、Python等编程语言的对比,让学生更好了解Java的优点与局限性;纵向延伸是指在Java应用系统开发中引入后继课程《企业级信息系统设计与开发》里涉及到软件工程的基本概念,如分析、设计、编码、调试和维护等基本术语与方法,引导部分学有余力的优秀学生在软件开发方面进行更深入地学习与实践。实施同类学科的交叉,是为了把Java编程放在一个较大的背景下,拓宽学生的思维视野,了解专业学习的路线图,更好激发学生进一步学习专业知识的积极性。

    2. 兼顾异类学科的交叉

    编程与写文章有相通之处,孔子云:“言而无文,行之不远”,足见文采对文章的重要性。一篇文章有文采而缺乏义理,则徒有其表,华而不实,又岂能震撼人的灵魂。义理相当于程序功能,是整个程序的核心和关键部分,表明程序能帮助用户解决什么实际问题;词章相当于程序界面,界面是否友好,操作是否简单,直接影响程序是否容易被用户接受和喜欢。

    在编程中,界面设计应满足用户审美需求,功能设计离不开逻辑分析与架构,重点在于功能的增强和完善。从审美角度反复修改用户界面,力求外形美观,操作方便;反复修改完善系统功能,帮助用户更有效地处理实际工作中面临的问题。编程和写文章一样,需要谋篇布局、遣词造句,精益求精,这是一个追求完美的过程,总之,编程和写文章具有异曲同工之妙,编程教学立足于文理科的交叉,让学生不仅得到理科的严谨训练,同时也得到文科的审美熏陶。

    在Java教学中,不应局限在书本知识,而应该以Java为中心,让学生在编程实践中接触到更多学科的知识,培养他们综合应用知识去分析和解决问题的能力。思维拓展和能力培养,比知识传授或技能培养对学生的前途更重要,Java授课教师应该思考,如何加强对学生思维与能力的培养。

    (二)改革教材结构

    传统编程教材,先理论后应用,先一般后特殊,先抽象后具体,这种编排方式,不符合认识论基本规律,也不符合学生学习的基本规律,对学生不友好,因此难以激发学生进一步学习兴趣。学生对教材的抵触情绪,应该引起教材编写者的思考。

    适合学生学习的教材,应该以问题为中心,从具体到抽象,从特殊到一般,激发学生思考,在分析和解决问题的过程中自然引出相关的理论知识,再利用所学的理论知识,去解决新的问题。从问题到理论,这个过程主要是归纳推理,而从理论到应用,这个过程主要是演绎推理。适合学生学习的教材,应该通过合理的编排方式,把两个过程“归纳推理”和“演绎推理”结合起来,构成一个有机的整体。

    目前Java编程教材,在这方面存在比较明显的缺陷。为了保证良好教学效果,编写一个适合学生学习的教材是必要的,教材编写要充分体现对学生“归纳推理”和“演绎推理”同时并重的指导思想。

    (四)系统重构教学内容

    教师不能拘泥于教材,不能完全按教材的顺序来处理。教师应对本课程有总体把握,对知识间内在联系有清晰的领悟,这样才能在实际教学中,根据学生实际情况,大胆对教材进行适当增删和重组,编写出适合学生具体情况、针对性强的教案。教材跟教案相互联系,各有其用,不能相互替代;同一个教材,可在多个班使用,但是教案必须体现出个性,体现出每个班不同的具体情况,体现出教师与学生互动之后的思考与调整。

    教材体现编著者对于专业知识体系的构思,而教案却生动体现教师对于教学活动的构思,如果教案仅仅是把教材内容提纲挈领地简化,而没有体现教师针对不同班级教学活动的构思,这种教案是缺乏实用价值的。教案是教师把教材和学生结合起来进行互动的一种构思,它体现了教师如何因人而异、灵活地处理教材的教学思想。要切实提高教学效果,作为一名合格的编程教师,必须注重教案或讲稿的编写工作。讲稿根据一次又一次的教学活动,不断地更新和完善,为今后编写适合学生学习或自学的教材奠定良好的基础。

    编写电子教案,或者多媒体教学课件,通过人机互动,实现学生自主学习。但是,人机互动总是有局限性,课件制作并不能充分考虑到学习者千差万别的具体情况,不能完全满足各类各层次学习者的需要,此时,师生之间的互动,就可以发挥更好的指导作用。悉尼协议强调成果导向,以学生为本,离开师生有效的互动,是无法实现这个目标的。

    尝试“1+1”总体教学安排,打破常规教学进度,具有一定的风险性。大概这样安排:前半期,提纲挈领地把整门课程基本知识点简单讲完,当然要有学生的密切配合,必须课前预习与课后复习双管齐下,才能跟上教学进度,这无疑对学生有很大的挑战性,只要做好思想动员工作,学生愿意通过努力实现预定目标;后半期,指导学生开发一至两个Java数据库应用系统,例如学生信息管理系统,工资管理系统。通过实战,把这门课程的理论知识融会贯通以解决系统开发中遇到的各种具体问题,让学生从理论到实践,再由实践到理论,把理论和实践结合起来,了解项目开发的流程,培养系统开发的技能。提高学生编程技能和项目开发能力,是这门课程教学的根本目的。只要有利于实现这个根本目的,那么这样的教学安排就是成功的。

    (五)注重学生品质培养

    Java教学有三个层次的目标。第一、知识层次:让学生了解和掌握Java编程基本知识,了解应用系统开发一般流程;第二、技能层次:通过实践培养学生熟练编写Java应用程序的基本技能;第三、品质层次:在编程实践中,培养学生严密的逻辑思维,细致耐心的品质,以及团队的协作精神。知识层次和技能层次固然重要,但品质层次更重要。在品质方面得到的培养,严密的逻辑思维,细致耐心的品质,以及团队的协作精神,将让学生受益一生。无论今后他们从事任何工作,这些品质都是必要的,有助于他们更好地与人协作,更有效地完成项目中分配的工作。

    三、基于应用的Java课程改革案例

    (一)确定教学内容——字符串常用操作

    在实际开发中经常会使用字符串,由许多单个字符连接而成。字符串可包含任意字符,但这些字符必须包含在一对英文双引号之内,比如"love"、“泸州@泸职院·信息工程学院 2020”。Java中定义了String和StringBuffer两个类来封装字符串,并提供了一系列操作字符串的方法。以字符串String常用操作方法为例说明。
    在这里插入图片描述

    图1 String常用操作方法思维导图


    以任务来驱动,以问题为中心,调动学生参与解决问题的积极性,在引导学生解决问题完成任务的过程中,选用适当的String类的操作方法,让学生不仅知道如何使用这些方法,而且明白了这些方法的适用场景,从而加深对这些操作方法的理解与掌握。

    (二)精心设计教学环节,融入课程思政

    在这里插入图片描述

    图2 教学环节融入课程思政

    1. 课前部分

    发布课前自学任务,让学生在超星学习通在线课程《Java高级程序设计》上找到相应章节的课件PPT与教学讲稿,预习字符串常用操作方法这一部分内容,并完成简单测试,教师可以从反馈结果了解学生课前预习的情况。

    2. 课中部分

    (1)引入

    数据清洗是大数据领域不可缺少的环节,用来发现并纠正数据中可能存在的错误,针对数据审查过程中发现的错误值、缺失值、异常值、可疑数据,选用适当方法进行“清理”,使“脏”数据变为“干净”数据。数据清洗是进行数据分析的前提。在Java课程中,案例涉及数据清洗,这是与专业和应用结合的一种体现,既让学生体会到字符串操作的实用价值,也提前了解了本专业后续课程的一些知识。

    解释网址清洗任务:网址中包含垃圾字符,可能是在网络传输过程中无意中添加的,也可能是为了安全考虑故意插入的。无论是什么原因引起的,现在都需要清除掉垃圾字符,得到有效的网址。

    比如ht%$tp:%$$%##%/ / w%##%w%w. %%$$%##%16%$$3.c##om,包含的垃圾字符有空格、%、$与#,需要清洗掉,变成规范的网址:http://www.163.com

    (2)实施

    通过分析,让学生理解“清洗垃圾字符”的含义,引导学生通过思考形成解决问题的思路,并且引导学生采用不同的方法来解决同一个问题,培养学生一题多解的能力。方法一、采用循环结构过滤垃圾字符,方法二、采用字符串类的replace()方法,方法三、采用字符串类的split()方法。教师讲解各种方法的思路,以及replace()方法与split()方法的使用,给出程序运行结果截图,具体任务交给学生自主完成,当然对学生具有一定的挑战性。虽然不是所有学生都能顺利完成,但是每个学生通过努力可以完成一部分,对于他们而言也是有一定的成就感。如果完全让学生照着教师给的代码敲一遍,进行验证性的学习,就没有真正体现以学生为本的理念,学生的思考能力就得不到锻炼。
    在这里插入图片描述

    图3 程序运行结果截图

    (3)检查

    教师检查学生任务完成情况,发现学生存在的主要问题,以及没有完成任务的学生,主要是哪些环节尚未掌握。

    (4)评价

    根据学生任务完成情况,给予学生一个反馈与评价。没有反馈的学习,是无效的学习。无论学生完成情况如何,他们都有一定的思考,此时再听教师的评讲,对比教师给出的示例代码与自己所写的代码,可以加深对相关知识点的理解,也容易搞明白自己究竟在哪个环节出了问题,有针对性地去解决具体问题。

    3. 课后部分

    提出拓展任务:待清洗的网址有很多个,保存到一个文件里,清洗完所有网址,生成一个新的文件。教师可以给出解决问题的思路,引导学生课后通过主动思考与尝试,完成拓展任务,动手能力得到更进一步的锻炼和提高。

    四、结语

    Java案例教学改革,与专业和应用相结合,以问题或任务驱动,能够更好整合零散的Java知识点,激发学生学Java与用Java的积极性,在引入和讲解案例的过程中,重点阐述解决问题的思路,再通过思考寻找合适的Java工具或方法来解决问题,实践证明,这种教学模式对于提高学生的学习兴趣与主动思考具有良好的促进作用,从学生评教结果和学期总结可以看出学生对于案例教学的理解与认可。更进一步,可以将企业实际项目改造成贯穿式教学案例,将案例分解成若干模块,每个模块又继续分解成若干个任务,任务会涉及到相应章节若干知识点,这样以项目为核心来重构整个课程,当课程结束,整个贯穿式案例也就完成,学生最终得到的不是零碎的知识点,而是一个项目从头到尾的完整实现。

    参考文献:

    [1] 悉尼协议解析[EB/OL].http://xnxyyjy.cn/html/guojijiegui/xinixieyi/650.Html.2017-11-02
    [2] 周晓龙,张忠伟,孙伟清.《悉尼协议》对我国高职工科专业群建设的启示[J]. 教育与职业. 2018(16): 97-100
    [3] 朱冬玲. 基于目标达成度的软件课程诊断与改进——以《Java语言程序设计》为例[J]. 电脑与电信. 2019 : 69-72
    [4] 王向华. 基于《悉尼协议》的软件专业课程改革策略研究[J]. 天津职业大学学报. 2018 : 30-33
    [5] 张燕乐. 大数据时代Java课程改革研究[J]. 《信息与电脑(理论版)》.2017(09): 259-260

    展开全文
  • 介绍了框架的概念、特征、应用案例及其在实验技能自动测评系统中的应用思路,并给出了实验技能自动测评系统框架设计与实现的技术路线。
  • 为了在瞬息万变的业务环境中获得竞争优势,公司除了在机构方面还应与客户和供应商合作,以提高运营效率,并相互合作以在服务和服务提供... 作为案例研究,使用此模型可以改善每个制造组织(如塞班岛公司)的现有状况。
  • 研究的主要目的是为大神银行设计云计算架构,这将减少管理 IT 基础设施和系统的人力需求和通过所需的安全验证来增强技术的使用。该研究侧重于云计算的设计方面。 该研究使用访谈、观察和文件分析来收集数据。 采访...
  • 本文是中英双语对照毕业设计论文外文文献翻译下载之后直接可用省去您找文献pdf整理成word以及翻译的时间一辈子也就一次的事 文献作者出处:John Arthur,Shiva Azadegan Proceedings of the Sixth International ...
  • 介绍了框架的概念、特征、应用案例及其在实验技能自动测评系统中的应用思路,并给出了实验技能自动测评系统框架设计与实现的技术路线。
  • 重点放在UI(动画,布局,过渡等)上,并应展示Flutter作为UI框架的功能。 入门 要在手机上安装该应用程序,您有两种选择。 对于Android,您可以从标签下载预编译的APK文件,或使用此。 您也可以在本地下载并构建...
  • MyBatis这是现在很流行ORM框架,这是非常强大。事实上现却比較简单、优雅。本文主要讲述MyBatis的架构设计思路,而且讨论...一、MyBatis的框架设计 注:上图非常大程度上參考了iteye 上的chenjc_it 所写...

            MyBatis这是现在很流行ORM框架,这是非常强大。事实上现却比較简单、优雅。

    本文主要讲述MyBatis的架构设计思路,而且讨论MyBatis的几个核心部件。然后结合一个select查询实例。深入代码,来探究MyBatis的实现。

    一、MyBatis的框架设计

            注:上图非常大程度上參考了iteye 上的chenjc_it   所写的博文原理分析之二:框架总体设计 中的MyBatis架构体图,chenjc_it总结的很好,赞一个!

    1.接口层---和数据库交互的方式

    MyBatis和数据库的交互有两种方式:

    a.使用传统的MyBatis提供的API;

    b. 使用Mapper接口

        1.1.使用传统的MyBatis提供的API

          这是传统的传递Statement Id 和查询參数给 SqlSession 对象。使用 SqlSession对象完毕和数据库的交互;MyBatis 提供了很方便和简单的API,供用户实现对数据库的增删改查数据操作,以及对数据库连接信息和MyBatis 自身配置信息的维护操作。

                      

          上述使用MyBatis 的方法,是创建一个和数据库打交道的SqlSession对象,然后依据Statement Id 和參数来操作数据库。这样的方式固然非常easy和有用。可是它不符合面向对象语言的概念和面向接口编程的编程习惯。因为面向接口的编程是面向对象的大趋势,MyBatis 为了适应这一趋势。添加了另外一种使用MyBatis 支持接口(Interface)调用方式。

    1.2. 使用Mapper接口

     MyBatis 将配置文件里的每个<mapper> 节点抽象为一个 Mapper 接口,而这个接口中声明的方法和跟<mapper> 节点中的<select|update|delete|insert> 节点项相应,即<select|update|delete|insert> 节点的id值为Mapper 接口中的方法名称,parameterType 值表示Mapper 相应方法的入參类型,而resultMap 值则相应了Mapper 接口表示的返回值类型或者返回结果集的元素类型。

     依据MyBatis 的配置规范配置好后,通过SqlSession.getMapper(XXXMapper.class) 方法。MyBatis 会依据对应的接口声明的方法信息。通过动态代理机制生成一个Mapper 实例,我们使用Mapper 接口的某一个方法时,MyBatis 会依据这种方法的方法名和參数类型。确定Statement Id,底层还是通过SqlSession.select("statementId",parameterObject);或者SqlSession.update("statementId",parameterObject); 等等来实现对数据库的操作。(至于这里的动态机制是如何实现的,我将准备专门一片文章来讨论。敬请关注~

    MyBatis 引用Mapper 接口这样的调用方式。纯粹是为了满足面向接口编程的须要。

    (事实上另一个原因是在于,面向接口的编程。使得用户在接口上能够使用注解来配置SQL语句,这样就能够脱离XML配置文件,实现“0配置”)。

    2.数据处理层

          数据处理层能够说是MyBatis 的核心,从大的方面上讲,它要完毕三个功能:

    a. 通过传入參数构建动态SQL语句;

    b. SQL语句的运行以及封装查询结果集成List<E>

         2.1.參数映射和动态SQL语句生成

           动态语句生成能够说是MyBatis框架很优雅的一个设计,MyBatis 通过传入的參数值,使用 Ognl 来动态地构造SQL语句,使得MyBatis 有非常强的灵活性和扩展性。

    參数映射指的是对于java 数据类型和jdbc数据类型之间的转换:这里有包含两个过程:查询阶段,我们要将java类型的数据。转换成jdbc类型的数据,通过 preparedStatement.setXXX() 来设值;还有一个就是对resultset查询结果集的jdbcType 数据转换成java 数据类型。

    至于详细的MyBatis是怎样动态构建SQL语句的,我将准备专门一篇文章来讨论。敬请关注~

         2.2. SQL语句的运行以及封装查询结果集成List<E>

                  动态SQL语句生成之后,MyBatis 将运行SQL语句,并将可能返回的结果集转换成List<E> 列表。MyBatis 在对结果集的处理中。支持结果集关系一对多和多对一的转换,而且有两种支持方式,一种为嵌套查询语句的查询,另一种是嵌套结果集的查询。

    3. 框架支撑层

         3.1. 事务管理机制

              事务管理机制对于ORM框架而言是必不可少的一部分。事务管理机制的质量也是考量一个ORM框架是否优秀的一个标准,对于数据管理机制我已经在我的博文《深入理解mybatis原理》 MyBatis事务管理机制 中有很具体的讨论,感兴趣的读者能够点击查看。

        3.2. 连接池管理机制

    因为创建一个数据库连接所占用的资源比較大。 对于数据吞吐量大和訪问量很大的应用而言,连接池的设计就显得很重要,对于连接池管理机制我已经在我的博文《深入理解mybatis原理》 Mybatis数据源与连接池 中有很具体的讨论,感兴趣的读者能够点击查看。

       3.3. 缓存机制

    为了提高数据利用率和减小server和数据库的压力。MyBatis 会对于一些查询提供会话级别的数据缓存,会将对某一次查询,放置到SqlSession 中,在同意的时间间隔内。对于全然同样的查询,MyBatis 会直接将缓存结果返回给用户,而不用再到数据库中查找。

    至于详细的MyBatis缓存机制,我将准备专门一篇文章来讨论,敬请关注~

      3. 4. SQL语句的配置方式

    传统的MyBatis 配置SQL 语句方式就是使用XML文件进行配置的,可是这样的方式不能非常好地支持面向接口编程的理念,为了支持面向接口的编程,MyBatis 引入了Mapper接口的概念。面向接口的引入,对使用注解来配置SQL 语句成为可能。用户仅仅须要在接口上加入必要的注解就可以。不用再去配置XML文件了,可是,眼下的MyBatis 仅仅是对注解配置SQL 语句提供了有限的支持,某些高级功能还是要依赖XML配置文件配置SQL 语句。


    4 引导层

    引导层是配置和启动MyBatis 配置信息的方式。MyBatis 提供两种方式来引导MyBatis :基于XML配置文件的方式和基于Java API 的方式,读者能够參考我的还有一片博文:Java Persistence with MyBatis 3(中文版) 第二章 引导MyBatis

       

    二、MyBatis的主要构件及其相互关系

      从MyBatis代码实现的角度来看,MyBatis的基本的核心部件有下面几个:

    • SqlSession            作为MyBatis工作的主要顶层API,表示和数据库交互的会话。完毕必要数据库增删改查功能
    • Executor              MyBatis运行器。是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
    • StatementHandler   封装了JDBC Statement操作。负责对JDBC statement 的操作。如设置參数、将Statement结果集转换成List集合。
    • ParameterHandler   负责对用户传递的參数转换成JDBC Statement 所须要的參数。
    • ResultSetHandler    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
    • TypeHandler          负责java数据类型和jdbc数据类型之间的映射和转换
    • MappedStatement   MappedStatement维护了一条<select|update|delete|insert>节点的封装,
    • SqlSource            负责依据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
    • BoundSql             表示动态生成的SQL语句以及对应的參数信息
    • Configuration        MyBatis全部的配置信息都维持在Configuration对象之中。

    注:这里仅仅是列出了我个人觉得属于核心的部件,请读者不要先入为主,觉得MyBatis就仅仅有这些部件哦!每一个人对MyBatis的理解不同,分析出的结果自然会有所不同。欢迎读者提出质疑和不同的意见。我们共同探讨~)

    它们的关系例如以下图所看到的:




    三、从MyBatis一次select 查询语句来分析MyBatis的架构设计

    一、数据准备(很熟悉和应用过MyBatis 的读者能够迅速浏览此节就可以)

         1. 准备数据库数据,创建EMPLOYEES表,插入数据:      

       
       --创建一个员工基本信息表
        create  table "EMPLOYEES"(
            "EMPLOYEE_ID" NUMBER(6) not null,
           "FIRST_NAME" VARCHAR2(20),
           "LAST_NAME" VARCHAR2(25) not null,
           "EMAIL" VARCHAR2(25) not null unique,
           "SALARY" NUMBER(8,2),
            constraint "EMP_EMP_ID_PK" primary key ("EMPLOYEE_ID")
        );
        comment on table EMPLOYEES is '员工信息表';
        comment on column EMPLOYEES.EMPLOYEE_ID is '员工id';
        comment on column EMPLOYEES.FIRST_NAME is 'first name';
        comment on column EMPLOYEES.LAST_NAME is 'last name';
        comment on column EMPLOYEES.EMAIL is 'email address';
        comment on column EMPLOYEES.SALARY is 'salary';
        
        --加入数据
    	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
    	values (100, 'Steven', 'King', 'SKING', 24000.00);
    	
    	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
    	values (101, 'Neena', 'Kochhar', 'NKOCHHAR', 17000.00);
    	
    	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
    	values (102, 'Lex', 'De Haan', 'LDEHAAN', 17000.00);
    	
    	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
    	values (103, 'Alexander', 'Hunold', 'AHUNOLD', 9000.00);
    	
    	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
    	values (104, 'Bruce', 'Ernst', 'BERNST', 6000.00);
    	
    	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
    	values (105, 'David', 'Austin', 'DAUSTIN', 4800.00);
    	
    	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
    	values (106, 'Valli', 'Pataballa', 'VPATABAL', 4800.00);
    	
    	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
    	values (107, 'Diana', 'Lorentz', 'DLORENTZ', 4200.00);    
    
     

      2. 配置Mybatis的配置文件,命名为mybatisConfig.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC" />
          <dataSource type="POOLED">
    	 <property name="driver" value="oracle.jdbc.driver.OracleDriver" />  
             <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />  
             <property name="username" value="louis" />  
             <property name="password" value="123456" />
          </dataSource>
        </environment>
      </environments>
        <mappers>
           <mapper  resource="com/louis/mybatis/domain/EmployeesMapper.xml"/>
        </mappers>
    </configuration>
    

    3.     创建Employee实体Bean 以及配置Mapper配置文件

    package com.louis.mybatis.model;
    
    import java.math.BigDecimal;
    
    public class Employee {
        private Integer employeeId;
    
        private String firstName;
    
        private String lastName;
    
        private String email;
    
        private BigDecimal salary;
    
        public Integer getEmployeeId() {
            return employeeId;
        }
    
        public void setEmployeeId(Integer employeeId) {
            this.employeeId = employeeId;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    
        public BigDecimal getSalary() {
            return salary;
        }
    
        public void setSalary(BigDecimal salary) {
            this.salary = salary;
        }
    }

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.louis.mybatis.dao.EmployeesMapper" >
    
      <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Employee" >
        <id column="EMPLOYEE_ID" property="employeeId" jdbcType="DECIMAL" />
        <result column="FIRST_NAME" property="firstName" jdbcType="VARCHAR" />
        <result column="LAST_NAME" property="lastName" jdbcType="VARCHAR" />
        <result column="EMAIL" property="email" jdbcType="VARCHAR" />
        <result column="SALARY" property="salary" jdbcType="DECIMAL" />
      </resultMap>
      
      <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
        select 
        	EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY
        	from LOUIS.EMPLOYEES
        	where EMPLOYEE_ID = #{employeeId,jdbcType=DECIMAL}
      </select>
    </mapper>

    4. 创建eclipse 或者myeclipse 的maven项目,maven配置例如以下:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>batis</groupId>
      <artifactId>batis</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
    
      <name>batis</name>
      <url>http://maven.apache.org</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
    
        <dependency>
    			<groupId>org.mybatis</groupId>
    			<artifactId>mybatis</artifactId>
    			<version>3.2.7</version>
    	</dependency>
        
    	<dependency>
    		<groupId>com.oracle</groupId>
    		<artifactId>ojdbc14</artifactId>
    		<version>10.2.0.4.0</version>
    	</dependency>
        
      </dependencies>
    </project>
    

     5. client代码:

    package com.louis.mybatis.test;
    
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import com.louis.mybatis.model.Employee;
    
    /**
     * SqlSession 简单查询演示类
     * @author louluan
     */
    public class SelectDemo {
    
    	public static void main(String[] args) throws Exception {
    		/*
    		 * 1.载入mybatis的配置文件,初始化mybatis,创建出SqlSessionFactory,是创建SqlSession的工厂
    		 * 这里仅仅是为了演示的须要,SqlSessionFactory暂时创建出来,在实际的使用中,SqlSessionFactory仅仅须要创建一次。当作单例来使用
    		 */
    		InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
    		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    		SqlSessionFactory factory = builder.build(inputStream);
    		
    		//2. 从SqlSession工厂 SqlSessionFactory中创建一个SqlSession。进行数据库操作
    		SqlSession sqlSession = factory.openSession();
    	
    		//3.使用SqlSession查询
    		Map<String,Object> params = new HashMap<String,Object>();
    		
    		params.put("min_salary",10000);
    		//a.查询工资低于10000的员工
    		List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);
    		//b.未传最低工资。查全部员工
    		List<Employee> result1 = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary");
    		System.out.println("薪资低于10000的员工数:"+result.size());
    		//~output :   查询到的数据总数:5  
    		System.out.println("全部员工数: "+result1.size());
    		//~output :  全部员工数: 8
    	}
    
    }


    二、SqlSession 的工作过程分析:

     1. 开启一个数据库訪问会话---创建SqlSession对象:

    SqlSession sqlSession = factory.openSession();

               MyBatis封装了对数据库的訪问。把对数据库的会话和事务控制放到了SqlSession对象中。

         

    2. 为SqlSession传递一个配置的Sql语句 的Statement Id和參数,然后返回结果:

    List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);
    上述的"com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",是配置在EmployeesMapper.xml 的Statement ID,params 是传递的查询參数。

    让我们来看一下sqlSession.selectList()方法的定义: 

      public <E> List<E> selectList(String statement, Object parameter) {
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
      }
    
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          //1.依据Statement Id。在mybatis 配置对象Configuration中查找和配置文件相相应的MappedStatement	
          MappedStatement ms = configuration.getMappedStatement(statement);
          //2. 将查询任务托付给MyBatis 的运行器 Executor
          List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
          return result;
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

    MyBatis在初始化的时候。会将MyBatis的配置信息所有载入到内存中,使用org.apache.ibatis.session.Configuration实例来维护。使用者能够使用sqlSession.getConfiguration()方法来获取。MyBatis的配置文件里配置信息的组织格式和内存中对象的组织格式差点儿全然相应的。上述样例中的

      <select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" >
        select 
        	EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY
        	from LOUIS.EMPLOYEES
        	<if test="min_salary != null">
        		where SALARY < #{min_salary,jdbcType=DECIMAL}
        	</if>
      </select>
    载入到内存中会生成一个相应的MappedStatement对象,然后会以key="com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary"valueMappedStatement对象的形式维护到Configuration的一个Map中。当以后须要使用的时候。仅仅须要通过Id值来获取就能够了。

    从上述的代码中我们能够看到SqlSession的职能是:

    SqlSession依据Statement ID, 在mybatis配置对象Configuration中获取到相应的MappedStatement对象,然后调用mybatis运行器来运行详细的操作。


    3.MyBatis运行器Executor依据SqlSession传递的參数运行query()方法(因为代码过长,读者仅仅需阅读我凝视的地方就可以):

    /**
    * BaseExecutor 类部分代码
    *
    */
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    	  
    	// 1.依据详细传入的參数。动态地生成须要运行的SQL语句,用BoundSql对象表示  
        BoundSql boundSql = ms.getBoundSql(parameter);
        // 2.为当前的查询创建一个缓存Key
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
     }
    
      @SuppressWarnings("unchecked")
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) throw new ExecutorException("Executor was closed.");
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          list = resultHandler == null ?

    (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { // 3.缓存中没有值,直接从数据库中读取数据 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); // issue #601 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); // issue #482 } } return list; }  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { //4. 运行查询。返回List 结果,然后 将查询的结果放入缓存之中 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }


    /**
    *
    *SimpleExecutor类的doQuery()方法实现
    *
    */
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          //5. 依据既有的參数。创建StatementHandler对象来运行查询操作
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          //6. 创建java.Sql.Statement对象。传递给StatementHandler对象
          stmt = prepareStatement(handler, ms.getStatementLog());
          //7. 调用StatementHandler.query()方法。返回List结果集
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }

    上述的Executor.query()方法几经转折,最后会创建一个StatementHandler对象。然后将必要的參数传递给StatementHandler,使用StatementHandler来完毕对数据库的查询。终于返回List结果集。

    从上面的代码中我们能够看出,Executor的功能和作用是:

    (1、依据传递的參数。完毕SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;

    (2、为查询创建缓存,以提高性能(详细它的缓存机制不是本文的重点,我会单独拿出来跟大家探讨,感兴趣的读者能够关注我的其它博文)。

    (3、创建JDBCStatement连接对象,传递给StatementHandler对象,返回List查询结果。

    4. StatementHandler对象负责设置Statement对象中的查询參数、处理JDBC返回的resultSet,将resultSet加工为List 集合返回:

          接着上面的Executor第六步,看一下:prepareStatement() 方法的实现:

    /**
    *
    *SimpleExecutor类的doQuery()方法实现
    *
    */
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 1.准备Statement对象,并设置Statement对象的參数 stmt = prepareStatement(handler, ms.getStatementLog()); // 2. StatementHandler运行query()方法,返回List结果 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
    
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection);
        //对创建的Statement对象设置參数,即设置SQL 语句中 ? 设置为指定的參数
        handler.parameterize(stmt);
        return stmt;
      }

          以上我们能够总结StatementHandler对象主要完毕两个工作:

             (1. 对于JDBCPreparedStatement类型的对象,创建的过程中。我们使用的是SQL语句字符串会包括 若干个?

    占位符。我们其后再对占位符进行设值。

    StatementHandler通过parameterize(statement)方法对Statement进行设值。       

             (2.StatementHandler通过List<E> query(Statement statement, ResultHandler resultHandler)方法来完毕运行Statement,和将Statement对象返回的resultSet封装成List


    5.   StatementHandler 的parameterize(statement) 方法的实现:

    /**
    *   StatementHandler 类的parameterize(statement) 方法实现 
    */
    public void parameterize(Statement statement) throws SQLException {
    	//使用ParameterHandler对象来完毕对Statement的设值  
        parameterHandler.setParameters((PreparedStatement) statement);
      }

      /**
       * 
       *ParameterHandler类的setParameters(PreparedStatement ps) 实现
       * 对某一个Statement进行设置參数
       */
      public void setParameters(PreparedStatement ps) throws SQLException {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
          for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
              Object value;
              String propertyName = parameterMapping.getProperty();
              if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                value = boundSql.getAdditionalParameter(propertyName);
              } else if (parameterObject == null) {
                value = null;
              } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                value = parameterObject;
              } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                value = metaObject.getValue(propertyName);
              }
              
              // 每个Mapping都有一个TypeHandler,依据TypeHandler来对preparedStatement进行设置參数
              TypeHandler typeHandler = parameterMapping.getTypeHandler();
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
              // 设置參数
              typeHandler.setParameter(ps, i + 1, value, jdbcType);
            }
          }
        }
      }

    从上述的代码能够看到,StatementHandler 的parameterize(Statement) 方法调用了 ParameterHandler的setParameters(statement) 方法,

    ParameterHandler的setParameters(Statement)方法负责 依据我们输入的參数,对statement对象的 ? 占位符处进行赋值。



    6.   StatementHandler 的List<E> query(Statement statement, ResultHandler resultHandler)方法的实现:

      /**
       * PreParedStatement类的query方法实现
       */
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    	// 1.调用preparedStatemnt。execute()方法。然后将resultSet交给ResultSetHandler处理  
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        //2. 使用ResultHandler来处理ResultSet
        return resultSetHandler.<E> handleResultSets(ps);
      }
    /**  
    *ResultSetHandler类的handleResultSets()方法实现
    *
    */
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        final List<Object> multipleResults = new ArrayList<Object>();
    
        int resultSetCount = 0;
        ResultSetWrapper rsw = getFirstResultSet(stmt);
    
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount);
        
        while (rsw != null && resultMapCount > resultSetCount) {
          ResultMap resultMap = resultMaps.get(resultSetCount);
          
          //将resultSet
          handleResultSet(rsw, resultMap, multipleResults, null);
          rsw = getNextResultSet(stmt);
          cleanUpAfterHandlingResultSet();
          resultSetCount++;
        }
    
        String[] resultSets = mappedStatement.getResulSets();
        if (resultSets != null) {
          while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
              String nestedResultMapId = parentMapping.getNestedResultMapId();
              ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
              handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
          }
        }
    
        return collapseSingleResultList(multipleResults);
      }

    从上述代码我们能够看出,StatementHandler List<E> query(Statement statement, ResultHandler resultHandler)方法的实现。是调用了ResultSetHandlerhandleResultSets(Statement) 方法。ResultSetHandler的handleResultSets(Statement) 方法会将Statement语句运行后生成的resultSet 结果集转换成List<E> 结果集:

      //
      // DefaultResultSetHandler 类的handleResultSets(Statement stmt)实现 
      //HANDLE RESULT SETS
      //
    
      public List<Object> handleResultSets(Statement stmt) throws SQLException {
        final List<Object> multipleResults = new ArrayList<Object>();
    
        int resultSetCount = 0;
        ResultSetWrapper rsw = getFirstResultSet(stmt);
    
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount);
        
        while (rsw != null && resultMapCount > resultSetCount) {
          ResultMap resultMap = resultMaps.get(resultSetCount);
          
          //将resultSet
          handleResultSet(rsw, resultMap, multipleResults, null);
          rsw = getNextResultSet(stmt);
          cleanUpAfterHandlingResultSet();
          resultSetCount++;
        }
    
        String[] resultSets = mappedStatement.getResulSets();
        if (resultSets != null) {
          while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
              String nestedResultMapId = parentMapping.getNestedResultMapId();
              ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
              handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
          }
        }
    
        return collapseSingleResultList(multipleResults);
      }



    因为上述的过程时序图太过复杂。就不贴出来了,读者能够下载MyBatis源代码, 使用Eclipse、Intellij IDEA、NetBeans 等IDE集成环境创建项目,Debug MyBatis源代码。一步步跟踪MyBatis的实现。这样对学习MyBatis框架非常有帮助~



     

    作者的话

        本文是《深入理解mybatis原理》系列的当中一篇。假设您有兴趣。请关注该系列的其它文章~

       认为本文不错,顺手点个赞哦~~您的鼓舞,是我继续分享知识的强大动力!


     

     


     

    -----------------------------------------------------------------------------------------------------------------------------------------

                                                                    本文源自  http://blog.csdn.net/luanlouis/。如需转载,请注明出处。谢谢!

          




    版权声明:本文博客原创文章,博客,未经同意,不得转载。

    转载于:https://www.cnblogs.com/lcchuguo/p/4721421.html

    展开全文
  • 为了回答这个问题,我们介绍了一个具体案例,并分析了当算法访问敏感信息以做出更公平的预测时,欧盟法律可能如何适用。 我们审查这些措施是否构成歧视,以及针对谁,根据我们如何定义歧视的危害和我们比较的群体...
  • 许多组织已经认识到将非结构化信息与结构化信息一起管理的潜力,... 最后,由于框架的大多数评估标准都集中在UIM应用程序和技术的可用性和功能上,因此所提出的框架设计师和信息官员之间的交流提供了一种通用语言。
  • 后者通常由私有的设计-建造-拥有(DBO)计划部署,这是PPP的一种形式,其中公共合作伙伴资助由私有合作伙伴完全设计,建造,管理和拥有的基础设施的推出。 尽管它们很受欢迎,但私人DBO在宽带市场上的表现仍是未知...
  • Apache Hadoop软件库是一个框架,该框架允许使用简单的编程模型跨计算机集群对大型数据集进行分布式处理。 它旨在从单个服务器扩展到数千台机器,每台机器都提供本地计算和存储。 库本身不用于依靠硬件来提供高...

    Apache Hadoop软件库是一个框架,该框架允许使用简单的编程模型跨计算机集群对大型数据集进行分布式处理。 它旨在从单个服务器扩展到数千台机器,每台机器都提供本地计算和存储。

    库本身不用于依靠硬件来提供高可用性,而是被设计用来检测和处理应用程序层的故障,因此可以在计算机集群的顶部提供高可用性服务,每台计算机都容易出现故障。 Hadoop库包含两个主要组件HDFS和MapReduce,在本文中,我们将深入研究HDFS的每个部分,并了解其内部工作方式。

    HDFS具有主/从体系结构。 HDFS群集由单个NameNode和管理文件系统名称空间并控制客户端对文件的访问的主服务器组成。 此外,还有许多数据节点,通常是集群中每个节点一个,用于管理与它们所运行的节点相连的存储。

    HDFS公开了文件系统名称空间,并允许用户数据存储在文件中。 在内部,文件被分成一个或多个块,这些块存储在一组DataNode中。 NameNode执行文件系统名称空间操作,例如打开,关闭和重命名文件和目录。 它还确定块到DataNode的映射。 DataNode负责处理来自文件系统客户端的读写请求。 DataNode还根据NameNode的指令执行块创建,删除和复制。

    hdfs体系结构

    HDFS分析

    在使用JArchitect分析Hadoop 之后 ,这是hdfs项目的依赖关系图。

    hadoop23

    为了完成其工作,hdfs使用了许多第三方库,例如番石榴,码头,杰克逊等。 DSM(设计结构矩阵)为我们提供了有关使用每个库的权重的更多信息。

    hadoop24

    HDFS主要使用rt,hadoop-common和protobuf库。 当使用外部库时,最好检查一下我们是否可以轻松地将另一个第三方库更改为另一个库而不影响整个应用程序,这有很多原因可以鼓励我们更改第三方库。 另一个库可以:

    • 具有更多功能
    • 表现更好
    • 更安全

    让我们以jetty lib为例,并搜索hdfs中的哪些方法直接使用它。 从方法中的m中,其中m.IsUsing(“ jetty-6.1.26”)&& m.ParentProject.Name ==“ hadoop-hdfs-0.23.6”选择新的{m,m.NbBCInstructions}

    hadoop25

    只有很少的方法直接使用jetty lib,而用另一种方法进行更改将非常容易。 通常,当您仅可以在某些类中使用外部库时,进行隔离非常有趣,它可以帮助轻松地维护和发展项目。 现在让我们发现主要的HDFS组件:

    数据节点

    启动

    为了发现如何启动数据节点,让我们在hdfs jar的所有入口点之前进行搜索。 从m中的方法中的m.Name.Contains(“ main(String [])”)&& m.IsStatic选择新的{m,m.NbBCInstructions}

    hadoop22

    hdfs具有许多条目,例如DFSAdmin,DfSsc,Balancer和HDFSConcat。 对于数据节点,相关的入口点是DataNode类,这是调用其main方法时发生的情况。

    hadoop11

    main方法调用第一个securemain并将参数securityresources传递给它,当在非安全群集中启动该节点时,此参数为null,但是在安全环境中启动该节点的情况下,将为该参数分配安全资源。 SecureResources类包含两个属性:

    1. StreamingSocket:用于将数据流传输到datanode的安全端口。
    2. listner:Web服务器的安全侦听器。

    这是从DataNode.StartDataNode调用的方法。

    hadoop3

    此方法初始化IPCServer,DataXceiver(这是用于处理传入/传出数据流的线程),创建数据节点度量实例。

    如何管理数据?

    DataNode类具有名为FSDatasetinterface类型的数据的属性。 FSDatasetinterface是用于基础存储的接口,用于存储数据节点的块。 让我们搜索Hadoop中可用的实现。 从类型为t.Implement(“ org.apache.hadoop.hdfs.server.datanode.FSDatasetInterface”)选择新的{t,t.NbBCInstructions}的类型中

    hadoop2

    Hadoop提供了FSDataset,它管理一组数据块并将其存储在目录中。 使用接口可实现低耦合,并使设计非常灵活,但是,如果使用实现而不是接口,我们将失去这一优势,并且要检查是否在任何地方都使用interfaceDataSet表示数据,让我们使用FSDataSet搜索所有方法。 从m.IsUsing(“ org.apache.hadoop.hdfs.server.datanode.FSDataset”)中选择新的{m,m.NbBCInstructions}的方法中的m

    hadoop14

    只有FSDataSet内部类直接使用它,而在所有其他地方都使用interfaceDataSet,这使得更改数据集类型的可能性变得非常容易。 但是,如何更改interfaceDataSet并给出自己的实现? 为此,让我们搜索FSDataSet的创建位置。

    从方法的m中开始,让depth0 = m.DepthOfCreateA(“ org.apache.hadoop.hdfs.server.datanode.FSDataset”),其中depth0 == 1选择新的{m,depth0}

    hadoop26

    工厂模式用于创建实例。 问题是,如果该工厂直接在getFactory方法中创建实现,则必须更改Hadoop代码以为其提供自定义数据集管理器。 让我们发现getFactory方法使用了哪些方法。 从m.IsUsedBy(“ org.apache.hadoop.hdfs.server.datanode.FSDatasetInterface $ Factory.getFactory(Configuration)”)中选择新的{m,m.NbBCInstructions}

    hadoop27

    好消息是工厂使用Configuration来获取类的实现,因此我们只能通过配置给出自定义的DataSet,我们还可以搜索配置可以提供的所有类。

    从m.IsUsing(“ org.apache.hadoop.conf.Configuration.getClass(String,Class,Class)”)中选择新的{m,m.NbBCInstructions}的方法中的m

    hadoop28

    可以在不更改其源代码的情况下将许多类注入Hadoop框架,这使其非常灵活。

    名称节点

    NameNode是所有HDFS元数据的仲裁器和存储库。 该系统的设计方式是,用户数据永远不会流过NameNode。 这是启动名称节点时调用的一些方法。

    hadoop5

    创建了RPC服务器,并加载了fsnamesystem,下面是对这两个组件的快速浏览:

    NameNodeRpcServer

    NameNodeRpcServer负责处理对NameNode的所有RPC调用。 例如,当启动一个数据节点时,它必须在NameNode上注册自己,rpc服务器接收该请求并将其转发到fsnamesystem,后者将其重定向到dataNodeManager。

    hadoop8

    另一个示例是当接收到数据块时。 从m.IsUsedBy(“ org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.blockReceived(DatanodeRegistration,String,Block [],String [])”)中的m中选择新的{m,m.NbBCInstructions}

    hadoop18

    图中的每个矩形与代码指令的字节数成正比,我们可以观察到BlockManager.addBlock可以完成大部分工作。 Haddop有趣的是,每个类都有特定的职责,并且任何请求都将重定向到相应的管理器。

    FS名称系统

    HDFS支持传统的分层文件组织。 用户或应用程序可以创建目录并将文件存储在这些目录中。 文件系统名称空间层次结构与大多数其他现有文件系统相似。 可以创建和删除文件,将文件从一个目录移动到另一个目录或重命名文件。 例如,这是有关符号链接创建的依赖图。

    hadoop15

    HDFS客户端

    DFSClient可以连接到Hadoop Filesystem并执行基本文件任务。 它使用ClientProtocol与NameNode守护程序进行通信,并直接连接到DataNode以读取/写入块数据。
    Hadoop DFS用户应获取DistributedFileSystem的实例,该实例使用DFSClient处理文件系统任务。 DistributedFileSystem充当外观并将请求重定向到DFSClient类,这是有关创建目录请求的依赖关系图。

    hadoop16

    结论:

    以用户身份使用框架非常有趣,但是深入该框架可以为我们提供更多信息,以便更好地理解它,并轻松地使其适应我们的需求。 Hadoop是许多公司使用的功能强大的框架,其中大多数公司都需要对其进行自定义,所幸的是,Hadoop非常灵活,允许我们在不更改源代码的情况下更改行为。

    翻译自: https://www.javacodegeeks.com/2013/04/how-hadoop-works-hdfs-case-study.html

    展开全文
  • 研究人员将采用案例研究设计来观察音乐参与度,评估参与度,观察教师提供的提示水平以及观察在马来西亚特殊需求教室中实施SoI所面临的挑战。 希望这些发现可以表明SoI将使自闭症儿童,其父母和特殊教育者受益,并为...
  • 我们使用访谈,对经验丰富的内部和外部审计师的调查以及两个案例研究来验证此框架。 该框架被认为对内部和外部审核员的机器人生产很有帮助。 我们开发和验证框架的方法论方法可以作为希望为新兴技术开发创新框架的...
  • 我们通过访谈、对经验丰富的内部和外部审计师的调查以及两个案例研究来验证这个框架。 通过从社会技术系统理论的角度开发和验证我们的框架,我们还提供了一些见解,有助于解释先前研究中关于新兴技术在审计中的有效...
  • 背景:印度政府为在2030年前消除该国的疟疾进行了框架研究[1] [2]。 但是像古吉拉特邦这样的进步国家必须在2024年之前实现这一目标。朝这个方向迈出的第一步是将年度寄生虫发病率降低到1.0以下。 在国家病媒传染病...
  • 本书是在校大学生学习使用Oracle进行课程设计的好帮手和重要参考资料,是在校学生了解企业开发的一个好窗口,也是研究生撰写毕业论文时好的参考文献。同时它还可供Oracle应用程序的开发人员,以及对相关技术感兴趣的...
  • 2D共价有机框架(COF)的层间堆叠不仅在确定COF内部通道的几何形状而且在确定COF层之间的载流子迁移率方面起着至关重要的作用。 但是,虽然可以通过构件的结构精确预测2D COFs单层的拓扑结构,但影响其层间堆叠的...
  • 框架应用于现有 DLT 系统的六个案例研究——比特币、以太坊、Ripple、Alastria、Verified.me 和匿名 DLT 系统——以说明其用于执行比较分析。 该报告最后展示了一个 DLT 系统生态系统图,该图根据从框架中获取的...
  • 然后,为了测试和完善由此产生的理论框架,提出了两个案例研究:法国大学的电子学习系统中的案例A和拉脱维亚银行中的贷款管理系统中的案例B。 最后,作者讨论了这项工作的局限性,贡献和观点。
  • 已开发了一个案例研究,其中使用沙特建筑法规(SBC)规定设计了12层,6层和3层住宅建筑的抗空间和周向抗弯框架。 进行了模态响应谱分析,以了解SSI对基本振动周期,顶层位移和层间位移限制的影响。 此外,已经进行...
  • 框架应用于一家提供各种化妆品的公司的实际案例研究,重点是提高客户忠诚度、提高销售团队生产力和降低客户管理成本。 本研究的重要性在于它提供了一种系统、实用和有效的方法来设计和实施 CRM 系统。 这种方法也...
  • 为满足SaaS软件服务设计中客户差异化的需求和不断变化的需求,本文提出SaaS软件服务基于大规模定制的业务逻辑框架,把业务过程抽象为软件构件,存储在软件构件库中,通过建立面向客户需求的动态业务流程模型,用动态...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 423
精华内容 169
关键字:

案例研究设计框架