แคชระดับ 1 และแคชระดับ 2
MyBatis ออกแบบแคชข้อมูลเป็นโครงสร้างสองระดับแบ่งออกเป็นแคชระดับแรกและแคชระดับที่สอง:
แคชระดับ 1 เป็นแคชระดับเซสชันเซสชันซึ่งอยู่ในวัตถุ SQLSession ที่แสดงถึงเซสชันฐานข้อมูลและเรียกว่าแคชท้องถิ่น แคชระดับ 1 เป็นคุณสมบัติที่นำมาใช้ภายในโดย mybatis ผู้ใช้ไม่สามารถกำหนดค่าและสนับสนุนโดยอัตโนมัติโดยค่าเริ่มต้น ผู้ใช้ไม่มีสิทธิ์ในการปรับแต่ง (แต่นี่ไม่ใช่แบบสัมบูรณ์และสามารถแก้ไขได้ผ่านปลั๊กอินการพัฒนา)
แคชระดับที่สองเป็นแคชระดับแอปพลิเคชันซึ่งมีวงจรชีวิตที่ยาวนานเช่นเดียวกับวงจรการประกาศของแอปพลิเคชันซึ่งหมายความว่าขอบเขตของฟังก์ชันเป็นแอปพลิเคชันทั้งหมด
องค์กรของแคชระดับแรกและแคชระดับที่สองใน mybatis แสดงในรูปด้านล่าง:
กลไกการทำงานของการแคชระดับหนึ่ง:
แคชระดับ 1 คือระดับเซสชัน โดยทั่วไปแล้ววัตถุ SQLSession จะใช้วัตถุผู้ดำเนินการเพื่อดำเนินการเซสชันให้เสร็จสมบูรณ์ วัตถุผู้ดำเนินการจะรักษาแคชแคชเพื่อปรับปรุงประสิทธิภาพการค้นหา
กลไกการทำงานของการแคชทุติยภูมิ:
ดังที่ได้กล่าวไว้ข้างต้นวัตถุ SQLSession จะใช้วัตถุ Executor เพื่อดำเนินการเซสชันให้เสร็จสมบูรณ์ กุญแจสำคัญในกลไกการแคชรองของ MyBatis คือการสร้างความยุ่งยากเกี่ยวกับวัตถุผู้ดำเนินการนี้ หากผู้ใช้ได้กำหนดค่า "cacheenabled = true" เมื่อ mybatis สร้างวัตถุ Executor สำหรับวัตถุ SQLSession มันจะเพิ่มมัณฑนากรให้กับวัตถุ Executor: CachingExecutor ในเวลานี้ SQLSession ใช้วัตถุ CachingExecutor เพื่อดำเนินการตามคำขอการดำเนินการ สำหรับคำขอแบบสอบถาม CachingExecutor จะพิจารณาก่อนว่าคำขอแบบสอบถามได้รับผลลัพธ์ในแคชรองระดับแอปพลิเคชันหรือไม่ หากมีผลการสอบถามมันจะส่งคืนผลลัพธ์ที่แคชโดยตรง หากไม่มีแคชมันจะถูกส่งไปยังวัตถุผู้ดำเนินการจริงเพื่อดำเนินการค้นหา หลังจากนั้น CachingExecutor จะวางผลลัพธ์การสืบค้นที่ส่งคืนโดยผู้ดำเนินการจริงลงในแคชแล้วส่งกลับไปยังผู้ใช้
แคชรองของ MyBatis ได้รับการออกแบบให้มีความยืดหยุ่นมากขึ้น คุณสามารถใช้การใช้งานแคชรองที่กำหนดโดย mybatis; นอกจากนี้คุณยังสามารถปรับแต่งแคชโดยใช้ org.apache.ibatis.cache.cache อินเตอร์เฟส; คุณยังสามารถใช้ไลบรารีแคชหน่วยความจำของบุคคลที่สามเช่น Memcached ฯลฯ
การแปลงแคช
คำถาม:
ปัญหาที่พบบ่อยที่สุดคือหลังจากเปิดแคชข้อมูลในหน้าแรกจะถูกส่งกลับไปยังหน้าเว็บเมื่อสอบถามเพจเพจ นอกจากนี้เมื่อใช้ปลั๊กอินการสร้าง SQL อัตโนมัติเพื่อสร้าง SQL สำหรับวิธี GET พารามิเตอร์ที่ผ่านไม่ทำงาน โดยไม่คำนึงถึงพารามิเตอร์ที่ผ่านมาผลลัพธ์การสืบค้นของพารามิเตอร์แรกจะถูกส่งคืน
ทำไมปัญหาเหล่านี้จึงเกิดขึ้น:
เมื่ออธิบายกระบวนการดำเนินการของ MyBatis มาก่อนมันถูกกล่าวถึงว่าภายใต้หลักฐานของการเปิดใช้งานแคชผู้ดำเนินการของ MyBatis จะอ่านข้อมูลจากแคชก่อนและไปที่ฐานข้อมูลเพื่อสอบถามหากไม่สามารถอ่านได้ ปัญหาอยู่ที่นี่ เวลาดำเนินการของปลั๊กอิน Generation Automatic Automatic Automatic และ Paging Plug-in อยู่ในคำสั่ง Handler และคำสั่ง Handler จะดำเนินการหลังจากผู้ดำเนินการ ไม่ว่าจะเป็นปลั๊กอินการสร้างแบบอัตโนมัติ SQL และปลั๊กอินเพจ Paging จะถูกนำมาใช้โดยการเขียน SQL ใหม่ผู้ดำเนินการใช้ SQL ดั้งเดิมเมื่อสร้างและอ่านคีย์แคช (คีย์ประกอบด้วย SQL และค่าพารามิเตอร์ที่สอดคล้องกัน) ดังนั้นแน่นอนว่ามีปัญหา
แก้ปัญหา:
เมื่อพบสาเหตุของปัญหาแล้วมันจะสะดวกในการแก้ปัญหา เพียงแค่แทนที่วิธีการสร้างคีย์ในตัวดำเนินการผ่านตัวดักจับและใช้ SQL ที่สร้างขึ้นโดยอัตโนมัติ (สอดคล้องกับปลั๊กอินการสร้างอัตโนมัติ SQL) หรือเพิ่มข้อมูลเพจ (สอดคล้องกับปลั๊กอินเพจ) เมื่อสร้าง
ลายเซ็นสกัดกั้น:
@Intercepts ({@signature (type = executor.class, method = "query", args = {mappedStatement.class, object.class, rowbounds.class, resulthandler.class})}) คลาสสาธารณะดังที่เห็นได้จากลายเซ็นประเภทเป้าหมายที่จะถูกดักจับคือ executor (หมายเหตุ: ประเภทสามารถกำหนดค่าเป็นประเภทอินเตอร์เฟสเท่านั้น) และวิธีการสกัดกั้นเป็นวิธีการสืบค้น
การดำเนินการสกัดกั้น:
การสกัดกั้นวัตถุสาธารณะ (การเรียกร้องการเรียกใช้) โยนได้ {Executor ExecutorProxy = (Executor) Invocation.getTarget (); metaobject metaexecutor = metaobject.forobject (executorproxy, default_object_factory, default_object_wrapper_factory); // แยกห่วงโซ่วัตถุพร็อกซีในขณะที่ (metaexecutor.hasgetter ("h")) {object object = metaexecutor.getValue ("H"); metaexecutor = metaobject.forobject (วัตถุ, default_object_factory, default_object_wrapper_factory); } // คลาสเป้าหมายที่แยกวัตถุพร็อกซีสุดท้ายในขณะที่ (metaexecutor.hasgetter ("เป้าหมาย")) {object object = metaexecutor.getValue ("เป้าหมาย"); metaexecutor = metaobject.forobject (วัตถุ, default_object_factory, default_object_wrapper_factory); } object [] args = chocation.getArgs (); ส่งคืนนี้คำถาม (Metaexecutor, Args); } สาธารณะ <E> รายการ <e> แบบสอบถาม (metaobject metaexecutor, object [] args) พ่น sqlexception {mappedStatement ms = (mappedStatement) args [0]; พารามิเตอร์วัตถุ = args [1]; Rowbounds Rowbounds = (Rowbounds) args [2]; resulthandler resulthandler = (resulthandler) args [3]; BoundSQL BUNTSQL = MS.GETBOUNDSQL (PARAMETEROBJECT); // เขียนใหม่การสร้าง CACHEY CACHEY KEY KEY KEY. Executor Executor = (Executor) MetaExecutor.getOriginalObject (); return executor.Query (MS, ParameterObject, Rowbounds, Resulthandler, Cachekey, BoundSQL); } CACHKEY PRIVATE CREATECACHEY (MAPPEDSTATEMENT MS, พารามิเตอร์ OBJECT PARAMETEROBJECT, ROWBOUNDS ROWBOUNDS, BUNTSQL BUNTSQL) {การกำหนดค่าการกำหนดค่า = MS.GETCONFIGURATION (); pagesQlid = configuration.gariables (). getProperty ("pagesqlid"); if (null == PagesQlid || "" .equals (PagesQLID)) {logger.warn ("คุณสมบัติ PagePagesQLID ไม่ได้ตั้งค่าใช้เงินเริ่มต้น '.*หน้า $'"); PagesQLid = defaultPagesQLid; } cachekey cachekey = new cachekey (); cachekey.update (Ms.getId ()); cachekey.update (rowbounds.getOffset ()); cachekey.update (rowbounds.getLimit ()); รายการ <ParameterMapping> ParameterMappings = boundSQL.GetParameterMappings (); // แก้ข้อผิดพลาดที่สร้าง SQL โดยอัตโนมัติและคำสั่ง SQL ว่างเปล่าทำให้คีย์สร้างข้อผิดพลาดถ้า (null == boundsql.getSql () || "" .Equals (boundSql.getSql ())) {string id = ms.getId (); id = id.substring (id.lastindexof (".") + 1); สตริง newsql = null; ลอง {if ("select" .equals (id)) {newsql = sqlbuilder.buildselectsql (parameterObject); } sqlSource sqlSource = buildSqlSource (การกำหนดค่า, newsQL, parameterObject.getClass ()); parametermappings = sqlsource.getBoundSQL (parameterObject) .getParameterMappings (); cachekey.update (NewsQL); } catch (exception e) {logger.error ("อัปเดต Cachekey Error", e); }} else {cachekey.update (boundsql.getsql ()); } metaObject metaobject = metaobject.forobject (parameterObject, default_object_factory, default_object_wrapper_factory); if (parameterMappings.size ()> 0 && parameterObject! = null) {typeHandlerRegistry typeHandlerRegistry = ms.getConfiguration (). getTypeHandlerRegistry (); if (typeHandlerRegistry.hastypehandler (parameterobject.getClass ())) {cachekey.update (พารามิเตอร์); } else {สำหรับ (parameterMapping parameterMapping: parameterMappings) {สตริงคุณสมบัติ name = parameterMapping.getProperty (); if (metaobject.hasgetter (PropertyName)) {cachekey.update (metaobject.getValue (PropertyName)); } อื่นถ้า (boundsql.hasadditionalParameter (PropertyName)) {cachekey.update (boundsql.getAdditionalParameter (PropertyName)); }}}} // เมื่อจำเป็นต้องใช้การสืบค้นเพจเพจให้เพิ่มหน้าปัจจุบันและจำนวนหน้าต่อหน้าในพารามิเตอร์หน้าไปยังแคชคีย์ถ้า (ms.getid (). จับคู่ (หน้าเว็บ) && metaobject.hasgetter ("หน้า")) if (null! = page) {cachekey.update (page.getCurrentPage ()); cachekey.update (page.getPagesize ()); }} ส่งคืน Cachekey; - การใช้ปลั๊กอิน:
ปลั๊กอินวัตถุสาธารณะ (เป้าหมายวัตถุ) {// เมื่อคลาสเป้าหมายเป็นประเภท CachingExecutor คลาสเป้าหมายจะถูกห่อหุ้มมิฉะนั้นจะกลับไปที่เป้าหมายโดยตรงลดจำนวนครั้งที่เป้าหมายจะถูก proxyed หาก (อินสแตนซ์เป้าหมายของ CachingExecutor) } else {return target; -