MyBatis ให้ฟังก์ชั่นการสืบค้นการเชื่อมโยงขั้นสูงซึ่งสามารถแมปชุดผลลัพธ์ที่ได้จากฐานข้อมูลที่ได้รับจากถั่วชวาที่กำหนดไว้ ต่อไปนี้เป็นตัวอย่างที่จะแสดงให้เห็นว่า mybatis เกี่ยวข้องกับการแมปที่ซับซ้อนแบบหนึ่งต่อหลายต่อหลายและหลายต่อหนึ่ง
ออกแบบระบบบล็อกอย่างง่ายที่ผู้ใช้สามารถเปิดบล็อกหลายบล็อกโพสต์บทความในบล็อกอนุญาตความคิดเห็นและบทความแท็ก ระบบบล็อกส่วนใหญ่ประกอบด้วยตารางต่อไปนี้:
ตารางผู้แต่ง: ตารางข้อมูลผู้แต่งบันทึกข้อมูลของผู้แต่งชื่อผู้ใช้และรหัสผ่านที่อยู่อีเมล ฯลฯ
ตารางบล็อก: บล็อกตารางผู้เขียนสามารถเปิดหลายบล็อกได้นั่นคือความสัมพันธ์ระหว่างผู้แต่งและบล็อกเป็นแบบหนึ่งต่อหลายคน
ตารางโพสต์: ตารางบันทึกบทความ, การบันทึกเวลาโพสต์สิ่งพิมพ์, ชื่อเรื่อง, ข้อความและข้อมูลอื่น ๆ ; อาจมีบทความมากมายภายใต้บล็อกและความสัมพันธ์ระหว่างบล็อกและโพสต์เป็นแบบหนึ่งต่อหลายคน
ตารางความคิดเห็น: บทความความคิดเห็นของบทความ, บันทึกบทความความคิดเห็น, บทความสามารถมีความคิดเห็นมากมาย: การติดต่อระหว่างโพสต์และความคิดเห็นเป็นแบบหนึ่งถึงหลายคน
Tag Table: Tag Table ซึ่งแสดงถึงการจำแนกแท็กของบทความ บทความสามารถมีหลายแท็กและแท็กสามารถนำไปใช้กับบทความที่แตกต่างกันได้ดังนั้นความสัมพันธ์ระหว่างแท็กและโพสต์จึงเป็นความสัมพันธ์แบบหลายต่อหลายครั้ง (ความสัมพันธ์แบบหลายต่อหลายครั้งระหว่างแท็กและโพสต์สะท้อนผ่านตาราง post_tag)
ตาราง post_tag: บันทึกการติดต่อระหว่างบทความและแท็ก
โดยทั่วไปแล้วเราจะสร้าง javabean (หรือ pojo) ที่สอดคล้องกันตามโครงสร้างของแต่ละตารางเพื่อให้การทำงาน CRUD พื้นฐานของตารางเสร็จสมบูรณ์
คำจำกัดความของ Javabean ข้างต้นของตารางเดียวบางครั้งไม่สามารถตอบสนองความต้องการทางธุรกิจได้ ในธุรกิจวัตถุบล็อกควรมีข้อมูลเกี่ยวกับผู้แต่งและรายการบทความดังแสดงในรูปด้านล่าง:
หากคุณต้องการรับอินสแตนซ์ของคลาสดังกล่าวคุณต้องใช้อย่างน้อยสองสามขั้นตอน:
1. สอบถามข้อมูลบล็อกผ่านรหัสบล็อกในตารางบล็อกและกำหนดบล็อกสอบถามและชื่อเรื่องให้กับวัตถุบล็อก
2. ตามที่ผู้เขียนสอบถามในข้อมูลบล็อกไปที่ตารางผู้เขียนเพื่อรับข้อมูลผู้เขียนที่เกี่ยวข้องรับวัตถุผู้แต่งแล้วมอบหมายให้กับวัตถุบล็อก
3. ตามบล็อกิดสอบถามรายการบทความโพสต์ที่เกี่ยวข้องในตารางโพสต์และกำหนดรายการ <post> วัตถุให้กับวัตถุบล็อก
ด้วยวิธีนี้คำสั่งแบบสอบถามอย่างน้อยสามข้อความจะถูกเรียกที่ชั้นล่าง โปรดดูรหัสต่อไปนี้:
/** รับวัตถุ BlogInfo ผ่าน blogid*/ public blogInfo OrganicQueryOntest (String Blogid) {BigDecimal ID = ใหม่ BigDecimal (blogid); SQLSESSION SESSION = SQLSessionFactory.opensession (); bloginfo bloginfo = ใหม่ bloginfo (); // 1. สอบถามวัตถุบล็อกตาม blogid และตั้งค่าเป็น bloginfo blog blog = (บล็อก) เซสชัน SESSENTSEECTONE ("com.foo.bean.blogmapper.selectbyprimarykey", id); bloginfo.setBlogid (blog.getBlogid ()); bloginfo.settitle (blog.gettitle ()); // 2. ตามที่ผู้เขียนในบล็อกป้อนฐานข้อมูลเพื่อสอบถามข้อมูลผู้แต่งและตั้งค่าผลลัพธ์ให้กับผู้เขียน Object BLOGINFO Author = (Author) Session.selectone ("com.foo.bean.authormapper.selectbyprimarykey", blog.getauthorid ()); bloginfo.setauthor (ผู้แต่ง); // 3. สอบถามวัตถุโพสต์และตั้งค่าเป็น bloginfo รายการโพสต์ = session.selectList ("com.foo.bean.postmapper.selectbyblogid", blog.getBlogid ()); bloginfo.setPosts (โพสต์); // พิมพ์วัตถุในรูปแบบของ json string jsonobject object = new jsonobject (bloginfo); System.out.println (Object.toString ()); กลับ bloginfo; -จากรหัสข้างต้นเราจะเห็นว่ามันเป็นปัญหามากขึ้นในการรับวัตถุ BlogInfo คุณต้องโทรไปที่ฐานข้อมูลแบบสอบถามทั้งหมดสามเท่ารับข้อมูลที่ต้องการจากนั้นรวบรวมวัตถุ BlogInfo
คำสั่งคำพูดที่ซ้อนกัน
MyBatis ให้กลไกที่เรียกว่าคิวรีคำสั่งซ้อนซึ่งสามารถทำให้การดำเนินการข้างต้นง่ายขึ้นอย่างมากและเพิ่มการกำหนดค่าและรหัสดังต่อไปนี้:
<resultmap type = "com.foo.bean.bloginfo" id = "bloginfo"> <id column = "blog_id" คุณสมบัติ = "blogid" /> <result column = "title" property = "title" /> <Association property = "Author" Author " select = "com.foo.bean.authormapper.selectByPrimaryKey"> </Association> <collection property = "โพสต์" คอลัมน์ = "blog_id" OfType = "com.foo.bean.post" select = "com.foo.bean.postmapper.selectbyblogid" resultmap = "bloginfo" parameterType = "java.math.bigdecimal"> เลือก b.blog_id, b.title, b.author_id เป็น blog_author_id จาก louluan.blog B
/** รับวัตถุ BlogInfo ผ่าน blogid*/ public bloginfo nestedQueryontest (String Blogid) {BigDecimal ID = ใหม่ BigDecimal (blogid); SQLSESSION SESSION = SQLSessionFactory.opensession (); bloginfo bloginfo = ใหม่ bloginfo (); bloginfo = (bloginfo) เซสชัน SESSECTONE ("com.foo.bean.blogmapper.QueryBloginFobyId", id); jsonObject object = new jsonObject (bloginfo); System.out.println (Object.toString ()); กลับ bloginfo; -แบบสอบถามก่อนหน้านี้สามารถรับรู้ได้อย่างสมบูรณ์ผ่านรหัสข้างต้น ที่นี่เราต้องการเพียง bloginfo = (bloginfo) session.selectone ("com.foo.bean.blogmapper.querybloginfobyid", id); ในรหัสเราสามารถรับวัตถุ BlogInfo ที่ซับซ้อน
หลักการสอบถามคำสั่งที่ซ้อนกัน
ในรหัสข้างต้น MyBatis จะดำเนินการตามขั้นตอนต่อไปนี้:
1. ก่อนดำเนินการคำสั่งที่สอดคล้องกันของ querybloginfobyId เพื่อรับชุดผลลัพธ์ชุดผลลัพธ์จากตารางบล็อก;
2. นำบันทึกที่ถูกต้องถัดไปของ Resultset จากนั้นสร้างวัตถุ BlogInFO ที่เกี่ยวข้องตามข้อกำหนดการแมปที่กำหนดโดยผลลัพธ์
3. เมื่อคุณต้องการกำหนดแอตทริบิวต์ผู้เขียนใน BlogInfo คุณจะพบว่ามีการสืบค้นที่เกี่ยวข้อง ในเวลานี้ MyBatis จะเรียกใช้คำสั่ง Select Query ก่อนเพื่อรับผลลัพธ์ที่ส่งคืนและตั้งค่าผลลัพธ์ให้กับแอตทริบิวต์ผู้เขียนของ BlogInfo;
4. เมื่อกำหนดโพสต์ของ BlogInfo กระบวนการที่คล้ายกันก็มีอยู่เช่นกัน
5. ทำซ้ำ 2 ขั้นตอนจนกระทั่งผลลัพธ์ ถัดไป () == เท็จ;
ต่อไปนี้เป็นแผนผังแผนผังของกระบวนการสร้างการก่อสร้างวัตถุ BlogInfo:
ฟังก์ชั่นที่ดีมากของการสืบค้นซ้อนที่เกี่ยวข้องนี้คือมันสามารถนำคำสั่ง SELECT มาใช้ใหม่และสร้างวัตถุที่ซับซ้อนผ่านการรวมกันระหว่างคำสั่ง Simple Select ทั้งสองข้อความเลือกที่ซ้อนอยู่ข้างต้น com.foo.bean.authormapper.selectbyprimarykey และ com.foo.bean.postmapper.selectbyblogid สามารถใช้งานได้อย่างอิสระ
ปัญหา n+1
ข้อเสียของมันก็ค่อนข้างชัดเจน: ปัญหาที่เรียกว่า N+1 แบบสอบถามซ้อนที่เกี่ยวข้องแสดงชุดผลลัพธ์จากนั้นคิวรีที่เกี่ยวข้องจะดำเนินการตามแต่ละระเบียนของชุดผลลัพธ์นี้
ตอนนี้สมมติว่ามีแบบสอบถามซ้อนกันเพียงครั้งเดียว (นั่นคือมีแท็กการเชื่อมโยงภายในผลลัพธ์) และจำนวนรายการที่ส่งคืนโดยการสืบค้นคือ n จากนั้นคำสั่งค้นหาที่เกี่ยวข้องจะถูกดำเนินการ N ครั้งรวมทั้งการสืบค้นเองจะส่งคืนผลลัพธ์ที่ตั้งค่าให้กับการสืบค้นหนึ่งครั้ง หาก N ค่อนข้างใหญ่การใช้การเข้าถึงฐานข้อมูลนั้นมีขนาดใหญ่มาก! ดังนั้นผู้ใช้ที่ใช้ข้อความค้นหาคำสั่งที่ซ้อนกันนี้จะต้องพิจารณาอย่างรอบคอบเพื่อให้แน่ใจว่าค่า n ไม่ใหญ่มาก
ตัวอย่างเช่นคำสั่ง SELECT เองจะส่งคืนชุดผลลัพธ์ด้วย com.foo.bean.blogmapper.QueryBloginFobyId ด้วย 1 เนื่องจากมีคำสั่งคำสั่งที่เกี่ยวข้องสองรายการจึงจำเป็นต้องเข้าถึงฐานข้อมูล 1* (1+1) = 3 ครั้ง
แบบสอบถามผลลัพธ์ซ้อนกัน
การสืบค้นข้อความที่ซ้อนกันอาจทำให้เวลาการเข้าถึงฐานข้อมูลที่ไม่แน่นอนซึ่งอาจส่งผลกระทบต่อประสิทธิภาพ MyBatis ยังสนับสนุนการสืบค้นผลลัพธ์ที่ซ้อนกัน: นั่นคือสำหรับการค้นหาของสถานการณ์แบบหนึ่งต่อหลายต่อหลายครั้งและหลายสถานการณ์ mybatis ค้นหาผลลัพธ์จากฐานข้อมูลในครั้งเดียวผ่านการสืบค้นร่วมและแปลงผลลัพธ์ตามความสัมพันธ์
กำหนดแผนที่ผลลัพธ์ของ BlogInfo
<resultmap type = "com.foo.bean.bloginfo" id = "bloginfo"> <id column = "blog_id" คุณสมบัติ = "blogid"/> <result column = "title" property = "title"/> <Association property = "Author"> "blog_author_id" คอลัมน์ = "user_name" คุณสมบัติ = "ชื่อผู้ใช้"/> <result column = "รหัสผ่าน" คุณสมบัติ = "รหัสผ่าน"/> <result column = "อีเมล" คุณสมบัติ = "อีเมล"/> <result column = "Biography" คุณสมบัติ = "ชีวประวัติ"/> <result column = "blog_id" property = "blogid"/> <result column = "create_time" คุณสมบัติ = "createTime"/> <result column = "subject" property = "subject"/> <result column = "body" property = "body"/> <result column = "draft"
คำสั่ง SQL ที่เกี่ยวข้องมีดังนี้:
<select id = "queryallbloginfo" resultmap = "bloginfo"> เลือก b.blog_id, b.title, b.author_id เป็น blog_author_id, a.author_id, a.user_name, a.password, a.email, a.biography, p.post_id, p.blog_id บล็อก B ซ้ายด้านนอกเข้าร่วมผู้แต่ง A บน b.author_id = a.author_id ซ้ายด้านนอกเข้าร่วมโพสต์ p บน p.blog_id = b.blog_id </select>
/** รับข้อมูลทั้งหมดเกี่ยวกับบล็อกทั้งหมด*/ public bloginfo nestedResultontest () {SQLSession Session = SQLSessionFactory.Opensession (); bloginfo bloginfo = ใหม่ bloginfo (); bloginfo = (bloginfo) เซสชัน SESSECTONE ("com.foo.bean.blogmapper.QueryAllBlogInfo"); jsonObject object = new jsonObject (bloginfo); System.out.println (Object.toString ()); กลับ bloginfo; - ขั้นตอนการดำเนินการสำหรับการสืบค้นผลลัพธ์ที่ซ้อนกัน:
1. ดำเนินการเข้าร่วมตามความสัมพันธ์ที่สอดคล้องกันของตารางเพื่อรับชุดผลลัพธ์
2. ตามข้อมูลชุดผลลัพธ์และข้อมูลคำจำกัดความของผลลัพธ์ของ BlogInfo ประกอบและกำหนดชุดผลลัพธ์ที่ส่งคืนในหน่วยความจำเพื่อสร้าง BlogInfo;
3. ส่งคืนผลลัพธ์ที่สร้างขึ้น <bloginfo> ผลลัพธ์
สำหรับการสืบค้นผลลัพธ์ที่เกี่ยวข้องหากเป็นความสัมพันธ์แบบหลายต่อหนึ่งมันจะถูกกำหนดค่าโดย <Association Property = "Author" คอลัมน์ = "blog_author_id" javatype = "com.foo.bean.author"> MyBatis จะดึงข้อมูลจากหน่วยความจำผ่านค่า uther_id ที่สอดคล้องกับคุณสมบัติคอลัมน์และห่อหุ้มไว้ในวัตถุผู้เขียน
หากเป็นความสัมพันธ์แบบหนึ่งถึงหลายคนเช่นเดียวกับความสัมพันธ์ระหว่างบล็อกและโพสต์มันจะถูกกำหนดค่าโดย <collection property = "posts" คอลัมน์ = "blog_post_id" OfType = "com.foo.bean.post">, mybatis ใช้ blog_id เพื่อดึงวัตถุโพสต์ในหน่วยความจำ
สำหรับการสอบถามผลลัพธ์ที่เกี่ยวข้องคุณจะต้องสอบถามฐานข้อมูลเพียงครั้งเดียวจากนั้นการรวมและการประกอบของผลลัพธ์จะถูกวางไว้ในหน่วยความจำทั้งหมด
ข้างต้นคือการแสดงให้เห็นถึงการประมวลผลวัตถุแบบหนึ่งต่อหลายต่อหลายครั้งโดยการสอบถามข้อมูลบล็อกทั้งหมด
PS: ตัวอย่างของการทำแผนที่การเชื่อมโยงตนเอง:
ชั้นเรียน
โมดูลคลาสสาธารณะ {ID INT ส่วนตัว; คีย์สตริงส่วนตัว ชื่อสตริงส่วนตัว; โมดูลส่วนตัว ParentModule; รายการส่วนตัว <Domule> เด็กโมดูล; URL สตริงส่วนตัว การเรียงลำดับ INT ส่วนตัว; การแสดงสตริงส่วนตัว; สตริงส่วนตัว Del; สาธารณะ int getId () {return id; } โมฆะสาธารณะ setId (int id) {this.id = id; } Public String getKey () {return key; } โมฆะสาธารณะ setKey (คีย์สตริง) {this.key = key; } สตริงสาธารณะ getName () {ชื่อคืน; } โมฆะสาธารณะ setName (ชื่อสตริง) {this.name = name; } โมดูลสาธารณะ getParentModule () {return parentModule; } โมฆะสาธารณะ setParentModule (โมดูล parentModule) {this.parentModule = parentModule; } สตริงสาธารณะ getUrl () {return url; } โมฆะสาธารณะ setUrl (rl String) {this.url = url; } public int getSort () {กลับเรียงลำดับ; } โมฆะสาธารณะ setSort (int sort) {this.sort = sort; } สตริงสาธารณะ getShow () {return show; } โมฆะสาธารณะ setShow (สตริงแสดง) {this.show = show; } สตริงสาธารณะ getdel () {return del; } โมฆะสาธารณะ setdel (String del) {this.del = del; } รายการสาธารณะ <Module> getChildrenmodules () {return childrenmodules; } โมฆะสาธารณะ setChildrenmodules (รายการ <Module> เด็ก ๆ modules) {this.childrenmodules = childrenmodules; - รหัส XML: <mapper namespace = "com.sagaware.caraccess.mapper.modulemapper"> <resultmap type = "module" id = "moduleresultmap"> <id property = "id" คอลัมน์ = "module_id"/> column = "module_url"/> <result property = "sort" คอลัมน์ = "module_sort"/> <property result = "show" คอลัมน์ = "module_show"/> <result property = "del" คอลัมน์ = "module_del"/> <! Submodule-> <collection property = "childrenmodules" คอลัมน์ = "module_id" select = "getChildrenmodules"/> </resultmap> <select id = "getModules" parameterType = "string" resultmap = parameterType = "int" resultmap = "moduleresultmap"> เลือก * จาก tb_module โดยที่ module_id = #{module_id} </select> <select id = "getChildrenmodues" parameterType = "int" int " </mapper>