SpringBoot Part4 - JPA 시작
목표
- 스프링 웹 어플리케이션의 데이터 저장 계층에 대해 이해한다.
- JPA에 대해 이해한다.
- 스프링데이터 JPA에 대해 이해한다.
- 스프링 데이터 JPA를 이용한 REST API 서버를 구축한다.
JPA(Java Persistence Applcation)이란?
설명에 앞서 JDBC에 대해 간략히 알아보자.
- JAVA App <-> JDBC API <-> JDBC Driver <-> (RDB)
- 아래 코드는 Connection 획득&반납, Statement 질의&반납, Resultset을 통해 결과 받아오기를 하는 코드다.
@Slf4j
public class SQLTest {
static final String JDBC_DRIVER = "org.h2.Driver";
static final String DB_URL = "jdbc:h2:~/test";
static final String USER = "sa";
static final String PASS = "";
static final String DROP_TABLE_SQL = "DROP TABLE customers IF EXISTS";
static final String CREATE_TABLE_SQL = "CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))";
static final String INSERT_SQL = "INSERT INTO customers(id, first_name, last_name) VALUES(1, 'changho', 'lee')";
@Test
void sql_sample() {
try {
Class.forName(JDBC_DRIVER);
Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);
log.info("GET CONNECTION");
Statement statement = connection.createStatement();
statement.executeUpdate(DROP_TABLE_SQL);
statement.executeUpdate(CREATE_TABLE_SQL);
log.info("CREATED TABLE");
statement.executeUpdate(INSERT_SQL);
log.info("INSERTED CUSTOMER INFORMATION");
ResultSet resultSet = statement.executeQuery("SELECT * FROM customers WHERE id = 1");
while (resultSet.next()) {
String fullName = resultSet.getString("first_name") + " " + resultSet.getString("last_name");
log.info("CUSTOMER FULL_NAME: {}", fullName);
}
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
jdbc를 사용하면 더 간편하다.
- 이제 connection, statement를 코드로 가져오고 반납하지 않아도 된다. 자동으로 해준다.
@Slf4j
@SpringBootTest
public class JDBCTest {
static final String DROP_TABLE_SQL = "DROP TABLE customers IF EXISTS";
static final String CREATE_TABLE_SQL = "CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))";
static final String INSERT_SQL = "INSERT INTO customers(id, first_name, last_name) VALUES(1, 'changho', 'lee')";
static final String SELECT_SQL = "SELECT * FROM customers WHERE id = 1";
@Autowired
JdbcTemplate jdbcTemplate;
@Test
void jdbcTemplate_sample() {
jdbcTemplate.update(DROP_TABLE_SQL);
jdbcTemplate.update(CREATE_TABLE_SQL);
log.info("CREATED TABLE USING JDBC TEMPLATE");
jdbcTemplate.update(INSERT_SQL);
log.info("INSERTED CUSTOMER INFORMATION USING JDBC TEMPLATE");
String fullName = jdbcTemplate.queryForObject(
SELECT_SQL,
(resultSet, i) -> resultSet.getString("first_name") + " "
+ resultSet.getString("last_name")
);
log.info("CUSTOMER FULL_NAME: {}", fullName);
}
}
jdbc 대신 쿼리 매퍼인 mybatis를 써보자
- jdbc의 경우 resultSet을 통해 쿼리결과를 매핑했다.
- mybatis는 객체에 자동으로 매핑 할 수 있다. (type-aliases-package 사용)
왜 JPA가 필요할까?
- sql -> JDBC -> mybatis 순으로 좀 더 편리하게 DB에 접근 할 수 있게 되었다.
- 여기서 mybatis보다 더 간편해진 방법이 바로 JPA다.
JPA 설정
- JPA는 인터페이스다. 그렇기 때문에 구현체를 정해야 한다. 요즘엔 hibernate를 많이 쓴다하니 hibernate로 해본다.
- DataSource 설정으로 어떤 DB를 사용할지 결정한다.
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:~/test");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
- JpaVendorAdapter로 JPA 인터페이스의 구현체 중 어떤 것을 사용할지 결정한다.
@Bean
public JpaVendorAdapter jpaVendorAdapter(JpaProperties jpaProperties) {
AbstractJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(jpaProperties.isShowSql());
jpaVendorAdapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
jpaVendorAdapter.setGenerateDdl(jpaProperties.isGenerateDdl());
return jpaVendorAdapter;
}
- EntityManagerFactoryBean으로 RDB의 Table과 매핑된 Entity들을 관리하도록 설정한다.
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource,
JpaVendorAdapter jpaVendorAdapter,
JpaProperties jpaProperties) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.exmaple.jpasettingpractice");
em.setJpaVendorAdapter(jpaVendorAdapter);
Properties properties = new Properties();
properties.putAll(jpaProperties.getProperties());
em.setJpaProperties(properties);
return em;
}
- TransactionManager로 트랜잭션을 관리하도록 설정한다.
@Bean
public PlatformTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean.getObject());
return transactionManager;
}