Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
* 김영한님의 스프링 DB 2편 강좌를 수강하며 정리한 글입니다. *
이전 글에서는
프로필
스프링은 로딩 시점에 application.properties의 프로필 속성을 읽어온다.
프로필은 환경마다 다른 설정을 적용해야 할 때 사용한다.
이를 local로 설정하면 스프링은 local 프로필로 동작한다.
@Bean
@Profile("local") //이벤트도 빈등록해야 호출됨
public TestDataInit testDataInit(ItemRepository itemRepository) {
return new TestDataInit(itemRepository);
}
위의 코드는 local 프로필에서 로컬에서 사용하기 위해 초기 메모리 리퍼지토리를 설정한다.
초기 테스트 데이터 등록은 @EventListener 어노테이션을 이용하면 된다.
@EventListener(ApplicationReadyEvent.class)
public void initData() {
//초기 데이터 삽입
}
ApplicationReadyEvent는 스프링 컨테이너, AOP 작업이 모두 끝난 후에 이벤트가 발생하여 충돌이 일어나게 된다.
테스트 용도로만 사용하고 싶은 것이 있다면 @Profile("test")로 설정해 두고, 프로필을 변경하면 된다.
create table item
(
id bigint generated by default as identity,
item_name varchar(10),
price integer,
quantity integer,
primary key (id)
);
id는 generated by default as identity 로 설정하면, 키 생성을 DB가 책임지고 1씩 증가하게 만들어 준다.
이러한 생성 방식은 Surrogate key이다.
테이블의 기본 키를 만드는 두 가지 방법이 있다.
1. natural key(자연키)
의미가 있는 키(이메일과 전화번호와 같은)
2. surrogate key(대리키, 대체키)
임의로 만들어진 키
자연 키는 고객이나 요구사항에 따라서 변하기 쉽다. 따라서 대리키를 기본적으로 사용하자.
JDBCTemplate
스프링의 JDBC 기본 라이브러리로, 설정 없이 바로 사용할 수 있다.
build.gradle에 추가만 해주자.
//JdbcTemplate 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
JDBC 사용 시의 반복 작업을 처리해준다.
이전 글에서 JdbcTemplate을 사용해 보았는데, 커낵션 획득, 파라미터 바인딩, 커낵션 마무리의 작업을 대신 수행해 주었다.
public Member save(Member member){
String sql = "insert into member(member_id, money) values(?, ?)";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, member.getMemberId());
pstmt.setInt(2,member.getMoney());
pstmt.executeUpdate(); //실제 db로 sql 전달
return member;
} catch (SQLException e) {
throw exTranslator.translate("save", sql, e);
}finally {
close(con,pstmt,null);
}
}
//JdbcTemplate 사용 시
private final JdbcTemplate template;
public Member save(Member member){
String sql = "insert into member(member_id, money) values(?, ?)";
template.update(sql, member.getMemberId(), member.getMemberId());
return member;
}
JdbcTemplate 생성은 dataSource가 필요한데, 스프링 부트는 @Configuration에 dataSource를 hikariDataSource로 자동 등록해준다.
대신 application.properties에 설정 정보를 넣어주자.
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.username=sa
spring.datasource.password=
template.update()는 데이터를 변경하는 INSERT, UPDATE, DELETE SQL 문에 사용한다.
데이터를 새로 저장한다면 id값이 필요한데, generated by default as identity 방식은 DB가 ID를 생성하기 때문에 데이터 등록 시점에는 알 수가 없다.
따라서 등록 후, KeyHolder 를 통해 생성된 ID값을 가져와야 한다.
데이터 조회(SELECT)에는 template.queryForObject()를 사용한다.
동적 쿼리 문제
JdbcTemplate의 문제는 동적으로 쿼리문을 생성할 때 발생한다.
예를 들어 조회 조건을 통한 SELECT문을 생성할 때 조건에 따라 쿼리문이 동적으로 변경(where, and 등의 추가)되어야 한다. 이를 해결한 것이 MyBatis이다.
파라미터 바인딩 문제
sql = "insert into member(member_id, money) values(?, ?)"의 ?에는 파라미터가 순서대로 바인딩된다.
이런 바인딩 기법은 순서가 바뀌는 실수가 생기기 쉬워서, 데이터가 잘못 등록되는 심각한 문제가 생긴다.
이런 문제는 직접적인 오류가 나지 않기 때문에 데이터 베이스를 복구해야 한다.
코드는 간략한 것보다는 길더라도 모호함을 줄이는 방식이 유지보수에 훨씬 중요하다.
이런 문제를 보안하기 위해 이름으로 파라미터를 바인딩하는 NamedParameterJdbcTemplate을 제공한다.
private final NamedParameterJdbcTemplate template;
public JdbcTemplateItemRepositoryV2(DataSource dataSource) {
this.template = new NamedParameterJdbcTemplate(dataSource);
}
이제 SQL문에 ?대신 :파라미터이름 으로 세팅할 수 있다.
이제 key(매핑할 파라미터 이름):value(데이터)를 가지는 데이터 구조를 template.update(sql, param, ...)에 넘겨주면 된다.
자료구조로는 Map<> 혹은 인터페이스인 SqlParameterSource의 구현체, BeanPropertySqlParameterSource를 사용한다.
특히 BeanPropertySqlParameterSource는 인스턴스를 넘기면 자동으로 변수 이름에 매핑하여 파라미터를 만들어준다.
String sql = "insert into item (item_name, price, quantity) " +
"values (:itemName,:price, :quantity)";
//객체의 변수들로 파라미터를 만들어줌
SqlParameterSource param = new BeanPropertySqlParameterSource(item);
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(sql, param, keyHolder);
자바는 camel 표기법을 사용 하지만, DB에는 관례상 언더_스코어를 많이 사용하기 때문에,
변수의 이름이 DB table의 column이름과 다르면 맵핑이 안될 때가 있다.
이때는 sql의 별칭 구문을 활용해서 해결하자.
select item_name as itemName
워낙 불편한 점이 많다 보니 언더스코어와 카멜 표기법은 자동으로 변환해 인식하도록 지원해준다.
자세한 JdbcTemplate의 활용법은 공식 문서를 확인하자.
'개발 > Spring DataBase' 카테고리의 다른 글
[Spring DB] 8. MyBatis (0) | 2022.07.26 |
---|---|
[Spring DB] 7. DataBase Test, 임베디드 DB (0) | 2022.07.25 |
[Spring DB] 5. 런타임 예외의 활용 (0) | 2022.07.21 |
[Spring DB] 4. 스프링의 트랜잭션 매니저 (0) | 2022.07.19 |
[Spring DB] 3. 트랜잭션과 ACID, DB Lock (0) | 2022.07.18 |