Sử dụng một clob Oracle trong một vị ngữ được tạo từ Chuỗi> 4k


11

Tôi đang cố gắng tạo một clob từ một chuỗi> 4000 ký tự (được cung cấp trong biến liên kết file_data) để được sử dụng trong một vị từ Oracle SELECT bên dưới:

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

Nếu tôi thêm tập tin tròn TO_CLOB (), nó sẽ thất bại giới hạn 4k của Oracle khét tiếng đối với một varchar (sẽ tốt cho các chuỗi <4k). Lỗi (trong SQL Developer) là:

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

FYI Hàm flexmatch được sử dụng để tìm kiếm các phân tử và được mô tả ở đây: http://help.accelrysonline.com/ulm/onelab/1.0/content/ulm_pdfs/direct/developers/direct_2016_developersguide.pdf

Bản thân hàm này hơi phức tạp nhưng thực chất là tham số thứ 2 phải là một nút. Vì vậy, câu hỏi của tôi là làm cách nào để chuyển đổi một chuỗi Java bind_variable gồm hơn 4000 ký tự thành một ký tự theo sql (hoặc Java).

Tôi đã thử phương thức bên dưới (hoạt động khi chèn clobs) trong Java (Spring boot 2) bằng cách sử dụng:

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

Phương pháp này sẽ hoạt động nhưng không thành công với lỗi flexmatch hội tụ mà FYI là:

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"

Lưu ý rằng tôi đang sử dụng SpringBoot 2 nhưng tôi không thể sử dụng bất kỳ phương thức nào bằng cách sử dụng OracleConnection (thu được từ đối tượng Spring NamedParameterJdbcTemplate của tôi) để hoạt động (ngay cả trên clobs <4k), vì vậy tôi nghi ngờ mình đã làm điều gì đó ngu ngốc. Tôi đã thử:

 @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);

ứng dụng. sản phẩm:

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

Lưu ý rằng nó hoạt động tốt nếu tôi đi học cũ và sử dụng kết nối không phải là lò xo cộng với PreparedStatement, có phương thức 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();

Nhưng tôi thích giải pháp Spring 2 hơn trong Java hoặc Sql. Bất kỳ trợ giúp, đề nghị đánh giá cao.


Đây là một câu hỏi khá hay +1, khác với những gì tôi đã làm cho đến nay. Bạn có thể chỉ ra các tài liệu API cho flexmatch()hàm không? Tôi muốn thấy sự cần thiết cho việc này. Thành thật mà nói, tôi không bao giờ sử dụng các giá trị lớn làm tham số trong WHEREmệnh đề. Tôi đã sử dụng chúng trong INSERTvà, tôi đã lấy chúng bằng cách sử dụng SELECT. Trường hợp của bạn là khác nhau.
The Impaler

@The_impaler có một liên kết đến các tài liệu trong câu hỏi. Đó không phải là một api tôi sợ, nhưng đó là tất cả những gì chúng ta có. Đây là một chức năng rất thích hợp. Nhu cầu là tôi đang tìm kiếm một đại diện kỹ thuật số của một phân tử và bạn cần một chức năng chuyên môn để làm điều này. tức là phân tử tôi đã tồn tại trong bảng dcr_mols.
DS.

Phiên bản Oracle nào bạn đang sử dụng?
isus

@areaus ojdbc6-11.2.1.0.1
DS.

Câu trả lời:


5

Truyền phát nó Bạn không thể chỉ dán một giá trị lớn vào câu lệnh SQL.

Bạn sẽ cần:

  • Chèn một BLOB trống trong INSERTcâu lệnh (sử dụng EMPTY_BLOB ()? ... không nhớ lắm).
  • Lấy luồng đầu ra cho blob trống.
  • Sau đó nhận được một luồng đầu vào từ tập tin. Vui lòng không tải toàn bộ tệp trong bộ nhớ.
  • Sau đó chuyển các khối từ luồng đầu vào vào luồng đầu ra bằng cách sử dụng bộ đệm. Một bộ đệm 16 KB nên làm.
  • Đóng cả hai luồng.

Đây là cách xử lý tiêu chuẩn với dữ liệu lớn trong Oracle. Có rất nhiều ví dụ ngoài kia.

Lấy dữ liệu lớn ( BLOBCLOBcác loại) hoạt động theo cùng một cách. Chỉ cần sử dụng InputStreams trong trường hợp đó.


@The_impaler Tôi không chèn một cái áo choàng. Tôi đang cung cấp một clob cho một chức năng được gọi là vị ngữ của một lựa chọn
DS.

1

Đọc tài liệu về API "BIOVIA Direct", có một ví dụ thú vị trên trang 27, đoạn trích được hiển thị bên dưới:

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

Nó sử dụng một CLOB đã được tải . Do đó, tôi đoán rằng một giải pháp đủ tốt sẽ là tải CLOB vào một bảng của bạn (hoặc tải trước tất cả chúng), và sau đó chỉ cần sử dụng chúng.

Bước # 1 - Tải CLOB của bạn vào bảng:

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

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

Và sử dụng mã Java của bạn để thực hiện việc chèn CLOB (có thể sử dụng các luồng) như được hiển thị trong một câu trả lời khác (rất nhiều ví dụ trên Internet). Ví dụ: chèn nội dung dữ liệu mol của bạn với ID = 123.

Bước # 2 - Chạy truy vấn của bạn, sử dụng tệp mol đã được tải:

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

Bạn có thể đặt :idtham số để 123sử dụng tệp bạn đã tải trước đó (hoặc bất kỳ tệp nào khác).


@The_impaler Cảm ơn, nhưng đó là một chút búa tạ để bẻ đai ốc. Chúng tôi có rất nhiều truy vấn để chạy và điều này sẽ làm phức tạp mã và làm chậm mọi thứ. Tôi đã cập nhật câu hỏi của mình, với một câu trả lời cũ của trường, tôi sẽ miễn cưỡng sử dụng nếu không có gì tốt hơn.
DS.

1

Bạn có thể gọi chức năng thời trang cũ của bạn như thế này. tạo một lớp để xử lý các kết quả

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

và gọi truy vấn của bạn bằng cách sử dụng JdbcTemplate trong phương thức của bạn

 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

Tôi đã phải hoàn nguyên về việc sử dụng PreparedStatement, nhưng tôi đã cải thiện việc triển khai bình thường một chút bằng cách nhận kết nối từ Spring và sử dụng apache commons BeanListHandler để ánh xạ Kết quả vào Danh sách đối tượng

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

Bạn có thể khai báo fileDataStr là CLOB bằng cách sử dụng con là kết nối

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

và sau đó sử dụng nó như dưới đây

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

Ngoài ra nếu bạn đang sử dụng SID thay vì tên Dịch vụ trong chuỗi kết nối của mình, hãy thử thay đổi tệp thuộc tính của bạn như bên dưới

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

Cảm ơn, nhưng điều đó rất khó xử. Dữ liệu có thể là bất kỳ kích thước nào, vì vậy tôi sẽ phải sử dụng sql động để tạo ra các khối, ngoài ra, tôi không chắc chắn bạn có thể phá vỡ một clob thành các phần như thế này.
DS.

loại tập tinDataStr
psaraj12

Đó là một chuỗi java
DS.

bạn có thể khai báo nó là CLOB và như java.sql.Clob fileDataStr = oracle.sql.CLOB.createT tạm (con, false, oracle.sql.CLOB.DURATION_SESSION);
psaraj12

xem ví dụ của tôi trong nhận xét về cách khai báo fileDataStr là CLOB nơi con đang kết nối
psaraj12
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.