Spring Boot Reference Guide Part4, Chapter 29 / 30
Spring Framework는 SQL DB 사용을 위한 강력한 기능을 제공한다. JDBC access를 위한 JdbcTemplate부터, Hibernate를 통한 ORM (Object Relational Mapping) 까지 지원한다.
Spring Framework에서는 Entity 클래스와 Repository 인터페이스 구현을 통해 기본적인 CRUD가 가능하다.
Java의 javax.sql.DataSource 인터페이스는 DB connection을 위한 기본 메소드를 제공하며, URL을 통해 DB 연결을 진행한다. Spring Boot에서 DataSource를 설정하기 위해 application.properties에 다음과 같이 추가할 수 있다.
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
만약 위와 같은 설정을 하지 않으면 Spring Boot는 자동으로 내장된 database를 사용할 것이다.
Spring Boot는 url을 보고 어떤 DB의 JDBC Driver를 사용할지 결정할 수 있으므로 꼭 driver-class-name의 설정이 필요한 것은 아니다. 만약 driver-class-name이 설정되면, Spring Boot는 이 Driver 클래스가 사용가능한지 체크하게 되므로, 반드시 관련 dependency를 추가해서 그 Driver 클래스가 사용할 수 있어야 한다.
Spring Boot에서 JdbcTemplate나 NamedParameterJdbcTemplate는 자동 설정되며, 다음과 같이 @Autowired annotation을 써서 사용가가 정의한 빈에서 추가할 수 있다.
package com.nhnent.hellospringboot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final JdbcTemplate jdbcTemplate;
@Autowired
public MyBean(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
JPA, Java Persistence API는 Object를 관계형 데이터베이스에 저장할 수 있도록 매핑을 해주는 기술로, sprig-boot-starter-data-jpa를 dependency로 추가함으로써 사용할 수 있다.
Spring Boot는 기본적으로 Hibernate 5.0.x version을 사용한다. 물론 4.3.x version이나, 5.2.x version도 사용가능하다.
JPA에서 Entity라는 것은 데이터베이스에 저장하기 위해 사용자가 정의하는 클래스이다. 일반적으로 RDBMS에서 Table의 정의와 같은 것이다. Table의 이름이나 컬럼들에 대한 정보를 가진다. 원래 persistence.xml 파일에서 정의되지만 Spring Boot에서는 Entity Scanning 으로 xml 파일을 추가할 필요가 없다.
먼저 Table을 다음과 같이 만든다.
그리고 Entity 클래스를 추가한다.
package com.nhnent.hellospringboot.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Member {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;
@Column
private String name;
@Column
private int age;
public Member() {
}
...
}
@Entity annotation을 클래스의 선언 부분에 넣어주고, 멤버 필드는 테이블의 각 컬럼에 대응하면 된다. 기본 생성자는 반드시 넣어주어야 한다.
Entity 클래스를 만들었다면, Repository 인터페이스를 구현해야 한다. Repository 인터페이스는 DB에 엑세스하기 위해 정의된 인터페이스로, 메소드 이름으로부터 JPA 쿼리가 자동으로 만들어진다.
Spring Framework에서는 Entity의 기본적인 삽입, 조회, 수정, 삭제 (CRUD)가 가능하도록 CrudRepository라는 인터페이스를 만들어두었다.
다음과 같이 CrudRepository 인터페이스를 구현해서 Member 테이블에 CRUD를 할 수 있도록 한다.
package com.nhnent.hellospringboot.dao;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import com.nhnent.hellospringboot.entity.Member;
public interface MemberRepository extends CrudRepository<Member, Long> {
List<Member> findByNameAndAgeLessThan(String name, int age);
@Query("select t from Member t where name=:name and age < :age")
List<Member> findByNameAndAgeLessThanSQL(@Param("name") String name, @Param("age") int age);
List<Member> findByNameAndAgeLessThanOrderByAgeDesc(String name, int age);
}
위의 코드는 Member Entity를 사용하기 위한 클래스로, 기본적인 메소드 외에 추가적인 메소드를 지정할 수 있다. 메소드 이름을 기반(Query Method)를 통해 만들 수도 있고, @Query annotation을 이용해 기존의 SQL 처럼 사용해도 된다.
위의 findByNameAndAgeLessThan 메소드와 findByNameAndAgeLessThanSQL 은 같은 루틴이다. 전자는 메소드 이름을 기반으로 쿼리를 날릴 것이고, 후자는 @Query annotation에 있는 SQL 문을 가지고 쿼리를 날릴 것이다.
메소드 이름을 기반으로 작성하는 것은 특정한 규칙이 있으므로, 만드는 방법은 다음을 참조한다. spring.io, Query Creation
다음과 같이 코드를 작성해서 추가한 Repository와 Entity 클래스를 사용하면 된다.
@Component
public class MyApplicationRunnerRoutine implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(MyApplicationRunnerRoutine.class);
@Autowired
private MemberRepository memberRepository;
public void run(ApplicationArguments args) {
memberRepository.save(new Member("a", 10));
memberRepository.save(new Member("b", 15));
memberRepository.save(new Member("c", 10));
memberRepository.save(new Member("a", 5));
Iterable<Member> memberList = memberRepository.findAll();
logger.info("findAll() method");
for (Member member : memberList) {
logger.info(member.toString());
}
memberList = memberRepository.findByNameAndAgeLessThan("a", 10);
logger.info("findAll() method");
for (Member member : memberList) {
logger.info(member.toString());
}
memberList = memberRepository.findByNameAndAgeLessThanSQL("a", 10);
logger.info("findAll() method");
for (Member member : memberList) {
logger.info(member.toString());
}
memberList = memberRepository.findByNameAndAgeLessThanOrderByAgeDesc("a", 15);
logger.info("findAll() method");
for (Member member : memberList) {
logger.info(member.toString());
}
memberRepository.deleteAll();
}
}
그러면 다음과 같은 결과를 확인할 수 있다.
Spring Data는 MongoDB, ElasticSearch, Redis, Cassandra와 같은 다양한 NoSQL을 지원한다. Spring Boot는 이러한 NoSQL을 위한 자동 설정을 지원한다.
Redis는 “REmote DIctionary System”의 약자로 memory 기반의 key / value store 이다. Spring Boot는 Jedis와 같은 Redis client 라이브러리를 위하 auto configuration을 지원하며, spring-boot-starter-data-redis 아티팩트 추가를 통해 쉽게 사용가능하다.
먼저 다음과 같이 Redis를 사용하기 위해 pom.xml에 dependency를 추가한다.
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
그리고 Redis를 사용하기 위해 application.properties에 다음과 같이 추가한다.
redis.host=localhost
redis.port=6379
redis.database=0
여기서는 HttpSession을 사용하여 Cookie 값을 Redis로 저장해보겠다. Spring Boot에서는 JedisConnectionFactory와 CookieSerializer를 @Bean annotation을 사용해서 주입할 수 있기 때문에, 다음과 같이 JedisConnectionFactory와 CookieSerializer를 리턴하는 @Bean 메소드를 정의한다.
package com.nhnent.hellospringboot;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
@Configuration
@EnableRedisHttpSession
@PropertySource("classpath:application.properties")
public class SessionConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private int port;
@Value("${redis.database}")
int database;
@Bean
public JedisConnectionFactory connectionFactory() {
JedisConnectionFactory conn = new JedisConnectionFactory();
conn.setHostName(host);
conn.setPort(port);
conn.setDatabase(database);
conn.setUsePool(true);
return conn;
}
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
return serializer;
}
}
위의 소스와 같이 application.properties 에 정의한 Redis 관련 값들을 @Value annotation으로 주입받아 JedisConnectionFactory를 초기화하고, CookieSerializer로는 DefaultCookieSerializer를 사용한다.
그리고 다음과 같이 controller에 테스트용 url를 만들어 Redis에 쿠키 관련 값들이 저장되는지 확인한다.
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloRestController {
@RequestMapping(path="/session-test", produces="text/plain")
public String sessionTest(HttpSession session) {
session.setAttribute("test", "hello");
return (String)session.getAttribute("test");
}
}
그리고 웹 브라우저로 /session-test에 접속, 현재 쿠키 값을 확인해보자.
redis-cli를 통해 Redis 에 저장된 쿠키 값을 다음과 같이 키로 저장되어 있는 것을 확인할 수 있을 것이다.