ใช้ Oracle clob ในเพรดิเคตที่สร้างจาก String> 4k


11

ฉันกำลังพยายามสร้าง clob จากสตริง> 4,000 chars (จัดทำในตัวแปรผูก file_data) เพื่อใช้ในเพรดิเคต Oracle SELECT ด้านล่าง:

myQuery=
select *
from dcr_mols
WHERE flexmatch(ctab,:file_data,'MATCH=ALL')=1;

ถ้าฉันเพิ่ม TO_CLOB () round file_data มันจะไม่ จำกัด Oracle 4k ที่น่าอับอายสำหรับ varchar (ใช้ได้กับสตริง <4k) ข้อผิดพลาด (ใน SQL Developer) คือ:

ORA-01460: unimplemented or unreasonable conversion requested
01460. 00000 -  "unimplemented or unreasonable conversion requested"

FYI ฟังก์ชัน flexmatch ใช้สำหรับค้นหาโมเลกุลและอธิบายไว้ที่นี่: http://help.accelrysonline.com/ulm/onelab/1.0/content/ulm_pdfs/direct/developers/direct_2016_developersguide.pdf

ฟังก์ชั่นนั้นค่อนข้างซับซ้อน แต่สาระสำคัญคือพารามิเตอร์ที่ 2 จะต้องมีการปิดบัง ดังนั้นคำถามของฉันคือฉันจะแปลง Java String bind_variable ของ 4000 chars เป็น clob ใน sql (หรือ Java) ได้อย่างไร

ฉันลองวิธีด้านล่าง (ซึ่งใช้งานได้เมื่อใส่ clobs) ใน Java (Spring boot 2) โดยใช้:

MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("file_data", fileDataStr,Types.CLOB);
jdbcNamedParameterTemplate.query(myQuery,parameters,…

วิธีนี้ควรใช้งานได้ แต่ล้มเหลวโดยมีข้อผิดพลาด flexmatch ที่มาบรรจบกันซึ่ง FYI คือ:

SQL state [99999]; error code [29902]; ORA-29902: error in executing ODCIIndexStart() routine\nORA-20100: 
MDL-0203: Unable to read from CLOB (csfrm=1, csid=873): 
ORA-22922: nonexistent LOB value\nMDL-0021: Unable to copy LOB to string\nMDL-1051: Molstructure search query is not a valid molecule\nMDL-0976: 
Molecule index search initialization failed\nORA-06512: at \"C$MDLICHEM80.MDL_MXIXMDL\", line 329\nORA-06512: at \"C$MDLICHEM80.MDL_MXIXMDL\", line 309\n; nested exception is java.sql.SQLException: 
ORA-29902: error in executing ODCIIndexStart() routine\nORA-20100: MDL-0203: Unable to read from CLOB (csfrm=1, csid=873): 
ORA-22922: nonexistent LOB value\nMDL-0021: Unable to copy LOB to string\nMDL-1051: Molstructure search query is not a valid molecule\nMDL-0976: 
Molecule index search initialization failed\nORA-06512: at \"C$MDLICHEM80.MDL_MXIXMDL\", line 329\nORA-06512: at \"C$MDLICHEM80.MDL_MXIXMDL\", line 309\n"

หมายเหตุฉันใช้ SpringBoot 2 แต่ฉันไม่สามารถรับวิธีการใด ๆ โดยใช้ OracleConnection (ได้รับจากวัตถุ Spring NamedParametersJdbcTemplate ของฉัน) เพื่อใช้งานได้ (แม้จะอยู่ในพื้นที่ <4k) ดังนั้นฉันสงสัยว่าฉันทำอะไรโง่ ๆ ฉันได้พยายาม:

 @Autowired
 NamedParameterJdbcTemplate  jdbcNamedParameterTemplate;
OracleConnection conn =  this.jdbcNamedParameterTemplate.getJdbcTemplate().getDataSource().getConnection().unwrap(OracleConnection.class);
Clob myClob =  conn.createClob();
myClob.setString( 1, fileDataStr);
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("file_data", myClob,Types.CLOB);

แอพลิเคชันคุณสมบัติ:

spring.datasource.url=jdbc:oracle:thin:@//${ORA_HOST}:${ORA_PORT}/${ORA_SID}
spring.datasource.username=${ORA_USER}
spring.datasource.password=${ORA_PASS}

โปรดทราบว่ามันใช้งานได้ดีถ้าฉันไปโรงเรียนเก่าและใช้การเชื่อมต่อที่ไม่ใช่สปริงบวกกับ PreparedStatement ซึ่งมีเมธอด setClob ():

OracleDataSource ods = new OracleDataSource();
String url ="jdbc:oracle:thin:@//" + ORA_HOST +":"+ORA_PORT +"/"+ORA_SID;
ods.setURL(url);
ods.setUser(user);
ods.setPassword(passwd);
Connection conn = ods.getConnection();
Clob myClob=conn.createClob();
PreparedStatement ps = conn.prepareStatement("select dcr_number from dcr_mols WHERE flexmatch(ctab,?,'MATCH=ALL')=1");
myClob.setString(1,myMol);
ps.setClob(1,myClob);
ResultSet rs =ps.executeQuery();

แต่ฉันต้องการโซลูชัน Spring 2 ใน Java หรือ SQL ความช่วยเหลือข้อเสนอแนะชื่นชม


นี่เป็นคำถามที่ดี +1 ซึ่งแตกต่างจากสิ่งที่ฉันทำไปแล้ว คุณสามารถชี้ให้เห็นเอกสาร API สำหรับflexmatch()ฟังก์ชั่นได้หรือไม่? ฉันต้องการเห็นความจำเป็นสำหรับสิ่งนี้ สุจริตฉันไม่เคยใช้ค่าขนาดใหญ่เป็นพารามิเตอร์ในWHEREข้อ ฉันใช้มันINSERTแล้วฉันก็ใช้มันกลับคืนSELECTมา กรณีของคุณแตกต่าง
The Impaler

@The_impaler มีลิงก์ไปยังเอกสารในคำถาม มันไม่ใช่ api im กลัว แต่มันคือทั้งหมดที่เรามี นี่คือฟังก์ชั่นเฉพาะมาก ความต้องการคือฉันกำลังค้นหาการแสดงโมเลกุลของดิจิทัลและคุณต้องการฟังก์ชันผู้เชี่ยวชาญเพื่อทำสิ่งนี้ นั่นคือโมเลกุลที่ฉันมีอยู่แล้วในตาราง dcr_mols
DS

คุณใช้ Oracle เวอร์ชันใดอยู่
areus

@areaus ojdbc6-11.2.1.0.1
DS

คำตอบ:


5

สตรีมมัน คุณไม่สามารถเพียงวางค่าขนาดใหญ่ลงในคำสั่ง SQL

คุณจะต้อง:

  • แทรก BLOB ที่ว่างเปล่าในINSERTคำสั่ง (โดยใช้ EMPTY_BLOB ()? ... ยังจำไม่ได้)
  • รับเอาต์พุตสตรีมสำหรับ blob ที่ว่างเปล่า
  • จากนั้นรับสตรีมอินพุตจากไฟล์ โปรดอย่าโหลดไฟล์ทั้งหมดในหน่วยความจำ
  • จากนั้นถ่ายโอนบล็อกจากอินพุตสตรีมไปยังสตรีมเอาต์พุตโดยใช้การบัฟเฟอร์ บัฟเฟอร์ 16 KB ควรทำ
  • ปิดสตรีมทั้งสอง

นี่เป็นวิธีมาตรฐานในการจัดการกับข้อมูลจำนวนมากใน Oracle มีตัวอย่างมากมายที่นั่น

การดึงข้อมูลขนาดใหญ่ ( BLOBและCLOBประเภท) ทำงานในลักษณะเดียวกัน เพียงแค่ใช้ InputStreams ในกรณีนั้น


@The_impaler ฉันไม่ได้ใส่เสื้อคลุม ฉันกำลังส่ง clob ให้กับฟังก์ชั่นที่ถูกเรียกใช้ในเพรดิเคตของตัวเลือก
DS

1

การอ่านเอกสารของ API "BIOVIA Direct" มีตัวอย่างที่น่าสนใจในหน้า 27 ข้อความที่ตัดตอนมาแสดงอยู่ด้านล่าง:

select ...
from ...
where flexmatch(
ctab,
(select ctab from nostruct_table),
'all'
)=1

จะใช้โหลดแล้ว CLOB ดังนั้นฉันเดาว่าทางออกที่ดีพอคือการโหลด CLOB ลงในตารางของคุณ (หรือโหลดล่วงหน้าทั้งหมดล่วงหน้า) แล้วใช้มัน

ขั้นตอน # 1 - โหลด CLOB ของคุณลงในตาราง:

create table mol_file (
  id number(12) primary key not null,
  content clob
);

insert into mol_file (id, content) values (:id, :content);

และใช้รหัส Java ของคุณเพื่อดำเนินการแทรก CLOB (อาจใช้กระแสข้อมูล) ดังแสดงในคำตอบอื่น (ตัวอย่างมากมายในอินเทอร์เน็ต) ตัวอย่างเช่นแทรกเนื้อหาข้อมูล mol ของคุณด้วย ID 123=

ขั้นตอนที่ # 2 - เรียกใช้แบบสอบถามของคุณโดยใช้ไฟล์ mol ที่โหลดแล้ว:

select *
from dcr_mols
WHERE flexmatch(
        ctab,
        (select content from mol_file where id = :id),
        'MATCH=ALL'
      ) = 1;

คุณสามารถตั้งค่า:idพารามิเตอร์ให้123ใช้ไฟล์ที่คุณโหลดมาก่อน (หรือไฟล์อื่น)


@The_impaler ขอบคุณ แต่นั่นเป็นบิตของค้อนขนาดใหญ่ที่จะแตกถั่ว เรามีข้อความค้นหาจำนวนมากที่ต้องเรียกใช้ซึ่งจะทำให้โค้ดซับซ้อนและทำให้ช้าลง ฉันได้อัปเดตคำถามของฉันด้วยคำตอบที่โรงเรียนเก่าซึ่งฉันจะใช้อย่างไม่เต็มใจถ้าไม่มีอะไรดีกว่า
DS

1

คุณสามารถเรียกฟังก์ชั่นแฟชั่นเก่าของคุณเช่นนี้ สร้าง Class เพื่อจัดการกับ ResultSet

 class MyPreparedStatementCallback implements PreparedStatementCallback {
    public Object doInPreparedStatement(PreparedStatement preparedStatement)
            throws SQLException, DataAccessException {
        ResultSet rs = preparedStatement.executeQuery();
        List result = new LinkedList();
        rs.close();
        return result;
    }
}

และโทรแบบสอบถามของคุณโดยใช้ JdbcTemplate ในวิธีการของคุณ

 jdbcTemplate.execute(new PreparedStatementCreator() {
        @Override
        public PreparedStatement createPreparedStatement(Connection connection)

                throws SQLException, DataAccessException {

            PreparedStatement ps = connection.prepareStatement("select dcr_number from dcr_mols WHERE flexmatch(ctab,?,'MATCH=ALL')=1");
            Clob myClob =  connection.createClob();
            myClob.setString( 1, fileDataStr);
            MapSqlParameterSource parameters = new MapSqlParameterSource();
            parameters.addValue("file_data", myClob, Types.CLOB);
            ps.setClob(1,myClob);
            return ps;

        };
    }, new MyPreparedStatementCallback());

1

ฉันต้องเปลี่ยนกลับไปใช้ PreparedStatement แต่ฉันได้ปรับปรุงการใช้งานปกติเล็กน้อยโดยรับการเชื่อมต่อจาก Spring และใช้ apache คอมมอนส์ BeanListHandler เพื่อแมปชุด ResultSet กับรายการวัตถุ

import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

@Autowired
NamedParameterJdbcTemplate  jdbcTemplate;

List<MyDao> myMethod(String fileData){
    String myQuery="select * from dcr_mols WHERE flexmatch(ctab,?,'MATCH=ALL')=1";

try {
    Connection conn =  this.jdbcTemplate.getJdbcTemplate().getDataSource().getConnection().unwrap(OracleConnection.class);   // Get connection from spring

    Clob myClob =  conn.createClob();   // Open a dB clob 
    myClob.setString( 1, fileData);     // add data to clob
    PreparedStatement ps = conn.prepareStatement(myQuery);
    ps.setClob(1,myClob);              // Add a clob into the PreparedStatement
    ResultSet rs =ps.executeQuery();   // Execute the prepared statement

    //ResultSetHandler<List<MyDao>> handler = new BeanListHandler<MyDao>(MyDao.class);   // Define the ResultSet handler
    ResultSetHandler<List<MyDao>> handler = new BeanListHandler<MyDao>(MyDao.class, new BasicRowProcessor(new GenerousBeanProcessor()));  // This is better than the above handler , because GenerousBeanProcessor removes the requirement for the column names to exactly match the java variables

    List<MyDao> myDaoList = handler.handle(rs);   // Map ResultSet to List of MyDao objects
    }catch (Exception e) {
        e.printStackTrace();
    }

return myDaoList;
}

0

คุณสามารถประกาศ fileDataStr เป็น CLOB โดยใช้การเชื่อมต่อซึ่งเป็น

java.sql.Clob fileDataStr = oracle.sql.CLOB.createTemporary
(con, false, oracle.sql.CLOB.DURATION_SESSION);

จากนั้นใช้มันเหมือนด้านล่าง

 parameters.addValue("file_data", fileDataStr,Types.CLOB);

นอกจากนี้หากคุณกำลังใช้ SID แทนชื่อบริการในสตริงการเชื่อมต่อของคุณลองเปลี่ยนไฟล์คุณสมบัติเช่นด้านล่าง

spring.datasource.url=jdbc:oracle:thin:@//${ORA_HOST}:${ORA_PORT}:${ORA_SID}

ขอบคุณ แต่นั่นมันช่างน่าอึดอัดใจมาก ข้อมูลสามารถมีขนาดใดก็ได้ดังนั้นฉันต้องใช้ dynamic sql เพื่อสร้างชิ้นข้อมูลเช่นกันฉันไม่แน่ใจว่าคุณสามารถแยกชิ้นส่วนออกเป็นชิ้นส่วนเช่นนี้ได้
DS

fileDataStr ประเภทใด
psaraj12

มันเป็นจาวาสตริง
DS

คุณสามารถประกาศเป็น CLOB และเช่น java.sql.Clob fileDataStr = oracle.sql.CLOB.createTemporary (แย้ง, เท็จ, oracle.sql.CLOB.DURATION_SESSION);
psaraj12

ดูตัวอย่างของฉันในความคิดเห็นเกี่ยวกับวิธีการประกาศ fileDataStr เป็น CLOB ซึ่งการเชื่อมต่อคือการเชื่อมต่อ
psaraj12
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.