H2 Database 설치, 서버 실행, 접속 방법 (Windows, MacOS)
H2 Database 설치, 서버 실행, 접속 방법 (Windows, MacOS) H2 데이터베이스는 설치가 필요 없고 용량이 매우 가벼우며 웹용 콘솔(쿼리툴)을 제공하여 개발용 로컬 DB로 사용하기 좋은 데이터베이스이다. H
atoz-develop.tistory.com
SQL을 직접 사용하는 경우에 스프링이 제공하는 JdbcTemplate은 아주 좋은 선택지다. JdbcTemplate
은 JDBC를 매우 편리하게 사용할 수 있게 도와준다.
설정의 편리함JdbcTemplate은 spring-jdbc 라이브러리에 포함되어 있는데, 이 라이브러리는 스프링으로 JDBC를 사용할 때 기본으로 사용되는 라이브러리이다. 그리고 별도의 복잡한 설정 없이 바로 사용할 수 있다.
JdbcTemplate은 spring-jdbc 라이브러리만 추가하면 된다. 별도의 추가 설정 과정은 없다.
동적 SQL을 해결하기 어렵다.이제 직접 JdbcTemplate을 설정하고 적용하면서 이해해보자.
데이터를 저장할 때 PK 생성에 identity (auto increment) 방식을 사용하기 때문에, PK인 ID 값을 개발자가 직접 지정하는 것이 아니라 비워두고 저장해야 한다. 그러면 데이터베이스가 PK인 ID를 대신 생성해준다.문제는 이렇게 데이터베이스가 대신 생성해주는 PK ID 값은 데이터베이스가 생성하기 때문에, 데이터베이스에 INSERT가 완료 되어야 생성된 PK ID 값을 확인할 수 있다.
KeyHolder 와 connection.prepareStatement(sql, new String[]{"id"}) 를 사용해서 id 를 지정해주면 INSERT 쿼리 실행 이후에 데이터베이스에서 생성된 ID 값을 조회할 수 있다.
JdbcTemplate이 제공하는 SimpleJdbcInsert 라는 훨씬 편리한 기능이 있으므로 대략 이렇게 사용한다 정도로만 알아두면 된다.
동적으로 달라져야 한다는 점이다.
예를 들어서 다음과 같다.
동적 쿼리가 언듯 보면 쉬워 보이지만, 막상 개발해보면 생각보다 다양한 상황을 고민해야 한다. 예를 들어서 어떤 경우에는 where 를 앞에 넣고 어떤 경우에는 and 를 넣어야 하는지 등을 모두 계산해야 한다.그리고 각 상황에 맞추어 파라미터도 생성해야 한다.
물론 실무에서는 이보다 훨씬 더 복잡한 동적 쿼리들이 사용된다.
참고로 이후에 설명할 MyBatis의 가장 큰 장점은 SQL을 직접 사용할 때 동적 쿼리를 쉽게 작성할 수 있다는 점이다. →사실 JdbcTemplate의 가장 큰 단점이 동적 쿼리를 직접 구현해야 한다는 점이다.
JdbcTemplate을 기본으로 사용하면 파라미터를 순서대로 바인딩 한다.
버그 중에서 가장 고치기 힘든 버그는 데이터베이스에 데이터가 잘못 들어가는 버그다. 이것은 코드만 고치는 수준이 아니라 데이터베이스의 데이터를 복구해야 하기 때문에 버그를 해결하는데 들어가는 리소스가 어마어마하다.실제로 수많은 개발자들이 이 문제로 장애를 내고 퇴근하지 못하는 일이 발생한다.
개발을 할 때는 코드를 몇줄 줄이는 편리함도 중요하지만, 모호함을 제거해서 코드를 명확하게 만드는 것이 유지보수 관점에서 매우 중요하다.
JdbcTemplate은 이런 문제를 보완하기 위해 NamedParameterJdbcTemplate 라는 이름을 지정해서 파라미터를 바인딩 하는 기능을 제공한다.
파라미터를 전달하려면 Map 처럼 key , value 데이터 구조를 만들어서 전달해야 한다.여기서 key 는 :파리이터이름 으로 지정한, 파라미터의 이름이고 , value 는 해당 파라미터의 값이 된다.
다음 코드를 보면 이렇게 만든 파라미터( param )를 전달하는 것을 확인할 수 있다. template.update(sql, param, keyHolder);
1. Map
단순히 Map 을 사용한다.findById() 코드에서 확인할 수 있다.
2. MapSqlParameterSource
Map 과 유사한데, SQL 타입을 지정할 수 있는 등 SQL에 좀 더 특화된 기능을 제공한다. SqlParameterSource 인터페이스의 구현체이다.MapSqlParameterSource 는 메서드 체인을 통해 편리한 사용법도 제공한다.
3. BeanPropertySqlParameterSource → 한줄로 getter setter 필요없이 데이터 가져와주는 개꿀템
자바빈 프로퍼티 규약을 통해서 자동으로 파라미터 객체를 생성한다.예) ( getXxx() -> xxx, getItemName() -> itemName )예를 들어서 getItemName() , getPrice() 가 있으면 다음과 같은 데이터를 자동으로 만들어낸다.
key=itemName, value=상품명 값 key=price, value=가격 값
SqlParameterSource 인터페이스의 구현체이다. save() , findAll() 코드에서 확인할 수 있다.
여기서 보면 BeanPropertySqlParameterSource 가 많은 것을 자동화 해주기 때문에 가장 좋아보이지만,BeanPropertySqlParameterSource 를 항상 사용할 수 있는 것은 아니다.예를 들어서 update() 에서는 SQL에 :id 를 바인딩 해야 하는데, update() 에서 사용하는 ItemUpdateDto 에는 itemId 가 없다. 따라서 BeanPropertySqlParameterSource 를 사용할 수 없고, 대신에 MapSqlParameterSource 를 사용했다.
이번 코드에서 V1 과 비교해서 변화된 부분이 하나 더 있다. 바로 BeanPropertyRowMapper 를 사용한 것이다.
BeanPropertyRowMapper 는 ResultSet 의 결과를 받아서 자바빈 규약에 맞추어 데이터를 변환한다. 예를 들어서 데이터베이스에서 조회한 결과가 select id, price 라고 하면 다음과 같은 코드를 작성해준다. (실제로는 리플렉션 같은 기능을 사용한다.)
별칭
그런데 select item_name 의 경우 setItem_name() 이라는 메서드가 없기 때문에 골치가 아프다. 이런 경우 개발자가 조회 SQL을 다음과 같이 고치면 된다.
select item_name as itemName
별칭 as를사용해서SQL조회결과의이름을변경하는것이다.실제로이방법은자주사용된다.특히 데이터베이스 컬럼 이름과 객체 이름이 완전히 다를 때 문제를 해결할 수 있다. 예를 들어서
데이터베이스에는 member_name 이라고 되어 있는데 객체에 username 이라고 되어 있다면 다음과 같이 해결할 수 있다.
select member_name as username이렇게 데이터베이스 컬럼 이름과 객체의 이름이 다를 때 별칭( as )을 사용해서 문제를 많이 해결한다.
JdbcTemplate 은 물론이고, MyBatis 같은 기술에서도 자주 사용된다.
관례의 불일치
자바 객체는 카멜( camelCase ) 표기법을 사용한다. itemName 처럼 중간에 낙타 봉이 올라와 있는 표기법이다.반면에 관계형 데이터베이스에서는 주로 언더스코어를 사용하는 snake_case 표기법을 사용한다. item_name 처럼 중간에 언더스코어를 사용하는 표기법이다.
이 부분을 관례로 많이 사용하다 보니 BeanPropertyRowMapper 는 언더스코어 표기법을 카멜로 자동 변환해준다.따라서 select item_name 으로 조회해도 setItemName() 에 문제 없이 값이 들어간다.
정리하면 snake_case 는 자동으로 해결되니 그냥 두면 되고, 컬럼 이름과 객체 이름이 완전히 다른 경우에는 조회 SQL에서 별칭을 사용하면 된다.
JdbcTemplate은 INSERT SQL를 직접 작성하지 않아도 되도록 SimpleJdbcInsert 라는 편리한 기능을 제공한다.
기본
JdbcTemplateItemRepositoryV3 은 ItemRepository 인터페이스를 구현했다.
this.jdbcInsert = new SimpleJdbcInsert(dataSource) : 생성자를 보면 의존관계 주입은 dataSource 를 받고 내부에서 SimpleJdbcInsert 을 생성해서 가지고 있다. 스프링에서는 JdbcTemplate 관련 기능을 사용할 때 관례상 이 방법을 많이 사용한다.
물론 SimpleJdbcInsert 을 스프링 빈으로 직접 등록하고 주입받아도 된다.
withTableName : 데이터를 저장할 테이블 명을 지정한다.usingGeneratedKeyColumns : key 를 생성하는 PK 컬럼 명을 지정한다.usingColumns : INSERT SQL에 사용할 컬럼을 지정한다. 특정 값만 저장하고 싶을 때 사용한다. 생략할
수 있다.
SimpleJdbcInsert 는 생성 시점에 데이터베이스 테이블의 메타 데이터를 조회한다. 따라서 어떤 컬럼이 있는지 확인 할 수 있으므로 usingColumns 을 생략할 수 있다. 만약 특정 컬럼만 지정해서 저장하고 싶다면 usingColumns 를 사용하면 된다.
JdbcTemplate의 기능을 간단히 정리해보자.
주요 기능
JdbcTemplate이 제공하는 주요 기능은 다음과 같다.
JdbcTemplate
순서 기반 파라미터 바인딩을 지원한다. NamedParameterJdbcTemplate
이름 기반 파라미터 바인딩을 지원한다. (권장) SimpleJdbcInsert
INSERT SQL을 편리하게 사용할 수 있다. SimpleJdbcCall
스토어드 프로시저를 편리하게 호출할 수 있다.
정리
실무에서 가장 간단하고 실용적인 방법으로 SQL을 사용하려면 JdbcTemplate을 사용하면 된다. JPA와 같은 ORM 기술을 사용하면서 동시에 SQL을 직접 작성해야 할 때가 있는데, 그때도 JdbcTemplate을 함께 사용하면 된다.그런데 JdbcTemplate의 최대 단점이 있는데, 바로 동적 쿼리 문제를 해결하지 못한다는 점이다. 그리고 SQL을 자바 코드로 작성하기 때문에 SQL 라인이 코드를 넘어갈 때 마다 문자 더하기를 해주어야 하는 단점도 있다.
동적 쿼리 문제를 해결하면서 동시에 SQL도 편리하게 작성할 수 있게 도와주는 기술이 바로 MyBatis 이다.
냉집사 Query Dsl 정리 (1) | 2024.02.09 |
---|---|
NGINX (0) | 2024.02.09 |
데이터 접근 기술 - 테스트 (2) | 2024.02.05 |
MyBatis (0) | 2024.02.02 |
Spring Security와 OAuth2를 이용한 소셜 로그인 (0) | 2024.02.02 |