SpringBoot Part4 (1)
Spring Frameworks์ ํ์ฉํ ๋ฐ์ดํฐ ๋ ์ด์ด(RDB) ์ ๊ทผ ๋ฐฉ๋ฒ
H2Database
: In-memory ๋ฐ์ดํฐ ๋ฒ ์ด์ค ( ์๋ฒ๊ฐ ๋ด๋ ค๊ฐ๋ฉด ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ ๋ถ ๋ ๋ผ๊ฐ )
: ๋ณดํต ์ํ์ฝ๋๋ ํ ์คํธ๋ฅผ ํ ๋ ์ฌ์ฉ.
JDBC
์๋ฐ ์ดํ๋ฆฌ์ผ์ด์ ์ JDBC API๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ ๊ณ์ธต๊ณผ ํต์ ํ๋ค.
@Slf4j
public class JDBCTest {
// H2DB์ url๊ณผ default userName, pass
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, 'honggu', 'kang')";
@Test
void jdbc_sample() {
try {
// jdbc driver ์ค์
Class.forName(JDBC_DRIVER);
// DB ์ฐ๊ฒฐ
Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);
log.info("Connection ํ๋");
// ํต์ ์ ํ๊ธฐ ์ํ statement ๊ฐ์ฒด - ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆด ์ ์๋ค.
Statement statement = connection.createStatement();
log.info("Statement ํ๋");
log.info("์ฟผ๋ฆฌ ์คํ");
statement.executeUpdate(DROP_TABLE_SQL);
statement.executeUpdate(CREATE_TABLE_SQL);
statement.executeUpdate(INSERT_SQL);
// ResultSet ์กฐํ๋ ์ ๋ณด๋ฅผ ์ํํ๋ฉฐ ๋ณผ์ ์๋ ๊ฐ์ฒด
ResultSet resultSet = statement.executeQuery("SELECT id, first_name, last_name FROM customers WHERE id = 1");
while(resultSet.next()) {
log.info(resultSet.getString("first_name"));
}
// ์
์ฑ๋ connection ๋ฐ๋ฉ
log.info("๋ฐ๋ฉ, ๋ฐ๋ฉ");
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- JDBC template
๊ธฐ์กด JDBC๋ฅผ ์ด์ฉํ์๋์ ๋ฐ๋ณต์ ์ธ ์์ ์ JDBC Template์ด ๋์ ์ํํด์ค๋ค.
JDBC template์ ์ฌ์ฉํ๊ธฐ ์ํด์ SpringApplicationContext๊ฐ ๋ ์ผํ๋ค.
@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, 'honggu', 'kang')";
@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("CREATED CUSTOMER RECORD JDBC TEMPLATE");
String fullName = jdbcTemplate.queryForObject(
"SELECT * FROM customers WHERE id = 1",
(resultSet,i)-> resultSet.getString("first_name")+ " "+ resultSet.getString("last_name"));
log.info("FULL NAME : {}",fullName);
}
}
๋จ์ → java code ์์ JDBC ๊ตฌ๋ฌธ์ ์์ฑํ์ฌ ์ฟผ๋ฆฌ ๊ด๋ฆฌ๊ฐ ์ด๋ ค์์ง๋ค.
MyBatis
JDBC์ ๋ฐ๋ณต์ ์ธ ์์ ์ ์ฟผ๋ฆฌ๋ฉํผ์ธ Mybatis๊ฐ ๋์ ์ํํ๋ฉฐ ์๋ฐ์ฝ๋์ ์ฟผ๋ฆฌ๋ฅผ ๋ถ๋ฆฌํด์ค๋ค.
์ฟผ๋ฆฌ ์์ ์ผ๋ก ์๋ฐ ์ฝ๋๋ฅผ ์์ ํ๊ฑฐ๋ ์ปดํ์ผ ํ๋ ์์ ์ ํ์ง ์์๋ ๋๋ค.
(spring-boot-starter ํจํค์ง๋ ๊ธฐ๋ณธ์ ์ผ๋ก auto configuration ์ผ๋ก ์ค์ ์ด ๊ฐ๋ฅ )
mybatis:
type-aliases-package: com.kdt.lecture.repository.domain # ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ(resultSet)๋ฅผ ์ด๋ค package์ mapping ํ ์ง
configuration:
map-underscore-to-camel-case: true # ์ธ๋๋ฐ ํํ ํ๋๋ฅผ -> ์นด๋ฉ์ผ์ด์ค ํํ๋ก ๋งค์นญ
default-fetch-size: 100 # ํ๋ฒ์ ๋ช๊ฐ์ฉ ๊ฐ์ ธ ์ฌ๊ฒ์ธ๊ฐ.
default-statement-timeout: 30 # statement ๊ฐ์ฒด๊ฐ ํต์ ํ๋ time limit
mapper-locations: classpath:mapper/*.xml # ์ฟผ๋ฆฌ๋ฅผ ๊ด๋ฆฌํ๋ ์์น
์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
1. Annotiation ์ด์ฉ
// Using Annotation
@Mapper
public interface CustomerMapper {
@Insert("INSERT INTO customers (id, first_name, last_name) VALUES(#{id}, #{firstName}, #{lastName})")
void save(Customer customer);
@Update("UPDATE customers SET first_name=#{firstName}, last_name=#{lastName} WHERE id=#{id}")
void update(Customer customer);
@Select("SELECT * FROM customers")
List<Customer> findAll();
@Select("SELECT * FROM customers WHERE id = #{id}")
Customer findById(@Param("id") long id);
}
2. XML ์ด์ฉ
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kdt.lecture.repository.CustomerMapper">
<insert id="save">
INSERT INTO customers (id, first_name, last_name)
VALUES (#{id}, #{firstName}, #{lastName})
</insert>
<update id="update">
UPDATE customers
SET first_name=#{firstName},
last_name=#{lastName}
WHERE id = #{id}
</update>
<select id="findById" resultType="customers">
SELECT *
FROM customers
WHERE id = #{id}
</select>
<select id="findAll" resultType="customers">
SELECT *
FROM customers
</select>
</mapper>
com.kdt.lecture.repository.CustomerMapper
@Mapper
public interface CustomerMapper {
void save(Customer customer);
Customer findById(long id);
}
์ฌ์ฉํ๊ธฐ
customerMapper.save(new Customer(1L, "youngji", "jang"));
Customer customer = customerMapper.findById(1L);
⇒ RDB์ java ๊ฐ์ฒด๊ฐ ๊ฐ๋ ํจ๋ฌ๋ค์์ ๋ถ์ผ์น๊ฐ ์๊ธฐ๊ฒ ๋๋๋ฐ JPA๋ฅผ ์ด์ฉํด ๋ณด์ํ ์ ์๋ค.
JPA (Object Relation Mapper)
Spring :
datasource:
hikari:
driver-class-name: org.h2.Driver
url: jdbc:h2:~/test
username: sa
password:
jpa:
generate-ddl: true #ddl option
open-in-view: false
show-sql: true #sql ์ฟผ๋ฆฌ ํ์ธ
๊ฐ์ฒด ์์ฑ
@Entity // ์ค์ RDB ํ
์ด๋ธ๊ณผ mapping์ด ๋๋ ๊ฐ์ฒด ๋ผ๋ ๊ฒ์ ๋ช
์
@Table(name = "customer") // table ใ
์ด๋
ธํ
์ด์
์ด ์์ผ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก class ๋ช
๊ณผ ๋์ผํ ํ
์ด๋ธ์ ์ฐพ๋๋ค.
public class CustomerEntity {
@Id // pk ๋ช
์
private long id;
private String firstName;
private String lastName;
//getter setter๋ฅผ ํตํด entity ์๋
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
๊ฐ์ฒด์ ๋งคํ๋ repository interface - JpaRepository ๊ตฌํ์ฒด ์์
public interface CustomerRepository extends JpaRepository<CustomerEntity, Long> {
}
์ฌ์ฉํ๊ธฐ
@Slf4j
@SpringBootTest
public class JPATest {
@Autowired
CustomerRepository customerRepository;
@BeforeEach
void setUp() {
}
@AfterEach
void tearDown() {
customerRepository.deleteAll();
}
@Test
void INSERT_TEST() {
//Given
CustomerEntity customer = new CustomerEntity();
customer.setId(1L);
customer.setFirstName("youngji");
customer.setLastName("jang");
//When
customerRepository.save(customer);
//then
CustomerEntity entity = customerRepository.findById(1L).get();
log.info("{} {}", entity.getFirstName(), entity.getLastName());
}
@Test
@Transactional // ์์์ ์ปจํ
์คํธ ์์์ ๊ด๋ฆฌ๋ฅผ ํ๊ฒ ๋ค.
void UPDATE_TEST(){
//Given
CustomerEntity customer = new CustomerEntity();
customer.setId(1L);
customer.setFirstName("youngji");
customer.setLastName("jang");
customerRepository.save(customer);
//When
CustomerEntity entity = customerRepository.findById(1L).get();
//then
entity.setFirstName("guppy");
entity.setLastName("kang");
// ๋ฐ๋ก ์ ์ฅ์ ํ์ง ์์๋ ์์์ ์ปจํ
์คํธ์ ์ํด ๋ํฐ ์ฒดํนํ๊ณ ์์ด ๊ฐ์ง๋ฅผ ํ๊ณ ์์ฑ์ด ๋ณ๊ฒฝ๋๋ฉด ์๋์ ์ผ๋ก update ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ๋ค.
CustomerEntity updated = customerRepository.findById(1L).get();
log.info("{} {}", updated.getFirstName(), updated.getLastName());
}
}
: entity ๊ฐ์ฒด๋ง ์์ ํ์ฌ๋ ์์์ฑ ์ปจํ ์คํธ์ ์ํด ๋ณ๊ฒฝ์ด ๊ฐ์ง๋์ ์๋์ ์ผ๋ก ์ฟผ๋ฆฌ๊ฐ ์คํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
: ์ปฌ๋ผ ์ถ๊ฐ๋ ๋ณ๊ฒฝ์ด ์์๋๋ ๊ฐ์ฒด๋ง ๋ณ๊ฒฝํ๋ฉด ์ฟผ๋ฆฌ๋ฅผ ์๋์ง ์๊ณ ์์ฝ๊ฒ ํ ์ด๋ธ ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ๋ค.
JPA ์ฌ์ฉ ์ด์ (ORM ํ๋ ์์ํฌ → ๊ฐ์ฒด์ ๊ด๊ณํ ํ ์ด๋ธ์ ๋งคํํด์ค๋ค.)
- ์์ฐ์ฑ ์ฆ์ง
- → SQL์ ์์กด์ ์ธ ๊ฐ๋ฐ์์ ํํผํ์ฌ, ๊ฐ์ฒด์ค์ฌ์ผ๋ก ์์ฐ์ ์ธ ๊ฐ๋ฐ์ด ๊ฐ๋ฅํ๋ค.
- ๊ฐ์ฒด์ ๊ด๊ณํ ํ
์ด๋ธ์ ํจ๋ฌ๋ค์ ๋ถ์ผ์น๋ฅผ ๋ณด์
- → ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋จ์ ์ถ์ํ, ์บก์ํ, ์์, ๋คํ์ฑ ๋ฑ์ ์ ๊ณตํ๋ค.
- → ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๋ฐ์ดํฐ ์ค์ฌ์ผ๋ก ๊ตฌ์กฐํ ๋์ด์์ผ๋ฉฐ, OOP์ ํน์ง์ ์ง์ํ์ง ์๋๋ค.
JPA ํ๋ก์ ํธ ์ธํ
- configration ์ธํ
→ spring boot์ auto configration์ผ๋ก ์ค์ ๊ฐ๋ฅ. ๋ด๋ถ์ ์ผ๋ก ์ด๋ป๊ฒ ์ธํ ๋๋์ง ํ์ธํ๊ธฐ~
@Configuration
public class DataSourceConfig {
@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;
}
// ์ด๋ค ๊ตฌํ์ฒด๋ฅผ ์ธ์ง ์ค์
// jpa ๊ตฌํ์ฒด๋ค ์ค์ hibernate๋ฅผ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ค.
@Bean
public JpaVendorAdapter jpaVendorAdapter(JpaProperties jpaProperties){ //yaml์ ํตํด ์ค์ ํ JpaProperties Bean์ ์ฃผ์
๋จ
AbstractJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(jpaProperties.isShowSql());
jpaVendorAdapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
jpaVendorAdapter.setGenerateDdl(jpaProperties.isGenerateDdl());
return jpaVendorAdapter;
}
// entity manager factory - ํ
์ด๋ธ๊ณผ ๋งคํ๋ entity๋ฅผ ๊ด๋ฆฌ
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource,JpaVendorAdapter jpaVendorAdapter,
JpaProperties jpaProperties) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource); // ์ด๋ค ๋ฐ์ดํฐ ๋ฒ ์ด์ค๋ฅผ ์ธ๊ฒ์ธ์ง
em.setPackagesToScan("com.example.kdtjpapractice.domain"); // entity๋ฅผ ์ด๋ค ํจํค์ง์ ๋๊ณ ๊ด๋ฆฌํ ๊ฒ์ธ์ง.
em.setJpaVendorAdapter(jpaVendorAdapter); // ์์ ๋ช
์ํ hibernate adapter๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค.
// ์ถ๊ฐ hibernate ํ๋ - yaml์ ์ค์
Properties properties = new Properties();
properties.putAll(jpaProperties.getProperties());
em.setJpaProperties(properties);
return em;
}
// transaction manger - RDB์ ํธ๋์ญ์
๊ด๋ฆฌ
@Bean
public PlatformTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean.getObject());
return transactionManager;
}
}
yaml JpaProperties ์ธํ
Spring :
h2:
console:
enabled: true
jpa:
generate-ddl: true #ddl option
hibernate:
ddl-auto:
database: H2
open-in-view: false
show-sql: true #sql ์ฟผ๋ฆฌ ํ์ธ
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
query.in_clause_parameter_padding: true
hbm2ddl:
auto: create-drop
properties.hibernate.dialect ์ค์ ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
https://2dongdong.tistory.com/66
ํด๋น ๊ตฌ๋ฌธ์ ์ง์ฐ๊ณ ์ด๋จ dialect๋ฅผ ์ฌ์ฉํ๋ ๋ณด์๋ค.
https://github.com/jojoldu/freelec-springboot2-webservice/issues/67
์๋์ ๊ฐ์ด ์ค์ ํ๋๊น ์ ๋์ํ๋ค.
dialect: org.hibernate.dialect.MySQL57Dialect
์๋์ผ๋ก ์ค์ ๋๋ ๊ฒ๊ณผ ์ด๋ค ์ฐจ์ด๊ฐ ์๋์ง ์์๋ด์ผ ํ ๊ฒ๊ฐ๋ค.
์ถ์ฒ - backend dev course ๊ฐํ๊ตฌ๋
'Back-end ๋ฐ๋ธ์ฝ์ค > week 08 TIL (Jpa)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TIL] 221209 - JPA : REST-API, API ๋ฌธ์ํ (0) | 2022.12.20 |
---|---|
[TIL] 221208 - JPA : SpringDataJPA (0) | 2022.12.14 |
[TIL] 221207 - JPA : ์ฐ๊ด๊ด๊ณ ๋งคํ, ๊ณ ๊ธ ๋งคํ, ํ๋ก์ (0) | 2022.12.14 |
[TIL] 221206 - JPA : ์์์ฑ ์ปจํ ์คํธ (0) | 2022.12.07 |
๋๊ธ