คำนำ
แคช MyBatis Level 2 คืออะไร?
แคชทุติยภูมิถูกแชร์โดย SQLSessions หลายตัวและขอบเขตของมันเป็นเนมสเปซเดียวกันของ Mapper
นั่นคือใน SQLSessions ที่แตกต่างกันภายใต้เนมสเปซเดียวกันคำสั่ง SQL เดียวกันและพารามิเตอร์ในเทมเพลต SQL ก็เหมือนกันและแคชจะถูกตี
หลังจากการดำเนินการครั้งแรกข้อมูลที่สอบถามในฐานข้อมูลจะถูกเขียนลงในแคชและข้อมูลจะถูกดึงจากแคชจะไม่ถูกสอบถามจากฐานข้อมูลอีกต่อไป
MyBatis ไม่เปิดใช้งานแคชทุติยภูมิโดยค่าเริ่มต้นและจำเป็นต้องเปิดใช้งานแคชทุติยภูมิในการกำหนดค่าส่วนกลาง (mybatis-config.xml)
บทความนี้อธิบายวิธีการใช้ Redis เป็นแคชเพื่อรวมเข้ากับ Springboot และ Mybatis
1. การพึ่งพาปอม
ใช้แพ็คเกจการรวม Springboot Redis เพื่ออำนวยความสะดวกในการเข้าถึง Redis เจไดใช้กับไคลเอนต์ Redis
นอกจากนี้การอ่านและเขียนแคช KV จะถูกจัดลำดับดังนั้นจึงมีการแนะนำแพ็คเกจการทำให้เป็นอนุกรม
<การพึ่งพา> <roupId> org.springframework.boot </groupid> <ratifactid> Spring-Boot-Starter-Redis </artifactid> </การพึ่งพาอาศัย> <การพึ่งพา> <roupid> redis.clients </groupid> <RoupID> com.alibaba </roupId> <ratifactId> fastjson </artifactId> <version> 1.2.19 </Serve> </Serpercess>
หลังจากการพึ่งพาเสร็จสมบูรณ์ขั้นตอนต่อไปคือการปรับไคลเอนต์ Redis
2. ถั่วที่ใช้โดย Redis Access
เพิ่มการกำหนดค่ากำหนดค่า jedisconnectionfactory bean และปล่อยให้มันใช้ในภายหลัง
โดยทั่วไปแล้วจะมีการสร้างถั่ว redistemplate แต่ก็ไม่ได้ใช้ในสถานการณ์ถัดไป
@ConfigurationPublic คลาส redisconfig {@value ("$ {spring.redis.host}") โฮสต์สตริงส่วนตัว; // Space มี จำกัด @bean สาธารณะ jedispoolconfig getRedisconfig () {jedispoolconfig config = new JedispoolConfig (); config.setMaxidle (maxidle); config.setmaxtotal (maxtotal); config.setmaxwaitmillis (Maxwaitmillis); config.setMinidle (minidle); ส่งคืนการกำหนดค่า; } @Bean (name = "JedisconnectionFactory") สาธารณะ JedisconnectionFactory getConnectionFactory () {JedisconnectionFactory โรงงาน = ใหม่ JedisconnectionFactory (); jedispoolconfig config = getRedisconfig (); Factory.SetPoolConfig (config); Factory.SethostName (โฮสต์); Factory.Setport (พอร์ต); Factory.SetDatabase (ฐานข้อมูล); Factory.SetPassword (รหัสผ่าน); Factory.settimeout (หมดเวลา); โรงงานกลับมา; } @Bean (name = "redistemplate") public redistemplate <?,?> getRedistemplate () {redistemplate <?,?> template = ใหม่ stringredistemplate (getConnectionFactory ()); เทมเพลตกลับ; -ที่นี่ @Value ใช้เพื่ออ่านการกำหนดค่าที่เกี่ยวข้องกับ REDIS และมีวิธีการอ่านการกำหนดค่าที่ง่ายกว่า (@ConfigurationProperties (คำนำหน้า = ... )) ซึ่งคุณสามารถลองได้
การกำหนดค่าที่เกี่ยวข้องกับ Redis มีดังนี้
#redisspring.redis.host = 10.93.84.53spring.redis.port = 6379Spring.redis.password = bigData123Spring.redis.database = 15Spring.redis หมดเวลา = 0spring.redis.pool.maxtotal = 8spring.redis.pool.maxwaitmillis = 1000spring.redis.pool.maxidle = 8spring.redis.pool.minidle = 0
ความหมายการกำหนดค่าของไคลเอนต์ Redis จะไม่ได้รับการอธิบายที่นี่ โดยทั่วไปที่เกี่ยวข้องกับสระว่ายน้ำเกี่ยวข้องกับประสิทธิภาพและพวกเขาจำเป็นต้องตั้งค่าตามจำนวนที่จับพร้อมกันของการจัดการหน่วยความจำและทรัพยากรอื่น ๆ
ไคลเอนต์ Redis ได้รับการตั้งค่าแล้วและเราเริ่มกำหนดค่า Redis เป็นแคชสำหรับ mybatis
3. แคช mybatis
ขั้นตอนนี้เป็นขั้นตอนที่สำคัญที่สุด วิธีการใช้งานคือการใช้อินเตอร์เฟส org.apache.ibatis.cache.cache ของ mybatis
อินเทอร์เฟซนี้ออกแบบแคชเขียนอ่านแคชทำลายแคชและการควบคุมการเข้าถึงการอ่านและเขียนล็อค
คลาสที่เราใช้เพื่อใช้งานอินเตอร์เฟสแคชคือ mybatisrediscache
mybatisrediscache.java
คลาสสาธารณะ MyBatisrediscache ใช้แคช {ส่วนตัวคงที่ JedisconnectionFactory JedisconnectionFactory; รหัสสตริงสุดท้ายส่วนตัว; Private Final ReadWriteLock ReadWriteLock = ใหม่ reentRantReadWriteLock (); สาธารณะ myBatisrediscache (รหัสสตริงสุดท้าย) {ถ้า (id == null) {โยน unlegalargumentException ใหม่ ("อินสแตนซ์แคชต้องใช้รหัส"); } this.id = id; } @Override โมฆะสาธารณะ Clear () {การเชื่อมต่อ redisconnection = null; ลอง {connection = jedisconnectionFactory.getConnection (); Connection.flushdb (); Connection.flushall (); } catch (jedisconnectionException e) {e.printstacktrace (); } ในที่สุด {ถ้า (การเชื่อมต่อ! = null) {connection.close (); }}} @Override สตริงสาธารณะ getId () {return this.id; } @Override วัตถุสาธารณะ getObject (คีย์วัตถุ) {object result = null; การเชื่อมต่อการเชื่อมต่อ redisconnection = null; ลอง {connection = jedisconnectionFactory.getConnection (); REDISSERIALIZER <Ojrop> serializer = ใหม่ jdkserializationReSerializer (); ผลลัพธ์ = serializer.deserialize (Connection.get (serializer.serialize (key))); } catch (jedisconnectionException e) {e.printstacktrace (); } ในที่สุด {ถ้า (การเชื่อมต่อ! = null) {connection.close (); }} ผลการส่งคืน; } @Override สาธารณะ readWriteLock getReadWriteLock () {return this.readWriteLock; } @Override สาธารณะ int getSize () {int result = 0; การเชื่อมต่อการเชื่อมต่อ redisconnection = null; ลอง {connection = jedisconnectionFactory.getConnection (); result = integer.valueof (connection.dbsize (). toString ()); } catch (jedisconnectionException e) {e.printstacktrace (); } ในที่สุด {ถ้า (การเชื่อมต่อ! = null) {connection.close (); }} ผลการส่งคืน; } @Override โมฆะสาธารณะ putobject (คีย์วัตถุค่าวัตถุ) {การเชื่อมต่อ redisconnection = null; ลอง {connection = jedisconnectionFactory.getConnection (); REDISSERIALIZER <Ojrop> serializer = ใหม่ jdkserializationReSerializer (); connection.set (serializer.serialize (คีย์), serializer.serialize (ค่า)); } catch (jedisconnectionException e) {e.printstacktrace (); } ในที่สุด {ถ้า (การเชื่อมต่อ! = null) {connection.close (); }}} @Override วัตถุสาธารณะ removeObject (คีย์วัตถุ) {การเชื่อมต่อ redisconnection = null; ผลลัพธ์ของวัตถุ = null; ลอง {connection = jedisconnectionFactory.getConnection (); REDISSERIALIZER <Ojrop> serializer = ใหม่ jdkserializationReSerializer (); result = connection.expire (serializer.serialize (คีย์), 0); } catch (jedisconnectionException e) {e.printstacktrace (); } ในที่สุด {ถ้า (การเชื่อมต่อ! = null) {connection.close (); }} ผลการส่งคืน; } โมฆะคงที่สาธารณะ setjedisconnectionFactory (JedisconnectionFactory JedisconnectionFactory) {MyBatisrediscache.jedisconnectionFactory = JedisconnectionFactory; -สังเกต:
อย่างที่คุณเห็นคลาสนี้ไม่ได้เป็นคลาสที่จัดการโดยเครื่องเสมือนจริง แต่มีคุณสมบัติคงที่ JedisconnectionFactory ที่ต้องฉีดถั่วฤดูใบไม้ผลินั่นคือถั่วที่สร้างขึ้นใน redisconfig
ในชั้นเรียนปกติโดยทั่วไปบริบทของสปริงจะใช้ในการใช้ Springboot IntroSpective SpringContextAware
ใช้วิธีอื่นที่นี่คือการฉีดแบบคงที่ วิธีการนี้ถูกนำไปใช้ผ่านการอ่านซ้ำ
4. การฉีดคงที่
rediscachetransfer.java
@componentpublic คลาส rediscachetransfer {@autowired โมฆะสาธารณะ setjedisconnectionfactory (jedisconnectionfactory JedisconnectionFactory) {mybatisrediscache.setjedisconnectionFactory (JedisconnectionFactory); -คุณจะเห็นได้ว่า Recachetransfer เป็นถั่วสปริงบูต เมื่อเริ่มต้นคอนเทนเนอร์ที่จุดเริ่มต้นของการสร้างถั่ว jedisconnectionfactory จะถูกฉีดเข้าไปในพารามิเตอร์ที่ผ่านของวิธี setjedisconnectionFactory
SetJedisconnectionFactory ตั้งค่าคุณสมบัติคงที่ของคลาส MyBatisrediscache โดยเรียกวิธีการคงที่
สิ่งนี้ฉีด JedisconnectionFactory ที่จัดการโดยคอนเทนเนอร์สปริงลงในโดเมนคงที่
ณ จุดนี้รหัสเสร็จสมบูรณ์โดยทั่วไปและต่อไปนี้เป็นการกำหนดค่าบางอย่าง สิ่งสำคัญคือ (1) สวิตช์ทั่วโลก; (2) สวิตช์ขอบเขตเนมสเปซ (3) การทำให้เป็นอนุกรมอินสแตนซ์แบบจำลอง
5. สวิตช์ทั่วโลกของแคชระดับ 2 mybatis ระดับ 2
ดังที่ได้กล่าวไว้ก่อนหน้านี้แคชระดับ 2 เริ่มต้นจะไม่เปิดและจำเป็นต้องตั้งค่าเป็น TRUE นี่คือสวิตช์สำหรับแคชระดับ 2 ระดับโลก
การกำหนดค่าทั่วโลก mybatis
<? XML เวอร์ชัน = "1.0" การเข้ารหัส = "UTF-8"?> <! การกำหนดค่า doctype สาธารณะ "-// mybatis.org//dtd config 3.0 // en" "http://mybatis.org/dtd/mybatis-3-config.dtd เปิดใช้งานหรือปิดการใช้งานแคช -> <การตั้งค่าชื่อ = "cacheenabled" value = "true"/> </settings> </การกำหนดค่า>
การโหลดการกำหนดค่าทั่วโลกในแหล่งข้อมูลอาจเป็นเช่นนี้
Bean.setMapperLocations (PathMatchingResourcePatternResolver (). getResources ("classpath: mybatis-mapper/*. xml"));
ระบุเส้นทางการจัดเก็บของ mapper.xml ภายใต้เส้นทาง mybatis-mapper คำต่อท้ายทั้งหมดที่มี. xml จะถูกอ่าน
Bean.setConfigLocation (ใหม่ classpathresource ("mybatis-config.xml"));
มีการระบุเส้นทางการจัดเก็บของ mybatis-config.xml และวางโดยตรงในไดเรกทอรีทรัพยากร
@bean (name = "moonlightsqlsessionfactory") @primary สาธารณะ sqlsessionfactory moonlightsqlsessionfactory (@qualifier ("moonlightdata") DataSource DataSource) โยนข้อยกเว้น Bean.setDatasource (แหล่งข้อมูล); Bean.setMapperLocations (PathMatchingResourcePatternResolver (). getResources ("classpath: mybatis-mapper/*. xml")); Bean.setConfigLocation (ใหม่ classpathresource ("mybatis-config.xml")); กลับ bean.getObject (); -6. กำหนดค่าเนมสเปซขอบเขต Mapper
ดังที่ได้กล่าวไว้ก่อนหน้านี้ขอบเขตของแคชทุติยภูมิคือเนมสเปซ Mapper ดังนั้นการกำหนดค่านี้จะต้องเขียนใน Mapper
<mapper namespace = "com.kangaroo.studio.moonlight.dao.mapper.moonlightmapper"> <cache type = "com.kangaroo.studio.moonlight.dao.cache.mybatisrediscache"/> type = "com.kangaroo.studio.moonlight.dao.model.geofence"> <constructor> <iDarg column = "id" javatype = "java.lang.integer" jdbctype = "integer" /> column = "type" javatype = "java.lang.integer" jdbctype = "จำนวนเต็ม" /> <arg column = "กลุ่ม" javatype = "java.lang.string" jdbctype = "varchargar" /> <arg column = "geo" Javatype = "Java.lang column = "createTime" javatype = "java.lang.string" jdbctype = "varchar" /> <arg column = "updateTime" javatype = "java.lang.string" querype = "varchar" parameterType = "com.kangaroo.studio.moonlight.dao.model.geofenceQueryParam" resultmap = "geofencelist"> เลือก <รวม refid = "base_column"/> จาก geofence ที่ 1 = 1 <ถ้าทดสอบ = "ประเภท! = null" concat ('%', #{name}, '%') </ถ้า> <ถ้า test = "group! = null"> และ `group` ชอบ concat ('%', #{group}, '%') </ถ้า> <ถ้าทดสอบ =" เริ่มต้น " </if> </select> </mapper>สังเกต:
แท็กแคชภายใต้เนมสเปซคือการกำหนดค่าของการโหลดแคชและการใช้แคชถูกนำไปใช้อย่างเป็นทางการโดย MyBatisrediscache เราเพิ่งนำไปใช้
<cache type = "com.kangaroo.studio.moonlight.dao.cache.mybatisrediscache"/>
มีเพียงการสืบค้นแบบสอบถามเท่านั้นที่นำมาใช้ที่นี่ คุณสามารถเปิดหรือปิดแคชของ SQL นี้ในแท็กเลือก ใช้ค่าคุณสมบัติ usecache = true/false
7. Mapper and Model
อ่านและเขียนโมเดลแคชต้องได้รับการจัดลำดับ: จำเป็นต้องใช้อินเทอร์เฟซ seriaziable เมื่อมีการประกาศคลาส
Geofence ระดับสาธารณะดำเนินการ serializable {// setter และ getter imitted} public class geofenceparam ใช้ serializable {// setter และ getter ละเว้น}Mapper ยังคงเหมือนเดิม เมื่อใช้ mapper.xml คุณจะต้องกำหนดฟังก์ชั่นนามธรรมเท่านั้น
@mapperpublic อินเตอร์เฟส moonlightmapper {list <geofence> querygeofence (GeofenceQueryParam GeofenceQueryParam);}ณ จุดนี้รหัสและการกำหนดค่าทั้งหมดจะเสร็จสิ้นให้ทดสอบด้านล่าง
8. ทดสอบ
โพสต์อินเทอร์เฟซดังกล่าวถูกนำไปใช้ในคอนโทรลเลอร์
@RequestMapping (value = "/Fence/Query", method = requestMethod.post) @ResponseBody ResponseEntity Public <s Response> QueryFence (@requestbody GeofenceQueryParam GeofenceQueryParam) {ลอง {Integer จำนวนเต็ม pageSize = geofenceQueryParam.getPagesize ()! = null? geofenceQueryParam.getPagesize (): 10; pagehelper.startpage (pagenum, pagesize); รายการ <eofence> list = moonlightmapper.querygeOfence (GeofenceQueryParam); ส่งคืน ResponseEntity ใหม่ <> (การตอบกลับใหม่ (ResultCode.Success, "Query Geofence Success", รายการ), httpstatus.ok); } catch (exception e) {logger.error ("Query Geofence ล้มเหลว", e); ส่งคืน ResponseEntity ใหม่ <> (การตอบกลับใหม่ (ResultCode.Exception, "Query Geofence ล้มเหลว", null), httpstatus.internal_server_error); -ใช้ Curl เพื่อส่งคำขอหมายเหตุ
1) -H -ประเภทเนื้อหา: เมธอดแอปพลิเคชัน/JSON
2) -d - ต่อไปนี้เป็นแพ็คเกจพารามิเตอร์ในรูปแบบ JSON
Curl -h "เนื้อหาประเภท: แอปพลิเคชัน/json" -xpost http: // - - /Moonlight/Fence/Query -d '{"Name": "Test", "Group": "Test", "Type": 1, "Starttime": "2017-12-06 00:00:00", "Endtime": "2017-12-06 16:00:00"ร้องขอสามครั้งบันทึกจะถูกพิมพ์ดังนี้
อย่างที่คุณเห็นเพียงครั้งแรกที่การสืบค้นเทมเพลต SQL ถูกดำเนินการและแคชก็ถูกโจมตี
ในสภาพแวดล้อมการทดสอบของเราเนื่องจากข้อมูลจำนวนน้อยการเพิ่มประสิทธิภาพแคชของความเร็วในการสืบค้นไม่ชัดเจน ฉันจะไม่อธิบายมากที่นี่