以下のOracle SELECT述語で使用するために、4000文字を超える文字列(file_dataバインド変数で提供)からCLOBを作成しようとしています。
myQuery=
select *
from dcr_mols
WHERE flexmatch(ctab,:file_data,'MATCH=ALL')=1;
TO_CLOB()ラウンドfile_dataを追加すると、varcharの悪名高いOracle 4k制限に失敗します(<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番目のパラメーターがCLOBでなければなりません。だから私の質問はどのように4000文字以上のJava String bind_variableをSQL(またはJava)のCLOBに変換するかです。
以下を使用して、以下の方法(CLOBを挿入するときに機能します)をJava(Spring boot 2))で試しました。
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("file_data", fileDataStr,Types.CLOB);
jdbcNamedParameterTemplate.query(myQuery,parameters,…
この方法は機能するはずですが、FYIである収束したflexmatchエラーで失敗します。
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を使用していますが、Oracle Connection(Spring NamedParametersJdbcTemplateオブジェクトから取得)を使用して(clobs <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);
application.properties:
spring.datasource.url=jdbc:Oracle:thin:@//${ORA_Host}:${ORA_PORT}/${ORA_SID}
spring.datasource.username=${ORA_USER}
spring.datasource.password=${ORA_PASS}
私が古い学校に行き、スプリング以外の接続に加えて、setClob()メソッドを持つPreparedStatementを使用する場合は問題なく機能することに注意してください。
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();
しかし、私はJavaまたはSql)でSpring 2ソリューションを好みます。
PreparedStatementを使用する必要がありましたが、Springから接続を取得し、Apache commons BeanListHandlerを使用してResultSetをオブジェクトリストにマップすることで、通常の実装を少し改善しました
import org.Apache.commons.dbutils.ResultSetHandler;
import org.Apache.commons.dbutils.handlers.BeanListHandler;
@Autowired
DataSource dataSource;
List<MyDao> myMethod(String fileData){
String myQuery="select * from dcr_mols WHERE flexmatch(ctab,?,'MATCH=ALL')=1";
try {
Connection conn = this.dataSource.getConnection() // 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;
}
「BIOVIA Direct」APIのドキュメントを読むと、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挿入を実行します。たとえば、ID = 123
でmolデータコンテンツを挿入します。 。
ステップ#2-既にロードされたmolファイルを使用してクエリを実行します:
select *
from dcr_mols
WHERE flexmatch(
ctab,
(select content from mol_file where id = :id),
'MATCH=ALL'
) = 1;
:id
パラメータを123
に設定して、以前にロードしたファイル(またはその他のファイル)を使用できます。
このようにして、古いファッション関数を呼び出すことができます。 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());
接続であるconを使用して、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}