springã®DBã¢ã¯ã»ã¹ã«ã¯JdbcTemplate
ãå¾æ¥ããåå¨ãã¦ãããç¾å¨ã§ã¯å¾ç¶ã®JdbcClient
ãå©ç¨å¯è½ã§ãããã¹ãã¢ãããã·ã¼ã¸ã£ã«ã¯SimpleJdbcCall
ã使ç¨ããã
æ¬ã¨ã³ããªã®æ³¨æç¹ã¨ãã¦ãç§ã¯ãã¾ãOracleã®ã¹ãã¢ãããã·ã¼ã¸ã£ã«è©³ãããªãããã®ããç¨èªãæ£ãã使ãã¦ããªãå¯è½æ§ãããã
ç°å¢
- Oracle Database 23ai Free Release 23.0.0.0.0
docker run --name oracle_queue -p 11521:1521 -e ORACLE_PWD="Oracle23" container-registry.oracle.com/database/free:23.5.0.0-lite
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.1'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.oracle.database.jdbc:ojdbc11'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
以éã®Javaã®ãµã³ãã«ã³ã¼ãã«ã¯importãã¯ã©ã¹å®£è¨ãªã©ã¯çç¥ãããããããã以ä¸ã®ããã«ãªã£ã¦ããã
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
@Service
@RequiredArgsConstructor
public class StoredSample {
final DataSource ds;
ã¾ããã¹ãã¢ãããã·ã¼ã¸ã£ã¯èª¬æã®ããã®ææãã§SYSTEM
ã§ä½æããã
説æ
PROCEDURE - å¼æ°ç¡ãã»æ»ãå¤ç¡ã
åä½ç¢ºèªç¨ã«ä»¥ä¸ã®PROCEDUREãä½æããã
CREATE OR REPLACE PROCEDURE simple_proc
AS
BEGIN
DBMS_OUTPUT.PUT_LINE('');
END;
public void simpleProc() {
SimpleJdbcCall jdbc = new SimpleJdbcCall(ds)
.withSchemaName("SYSTEM")
.withProcedureName("simple_proc");
Map<String, Object> result = jdbc.execute();
System.out.println(result);
}
å®è¡çµæ㯠{}
ã¨åºåãããã
withSchemaName
ã§ãã®ããã·ã¼ã¸ã£ãåå¨ããã¹ãã¼ããæå®ããwithProcedureName
ã§ããã·ã¼ã¸ã£ã®ååãæå®ããexecute
ã§å®è¡ããã
SYSTEM.simple_proc
ã®ããã«ã¯æå®åºæ¥ãªããæ»ãå¤ãä½ããªãå ´åã¯åã«ç©ºã®mapãè¿ãããã
PROCEDURE - å¼æ°æãã»æ»ãå¤ç¡ã
以ä¸ã¯æå³ã®ç¡ãã³ã¼ãã ããåä½ç¢ºèªã¨ãã¦Oracleã®DBMS_OUTPUT.PUT_LINE
ãå¼ã³åºãã
public void putLine() {
SimpleJdbcCall jdbc = new SimpleJdbcCall(ds)
.withSchemaName("DBMS_OUTPUT")
.withProcedureName("PUT_LINE")
.withoutProcedureColumnMetaDataAccess()
.declareParameters(
new SqlParameter("inputString", Types.VARCHAR)
);
Map<String, Object> result = jdbc.execute("asdf");
System.out.println(result);
MapSqlParameterSource s = new MapSqlParameterSource();
s.addValue("inputString", "asdfasdasd");
String result1 = jdbc.executeFunction(String.class, s);
System.out.println(result1);
}
declareParameters
ã§å¼ã³åºãã¹ãã¢ãã®å¼æ°ã宣è¨ãããSqlParameter
ã®keyã¯springå´ã§ã®èå¥åãªã®ã§å¿
ãããã¹ãã¢ãã¨ä¸è´ããå¿
è¦ã¯ç¡ããããã
execute
ã§å¼æ°ãæå®ãã¦å®è¡ããã
ã¾ãã¯ãexecuteFunction
ã¨SqlParameterSource
ã®çµã¿åãããå¯è½ããã¡ãã®å ´åkeyã¯SqlParameter
ã®ã¨ä¸è´ã®å¿
è¦ãããã
å®è¡çµæã¯ä»¥ä¸ã®éããexecuteFunction
ã¯æ»ãå¤åãæå®å¯è½ã ããåå¨ããªããã°null
ã«ãªãããã ã
{}
null
FUNCTION - å¼æ°æãã»æ»ãå¤æãã»è¤æ°ã®åºåãã©ã¡ã¼ã¿
ãµã³ãã«ã¨ãã¦ä»¥ä¸ã®functionã使ç¨ããã
CREATE OR REPLACE function sample_func3(
inStr1 IN nvarchar2
, outStr OUT VARCHAR2
, outInt OUT NUMBER
)
return VARCHAR2
AS
BEGIN
outStr := inStr1 || 'hoge';
outInt := 123;
return 'returnStr';
END sample_func3;
ããã§ã¯ãæ»ãå¤ãã¯return
ã®å¤ããåºåãã©ã¡ã¼ã¿ãã¯OUT
ãæããã®ã¨ããï¼oracleã®æ£ç¢ºãªç¨èªãã©ããã¯èªèº«ç¡ãï¼ã
public void sampleFunc3() {
SimpleJdbcCall jdbc = new SimpleJdbcCall(ds)
.withSchemaName("SYSTEM")
.withFunctionName("sample_func3")
.declareParameters(
new SqlParameter("inputString", Types.VARCHAR));
Map<String, Object> result = jdbc.execute("asdf");
System.out.println(result);
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("INSTR1", "asdfasdasd");
String result1 = jdbc.executeFunction(String.class, param);
System.out.println(result1);
}
å®è¡çµæã¯ä»¥ä¸ã®éãã
{return=returnStr, OUTSTR=asdfhoge, OUTINT=123}
returnStr
functionãªã®ã§withFunctionName
ã使ç¨ããã
å®è¡çµæã®éããexecute
ã®æ»ãå¤Map
ã«ã¯ã¹ãã¢ãã®æ»ãå¤ãã©ã¹åºåãã©ã¡ã¼ã¿ã®ä¸¡æ¹ãå«ãã対ãã¦executeFunction
ã®æ»ãå¤ã¯ã¹ãã¢ãã®æ»ãå¤ã«ãªãããã ã
execute
ãã®ã¡ã½ããã®ãã©ã¡ã¼ã¿ã®åã®éãã«ããéãã¯ã»ã¼è¦ãã¾ã¾ã
public void sampleFunc3() {
SimpleJdbcCall jdbc = new SimpleJdbcCall(ds)
.withSchemaName("SYSTEM")
.withFunctionName("sample_func3");
jdbc.execute("asdf");
jdbc.execute(Map.of("INSTR1", "asdf") );
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("INSTR1", "asdfasdasd");
jdbc.execute(param);
Object...
ã¯the same order as the parameters are defined for the stored procedure
ã¨ã®äºãªã®ã§ã¹ãã¢ãã®å®ç¾©é ã
Map<String,?>
ã¯the parameter values to be used in the call
ã¨ã®äºãªã®ã§keyåã§æå®ã
SqlParameterSource
ã¯å°ç¨ã¯ã©ã¹ãªã ãã§Map
ã¨ã»ã¼åçã¨æãããã
è£è¶³
https://vkuzel.com/calling-oracle-function-with-spring-5-3-simple-jdbc-tools ã«è§£èª¬ãããã
If a function is located in another schema and only a synonym is available in local schema, we have to disable Spring's metadata resolution via the SimpleJdbcCall.withoutProcedureColumnMetaDataAccess() method.
The reason is, Spring resolves metadata of a called objects from all_procedures and all_arguments tables. While doing so, it checks whether owner of procedure or argument is equal to the current schema which is resolved from connection, e.g. all_arguments.owner = 'local_schema_name' AND all_procedures.owner = 'local_schema_name'.
Because the real owner is located elsewhere, the metadata resolution fails on the SQLException: Missing IN or OUT parameter at index:: 1 error.
https://vkuzel.com/calling-oracle-function-with-spring-5-3-simple-jdbc-tools Calling a function synonym ããæç²
springã¯RDBMSã®ã¡ã¿ãã¼ã¿ã確èªãã¦ã¹ãã¢ãå¼åºãçµã¿ç«ã¦ããå
·ä½çã«ã¯Oracleã§ã¯all_arguments
ã all_procedures
ãåç
§ããããããããã§ãèªåããªã¼ãã¼ã§ã¯ç¡ãã¹ãã¢ãã ã¨ã¡ã¿ãã¼ã¿åç
§ã«å¤±æãã¦ãã¾ããã¨ã©ã¼ã«ãªããããããããã£ã¦ããããç¡è¦ããè¨å®withoutProcedureColumnMetaDataAccess
ãå¿
è¦ã¨ãªããã¨ã®ãã¨ã
declareParametersã¯å¿
ãããå¿
è¦ã§ã¯ç¡ãï¼
ã¡ãã£ã¨èª¿ã¹åããªãã£ãã®ã ãã該å½ã¡ã½ããã®javadocã«ã¯ä»¥ä¸ã®ãããªè¨è¿°ããããå¤åã ãã©ãã¡ã¿ãã¼ã¿ã ã¨ã使ãæ¹ã ã¨ãããã®è¾ºã®å
¼ãåãã«ãã£ã¦ã¯æ示çãªãã©ã¡ã¼ã¿å®£è¨ã¯ä¸è¦ãªã®ã ã¨æãã
public SimpleJdbcCall declareParameters(SqlParameter... sqlParameters)
Specify one or more parameters if desired. These parameters will be supplemented with any parameter information retrieved from the database meta-data.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/simple/SimpleJdbcCall.html#declareParameters(org.springframework.jdbc.core.SqlParameter...)
OBJECTåã¨ãã¯ä½¿ããªãï¼
調ã¹ãéãã§ã¯ä»¥ä¸ã®ãããªOBJECTåããã©ã¡ã¼ã¿ãæ»ãå¤ã«ä½¿ç¨ããã¹ãã¢ãã¯SimpleJdbcCall
ããã¯å¼ã³åºããªãããã«è¦ãããå¼ã³åºãæ¹ãåãããªãã£ãããOBJECTåã¨Javaã®ãããã³ã°æ¹æ³ãåãããªãã£ãã
CREATE TYPE MYMESG AS OBJECT (
ID NUMBER(10,0),
MESG VARCHAR2(30)
);
CREATE OR REPLACE function sample_func5 return SYSTEM.MYMESG
IS
msg SYSTEM.MYMESG;
BEGIN
msg := SYSTEM.MYMESG(1, 'asd');
return msg;
END sample_func5;
JdbcTemplate
ã§ã¯ä»¥ä¸ã®ããã«å¼ã³åºããããããã ãå¼ã³åºããäºã¯åºæ¥ããæ§é ä½ã®ãããã³ã°æ¹æ³ã¯åãããªãã£ãããªã®ã§ã以ä¸ã®ãµã³ãã«ã³ã¼ãã¯æ§é ä½ãç´æ¥ã§ã¯ãªããã®ä¸èº«ãåãåºãã¦ãããã«ãã¦ããã
import java.sql.Types;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.CallableStatementCreatorFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class StoreadSample2 {
final DataSource ds;
public void sampleFunc5() {
CallableStatementCreatorFactory factory = new CallableStatementCreatorFactory("""
declare
msg SYSTEM.MYMESG;
begin
? := SYSTEM.sample_func5().MESG;
end;
""", List.of(new SqlOutParameter("result", Types.VARCHAR)));
CallableStatementCreator creator = factory.newCallableStatementCreator(Map.of());
JdbcTemplate jdbc = new JdbcTemplate(ds);
Map<String, Object> result = jdbc.call(
creator,
List.of(new SqlOutParameter("result", Types.VARCHAR)));
System.out.println(result);
}
}
ããã£ãç¹
Unable to determine the correct call signature for 'SYSTEM.SIMPLE_PROC'
.withProcedureName("SYSTEM.simple_proc")
ã®ããã«ã¹ãã¼ãã¨ããã·ã¼ã¸ã£åãä¸ç·ã«æå®ããã¨ãã®ã¨ã©ã¼ã«ãªãã
org.springframework.dao.InvalidDataAccessApiUsageException: Unable to determine the correct call signature for 'SYSTEM.SIMPLE_PROC' - package name should be specified separately using '.withCatalogName("SYSTEM")'
Missing IN or OUT parameter at index: 1ã¨ãwrong number or types of arguments in call to
withoutProcedureColumnMetaDataAccess
ãå¿
è¦ã詳細ãªçç±ã¯è£è¶³ã®é
ãåç
§ã
以ä¸ã®ã¨ã©ã¼ãã°ã¯DBMS_OUTPUT.PUT_LINE
ãwithProcedureName
ã§å¼ã³åºããå ´åã
org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call DBMS_OUTPUT.PUT_LINE()}]
...
Caused by: java.sql.SQLException: ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
以ä¸ã®ã¨ã©ã¼ãã°ã¯withFunctionName
ã§å¼ã³åºããå ´åããããããã¡ã¿ãã¼ã¿åç
§ã«å¤±æããããã«functionãªã®ã§åå¨ããªãæ»ãå¤ã«ãã¬ã¼ã¹ãã«ããèªåçã«å²ãå½ã¦ã¦ãã¾ã£ã¦ããããã®ãããwithProcedureName
ã¨ã¯ç°ãªãã¨ã©ã¼ã«ãªããã¨æ¨æ¸¬ããã
org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{? = call DBMS_OUTPUT.PUT_LINE()}]; SQL state [99999]; error code [17041]; ORA-17041: Missing IN or OUT parameter at index: 1
https://docs.oracle.com/error-help/db/ora-17041/
...
Caused by: java.sql.SQLException: ORA-17041: Missing IN or OUT parameter at index: 1
BadSqlGrammarException (ç¥) bad SQL grammar [{call SYSTEM.SAMPLE_FUNC3(?, ?, ?, ?)}]
withFunctionName
ã¨ãã¹ãã¨ãããwithProcedureName
ã«ãã¦ãã¾ãã¨ä»¥ä¸ã®ãããªã¨ã©ã¼ã«ãªãã
org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call SYSTEM.SAMPLE_FUNC3(?, ?, ?, ?)}]
..
Caused by: java.sql.SQLException: ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'SAMPLE_FUNC3'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
åèURL