ก่อนอื่นให้เข้าใจว่าประเภท XMLTYPE คืออะไร
XMLType เป็นประเภทข้อมูลที่ไม่ซ้ำกันสำหรับ Oracle ตั้งแต่ 9i มันคือการดำรงอยู่ที่ทรงพลังที่สืบทอดหยด สามารถใช้ในการจัดเก็บ XML และให้ฟังก์ชั่นการทำงานจำนวนมาก ในทางทฤษฎีมันสามารถบันทึกข้อมูล 2G
แล้วคุณจะแทรกข้อมูล XMLType ผ่าน Java ได้อย่างไร? โครงการใช้ mybatis และมีข้อยกเว้นที่อธิบายไม่ได้เสมอ ฉันไม่สามารถเข้าใจได้ว่ามันเป็นปัญหาของ mybatis หรือ JDBC เองดังนั้นฉันจึงวางแผนที่จะทำทีละขั้นตอนก่อนแก้ JDBC ก่อนแล้วจึงแก้ mybatis
JDBC
หลังจากการต่อสู้เป็นเวลานานฉันพบว่ามีสามวิธีหลักสำหรับการดำเนินงาน JDBC:
1. ใช้ XMLType เป็นสตริงใน Java และงานเฉพาะของการสร้าง XMLType นั้นถูกส่งไปยังฐานข้อมูลอย่างสมบูรณ์:
String SQL = "แทรกลงในค่า XMLTable (XML) (sys.xmltype.createxml (?))"; String xmldata = "<label> นี่คือชิ้นส่วน XML </label>"; ps.setstring (1, xmldata); ps.executeUpdate ();
วิธีนี้จะทำให้ความเครียดของฐานข้อมูลมากเกินไปเนื่องจากวิธีนี้ง่ายและไม่ต้องการการอ้างอิงเพิ่มเติม วิธีนี้ใช้ในตอนแรก แต่ในระหว่างการใช้งานจริงพบว่าเมื่อความยาวของเนื้อหาเกินกว่าประมาณ 4,000 มันจะโยน: ORA-01461: สามารถผูกค่ายาวสำหรับการแทรกลงในคอลัมน์ยาว ตอนแรกฉันคิดว่าเหตุผลในการใช้ mybatis ยังคงเหมือนเดิมเมื่อใช้การทดสอบ JDBC และไม่มีวิธีแก้ปัญหาเมื่อใช้วิธีการมากมาย เป็นไปไม่ได้ที่จะบันทึกข้อมูลด้วยความยาวน้อยกว่า 4,000 เมื่อใช้ฟิลด์ขนาดใหญ่นี้ในโครงการ ด้วยวิธีนี้การใช้ VARCHAR2 นั้นเพียงพอดังนั้นวิธีนี้จะถูกกำจัด
2. ใช้ประเภท CLOB เพื่อใช้งาน XMLType สืบทอดการดำรงอยู่ของ CLOB ดังนั้นจึงสามารถดำเนินการผ่าน CLOB วิธีการคือการสร้างข้อมูล CLOB บนไคลเอนต์และส่งผ่านไปยังฐานข้อมูลเพื่อสร้างค่า XMLTYPE ผ่านฟังก์ชั่น XMLTYPE () ของ Oracle:
String SQL = "แทรกลงในค่า XMLTable (XML) (XMLTYPE (?))"; String xmldata = "<label> นี่คือชิ้นส่วน XML </label>"; // สร้าง clobclob tempclob = clob.createTemporary (การเชื่อมต่อ, false, clob.duration_session); // เปิด clobtempclob.open (clob.mode_readwrite); // รับ writerwriter clobwriter = tempclob.setcharactersstream (100); // เขียนข้อมูล clobwriter.write (xmldata); // fresh clobwriter.flush (); // close writerclobwriter.close (); // ปิด clobtempclob.close (); pst.setObject (1, tempclob);
ไคลเอนต์วิธีการนี้และฐานข้อมูลมีหน้าที่สร้าง XMLType ในเวลาเดียวกันดังนั้นความดันจึงค่อนข้างเฉลี่ยและไม่มีปัญหาในการเกินความยาว อย่างไรก็ตามในระหว่างการใช้งานจริงพบว่าส่วนหัวเนื้อหาของ XML ไม่สามารถมีข้อมูลต่อไปนี้:
<? xml version = "1.0" encoding = "utf-8"?>?
มิฉะนั้นจะมีการโยนข้อยกเว้น:
ชื่อ PI เริ่มต้นด้วย XML สงวนไว้
อย่าพูดคุยกันว่าคุณจะพบปัญหารหัสที่อ่านไม่ออกเมื่อประมวลผลเนื้อหาของการรวม XML ในภาษาจีนในอนาคตหรือไม่ เพียงแค่มองมันทำให้ผู้คนรู้สึกอึดอัดและข้อกำหนดก็ต้องประหยัดเช่นกัน ไม่มีทางและวิธีนี้จะไม่ทำงาน
3. ใช้คลาส oracle.xdb.xmltype ที่จัดทำโดย Oracle หลังจากไคลเอนต์สร้าง XMLType วัตถุจะถูกส่งผ่านไปยังฐานข้อมูลโดยตรง:
Connection conn = ... ; // รับการเชื่อมต่อ preparedStatement ps = ... ; // รับสตริงที่เตรียมการ sql = "แทรกลงในค่า xmltable (xml) (?)"; String xmldata = "<label> นี่คือชิ้นส่วน XML </label>"; // สร้างวัตถุ XMLTYPE XMLTYPE XMLTYPE = XMLTYPE.CREATEXML (Conn, XMLDATA); ps.setObject (1, xmltype); ps.executeUpdate ();
วิธีนี้ส่งมอบงานสร้าง XMLType ให้กับไคลเอนต์อย่างสมบูรณ์ดังนั้นไคลเอนต์จึงอยู่ภายใต้แรงกดดันอย่างมากและฐานข้อมูลอยู่ภายใต้ความดันต่ำ ในระหว่างการทดสอบจริงจำเป็นต้องเพิ่มแพ็คเกจ JAR สองชุดมิฉะนั้นจะไม่พบข้อผิดพลาดในชั้นเรียน:
xdb.jarxmlparserv2.jar
จำเป็นต้องทราบว่าแพ็คเกจ JAR นี้ไม่มีคำอธิบายประกอบเวอร์ชันดังนั้นจึงเป็นเรื่องง่ายที่จะทำผิดพลาด ในตอนแรกฉันดาวน์โหลด XDB.JAR แต่ไม่ว่าฉันจะทำอย่างไรมันก็พร้อมที่จะหาชั้นเรียนไม่ได้ หลังจากตรวจสอบฉันพบว่ามันเป็นของ Oracle รุ่นก่อนหน้า หลังจากดาวน์โหลด XDB.JAR อีกครั้งมันเป็นเรื่องปกติ
สามวิธีข้างต้นถูกเปรียบเทียบโดยการแทรกข้อมูล 200,000 ชิ้น:
วิธีแรก: เวลาที่สั้นที่สุดและการบริโภค CPU ของเซิร์ฟเวอร์นั้นใหญ่ที่สุด
วิธีที่สอง: ใช้เวลานานที่สุดและการบริโภค CPU ของเซิร์ฟเวอร์เป็นศูนย์กลาง
วิธีที่สาม: ใช้เวลานานและเป็นศูนย์กลางการบริโภค CPU ของเซิร์ฟเวอร์นั้นน้อยที่สุด
ณ จุดนี้ JDBC ได้ทำสิ่งเล็ก ๆ น้อย ๆ ในการทำงานของข้อมูลประเภท XMLType ไม่จำเป็นต้องพูดวิธีแก้ปัญหาที่สามถูกนำมาใช้ แต่โดยทั่วไปแล้วโครงการไม่ได้ใช้ JDBC โดยตรงเพื่อใช้งาน ตัวอย่างเช่นในโครงการปัจจุบันใช้ mybatis ข้างต้นยังกล่าวว่ามีข้อยกเว้นเสมอเมื่อใช้ mybatis หลังจากตรวจสอบ mybatis ไม่มีการใช้งาน XMLType ดูเหมือนว่ายังมีปัญหาอยู่บ้าง แต่ JDBC ได้ทำไปแล้วดังนั้นความคิดนั้นชัดเจนใช่ไหม?
mybatis
การใช้ mybatis เพื่อใช้งาน XMLType เรายังแมปเป็นประเภทสตริงที่ด้าน Java เมื่อการดำเนินการโดยตรงไม่ได้ทำการประมวลผลใด ๆ เช่น JDBC ทุกอย่างเป็นเรื่องปกติเมื่อเนื้อหาที่ส่งน้อยกว่า 4000 เมื่อเนื้อหาที่ส่งมากกว่าประมาณ 4000 ข้อยกเว้นก็จะถูกโยนลง:
ORA-01461: สามารถผูกค่ายาวสำหรับการแทรกลงในคอลัมน์ยาวได้
จะเห็นได้ว่าการดำเนินการของ MyBatis นั้นเหมือนกับ JDBC ยกเว้นว่ามันจะห่อหุ้มเลเยอร์นอก JDBC เพื่อให้เราสามารถใช้ไฟล์การกำหนดค่าและวิธีการแมปอื่น ๆ เพื่อเข้าถึงฐานข้อมูลได้อย่างสะดวกยิ่งขึ้น สิ่งที่เราต้องทำคือการแทรกข้อมูลประเภท XMLType ตามความสะดวกสบายของ MyBatis ดั้งเดิม ในกรณีนี้การใช้โปรเซสเซอร์ TypeHandler แบบกำหนดเองของประเภท XMLType เป็นตัวเลือกที่ดีที่สุด
ที่นี่เรายังคงใช้โซลูชันที่สามที่กล่าวถึงข้างต้น ตามธรรมชาติแพ็คเกจสองขวด: XDB.JAR และ XMLPARSERV2.JAR ยังต้องเพิ่มด้วย
เพิ่ม XMLTypetypehandler เพื่อใช้อินเตอร์เฟส typeHandler เนื่องจากการแทรกข้อมูลส่วนใหญ่ใช้วิธีการ setparameter วิธีนี้เท่านั้นที่แสดงอยู่ที่นี่ รหัสวิธีอื่นถูกละเว้น:
/*** oracle sys.xmltype โปรเซสเซอร์ที่กำหนดเอง*/คลาสสาธารณะ XMLTYPETYPEHANDLER ใช้ TypeHandler <String> {@Override โมฆะสาธารณะ setParameter (PreparedStatement PS, int i, พารามิเตอร์สตริง, JDBCTYPE JDBCTYPE)MyBatis ใช้วิธีการ setParameter เพื่อตั้งค่าพารามิเตอร์เมื่อแทรกข้อมูลลงในฐานข้อมูล สำหรับพารามิเตอร์ของวิธีนี้ฉันเชื่อว่าคุณเข้าใจรหัสแล้ว เราจะแทรกรหัสต่อไปนี้ที่นี่ตามวิธีการใช้งาน JDBC ก่อนหน้านี้:
โมฆะสาธารณะ setParameter (PreparedStatement PS, int i, พารามิเตอร์สตริง, jdbctype jdbctype) พ่น sqlexception {xmltype xmltype = xmltype.createxml (ps.getConnection (), พารามิเตอร์); ps.setObject (i, xmltype);}และลงทะเบียนตัวแปลงใน mapper-config.xml เนื่องจากในการแจงนับที่กำหนดโดย mybatis org.apache.ibatis.type.jdbctype ไม่มีประเภท xmltype ที่เราต้องการที่นี่
<การกำหนดค่า> <PypeHandlers> <typeHandler javatype = "String" jdbcType = "undefined" handler = "com.tyyd.dw.context.xmltypetypefandler"/> </typehandlers>
ในพารามิเตอร์ไฟล์การกำหนดค่าใช้ตัวแปลงที่กำหนดไว้ของเราเพื่อให้ mybatis สามารถค้นหาได้:
#{xmlfile, jdbcType = undefined}แน่นอนคุณสามารถเป็นมาตรฐานมากขึ้นและเขียนประเภทและตัวแปลงที่คุณใช้อย่างสมบูรณ์:
#{xmlfile, javatype = string, jdbctype = undefined, typeHandler = com.tyyd.dw.context.xmltypetypephandler},
ทำตามขั้นตอนข้างต้นและทำทุกอย่างอย่างมีเหตุผลให้เรียกใช้กันเถอะ
ผลลัพธ์ถูกโยนลงไป: java.lang.ClassCastException: org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to oracle.jdbc.OracleConnection
ไม่สามารถแปลงเป็น Oracleconnection Oracleconnection ของ Oracleconnection ได้ หลังจากตรวจสอบแล้วเราพบว่าแหล่งข้อมูลของเราใช้ DBCP ของ Apache ซึ่งควรจะเข้ากันไม่ได้กับทั้งสอง ฉันตรวจสอบออนไลน์และผู้ชายคนหนึ่งบอกว่าเขาให้โซลูชันที่สมบูรณ์แบบซึ่งก็คือการโหลดคลาสไดรเวอร์ Oracle ในวิธี SetParameter เพื่อสร้างการเชื่อมต่อดังนี้:
class.forName ("oracle.jdbc.oracledriver"); การเชื่อมต่อการเชื่อมต่อ = drivermanager.getConnection (URL, ชื่อผู้ใช้, รหัสผ่าน);สิ่งนี้สามารถแก้ปัญหาที่วัตถุการเชื่อมต่อไม่สามารถแปลงได้ 100%แต่ในแง่ของการใช้งานฮ่าฮ่าฉันยังคงไม่แสดงความคิดเห็น นอกจากนี้ยังมีผู้คนที่ผ่านอินเทอร์เน็ตโดยบอกว่าพวกเขาสามารถแปลงเป็นวัตถุ PoolableConnection จากนั้นใช้วิธี getDelegate เพื่อรับลิงค์พร็อกซีดั้งเดิม ดูเหมือนจะเป็นไปได้ลองมาดูกัน:
การเชื่อมต่อ placleConnection = (placleConnection) ps.getConnection (); xmlType xmlType = xmlType.createxml (connection.getDelegate (), พารามิเตอร์); ps.setObject (i, xmltype);
เป็นผลให้มีข้อยกเว้นอีกประการหนึ่งคือการโยน:
org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to org.apache.commons.dbcp.PoolableConnection ไม่สามารถแปลงได้
ไม่มีทางดูเหมือนว่าบทความที่หมุนเวียนออนไลน์ไม่น่าเชื่อถือดังนั้นจึงไม่มีทางลัดดังนั้นคุณควรตรวจสอบซอร์สโค้ดด้วยตัวเอง
โดยการดูที่ซอร์สโค้ดเราพบว่าการเชื่อมต่อแบบ plarableConnection สืบทอดคลาส DelegatingConnection และคลาส DelegatingConnection ใช้อินเตอร์เฟสการเชื่อมต่อ ลองแปลงเป็นตัวแทนที่ได้รับมอบหมายให้ลอง:
DelegatingConnection Connection = (DelegatingConnection) ps.getConnection (); xmlType xmlType = xmlType.createxml (Connection.getDelegate (), พารามิเตอร์); ps.setObject (i, xmltype);
เป็นผลให้มีข้อยกเว้นถูกโยนลงไป: ไม่สามารถสร้างคำอธิบาย: อาร์กิวเมนต์ที่ไม่ถูกต้อง; ข้อยกเว้นที่ซ้อนกันคือ java.sql.sqlexception: ไม่สามารถสร้าง descriptors: อาร์กิวเมนต์ที่ไม่ถูกต้องผ่านการดีบักเบรกพอยต์ฉันพบว่าวัตถุการเชื่อมต่อนั้นเป็นโมฆะจริง มันจะเป็นโมฆะได้อย่างไร? ผู้คนบนอินเทอร์เน็ตใช้งานได้ดี แต่มันจะไม่ทำงานกับฉัน มันเป็นความเจ็บปวดจริงๆ สิ่งนี้ไม่สามารถแก้ไขได้ คุณต้องโหลดคลาสไดรเวอร์เพียงอย่างเดียวอย่างที่ผู้ชายข้างบนพูดหรือไม่? ไม่มีทางลองศึกษาอีกครั้ง
ในที่สุดฉันก็พบว่าการเชื่อมต่อพร็อกซีดั้งเดิมสามารถรับได้ผ่านวิธี getMetadata มันสว่างมากและการทดสอบนั้นชัดเจนมาก ในที่สุดมันก็เป็นเรื่องปกติและไม่ใช่เรื่องง่าย รหัสสุดท้ายมีดังนี้:
@OverridePublic เป็นโมฆะ setParameter (PreparedStatement PS, int i, พารามิเตอร์สตริง, JDBCTYPE JDBCTYPE) พ่น sqlexception {มอบหมายการเชื่อมต่อการเชื่อมต่อ = (มอบหมายการเชื่อมต่อ) ps.getConnection () getMetAdata () XMLTYPE XMLTYPE = XMLTYPE.CREATEXML (Connection.getDelegate (), พารามิเตอร์); ps.setObject (i, xmltype);}ณ จุดนี้การใช้ mybatis เพื่อใช้งานประเภท XMLType ได้ดำเนินการในที่สุดและกระบวนการดังกล่าวเต็มไปด้วยการบิดและเปลี่ยน แน่นอนจะต้องมีการสืบค้นเมื่อมีการแทรกข้อมูล ต่อไปเราจำเป็นต้องใช้การดำเนินการค้นหาประเภท XMLType