Loading...
Spring Framework Reference Documentation 7.0.2의 Simplifying JDBC Operations with the SimpleJdbc Classes의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
SimpleJdbc ClassesSimpleJdbcInsert와 SimpleJdbcCall 클래스는 JDBC 드라이버를 통해 조회할 수 있는 데이터베이스 메타데이터를 활용하여 단순화된 설정을 제공합니다.
이는 미리 설정해야 할 것이 더 적다는 것을 의미하며, 원한다면 메타데이터 처리을 오버라이드하거나 끌 수 있고, 코드에서 모든 세부 사항을 직접 제공할 수도 있습니다.
SimpleJdbcInsert가장 최소한의 설정 옵션으로 SimpleJdbcInsert 클래스를 살펴보는 것부터 시작합니다.
데이터 액세스 레이어의 초기화 메서드에서 SimpleJdbcInsert를 인스턴스화해야 합니다.
이 예제에서 초기화 메서드는 setDataSource 메서드입니다.
SimpleJdbcInsert 클래스를 서브클래스할 필요는 없습니다.
대신 새 인스턴스를 생성하고 withTableName 메서드를 사용하여 테이블 이름을 설정할 수 있습니다.
이 클래스의 설정 메서드는 fluid 스타일을 따르며, SimpleJdbcInsert 인스턴스를 반환하여 모든 설정 메서드를 체이닝할 수 있게 해 줍니다.
다음 예제는 하나의 설정 메서드만 사용합니다(나중에 여러 메서드를 사용하는 예제를 보여 줍니다):
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcInsert insertActor; 4 5 public void setDataSource(DataSource dataSource) { 6 this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor"); 7 } 8 9 public void add(Actor actor) { 10 Map<String, Object> parameters = new HashMap<>(3); 11 parameters.put("id", actor.getId()); 12 parameters.put("first_name", actor.getFirstName()); 13 parameters.put("last_name", actor.getLastName()); 14 insertActor.execute(parameters); 15 } 16 17 // ... additional methods 18}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val insertActor = SimpleJdbcInsert(dataSource).withTableName("t_actor") 4 5 fun add(actor: Actor) { 6 val parameters = mutableMapOf<String, Any>() 7 parameters["id"] = actor.id 8 parameters["first_name"] = actor.firstName 9 parameters["last_name"] = actor.lastName 10 insertActor.execute(parameters) 11 } 12 13 // ... additional methods 14}
여기에서 사용된 execute 메서드는 하나의 파라미터로 일반 java.util.Map만을 받습니다.
여기서 중요한 점은 Map에 사용된 키가 데이터베이스에 정의된 테이블의 컬럼 이름과 일치해야 한다는 것입니다.
이는 실제 insert 구문을 구성하기 위해 메타데이터를 읽기 때문입니다.
SimpleJdbcInsert다음 예제는 앞선 예제와 같은 insert를 사용하지만 id를 전달하는 대신 자동 생성된 키를 조회하여 새 Actor 객체에 설정합니다.
SimpleJdbcInsert를 생성할 때, 테이블 이름을 지정하는 것에 더해 usingGeneratedKeyColumns 메서드로 생성된 키 컬럼의 이름을 지정합니다.
다음 listing은 작동 방식을 보여 줍니다:
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcInsert insertActor; 4 5 public void setDataSource(DataSource dataSource) { 6 this.insertActor = new SimpleJdbcInsert(dataSource) 7 .withTableName("t_actor") 8 .usingGeneratedKeyColumns("id"); 9 } 10 11 public void add(Actor actor) { 12 Map<String, Object> parameters = new HashMap<>(2); 13 parameters.put("first_name", actor.getFirstName()); 14 parameters.put("last_name", actor.getLastName()); 15 Number newId = insertActor.executeAndReturnKey(parameters); 16 actor.setId(newId.longValue()); 17 } 18 19 // ... additional methods 20}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val insertActor = SimpleJdbcInsert(dataSource) 4 .withTableName("t_actor").usingGeneratedKeyColumns("id") 5 6 fun add(actor: Actor): Actor { 7 val parameters = mapOf( 8 "first_name" to actor.firstName, 9 "last_name" to actor.lastName) 10 val newId = insertActor.executeAndReturnKey(parameters); 11 return actor.copy(id = newId.toLong()) 12 } 13 14 // ... additional methods 15}
이 두 번째 접근 방식을 사용하여 insert를 실행할 때의 주요 차이점은 Map에 id를 추가하지 않고 executeAndReturnKey 메서드를 호출한다는 것입니다.
이는 도메인 클래스에서 사용되는 숫자 타입의 인스턴스를 생성할 수 있는 java.lang.Number 객체를 반환합니다.
여기에서 모든 데이터베이스가 특정 Java 클래스를 반환한다고 가정할 수는 없습니다.
의존할 수 있는 기본 클래스는 java.lang.Number입니다.
여러 자동 생성 컬럼이 있거나 생성된 값이 숫자가 아닌 경우, executeAndReturnKeyHolder 메서드에서 반환되는 KeyHolder를 사용할 수 있습니다.
SimpleJdbcInsert다음 예제에서 보듯이 usingColumns 메서드로 컬럼 이름 목록을 지정하여 insert에 사용할 컬럼을 제한할 수 있습니다:
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcInsert insertActor; 4 5 public void setDataSource(DataSource dataSource) { 6 this.insertActor = new SimpleJdbcInsert(dataSource) 7 .withTableName("t_actor") 8 .usingColumns("first_name", "last_name") 9 .usingGeneratedKeyColumns("id"); 10 } 11 12 public void add(Actor actor) { 13 Map<String, Object> parameters = new HashMap<>(2); 14 parameters.put("first_name", actor.getFirstName()); 15 parameters.put("last_name", actor.getLastName()); 16 Number newId = insertActor.executeAndReturnKey(parameters); 17 actor.setId(newId.longValue()); 18 } 19 20 // ... additional methods 21}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val insertActor = SimpleJdbcInsert(dataSource) 4 .withTableName("t_actor") 5 .usingColumns("first_name", "last_name") 6 .usingGeneratedKeyColumns("id") 7 8 fun add(actor: Actor): Actor { 9 val parameters = mapOf( 10 "first_name" to actor.firstName, 11 "last_name" to actor.lastName) 12 val newId = insertActor.executeAndReturnKey(parameters); 13 return actor.copy(id = newId.toLong()) 14 } 15 16 // ... additional methods 17}
insert 실행은 메타데이터에 의존하여 사용할 컬럼을 결정한 경우와 동일합니다.
SqlParameterSource to Provide Parameter Values파라미터 값을 제공하기 위해 Map을 사용하는 것은 잘 동작하지만 가장 편리한 클래스는 아닙니다.
Spring은 대신 사용할 수 있는 SqlParameterSource 인터페이스의 몇 가지 구현을 제공합니다.
첫 번째는 BeanPropertySqlParameterSource로, 값이 포함된 JavaBean 규약을 따르는 클래스가 있을 때 매우 편리한 클래스입니다.
이는 해당 getter 메서드를 사용하여 파라미터 값을 추출합니다.
다음 예제는 BeanPropertySqlParameterSource를 사용하는 방법을 보여 줍니다:
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcInsert insertActor; 4 5 public void setDataSource(DataSource dataSource) { 6 this.insertActor = new SimpleJdbcInsert(dataSource) 7 .withTableName("t_actor") 8 .usingGeneratedKeyColumns("id"); 9 } 10 11 public void add(Actor actor) { 12 SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor); 13 Number newId = insertActor.executeAndReturnKey(parameters); 14 actor.setId(newId.longValue()); 15 } 16 17 // ... additional methods 18}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val insertActor = SimpleJdbcInsert(dataSource) 4 .withTableName("t_actor") 5 .usingGeneratedKeyColumns("id") 6 7 fun add(actor: Actor): Actor { 8 val parameters = BeanPropertySqlParameterSource(actor) 9 val newId = insertActor.executeAndReturnKey(parameters) 10 return actor.copy(id = newId.toLong()) 11 } 12 13 // ... additional methods 14}
또 다른 옵션은 MapSqlParameterSource로, Map과 유사하지만 체이닝이 가능한 보다 편리한 addValue 메서드를 제공합니다.
다음 예제는 이를 사용하는 방법을 보여 줍니다:
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcInsert insertActor; 4 5 public void setDataSource(DataSource dataSource) { 6 this.insertActor = new SimpleJdbcInsert(dataSource) 7 .withTableName("t_actor") 8 .usingGeneratedKeyColumns("id"); 9 } 10 11 public void add(Actor actor) { 12 SqlParameterSource parameters = new MapSqlParameterSource() 13 .addValue("first_name", actor.getFirstName()) 14 .addValue("last_name", actor.getLastName()); 15 Number newId = insertActor.executeAndReturnKey(parameters); 16 actor.setId(newId.longValue()); 17 } 18 19 // ... additional methods 20}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val insertActor = SimpleJdbcInsert(dataSource) 4 .withTableName("t_actor") 5 .usingGeneratedKeyColumns("id") 6 7 fun add(actor: Actor): Actor { 8 val parameters = MapSqlParameterSource() 9 .addValue("first_name", actor.firstName) 10 .addValue("last_name", actor.lastName) 11 val newId = insertActor.executeAndReturnKey(parameters) 12 return actor.copy(id = newId.toLong()) 13 } 14 15 // ... additional methods 16}
보는 것처럼 설정은 동일합니다. 이러한 대체 입력 클래스를 사용하도록 실행 코드만 변경하면 됩니다.
SimpleJdbcCallSimpleJdbcCall 클래스는 데이터베이스의 메타데이터를 사용하여 in 및 out 파라미터의 이름을 조회하므로, 이를 명시적으로 선언할 필요가 없습니다.
원한다면 파라미터를 선언할 수도 있고, Java 클래스에 자동으로 매핑되지 않는 파라미터가 있을 때도 선언할 수 있습니다.
첫 번째 예제는 MySQL 데이터베이스에서 VARCHAR와 DATE 포맷의 스칼라 값만 반환하는 단순 프로시저를 보여 줍니다.
예제 프로시저는 지정된 actor 엔트리를 읽고 out 파라미터의 형태로 first_name, last_name, birth_date 컬럼을 반환합니다.
다음 listing은 첫 번째 예제를 보여 줍니다:
1CREATE PROCEDURE read_actor ( 2 IN in_id INTEGER, 3 OUT out_first_name VARCHAR(100), 4 OUT out_last_name VARCHAR(100), 5 OUT out_birth_date DATE) 6BEGIN 7 SELECT first_name, last_name, birth_date 8 INTO out_first_name, out_last_name, out_birth_date 9 FROM t_actor where id = in_id; 10END;
in_id 파라미터는 조회하려는 actor의 id를 포함합니다.
out 파라미터는 테이블에서 읽은 데이터를 반환합니다.
SimpleJdbcInsert를 선언하는 것과 유사한 방식으로 SimpleJdbcCall을 선언할 수 있습니다.
데이터 액세스 레이어의 초기화 메서드에서 클래스를 인스턴스화하고 설정해야 합니다.
StoredProcedure 클래스와 비교할 때 서브클래스를 만들 필요가 없고 데이터베이스 메타데이터에서 조회할 수 있는 파라미터를 선언할 필요도 없습니다.
다음 SimpleJdbcCall 설정 예제는 앞의 저장 프로시저를 사용합니다(DataSource 외에 유일한 설정 옵션은 저장 프로시저의 이름입니다):
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcCall procReadActor; 4 5 public void setDataSource(DataSource dataSource) { 6 this.procReadActor = new SimpleJdbcCall(dataSource) 7 .withProcedureName("read_actor"); 8 } 9 10 public Actor readActor(Long id) { 11 SqlParameterSource in = new MapSqlParameterSource() 12 .addValue("in_id", id); 13 Map out = procReadActor.execute(in); 14 Actor actor = new Actor(); 15 actor.setId(id); 16 actor.setFirstName((String) out.get("out_first_name")); 17 actor.setLastName((String) out.get("out_last_name")); 18 actor.setBirthDate((Date) out.get("out_birth_date")); 19 return actor; 20 } 21 22 // ... additional methods 23}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val procReadActor = SimpleJdbcCall(dataSource) 4 .withProcedureName("read_actor") 5 6 fun readActor(id: Long): Actor { 7 val source = MapSqlParameterSource().addValue("in_id", id) 8 val output = procReadActor.execute(source) 9 return Actor( 10 id, 11 output["out_first_name"] as String, 12 output["out_last_name"] as String, 13 output["out_birth_date"] as Date) 14 } 15 16 // ... additional methods 17}
call 실행을 위해 작성하는 코드는 IN 파라미터를 포함하는 SqlParameterSource를 생성하는 작업을 포함합니다.
입력 값에 제공한 이름은 저장 프로시저에 선언된 파라미터 이름과 일치해야 합니다.
저장 프로시저에서 데이터베이스 객체를 참조하는 방법을 결정하기 위해 메타데이터를 사용하므로, 대소문자는 일치할 필요가 없습니다.
저장 프로시저의 소스에 지정된 방식이 데이터베이스에 저장되는 방식과 반드시 같지는 않습니다. 일부 데이터베이스는 이름을 모두 대문자로 변환하고, 다른 데이터베이스는 소문자를 사용하거나 지정된 대소문자를 그대로 사용합니다.
execute 메서드는 IN 파라미터를 받아 저장 프로시저에 지정된 이름으로 키가 지정된 out 파라미터를 포함하는 Map을 반환합니다.
이 경우 out_first_name, out_last_name, out_birth_date입니다.
execute 메서드의 마지막 부분은 반환할 데이터를 담기 위한 Actor 인스턴스를 생성합니다.
다시 말하지만 저장 프로시저에 선언된 대로 out 파라미터의 이름을 사용하는 것이 중요합니다.
또한 결과 map에 저장된 out 파라미터 이름의 대소문자는 데이터베이스의 out 파라미터 이름과 일치하며, 데이터베이스마다 다를 수 있습니다.
코드의 이식성을 높이기 위해 대소문자를 구분하지 않는 조회를 수행하거나 Spring에 LinkedCaseInsensitiveMap을 사용하도록 지시해야 합니다.
후자를 수행하려면 자체 JdbcTemplate을 생성하고 setResultsMapCaseInsensitive 프로퍼티를 true로 설정할 수 있습니다.
그런 다음 이 커스텀 JdbcTemplate 인스턴스를 SimpleJdbcCall의 생성자에 전달할 수 있습니다.
다음 예제는 이 설정을 보여 줍니다:
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcCall procReadActor; 4 5 public void setDataSource(DataSource dataSource) { 6 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 7 jdbcTemplate.setResultsMapCaseInsensitive(true); 8 this.procReadActor = new SimpleJdbcCall(jdbcTemplate) 9 .withProcedureName("read_actor"); 10 } 11 12 // ... additional methods 13}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private var procReadActor = SimpleJdbcCall(JdbcTemplate(dataSource).apply { 4 isResultsMapCaseInsensitive = true 5 }).withProcedureName("read_actor") 6 7 // ... additional methods 8}
이 작업을 수행함으로써 반환된 out 파라미터 이름에 사용된 대소문자의 충돌을 피할 수 있습니다.
SimpleJdbcCall이 장의 앞부분에서 파라미터가 메타데이터에서 유추되는 방식에 대해 설명했지만, 원한다면 파라미터를 명시적으로 선언할 수 있습니다.
이를 위해 declareParameters 메서드로 SimpleJdbcCall을 생성하고 설정할 수 있으며, 이 메서드는 입력으로 가변 개수의 SqlParameter 객체를 받습니다.
SqlParameter를 정의하는 방법에 대한 자세한 내용은 다음 섹션을 참조하세요.
사용 중인 데이터베이스가 Spring에서 지원하는 데이터베이스가 아닌 경우, 명시적 선언이 필요합니다.<br>현재 Spring은 다음 데이터베이스에 대해 저장 프로시저 호출의 메타데이터 조회를 지원합니다:<br>Apache Derby, DB2, MySQL, Microsoft SQL Server, Oracle, Sybase.<br>또한 MySQL, Microsoft SQL Server, Oracle에 대해서는 저장 함수의 메타데이터 조회도 지원합니다.
원한다면 파라미터를 하나, 일부, 또는 전부 명시적으로 선언할 수 있습니다.
파라미터 메타데이터는 명시적으로 선언하지 않은 파라미터에 대해서는 계속 사용됩니다.
잠재적인 파라미터에 대한 메타데이터 조회 처리를 모두 우회하고 선언된 파라미터만 사용하려면, 선언의 일부로 withoutProcedureColumnMetaDataAccess 메서드를 호출할 수 있습니다.
데이터베이스 함수에 대해 두 개 이상의 서로 다른 call 시그니처가 선언되어 있다고 가정해 봅시다.
이 경우 해당 시그니처에 포함할 IN 파라미터 이름 목록을 지정하기 위해 useInParameterNames를 호출합니다.
다음 예제는 앞의 예제 정보를 사용하여 완전히 선언된 프로시저 호출을 보여 줍니다:
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcCall procReadActor; 4 5 public void setDataSource(DataSource dataSource) { 6 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 7 jdbcTemplate.setResultsMapCaseInsensitive(true); 8 this.procReadActor = new SimpleJdbcCall(jdbcTemplate) 9 .withProcedureName("read_actor") 10 .withoutProcedureColumnMetaDataAccess() 11 .useInParameterNames("in_id") 12 .declareParameters( 13 new SqlParameter("in_id", Types.NUMERIC), 14 new SqlOutParameter("out_first_name", Types.VARCHAR), 15 new SqlOutParameter("out_last_name", Types.VARCHAR), 16 new SqlOutParameter("out_birth_date", Types.DATE) 17 ); 18 } 19 20 // ... additional methods 21}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val procReadActor = SimpleJdbcCall(JdbcTemplate(dataSource).apply { 4 isResultsMapCaseInsensitive = true 5 }).withProcedureName("read_actor") 6 .withoutProcedureColumnMetaDataAccess() 7 .useInParameterNames("in_id") 8 .declareParameters( 9 SqlParameter("in_id", Types.NUMERIC), 10 SqlOutParameter("out_first_name", Types.VARCHAR), 11 SqlOutParameter("out_last_name", Types.VARCHAR), 12 SqlOutParameter("out_birth_date", Types.DATE) 13 ) 14 15 // ... additional methods 16}
두 예제의 실행과 최종 결과는 동일합니다. 두 번째 예제는 메타데이터에 의존하지 않고 모든 세부 사항을 명시적으로 지정합니다.
SqlParametersSimpleJdbc 클래스와 RDBMS 연산 클래스( Modeling JDBC Operations as Java Objects에 설명됨)에 대한 파라미터를 정의하려면 SqlParameter 또는 그 서브클래스 중 하나를 사용할 수 있습니다.
이를 위해 일반적으로 생성자에서 파라미터 이름과 SQL 타입을 지정합니다.
SQL 타입은 java.sql.Types 상수를 사용하여 지정합니다.
이 장의 앞부분에서 다음과 유사한 선언을 보았습니다:
1new SqlParameter("in_id", Types.NUMERIC), 2new SqlOutParameter("out_first_name", Types.VARCHAR),
1SqlParameter("in_id", Types.NUMERIC), 2SqlOutParameter("out_first_name", Types.VARCHAR),
SqlParameter가 있는 첫 번째 라인은 IN 파라미터를 선언합니다.
IN 파라미터는 저장 프로시저 호출과 SqlQuery 및 그 서브클래스( Understanding SqlQuery에 설명됨)를 사용하는 쿼리 모두에 사용할 수 있습니다.
두 번째 라인(SqlOutParameter가 있는)은 저장 프로시저 호출에 사용할 out 파라미터를 선언합니다.
InOut 파라미터( 프로시저에 IN 값을 제공하고 값을 반환하기도 하는 파라미터)를 위한 SqlInOutParameter도 있습니다.
SqlParameter와SqlInOutParameter로 선언된 파라미터만 입력 값을 제공하는 데 사용됩니다.<br>이는 입력 값을SqlOutParameter로 선언된 파라미터에도 제공할 수 있도록 하는(하위 호환성을 위한)<br>StoredProcedure클래스와는 다릅니다.
IN 파라미터의 경우 이름과 SQL 타입 외에도 숫자 데이터에 대한 스케일이나 커스텀 데이터베이스 타입에 대한 타입 이름을 지정할 수 있습니다.
out 파라미터의 경우 REF 커서에서 반환된 row의 매핑을 처리하기 위한 RowMapper를 제공할 수 있습니다.
또 다른 옵션은 반환 값의 커스터마이즈된 처리를 정의할 수 있는 SqlReturnType을 지정하는 것입니다.
SimpleJdbcCall저장 함수는 저장 프로시저를 호출할 때와 거의 동일한 방식으로 호출할 수 있지만, 프로시저 이름 대신 함수 이름을 제공합니다.
함수 호출을 하고자 한다는 것을 나타내기 위해 설정의 일부로 withFunctionName 메서드를 사용하며, 함수 호출을 위한 해당 문자열이 생성됩니다.
특화된 호출(executeFunction)은 함수를 실행하는 데 사용되며, 함수 반환 값을 지정된 타입의 객체로 반환합니다.
이는 결과 map에서 반환 값을 조회할 필요가 없다는 것을 의미합니다.
유사한 편의 메서드(executeObject라는 이름)는 out 파라미터가 하나만 있는 저장 프로시저에도 사용할 수 있습니다.
다음 예제(MySQL용)는 actor의 전체 이름을 반환하는 get_actor_name이라는 저장 함수를 기반으로 합니다:
1CREATE FUNCTION get_actor_name (in_id INTEGER) 2RETURNS VARCHAR(200) READS SQL DATA 3BEGIN 4 DECLARE out_name VARCHAR(200); 5 SELECT concat(first_name, ' ', last_name) 6 INTO out_name 7 FROM t_actor where id = in_id; 8 RETURN out_name; 9END;
이 함수를 호출하기 위해 초기화 메서드에서 다시 SimpleJdbcCall을 생성합니다.
다음 예제는 이를 보여 줍니다:
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcCall funcGetActorName; 4 5 public void setDataSource(DataSource dataSource) { 6 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 7 jdbcTemplate.setResultsMapCaseInsensitive(true); 8 this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate) 9 .withFunctionName("get_actor_name"); 10 } 11 12 public String getActorName(Long id) { 13 SqlParameterSource in = new MapSqlParameterSource() 14 .addValue("in_id", id); 15 String name = funcGetActorName.executeFunction(String.class, in); 16 return name; 17 } 18 19 // ... additional methods 20}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val jdbcTemplate = JdbcTemplate(dataSource).apply { 4 isResultsMapCaseInsensitive = true 5 } 6 private val funcGetActorName = SimpleJdbcCall(jdbcTemplate) 7 .withFunctionName("get_actor_name") 8 9 fun getActorName(id: Long): String { 10 val source = MapSqlParameterSource().addValue("in_id", id) 11 return funcGetActorName.executeFunction(String::class.java, source) 12 } 13 14 // ... additional methods 15}
사용된 executeFunction 메서드는 함수 호출의 반환 값을 포함하는 String을 반환합니다.
ResultSet or REF Cursor from a SimpleJdbcCall결과 집합을 반환하는 저장 프로시저 또는 함수를 호출하는 것은 약간 까다롭습니다.
일부 데이터베이스는 JDBC 결과 처리 동안 결과 집합을 반환하는 반면, 다른 데이터베이스는 특정 타입의 명시적으로 등록된 out 파라미터를 요구합니다.
두 접근 방식 모두 결과 집합을 순회하고 반환된 row를 처리하기 위한 추가 처리가 필요합니다.
SimpleJdbcCall에서는 returningResultSet 메서드를 사용하고 특정 파라미터에 사용할 RowMapper 구현을 선언할 수 있습니다.
결과 집합이 결과 처리 동안 반환되는 경우 이름이 정의되어 있지 않으므로, 반환된 결과는 선언한 RowMapper 구현의 순서와 일치해야 합니다.
지정된 이름은 여전히 execute 구문에서 반환된 결과 map에 처리된 결과 목록을 저장하는 데 사용됩니다.
다음 예제(MySQL용)는 IN 파라미터를 받지 않고 t_actor 테이블의 모든 row를 반환하는 저장 프로시저를 사용합니다:
1CREATE PROCEDURE read_all_actors() 2BEGIN 3 SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a; 4END;
이 프로시저를 호출하기 위해 RowMapper를 선언할 수 있습니다.
매핑하려는 클래스가 JavaBean 규칙을 따르므로, newInstance 메서드에 매핑할 클래스를 전달하여 생성된 BeanPropertyRowMapper를 사용할 수 있습니다.
다음 예제는 이를 수행하는 방법을 보여 줍니다:
1public class JdbcActorDao implements ActorDao { 2 3 private SimpleJdbcCall procReadAllActors; 4 5 public void setDataSource(DataSource dataSource) { 6 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 7 jdbcTemplate.setResultsMapCaseInsensitive(true); 8 this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate) 9 .withProcedureName("read_all_actors") 10 .returningResultSet("actors", 11 BeanPropertyRowMapper.newInstance(Actor.class)); 12 } 13 14 public List getActorsList() { 15 Map m = procReadAllActors.execute(new HashMap<String, Object>(0)); 16 return (List) m.get("actors"); 17 } 18 19 // ... additional methods 20}
1class JdbcActorDao(dataSource: DataSource) : ActorDao { 2 3 private val procReadAllActors = SimpleJdbcCall(JdbcTemplate(dataSource).apply { 4 isResultsMapCaseInsensitive = true 5 }).withProcedureName("read_all_actors") 6 .returningResultSet( 7 "actors", 8 BeanPropertyRowMapper.newInstance(Actor::class.java) 9 ) 10 11 fun getActorsList(): List<Actor> { 12 val m = procReadAllActors.execute(mapOf<String, Any>()) 13 return m["actors"] as List<Actor> 14 } 15 16 // ... additional methods 17}
이 call은 파라미터를 받지 않으므로 execute 호출은 빈 Map을 전달합니다.
그런 다음 결과 map에서 actor 목록을 조회하여 호출자에게 반환합니다.
JDBC Batch Operations
Modeling JDBC Operations as Java Objects