이 섹션에서는 JDBC core 클래스들을 사용하여 오류 처리를 포함한 기본 JDBC 처리를 제어하는 방법을 다룹니다. 여기에는 다음 주제가 포함됩니다.
JdbcTemplate 사용NamedParameterJdbcTemplate 사용JdbcClientSQLExceptionTranslator 사용JdbcTemplateJdbcTemplate은 JDBC 코어 패키지의 중심 클래스입니다. 이는 리소스 생성 및 해제를 처리하므로 연결을 닫는 것을 잊어버리는 등의 일반적인 오류를 방지하는 데 도움이 됩니다. 이는 핵심 JDBC 워크플로우의 기본 작업(예: 명령문 생성 및 실행)을 수행하고, 애플리케이션 코드는 SQL을 제공하고 결과를 추출하도록 남겨둡니다. JdbcTemplate 클래스:
ResultSet 인스턴스에 대해 반복을 수행하고 반환된 매개변수 값을 추출합니다.org.springframework.dao 패키지에 정의된 일반적이고 더 많은 정보를 제공하는 예외 계층 구조로 변환합니다. (일관된 예외 계층 구조를 참조하세요.)코드에 JdbcTemplate을 사용할 때 콜백 인터페이스를 구현하고 명확하게 정의된 계약(contract)을 제공하기만 하면 됩니다. JdbcTemplate 클래스에서 제공하는 Connection이 주어지면, preparedStatementCreator 콜백 인터페이스는 SQL 및 필요한 매개변수를 제공하는 준비된 명령문을 생성합니다. 호출 가능한 명령문을 생성하는 CallableStatementCreator 인터페이스의 경우에도 마찬가지입니다. RowCallbackHandler 인터페이스는 ResultSet의 각 행에서 값을 추출합니다.
DataSource 참조로 직접 인스턴스화를 통해 DAO 구현 내에서 JdbcTemplate을 사용하거나 Spring IoC 컨테이너에서 구성하여 DAO에 Bean 참조로 제공할 수 있습니다.
[Note]
DataSource는 항상 Spring IoC 컨테이너에서 빈으로 구성되어야 합니다. 첫 번째 경우에는 Bean이 서비스에 직접 제공됩니다. 두 번째 경우에는 준비된 템플릿에 제공됩니다.
이 클래스에 의해 실행된 모든 SQL은 템플릿 인스턴스(일반적으로 JdbcTemplate이지만 JdbcTemplate 클래스의 사용자 정의 하위 클래스를 사용하는 경우 다를 수 있음)의 정규화된 클래스 이름에 해당하는 범주 아래 DEBUG 수준에 기록(logged)됩니다.
다음 섹션에서는 JdbcTemplate 사용의 몇 가지 예를 제공합니다. 이 예제는 JdbcTemplate에 의해 노출된 모든 기능의 완전한 목록이 아닙니다. 이에 대해서는 따라오는 javadoc을 참조하십시오.
SELECT)다음 쿼리는 관계(relation)의 행(row) 수를 가져옵니다.
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
다음 쿼리는 바인드(bind) 변수를 사용합니다.
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
다음 쿼리는 String을 찾습니다.
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);
다음 쿼리는 단일 도메인 객체를 찾아서 채웁(populate)니다.
Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
(resultSet, rowNum) -> {
Actor newActor = new Actor();
newActor.setFirstName(resultSet.getString("first_name"));
newActor.setLastName(resultSet.getString("last_name"));
return newActor;
},
1212L);
다음 쿼리는 도메인 객체 목록을 찾아서 채웁니다.
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
(resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
});
마지막 두 코드 조각이 실제로 동일한 애플리케이션에 존재하는 경우 두 개의 RowMapper 람다 식에 있는 중복을 제거하고 필요에 따라 DAO 메서드에서 참조할 수 있는 단일 필드로 추출하는 것이 합리적입니다. 예를 들어 앞의 코드 조각을 다음과 같이 작성하는 것이 더 나을 수 있습니다.
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
};
public List<Actor> findAllActors() {
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
}
INSERT, UPDATE and DELETE) with jdbcTemplateupdate(..) 메서드를 사용하여 삽입, 업데이트 및 삭제 작업을 수행할 수 있습니다. 매개변수 값은 일반적으로 가변(variable) 인수로 제공되거나 객체 배열로 제공됩니다. 다음 예에서는 새 항목(entry)을 삽입합니다.
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
다음 예에서는 기존 항목을 업데이트합니다.
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
다음 예에서는 항목을 삭제합니다.
this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
jdbcTemplate Operations임의의 SQL을 실행하려면 execute(..) 메서드를 사용할 수 있습니다. 결과적으로 이 메서드는 DDL 문에 자주 사용됩니다. 콜백 인터페이스, 변수 배열에 바인딩하기 등을 사용하는 변형으로 인해 과부하가 심하게 발생합니다(또는 overload 되었습니다.) 다음 예시에서는 테이블을 생성합니다.
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
다음 예에서는 저장된 프로시저를 호출합니다.
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));
보다 정교한 저장 프로시저 지원은 나중에 다룹니다.
JdbcTemplate Best PracticesJdbcTemplate 클래스의 인스턴스는 일단 구성되면 스레드로부터 안전합니다. 이는 JdbcTemplate의 단일 인스턴스를 구성한 다음 이 공유(shared) 참조를 여러 DAO(또는 저장소)에 안전하게 삽입할 수 있다는 의미이므로 중요합니다. JdbcTemplate은 DataSource에 대한 참조를 유지한다는 점에서 stateful이지만 이 상태는 conversational state가 아닙니다.
JdbcTemplate 클래스(및 관련 NamedParameterJdbcTemplate 클래스)를 사용할 때 일반적인 방법은 Spring 구성 파일에서 DataSource를 구성한 다음 해당 공유 DataSource 빈을 DAO 클래스에 종속성 주입하는 것입니다. JdbcTemplate은 DataSource의 setter에서 생성됩니다. 이로 인해 다음과 유사한 DAO가 생성됩니다.
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
다음 예에서는 해당 XML 구성을 보여줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
명시적 구성의 대안은 종속성 주입을 위한 구성 요소 검색 및 어노테이션 지원을 사용하는 것입니다. 이 경우 클래스에 @Repository(컴포넌트 검색 후보가 됨)로 어노테이션을 달고 DataSource setter 메서드에 @Autowired로 어노테이션을 달 수 있습니다. 다음 예에서는 그 방법을 보여줍니다.
@Repository // (1)
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
@Autowired // (2)
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource); // (3)
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
(1) @Repository로 클래스에 어노테이션을 답니다.
(2) @Autowired로 DataSource setter 메소드에 어노테이션을 답니다.
(3) DataSource를 사용하여 새 JdbcTemplate을 만듭니다.
다음 예에서는 해당 XML 구성을 보여줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- Scans within the base package of the application for @Component classes to configure as beans -->
<context:component-scan base-package="org.springframework.docs.test" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
Spring의 JdbcDaoSupport 클래스를 사용하고 다양한 JDBC 지원 DAO 클래스가 이 클래스에서 확장되면 하위 클래스는 JdbcDaoSupport 클래스에서 setDataSource(..) 메서드를 상속합니다. 이 클래스에서 상속할지 여부를 선택할 수 있습니다. JdbcDaoSupport 클래스는 편의를 위해서만 제공됩니다.
위의 템플릿 초기화 스타일 중 어떤 것을 사용하기로 선택했는지 여부에 관계없이 SQL을 실행할 때마다 JdbcTemplate 클래스의 새 인스턴스를 생성할 필요가 거의 없습니다. 일단 구성되면 JdbcTemplate 인스턴스는 스레드로부터 안전합니다. 애플리케이션이 여러 데이터베이스에 액세스하는 경우 여러 JdbcTemplate 인스턴스가 필요할 수 있으며, 이를 위해서는 여러 DataSource가 필요하고 이후에는 다르게 구성된 여러 JdbcTemplate 인스턴스가 필요합니다.
NamedParameterJdbcTemplateNamedParameterJdbcTemplate 클래스는 기존 자리 표시자('?') 인수만 사용하여 JDBC 문을 프로그래밍하는 것과 달리 명명된 매개 변수를 사용하여 JDBC 문 프로그래밍에 대한 지원을 추가합니다. NamedParameterJdbcTemplate 클래스는 JdbcTemplate을 래핑하고 래핑된 JdbcTemplate에 위임하여 많은 작업을 수행합니다. 이 섹션에서는 JdbcTemplate 자체와 다른 NamedParameterJdbcTemplate 클래스의 영역, 즉 명명된 매개 변수를 사용하여 JDBC 문을 프로그래밍하는 영역만 설명합니다. 다음 예에서는 NamedParameterJdbcTemplate을 사용하는 방법을 보여줍니다.
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from t_actor where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
sql 변수에 할당된 값과 namedParameters 변수(MapSqlParameterSource 타입)에 연결(plugged into)되는 해당 값에서 명명된 매개 변수 표기법을 사용합니다.
또는 Map 기반 스타일을 사용하여 명명된 매개 변수와 해당 값을 NamedParameterJdbcTemplate 인스턴스에 전달할 수 있습니다. NamedParameterJdbcOperations에 의해 노출되고 NamedParameterJdbcTemplate 클래스에 의해 구현되는 나머지 메서드는 비슷한 패턴을 따르므로 여기서는 다루지 않습니다.
다음 예에서는 Map 기반 스타일의 사용을 보여줍니다.
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from t_actor where first_name = :first_name";
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
NamedParameterJdbcTemplate(및 동일한 Java 패키지에 존재)과 관련된 유용한 기능 중 하나는 SqlParameterSource 인터페이스입니다. 이전 코드 조각(MapSqlParameterSource 클래스) 중 하나에서 이 인터페이스 구현의 예를 이미 확인했습니다. SqlParameterSource는 NamedParameterJdbcTemplate에 대한 명명된 매개 변수 값의 소스입니다. MapSqlParameterSource 클래스는 java.util.Map 주변의 어댑터의 간단한 구현입니다. 여기서 키는 매개변수 이름이고 값은 매개변수 값입니다.
또 다른 SqlParameterSource 구현은 BeanPropertySqlParameterSource 클래스입니다. 이 클래스는 임의의 JavaBean(즉, JavaBean 규칙을 준수하는 클래스의 인스턴스)을 래핑하고 래핑된 JavaBean의 속성(property)을 명명된 매개변수 값의 소스로 사용합니다.
다음 예는 전형적인 JavaBean을 보여줍니다.
public class Actor {
private Long id;
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public Long getId() {
return this.id;
}
// setters omitted...
}
NamedParameterJdbcTemplate을 사용하여 이전 예제에 표시된 클래스 멤버 수를 반환합니다.
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActors(Actor exampleActor) {
// notice how the named parameters match the properties of the above 'Actor' class
String sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
NamedParameterJdbcTemplate 클래스는 클래식 JdbcTemplate 템플릿을 래핑한다는 점을 기억하세요. JdbcTemplate 클래스에만 존재하는 기능에 액세스하기 위해 래핑된 JdbcTemplate 인스턴스에 액세스해야 하는 경우 getJdbcOperations() 메서드를 사용하여 JdbcOperations 인터페이스를 통해 래핑된 JdbcTemplate에 액세스할 수 있습니다.
애플리케이션 컨텍스트에서 NamedParameterJdbcTemplate 클래스를 사용하는 방법에 대한 지침은 JdbcTemplate 모범 사례도 참조하세요.
JdbcClient6.1부터 NamedParameterJdbcTemplate의 명명된 매개 변수 문과 일반 JdbcTemplate의 위치 매개 변수 문은 유려한 상호 작용 모델을 갖춘 통합 클라이언트 API를 통해 사용할 수 있습니다.
예를 들어, 위치 매개변수의 경우:
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = ?")
.param(firstName)
.query(Integer.class).single();
}
예를 들어 명명된 매개변수를 사용하면 다음과 같습니다.
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = :firstName")
.param("firstName", firstName)
.query(Integer.class).single();
}
유연한 결과 resolution을 통해 RowMapper 기능도 사용할 수 있습니다.
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query((rs, rowNum) -> new Actor(rs.getString("first_name"), rs.getString("last_name")))
.list();
사용자 정의 RowMapper 대신 매핑할 클래스를 지정할 수도 있습니다. 예를 들어 Actor가 레코드 클래스, 사용자 정의 생성자, Bean 속성 또는 일반 필드로 firstName 및 lastName 속성(property)을 가지고 있다고 가정합니다.
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query(Actor.class)
.list();
필수 단일 객체 결과:
Actor actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
.param(1212L)
.query(Actor.class)
.single();
java.util.Optional 결과:
Optional<Actor> actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
.param(1212L)
.query(Actor.class)
.optional();
그리고 업데이트 문의 경우:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (?, ?)")
.param("Leonor").param("Watling")
.update();
또는 명명된 매개변수가 있는 업데이트 문:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.param("firstName", "Leonor").param("lastName", "Watling")
.update();
개별 명명된 매개변수 대신 매개변수 소스 객체(예: 레코드 클래스, Bean 속성(property)이 있는 클래스 또는 위의 Actor 클래스처럼 firstName 및 lastName 속성(property)을 제공하는 일반 필드 홀더)를 지정할 수도 있습니다.
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.paramSource(new Actor("Leonor", "Watling")
.update();
매개변수에 대한 자동 Actor 클래스 매핑과 위의 쿼리 결과는 직접 사용할 수도 있는 암시적 SimplePropertySqlParameterSource 및 SimplePropertyRowMapper 전략을 통해 제공됩니다. 이는 JdbcTemplate 및 NamedParameterJdbcTemplate 자체와 함께 BeanPropertySqlParameterSource 및 BeanPropertyRowMapper/DataClassRowMapper에 대한 일반적인 대체(common replacement) 역할을 할 수 있습니다.
[Note]
JdbcClient는 JDBC 쿼리/업데이트 문을 위한 유연하지만 단순화된 facade입니다. 일괄(batch) 삽입 및 저장된 프로시저 호출과 같은 고급 기능에는 일반적으로 추가 사용자 정의가 필요합니다.JdbcClient에서 사용할 수 없는 이러한 기능에 대해서는 Spring의SimpleJdbcInsert및SimpleJdbcCall클래스 또는 일반 직접JdbcTemplate사용을 고려하세요.
SQLEcveptionTranslatorSQLExceptionTranslator는 SQLException과 Spring 자체의 org.springframework.dao.DataAccessException 사이를 변환(translate)할 수 있는 클래스에 의해 구현되는 인터페이스입니다. 이는 데이터 액세스 전략과 관련하여 불가지론적(agnostic)입니다. 정밀도를 높이기 위해 구현은 일반적(generic)(예: JDBC용 SQLState 코드 사용) 또는 독점적(proprietary)(예: Oracle 오류 코드 사용)일 수 있습니다. 이 예외 변환 메커니즘은 SQLException을 전파하지 않고 DataAccessException을 전파하는 공통 JdbcTemplate 및 JdbcTransactionManager 진입점 뒤에서 사용됩니다.
여기서 "generic"과 "proprietary"는 SQLExceptionTranslator 인터페이스의 구현체가 다양한 방식으로 SQLException을 처리하는 방식을 설명합니다.
Generic: Generic 구현은 JDBC의 SQLState 코드 등과 같은 일반적인 정보를 사용하여 SQLException을 처리합니다. 이는 특정 데이터베이스 제품에 종속되지 않고, 일반적인 에러 상황을 다룰 수 있도록 합니다. 예를 들어, SQLExceptionTranslator 구현체가 JDBC의 SQLState 코드에 따라 예외를 변환할 수 있습니다. 이것은 여러 데이터베이스에서 유사한 예외를 처리하는 데 유용합니다.
Proprietary: Proprietary 구현은 특정 데이터베이스 제품의 오류 코드 등과 같은 고유한 정보를 사용하여 SQLException을 처리합니다. 이는 특정 데이터베이스 벤더의 특수한 예외 처리를 지원합니다. 예를 들어, Oracle 데이터베이스의 경우 특정 오류 코드가 있는데, 이를 이용하여 SQLException을 정확하게 처리할 수 있습니다.
즉, generic 구현은 데이터베이스 종류에 관계없이 일반적인 방식으로 SQLException을 처리하고, proprietary 구현은 특정 데이터베이스 제품에 종속된 방식으로 SQLException을 처리합니다.
[Note]
6.0부터 기본 예외 변환기는SQLExceptionSubclassTranslator로, 몇 가지 추가 검사를 통해 JDBC 4SQLException하위 클래스를 감지하고SQLStateSQLExceptionTranslator를 통해SQLState자체 검사를 수행합니다. 이는 일반적으로 일반적인 데이터베이스 액세스에 충분하며 공급업체별 감지가 필요하지 않습니다. 이전 버전과의 호환성을 위해 아래 설명된 대로 잠재적으로 사용자 지정 오류 코드 매핑과 함께SQLErrorCodeSQLExceptionTranslator를 사용하는 것을 고려하세요.
SQLErrorCodeSQLExceptionTranslator는 classpath의 루트에 sql-error-codes.xml이라는 파일이 있을 때 기본적으로 사용되는 SQLExceptionTranslator의 구현입니다. 이 구현에서는 특정 공급업체 코드를 사용합니다. 이는 SQLState 또는 SQLException 하위 클래스 변환보다 더 정확합니다. 오류 코드 변환은 SQLErrorCodes라는 JavaBean 타입 클래스에 포함된 코드를 기반으로 합니다. 이 클래스는 sql-error-codes.xml이라는 구성 파일의 내용을 기반으로 SQLErrorCode를 생성하기 위한 팩토리인 SQLErrorCodesFactory에 의해 생성되고 채워집니다. 이 파일은 공급업체 코드로 채워지며 DatabaseMetaData에서 가져온 DatabaseProductName을 기반으로 합니다. 사용하고 있는 실제 데이터베이스의 코드가 사용됩니다.
SQLErrorCodeSQLExceptionTranslator는 다음 순서로 일치(matching) 규칙을 적용합니다.
서브클래스에 의해 구현된 모든 사용자 정의 번역입니다. 일반적으로 제공된 구체(concrete) SQLErrorCodeSQLExceptionTranslator가 사용되므로 이 규칙은 적용되지 않습니다. 실제로 하위 클래스 구현을 제공받은 경우에만 적용됩니다.
SQLErrorCodes 클래스의 customSqlExceptionTranslator 속성(property)으로 제공되는 SQLExceptionTranslator 인터페이스의 사용자 정의 구현입니다.
CustomSQLErrorCodesTranslation 클래스(SQLErrorCodes 클래스의 customTranslations 속성(property)에 제공됨)의 인스턴스 목록에서 일치 항목이 검색됩니다.
오류 코드 일치(matching)가 적용됩니다.
대체(fallback) 변환기(translator)를 사용하십시오. SQLExceptionSubclassTranslator는 기본 대체 변환기입니다. 이 변환을 사용할 수 없는 경우 다음 대체 변환기는 SQLStateSQLExceptionTranslator입니다.
[Note]
SQLErrorCodesFactory는 기본적으로 오류 코드 및 사용자 정의 예외 변환을 정의하는 데 사용됩니다. 이는 classpath의sql-error-codes.xml파일에서 조회되며, 일치하는SQLErrorCodes인스턴스는 사용 중인 데이터베이스의 데이터베이스 메타데이터에 있는 데이터베이스 이름을 기반으로 찾습니다.
다음 예제와 같이 SQLErrorCodeSQLExceptionTranslator를 확장할 수 있습니다.
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) {
if (sqlEx.getErrorCode() == -12345) {
return new DeadlockLoserDataAccessException(task, sqlEx);
}
return null;
}
}
앞의 예에서는 특정 오류 코드(-12345)가 변환(translate)되고 다른 오류는 기본 변환기 구현에 의해 변환됩니다. 이 사용자 정의 변환기를 사용하려면 setExceptionTranslator 메소드를 통해 이를 JdbcTemplate에 전달해야 하며 이 변환기가 필요한 모든 데이터 액세스 처리에 이 JdbcTemplate을 사용해야 합니다. 다음 예에서는 이 사용자 정의 변환기를 사용하는 방법을 보여줍니다.
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
// create a JdbcTemplate and set data source
this.jdbcTemplate = new JdbcTemplate();
this.jdbcTemplate.setDataSource(dataSource);
// create a custom translator and set the DataSource for the default translation lookup
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
tr.setDataSource(dataSource);
this.jdbcTemplate.setExceptionTranslator(tr);
}
public void updateShippingCharge(long orderId, long pct) {
// use the prepared JdbcTemplate for this update
this.jdbcTemplate.update("update orders" +
" set shipping_charge = shipping_charge * ? / 100" +
" where id = ?", pct, orderId);
}
sql-error-codes.xml에서 오류 코드를 조회하기 위해 사용자 정의 변환기에 데이터 소스가 전달됩니다.
SQL 문을 실행하려면 코드가 거의 필요하지 않습니다. JdbcTemplate과 함께 제공되는 편리한 메소드를 포함하여 DataSource 및 JdbcTemplate이 필요합니다. 다음 예에서는 새 테이블을 생성하는 최소한이지만 완전한 기능을 갖춘 클래스에 포함해야 할 사항을 보여줍니다.
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAStatement {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void doExecute() {
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
}
}
일부 쿼리 메서드는 단일 값을 반환합니다. 한 행에서 개수(count)나 특정 값을 검색하려면 queryForObject(..)를 사용하세요. 후자는 반환된 JDBC Type을 인수로 전달되는 Java 클래스로 변환합니다. 유형 변환이 유효하지 않은 경우 InvalidDataAccessApiUsageException이 발생합니다. 다음 예제에는 두 개의 쿼리 메서드가 포함되어 있습니다. 하나는 int에 대한 것이고 다른 하나는 String에 대해 쿼리하는 것입니다.
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class RunAQuery {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int getCount() {
return this.jdbcTemplate.queryForObject("select count(*) from mytable", Integer.class);
}
public String getName() {
return this.jdbcTemplate.queryForObject("select name from mytable", String.class);
}
}
단일 결과 쿼리 메서드 외에도 여러 메서드가 쿼리가 반환한 각 행(row)에 대한 항목(entry)이 포함된 목록을 반환합니다. 가장 일반적인 방법은 queryForList(..)입니다. 이는 각 요소가 열(column) 이름을 키로 사용하여 각 열(column)에 대해 하나의 항목(entry)을 포함하는 Map인 List를 반환합니다. 모든 행의 목록을 검색하기 위해 이전 예제에 메서드를 추가하는 경우 다음과 같을 수 있습니다.
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public List<Map<String, Object>> getList() {
return this.jdbcTemplate.queryForList("select * from mytable");
}
반환된 목록은 다음과 유사합니다.
[{name=Bob, id=1}, {name=Mary, id=2}]
다음 예에서는 특정 기본 키에 대한 열을 업데이트합니다.
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAnUpdate {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void setName(int id, String name) {
this.jdbcTemplate.update("update mytable set name = ? where id = ?", name, id);
}
}
앞의 예에서 SQL 문에는 행(row) 매개변수에 대한 자리 표시자가 있습니다. 매개변수 값을 가변 인수(varargs) 또는 객체 배열로 전달할 수 있습니다. 따라서 primitive 래퍼 클래스에서 primitive 형식을 명시적으로 래핑하거나 auto-boxing을 사용해야 합니다.
update() 편의 메서드는 데이터베이스에서 생성된 기본 키 검색을 지원합니다. 이 지원은 JDBC 3.0 표준의 일부입니다. 자세한 내용은 사양의 13.6장을 참조하세요. 이 메소드는 첫 번째 인수로 PreparedStatementCreator를 사용하며 이것이 필수(required) 삽입(insert) 문이 지정되는 방식입니다. 다른 인수는 업데이트에서 성공적으로 반환될 때 생성된 키를 포함하는 KeyHolder입니다. 적절한 PreparedStatement를 생성하는 표준적인 단일 방법은 없습니다(메서드 시그니처가 현재와 같은 방식인 이유를 설명함). 다음 예는 Oracle에서는 작동하지만 다른 플랫폼에서는 작동하지 않을 수 있습니다.
final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(INSERT_SQL, new String[] { "id" });
ps.setString(1, name);
return ps;
}, keyHolder);
// keyHolder.getKey() now contains the generated key