이번글은 공식 문서에서 소개하는 JDBC Batch Operations에 대해 정리했습니다.
Simplifying JDBC Operations with the SimpleJdbc Classes
The SimpleJdbcInsert and SimpleJdbcCall classes provide a simplified configuration by taking advantage of database metadata that can be retrieved through the JDBC driver. This means that you have less to configure up front, although you can override or turn off the metadata processing if you prefer to provide all the details in your code.
SimpleJdbcInsert와 SimpleJdbcCall 클래스는 JDBC 드라이버를 통해 가져올 수 있는 데이터베이스 메타데이터를 활용함으로써,
설정을 단순화해주는 기능을 제공합니다. 이 말은 즉, 사전에 설정해야 할 내용이 줄어든다는 뜻입니다.
물론, 원한다면 메타데이터 처리 기능을 직접 설정하거나 비활성화해서 모든 세부 사항을 코드에 직접 명시할 수도 있습니다.
Inserting Data by Using SimpleJdbcInsert
We start by looking at the SimpleJdbcInsert class with the minimal amount of configuration options. You should instantiate the SimpleJdbcInsert in the data access layer’s initialization method. For this example, the initializing method is the setDataSource method. You do not need to subclass the SimpleJdbcInsert class. Instead, you can create a new instance and set the table name by using the withTableName method. Configuration methods for this class follow the fluid style that returns the instance of the SimpleJdbcInsert, which lets you chain all configuration methods. The following example uses only one configuration method (we show examples of multiple methods later):
우리는 최소한의 설정 옵션만으로 SimpleJdbcInsert 클래스를 살펴보는 것부터 시작합니다.
SimpleJdbcInsert는 데이터 접근 계층의 초기화 메서드에서 인스턴스를 생성해야 합니다.
이 예제에서는 초기화 메서드가 setDataSource 메서드입니다. SimpleJdbcInsert 클래스를 상속할 필요는 없습니다.
대신, 새로운 인스턴스를 만들고 withTableName 메서드를 사용하여 테이블 이름을 설정하면 됩니다.
이 클래스의 설정 메서드들은 인스턴스를 반환하는 방식(fluent style)을 따르기 때문에 설정 메서드들을 체이닝 방식으로 연결해서 사용할 수 있습니다. 다음 예제에서는 하나의 설정 메서드만 사용하고 있지만, (나중에 여러 개의 설정 메서드를 사용하는 예제도 보여줄 것입니다.)
public class JdbcActorDao implements ActorDao {
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<>(3);
parameters.put("id", actor.getId());
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
insertActor.execute(parameters);
}
// ... additional methods
}
The execute method used here takes a plain java.util.Map as its only parameter. The important thing to note here is that the keys used for the Map must match the column names of the table, as defined in the database. This is because we read the metadata to construct the actual insert statement.
여기에서 사용된 execute 메서드는 java.util.Map을 유일한 매개변수로 받습니다.
여기서 중요한 점은, Map에 사용된 키들이 데이터베이스에 정의된 테이블의 컬럼 이름과 일치해야 한다는 것입니다.
이는 실제 insert 문장을 생성할 때 메타데이터를 읽어오기 때문입니다.
Retrieving Auto-generated Keys by Using SimpleJdbcInsert
The next example uses the same insert as the preceding example, but, instead of passing in the id, it retrieves the auto-generated key and sets it on the new Actor object. When it creates the SimpleJdbcInsert, in addition to specifying the table name, it specifies the name of the generated key column with the usingGeneratedKeyColumns method. The following listing shows how it works:
다음 예제는 앞선 예제와 동일한 insert를 사용하지만, id 값을 직접 전달하는 대신, 자동으로 생성된 키를 조회해서 새 Actor 객체에 설정합니다. SimpleJdbcInsert를 생성할 때는 테이블 이름을 지정하는 것 외에, usingGeneratedKeyColumns 메서드를 사용해서 자동 생성되는 키 컬럼의 이름도 지정합니다. 다음 예제는 그것이 어떻게 작동하는지를 보여줍니다.
public class JdbcActorDao implements ActorDao {
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.insertActor = new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<>(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}
The main difference when you run the insert by using this second approach is that you do not add the id to the Map, and you call the executeAndReturnKey method. This returns a java.lang.Number object with which you can create an instance of the numerical type that is used in your domain class. You cannot rely on all databases to return a specific Java class here. java.lang.Number is the base class that you can rely on. If you have multiple auto-generated columns or the generated values are non-numeric, you can use a KeyHolder that is returned from the executeAndReturnKeyHolder method.
이 두 번째 방식으로 insert를 실행할 때의 주요 차이점은, id 값을 Map에 추가하지 않고, executeAndReturnKey 메서드를 호출한다는 것입니다. 이 메서드는 java.lang.Number 객체를 반환하며, 이를 사용하여 도메인 클래스에서 사용하는 숫자 타입의 인스턴스를 생성할 수 있습니다. 모든 데이터베이스가 특정한 Java 클래스 타입을 반환한다고 믿어서는 안 됩니다. java.lang.Number는 신뢰할 수 있는 기본 클래스입니다. 만약 자동 생성되는 컬럼이 여러 개이거나, 생성된 값이 숫자가 아니라면, executeAndReturnKeyHolder 메서드가 반환하는 KeyHolder를 사용할 수 있습니다.
Specifying Columns for a SimpleJdbcInsert
You can limit the columns for an insert by specifying a list of column names with the usingColumns method, as the following example shows:
다음 예제에서 볼 수 있듯이, usingColumns 메서드를 사용하여 컬럼 이름 목록을 지정하면, insert 시 사용할 컬럼을 제한할 수 있습니다.
public class JdbcActorDao implements ActorDao {
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.insertActor = new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingColumns("first_name", "last_name")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<>(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}
The execution of the insert is the same as if you had relied on the metadata to determine which columns to use.
insert를 실행하는 방식은, 어떤 컬럼을 사용할지 메타데이터에 의존했을 때와 동일합니다.
Using SqlParameterSource to Provide Parameter Values
Using a Map to provide parameter values works fine, but it is not the most convenient class to use. Spring provides a couple of implementations of the SqlParameterSource interface that you can use instead. The first one is BeanPropertySqlParameterSource, which is a very convenient class if you have a JavaBean-compliant class that contains your values. It uses the corresponding getter method to extract the parameter values. The following example shows how to use BeanPropertySqlParameterSource:
파라미터 값을 제공하기 위해 Map을 사용하는 것은 잘 작동하지만, 가장 편리한 클래스는 아닙니다.
Spring은 이를 대신할 수 있는 SqlParameterSource 인터페이스의 몇 가지 구현체를 제공합니다.
그 중 첫 번째는 BeanPropertySqlParameterSource이며, 값을 포함하는 클래스가 JavaBean 규약을 따를 경우 매우 편리한 클래스입니다. 이 클래스는 해당하는 getter 메서드를 사용해서 파라미터 값을 추출합니다.
다음 예제는 BeanPropertySqlParameterSource를 사용하는 방법을 보여줍니다.
public class JdbcActorDao implements ActorDao {
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.insertActor = new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor);
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}
Another option is the MapSqlParameterSource that resembles a Map but provides a more convenient addValue method that can be chained. The following example shows how to use it:
또 다른 옵션은 MapSqlParameterSource입니다.
이 클래스는 Map과 비슷하지만, addValue 메서드를 제공하여 더 편리하게 값을 추가할 수 있으며, 메서드 체이닝도 가능합니다.
다음 예제는 MapSqlParameterSource를 사용하는 방법을 보여줍니다.
public class JdbcActorDao implements ActorDao {
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.insertActor = new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
SqlParameterSource parameters = new MapSqlParameterSource()
.addValue("first_name", actor.getFirstName())
.addValue("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}
As you can see, the configuration is the same. Only the executing code has to change to use these alternative input classes.
보시다시피, 설정 부분은 동일합니다. 이러한 대체 입력 클래스를 사용하려면 실행하는 코드만 변경하면 됩니다.
Calling a Stored Procedure with SimpleJdbcCall
The SimpleJdbcCall class uses metadata in the database to look up names of in and out parameters so that you do not have to explicitly declare them. You can declare parameters if you prefer to do that or if you have parameters that do not have an automatic mapping to a Java class. The first example shows a simple procedure that returns only scalar values in VARCHAR and DATE format from a MySQL database. The example procedure reads a specified actor entry and returns first_name, last_name, and birth_date columns in the form of out parameters. The following listing shows the first example:
SimpleJdbcCall 클래스는 데이터베이스의 메타데이터를 사용하여 입력(in) 및 출력(out) 파라미터의 이름을 조회하기 때문에,
개발자가 이를 명시적으로 선언할 필요가 없습니다.
물론, 원한다면 파라미터를 직접 선언할 수도 있고, 또는 자동으로 Java 클래스에 매핑되지 않는 파라미터가 있는 경우에는 직접 선언해야 할 수도 있습니다.
첫 번째 예제는 MySQL 데이터베이스에서 VARCHAR와 DATE 형식의 스칼라 값을 반환하는 간단한 프로시저를 보여줍니다.
이 프로시저는 지정된 배우(actor)의 데이터를 읽고,first_name, last_name, birth_date 컬럼을 out 파라미터 형태로 반환합니다.
다음 예제는 그 첫 번째 예제를 보여줍니다.
CREATE PROCEDURE read_actor (
IN in_id INTEGER,
OUT out_first_name VARCHAR(100),
OUT out_last_name VARCHAR(100),
OUT out_birth_date DATE)
BEGIN
SELECT first_name, last_name, birth_date
INTO out_first_name, out_last_name, out_birth_date
FROM t_actor where id = in_id;
END;
The in_id parameter contains the id of the actor that you are looking up. The out parameters return the data read from the table.
You can declare SimpleJdbcCall in a manner similar to declaring SimpleJdbcInsert. You should instantiate and configure the class in the initialization method of your data-access layer. Compared to the StoredProcedure class, you need not create a subclass and you need not to declare parameters that can be looked up in the database metadata. The following example of a SimpleJdbcCall configuration uses the preceding stored procedure (the only configuration option, in addition to the DataSource, is the name of the stored procedure):
in_id 파라미터는 조회하려는 배우의 ID 값을 담고 있습니다. out 파라미터들은 테이블에서 읽어온 데이터를 반환합니다.
SimpleJdbcInsert를 선언하는 방식과 유사하게 SimpleJdbcCall도 선언할 수 있습니다.
이 클래스는 데이터 접근 계층의 초기화 메서드에서 인스턴스를 생성하고 설정해야 합니다.
StoredProcedure 클래스와 비교했을 때, SimpleJdbcCall은 하위 클래스를 만들 필요도 없고,
데이터베이스 메타데이터에서 조회 가능한 파라미터를 직접 선언할 필요도 없습니다.
다음 예제는 앞에서 사용한 저장 프로시저를 기반으로 한 SimpleJdbcCall 설정 예시입니다.
(DataSource 외에 필요한 유일한 설정은 프로시저 이름입니다.)
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
this.procReadActor = new SimpleJdbcCall(dataSource)
.withProcedureName("read_actor");
}
public Actor readActor(Long id) {
SqlParameterSource in = new MapSqlParameterSource()
.addValue("in_id", id);
Map out = procReadActor.execute(in);
Actor actor = new Actor();
actor.setId(id);
actor.setFirstName((String) out.get("out_first_name"));
actor.setLastName((String) out.get("out_last_name"));
actor.setBirthDate((Date) out.get("out_birth_date"));
return actor;
}
// ... additional methods
}
The code you write for the execution of the call involves creating an SqlParameterSource containing the IN parameter. You must match the name provided for the input value with that of the parameter name declared in the stored procedure. The case does not have to match because you use metadata to determine how database objects should be referred to in a stored procedure. What is specified in the source for the stored procedure is not necessarily the way it is stored in the database. Some databases transform names to all upper case, while others use lower case or use the case as specified.
당신이 호출을 실행하기 위해 작성하는 코드는 IN 파라미터를 포함하는 SqlParameterSource를 생성하는 작업을 포함합니다. 입력 값에 제공된 이름은 저장 프로시저에 선언된 파라미터 이름과 일치해야 합니다. 하지만 대소문자는 일치하지 않아도 됩니다. 왜냐하면, 메타데이터를 사용하여 저장 프로시저에서 데이터베이스 객체가 어떻게 참조되어야 하는지를 결정하기 때문입니다. 저장 프로시저의 소스에 명시된 방식이 반드시 데이터베이스에 저장된 방식과 같지는 않습니다. 일부 데이터베이스는 이름을 모두 대문자로 변환해서 저장하고, 다른 데이터베이스는 소문자나 지정된 대소문자 형태 그대로 저장합니다.
The execute method takes the IN parameters and returns a Map that contains any out parameters keyed by the name, as specified in the stored procedure. In this case, they are out_first_name, out_last_name, and out_birth_date.
execute 메서드는 IN 파라미터들을 받아들이고, 저장 프로시저에 명시된 이름을 키로 갖는 OUT 파라미터들을 포함하는 Map을 반환합니다. 이 경우, 반환되는 OUT 파라미터들은 out_first_name, out_last_name, out_birth_date입니다.
The last part of the execute method creates an Actor instance to use to return the data retrieved. Again, it is important to use the names of the out parameters as they are declared in the stored procedure. Also, the case in the names of the out parameters stored in the results map matches that of the out parameter names in the database, which could vary between databases. To make your code more portable, you should do a case-insensitive lookup or instruct Spring to use a LinkedCaseInsensitiveMap. To do the latter, you can create your own JdbcTemplate and set the setResultsMapCaseInsensitive property to true. Then you can pass this customized JdbcTemplate instance into the constructor of your SimpleJdbcCall. The following example shows this configuration:
execute 메서드의 마지막 부분에서는, 조회된 데이터를 반환하기 위해 Actor 인스턴스를 생성합니다. 이때, 저장 프로시저에서 선언된 OUT 파라미터 이름을 사용하는 것이 중요합니다.
또한 결과 Map에 저장된 OUT 파라미터 이름의 대소문자는 데이터베이스에 저장된 OUT 파라미터 이름의 대소문자와 일치하는데, 이는 데이터베이스에 따라 달라질 수 있습니다.
코드의 이식성을 높이기 위해서는 대소문자를 구분하지 않는 조회(case-insensitive lookup)를 하거나, Spring에게 LinkedCaseInsensitiveMap을 사용하도록 지시해야 합니다. 후자의 방법을 사용하려면, 직접 JdbcTemplate을 생성하고 setResultsMapCaseInsensitive 속성을 true로 설정하면 됩니다. 그런 다음, 이 커스터마이징된 JdbcTemplate 인스턴스를 SimpleJdbcCall의 생성자에 전달하면 됩니다. 다음은 이 설정을 보여주는 예시입니다.
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadActor = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_actor");
}
// ... additional methods
}
By taking this action, you avoid conflicts in the case used for the names of your returned out parameters.
이렇게 하면 반환된 OUT 파라미터 이름에 사용된 대소문자로 인한 충돌을 피할 수 있습니다.
Explicitly Declaring Parameters to Use for a SimpleJdbcCall
Earlier in this chapter, we described how parameters are deduced from metadata, but you can declare them explicitly if you wish. You can do so by creating and configuring SimpleJdbcCall with the declareParameters method, which takes a variable number of SqlParameter objects as input. See the next section for details on how to define an SqlParameter.
이 장의 앞부분에서 우리는 파라미터가 메타데이터로부터 유추되는 방식에 대해 설명했지만, 원한다면 그것들을 명시적으로 선언할 수도 있습니다. 그렇게 하려면 SimpleJdbcCall을 생성하고 구성할 때 declareParameters 메서드를 사용하면 되며, 이 메서드는 가변 개수의 SqlParameter 객체들을 입력으로 받습니다. SqlParameter를 정의하는 방법에 대한 자세한 내용은 다음 절을 참조하십시오.
Explicit declarations are necessary if the database you use is not a Spring-supported database. Currently, Spring supports metadata lookup of stored procedure calls for the following databases: Apache Derby, DB2, MySQL, Microsoft SQL Server, Oracle, and Sybase. We also support metadata lookup of stored functions for MySQL, Microsoft SQL Server, and Oracle. |
명시적인 선언은 사용 중인 데이터베이스가 Spring에서 지원하는 데이터베이스가 아닌 경우에 필요합니다. 현재 Spring은 저장 프로시저 호출에 대한 메타데이터 조회를 다음 데이터베이스에 대해 지원합니다: Apache Derby, DB2, MySQL, Microsoft SQL Server, Oracle, 그리고 Sybase. 또한, 저장 함수에 대한 메타데이터 조회는 MySQL, Microsoft SQL Server, 그리고 Oracle에서 지원합니다.
You can opt to explicitly declare one, some, or all of the parameters. The parameter metadata is still used where you do not explicitly declare parameters. To bypass all processing of metadata lookups for potential parameters and use only the declared parameters, you can call the method withoutProcedureColumnMetaDataAccess as part of the declaration. Suppose that you have two or more different call signatures declared for a database function. In this case, you call useInParameterNames to specify the list of IN parameter names to include for a given signature.
하나, 일부 또는 모든 파라미터를 명시적으로 선언하도록 선택할 수 있습니다. 파라미터를 명시적으로 선언하지 않은 경우에는 여전히 파라미터 메타데이터가 사용됩니다. 잠재적인 파라미터에 대한 모든 메타데이터 조회 처리를 우회하고 오직 선언된 파라미터만 사용하려면, 선언 과정의 일부로 withoutProcedureColumnMetaDataAccess 메서드를 호출하면 됩니다. 데이터베이스 함수에 대해 두 개 이상의 서로 다른 호출 시그니처가 선언되어 있다고 가정해 봅시다. 이 경우에는 useInParameterNames를 호출하여 주어진 시그니처에 포함시킬 IN 파라미터 이름 목록을 지정할 수 있습니다.
The following example shows a fully declared procedure call and uses the information from the preceding example:
다음 예제는 완전히 선언된 프로시저 호출을 보여주며, 앞선 예제의 정보를 사용합니다.
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadActor = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_actor")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames("in_id")
.declareParameters(
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
new SqlOutParameter("out_last_name", Types.VARCHAR),
new SqlOutParameter("out_birth_date", Types.DATE)
);
}
// ... additional methods
}
두 예제의 실행과 최종 결과는 동일합니다. 두 번째 예제는 메타데이터에 의존하지 않고 모든 세부 정보를 명시적으로 지정합니다.
How to Define SqlParameters
To define a parameter for the SimpleJdbc classes and also for the RDBMS operations classes (covered in Modeling JDBC Operations as Java Objects) you can use SqlParameter or one of its subclasses. To do so, you typically specify the parameter name and SQL type in the constructor. The SQL type is specified by using the java.sql.Types constants. Earlier in this chapter, we saw declarations similar to the following:
SimpleJdbc 클래스와 RDBMS 작업 클래스(이 장의 “JDBC 작업을 자바 객체로 모델링하기”에서 다룸)에 대해 파라미터를 정의하려면, SqlParameter 또는 그 하위 클래스를 사용할 수 있습니다. 이를 위해 일반적으로 생성자에서 파라미터 이름과 SQL 타입을 지정합니다. SQL 타입은 java.sql.Types의 상수를 사용하여 지정합니다. 이 장의 앞부분에서는 다음과 유사한 선언을 보았습니다.
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
The first line with the SqlParameter declares an IN parameter. You can use IN parameters for both stored procedure calls and for queries by using the SqlQuery and its subclasses (covered in Understanding SqlQuery).
SqlParameter가 있는 첫 번째 줄은 IN 파라미터를 선언합니다. IN 파라미터는 저장 프로시저 호출뿐만 아니라, SqlQuery 및 그 하위 클래스를 사용한 쿼리에도 사용할 수 있습니다(이는 “SqlQuery 이해하기”에서 다룹니다).
The second line (with the SqlOutParameter) declares an out parameter to be used in a stored procedure call. There is also an SqlInOutParameter for InOut parameters (parameters that provide an IN value to the procedure and that also return a value).
두 번째 줄(SqlOutParameter)은 저장 프로시저 호출에 사용될 OUT 파라미터를 선언합니다. 또한 SqlInOutParameter도 있는데, 이는 IN 값을 프로시저에 제공하면서 동시에 값을 반환받는 InOut 파라미터를 위한 것입니다.
Only parameters declared as SqlParameter and SqlInOutParameter are used to provide input values. This is different from the StoredProcedure class, which (for backwards compatibility reasons) lets input values be provided for parameters declared as SqlOutParameter.
SqlParameter와 SqlInOutParameter로 선언된 파라미터만 입력값을 제공하는 데 사용됩니다. 이는 StoredProcedure 클래스와는 다른데, StoredProcedure 클래스는 (하위 호환성 이유로) SqlOutParameter로 선언된 파라미터에도 입력값을 제공할 수 있도록 허용합니다.
For IN parameters, in addition to the name and the SQL type, you can specify a scale for numeric data or a type name for custom database types. For out parameters, you can provide a RowMapper to handle mapping of rows returned from a REF cursor. Another option is to specify an SqlReturnType that provides an opportunity to define customized handling of the return values.
IN 파라미터의 경우, 이름과 SQL 타입 외에도 숫자 데이터에 대해 소수 자릿수(scale)를 지정하거나 사용자 정의 데이터베이스 타입에 대해 타입 이름(type name)을 지정할 수 있습니다. OUT 파라미터의 경우에는, REF 커서에서 반환된 행들을 매핑하기 위해 RowMapper를 제공할 수 있습니다. 또 다른 옵션으로는, 반환 값에 대한 사용자 정의 처리를 할 수 있도록 SqlReturnType을 지정하는 방법도 있습니다.
Calling a Stored Function by Using SimpleJdbcCall
You can call a stored function in almost the same way as you call a stored procedure, except that you provide a function name rather than a procedure name. You use the withFunctionName method as part of the configuration to indicate that you want to make a call to a function, and the corresponding string for a function call is generated. A specialized call (executeFunction) is used to run the function, and it returns the function return value as an object of a specified type, which means you do not have to retrieve the return value from the results map. A similar convenience method (named executeObject) is also available for stored procedures that have only one out parameter. The following example (for MySQL) is based on a stored function named get_actor_name that returns an actor’s full name:
저장 함수를 호출하는 것은 저장 프로시저를 호출하는 것과 거의 동일한 방식으로 수행할 수 있습니다. 단지 프로시저 이름 대신 함수 이름을 제공한다는 점만 다릅니다. 함수 호출을 원한다는 것을 나타내기 위해 설정의 일부로 withFunctionName 메서드를 사용하고, 이에 따라 함수 호출에 대한 적절한 문자열이 생성됩니다.
함수를 실행하기 위해서는 executeFunction이라는 특수한 호출 메서드를 사용하며, 이 메서드는 함수의 반환 값을 지정된 타입의 객체로 반환합니다. 즉, 반환 값을 결과 맵에서 따로 꺼낼 필요가 없습니다.
또한, 하나의 OUT 파라미터만 가지는 저장 프로시저의 경우에도 유사한 편의 메서드인 executeObject를 사용할 수 있습니다.
다음 예제(MySQL 기준)는 get_actor_name이라는 이름의 저장 함수를 기반으로 하며, 이 함수는 배우의 전체 이름을 반환합니다.
CREATE FUNCTION get_actor_name (in_id INTEGER)
RETURNS VARCHAR(200) READS SQL DATA
BEGIN
DECLARE out_name VARCHAR(200);
SELECT concat(first_name, ' ', last_name)
INTO out_name
FROM t_actor where id = in_id;
RETURN out_name;
END;
To call this function, we again create a SimpleJdbcCall in the initialization method, as the following example shows:
이 함수를 호출하기 위해, 우리는 다시 초기화 메서드에서 SimpleJdbcCall을 생성합니다. 다음 예제는 그 방법을 보여줍니다.
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall funcGetActorName;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate)
.withFunctionName("get_actor_name");
}
public String getActorName(Long id) {
SqlParameterSource in = new MapSqlParameterSource()
.addValue("in_id", id);
String name = funcGetActorName.executeFunction(String.class, in);
return name;
}
// ... additional methods
}
The executeFunction method used returns a String that contains the return value from the function call.
사용된 executeFunction 메서드는 함수 호출로부터 반환된 값을 포함하는 문자열(String)을 반환합니다.
Returning a ResultSet or REF Cursor from a SimpleJdbcCall
Calling a stored procedure or function that returns a result set is a bit tricky. Some databases return result sets during the JDBC results processing, while others require an explicitly registered out parameter of a specific type. Both approaches need additional processing to loop over the result set and process the returned rows. With the SimpleJdbcCall, you can use the returningResultSet method and declare a RowMapper implementation to be used for a specific parameter. If the result set is returned during the results processing, there are no names defined, so the returned results must match the order in which you declare the RowMapper implementations. The name specified is still used to store the processed list of results in the results map that is returned from the execute statement.
SimpleJdbcCall을 사용하여 ResultSet 또는 REF Cursor를 반환받는 것은 약간 까다롭습니다. 저장 프로시저나 함수를 호출할 때 결과 집합(result set)을 반환받는 방식은 데이터베이스마다 다릅니다. 어떤 데이터베이스는 JDBC 결과 처리 중에 결과 집합을 반환하고, 다른 데이터베이스는 특정 타입의 OUT 파라미터를 명시적으로 등록해야 합니다.
두 방식 모두, 결과 집합을 순회하면서 반환된 행들을 처리하기 위한 추가 작업이 필요합니다.
SimpleJdbcCall을 사용할 경우, returningResultSet 메서드를 이용하여 특정 파라미터에 대해 사용할 RowMapper 구현체를 선언할 수 있습니다.
만약 결과 집합이 결과 처리 도중에 반환된다면, 이름이 정의되어 있지 않기 때문에 반환된 결과는 RowMapper 구현체를 선언한 순서와 일치해야 합니다. 지정된 이름은 여전히 execute 호출 결과로 반환되는 결과 맵 안에서 처리된 결과 리스트를 저장하는 데 사용됩니다.
The next example (for MySQL) uses a stored procedure that takes no IN parameters and returns all rows from the t_actor table:
다음 예제(MySQL 기준)는 IN 파라미터 없이 t_actor 테이블의 모든 행을 반환하는 저장 프로시저를 사용합니다.
CREATE PROCEDURE read_all_actors()
BEGIN
SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a;
END;
To call this procedure, you can declare the RowMapper. Because the class to which you want to map follows the JavaBean rules, you can use a BeanPropertyRowMapper that is created by passing in the required class to map to in the newInstance method. The following example shows how to do so:
이 프로시저를 호출하려면, RowMapper를 선언할 수 있습니다. 매핑하려는 클래스가 JavaBean 규칙을 따르기 때문에, newInstance 메서드에 매핑 대상 클래스를 전달하여 생성된 BeanPropertyRowMapper를 사용할 수 있습니다. 다음 예제는 그 방법을 보여줍니다.
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadAllActors;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_all_actors")
.returningResultSet("actors",
BeanPropertyRowMapper.newInstance(Actor.class));
}
public List getActorsList() {
Map m = procReadAllActors.execute(new HashMap<String, Object>(0));
return (List) m.get("actors");
}
// ... additional methods
}
The execute call passes in an empty Map, because this call does not take any parameters. The list of actors is then retrieved from the results map and returned to the caller.
이 호출은 파라미터를 받지 않기 때문에 execute 호출 시 빈 Map을 전달합니다. 그런 다음, 배우 목록은 결과 맵에서 꺼내어 호출자에게 반환됩니다.
정리
SimpleJdbcInsert와 SimpleJdbcCall은 반복적인 JDBC 코드 없이 insert, 프로시저, 함수 호출을 간단하게 처리할 수 있는 Spring의 유틸리티 클래스다.
'공식문서' 카테고리의 다른 글
[Spring Docs] Common Problems with Parameter and Data Value Handling (0) | 2025.04.11 |
---|---|
[Spring Docs] Modeling JDBC Operations as Java Objects (0) | 2025.04.10 |
[Spring Docs] JDBC Batch Operations (0) | 2025.04.04 |
[Spring Docs] Controlling Database Connection (0) | 2025.04.02 |
Garbage Collector #4 (0) | 2025.04.01 |