SpringBoot Part2 (4)
1. Embedded DataBase
๊ธฐ์กด ํ ์คํธ : DB ๊ตฌ๋์ด ๋์ง์์ผ๋ฉด ํ ์คํธ์ ์คํจํ๋ ๊ตฌ์กฐ
→ ์ธ๋ถํ๊ฒฝ์ ์ํฅ์ ๋ฐ๋ ํ ์คํธ → ํ ์คํธ ์๋ํ(jenkins๊ฐ์ CI ํด์ ์ด์ฉํ) ๋ถ๊ฐ
Spring์์ Embedded Database ์ ๊ณต
Data Access
The Data Access Object (DAO) support in Spring is aimed at making it easy to work with data access technologies (such as JDBC, Hibernate, or JPA) in a consistent way. This lets you switch between the aforementioned persistence technologies fairly easily, a
docs.spring.io
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.build();
function์ ๊ฑฐ์ ์ง์ํ์ง์์ ์ต๋ํ Ansi ํ์ค์ ๋ง๊ฒ sql์ ์์ฑํด์ผํ๋ค.
MySQL์ด๋ PostSQL๊ฐํผ ๊ฒฝ์ฐ Embedded ์คํ์์ค๋ฅผ ์ ๊ณตํ์ฌ ๊ทธ๊ฒ์ ์ด์ฉํ์ฌ ์ค์ mySQL DB๊ฐ ๊ตฌ๋๋๊ฒ ํด์ผํ๋ค.
Embedded MySQL
https://github.com/wix/wix-embedded-mysql
<dependency>
<groupId>com.wix</groupId>
<artifactId>wix-embedded-mysql</artifactId>
<version>4.6.1</version>
<scope>test</scope>
</dependency>
-
import com.wix.mysql.EmbeddedMysql;
import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql;
import static com.wix.mysql.ScriptResolver.classPathScript;
import static com.wix.mysql.config.Charset.UTF8;
import static com.wix.mysql.config.MysqldConfig.aMysqldConfig;
import static com.wix.mysql.distribution.Version.v8_0_11;
@SpringJUnitConfig
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS) // PER_CLASS : class ๋ง๋ค ์ธ์คํด์ค๊ฐ ํ๋
class CustomerJdbcRepositoryTest {
@Configuration
@ComponentScan(
basePackages = {"org.prgrms.kdt.customer"}
)
static class Config {
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = DataSourceBuilder.create()
.url("jdbc:mysql://localhost:2215/test-order_mgmt")
.username("test")
.password("test1234!") // ํด๋น ํ
์คํธ์์๋ง ์ฌ์ฉ๋ id,pw
.type(HikariDataSource.class)
.build();
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
@Autowired
CustomerJdbcRepository customerJdbcRepository;
@Autowired
DataSource dataSource;
EmbeddedMysql embeddedMysql;
@BeforeAll
void setup() {
//customerJdbcRepository.deleteAll(); // embedded ์ฌ์ฉ์ ์ด์ฐจํผ ์น๋ค ๋๋น๊ฐ ๋ด๋ ค๊ฐ๋ค ์ฌ๋ผ์ค๊ธฐ ๋๋ฌธ์ ์ญ์ ํด์ค ํ์์์
var mysqlConfig = aMysqldConfig(v8_0_11)
.withCharset(UTF8)
.withPort(2215)
.withUser("test", "test1234!")
.withTimeZone("Asia/Seoul")
.build();
embeddedMysql = anEmbeddedMysql(mysqlConfig)
.addSchema("test-order_mgmt", classPathScript("schema.sql"))
.start();
}
@AfterAll
void cleanUp() {
embeddedMysql.stop();
}
}
๋ณ๋์ DB๋ฅผ ๊ตฌ๋์ํค์ง์์๋ ํ ์คํธ์ ํต๊ณผํ๋ ๊ฒ์ ํ์ธ ํ ์์๋ค!
UUID ์ค๋ฅ
https://remarkablemark.org/blog/2020/05/21/mysql-uuid-bin/
2. NamedParameterJDBCTemplate
NamedParameterJDBCTemplate
: keyHolder ์ธ๋ฑ์ค๊ธฐ๋ฐ์์ ์ด๋ฆ๊ธฐ๋ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ค์ ํ ์ ์๊ฒํด์ฃผ๋ ํ ํ๋ฆฟ
: JDBCTemplate์ ํฌํจํ๋ฉฐ ํ๋ผ๋ฏธํฐ๋ง ๋ณ๊ฒฝํด์ฃผ๋ ์ญํ ์ ํ์
ํ ํ๋ฆฟ ์ ์ฉ
@Repository
public class CustomerJdbcRepository implements CustomerRepository {
private static final Logger logger = LoggerFactory.getLogger(CustomerJdbcRepository.class);
private final NamedParameterJdbcTemplate jdbcTemplate;
private static final RowMapper<Customer> customerRowMapper = new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int rowNum) throws SQLException { //resultSet, index
var customerName = rs.getString("name");
var customerId = toUUID(rs.getBytes("customer_id"));
var email = rs.getString("email");
var createdAt = rs.getTimestamp("created_at").toLocalDateTime();
var lastLoginAt = rs.getTimestamp("last_login_at") != null ?
rs.getTimestamp("last_login_at").toLocalDateTime() : null;
return new Customer(customerId, customerName, email, lastLoginAt, createdAt);
}
};
private Map<String, Object> toParamMap(Customer customer) {
return new HashMap<>(){{
put("customerId", customer.getCustomerId().toString().getBytes());
put("name",customer.getName());
put("email",customer.getEmail());
put("lastLoginAt", customer.getLastLoginAt() != null ? Timestamp.valueOf(customer.getLastLoginAt()):null);
put("createdAt", customer.getCreatedAt());
}};
}
public CustomerJdbcRepository(NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Customer insert(Customer customer) {
var update = jdbcTemplate.update("INSERT INTO customers(customer_id,name,email, created_at)" +
" VALUES (UUID_TO_BIN(:customerId),:name,:email,:createdAt)",toParamMap(customer));
if(update != 1) throw new RuntimeException("Nothing was inserted!");
return customer;
}
@Override
public Customer update(Customer customer) {
var update = jdbcTemplate.update("UPDATE customers SET name= :name, email= :email, last_login_at= :lastLoginAt WHERE customer_id = UUID_TO_BIN(:customerId)",toParamMap(customer));
if(update != 1) throw new RuntimeException("Nothing was updated!");
return customer;
}
@Override
public int count() {
return jdbcTemplate.queryForObject("select count(*) from customers",Collections.EMPTY_MAP,Integer.class);
}
@Override
public List<Customer> findAll() {
return jdbcTemplate.query("select * from customers", customerRowMapper); //sql,row mapper ์ ๋ฌ
}
@Override
public Optional<Customer> findById(UUID customerId) {
try {
return Optional.ofNullable(jdbcTemplate.queryForObject("select * from customers WHERE customer_id = UUID_TO_BIN(:customerId)",
Collections.singletonMap("customerId",customerId.toString().getBytes()), customerRowMapper));
// queryForObject -> ํ๋๋ง ๊บผ๋ด๊ธฐ ๊ฐ์ด ์์ผ๋ฉด ใ
ใ
์ธ๋ฐ์
}catch (EmptyResultDataAccessException e){
return Optional.empty();
}
}
@Override
public void deleteAll() {
//jdbcTemplate.update("DELETE FROM customers",Collections.emptyMap());
//empty map์ ์ ๋ฌํ๊ธฐ ๊ท์ฐฎ์ผ๋ฉด ๊ธฐ์กด ํ
ํ๋ฆฟ์ ๊ฐ์ ธ์ ์ฌ์ฉํด๋๋๋ค.
jdbcTemplate.getJdbcTemplate().update("DELETE FROM customers");
}
static UUID toUUID(byte[] bytes) {
var byteBuffer = ByteBuffer.wrap(bytes);
return new UUID(byteBuffer.getLong(), byteBuffer.getLong());
}
}
DB๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ์ํ ์ ์๋ ์์ธ๊ฐ ๋ค์ํ๋ค. → JDBC๊ฐ SQLException์ ๋ฐ์์ํจ๋ค.
Spring์์ ์์ธ๋ค์ ์ถ์ํ์ํจ DataAccessException ์ ๊ณต
3. ํธ๋์ญ์
ํ๋์ ์์ ์ ์ํํ๊ธฐ ์ํด ํ์ํ ์ฐ์ฐ๋ค์ ๋ชจ์๋๊ฒ. ๋ ผ๋ฆฌ์ ์ธ ์์ ์ ํ ๋จ์
์ถ์ฒ - https://jerryjerryjerry.tistory.com/48
: ๊ฐ๊ฐ์ ํธ๋์ญ์ ์ ์์์ฑ, ์ผ๊ด์ฑ, ๋ ๋ฆฝ์ฑ, ์๊ตฌ์ฑ์ ๋ณด์ฅํ๋ค.
: ์์์ฑ์ด commit๊ณผ rollback์ ์ํด ๋ณด์ฅ๋๋ค.
: ํธ๋์ญ์ ์ํ๋ ํธ๋์ญ์ ์๋ ๋ด๊ฐ ์ ์ ์ฟผ๋ฆฌ๋ฌธ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์ต์ข ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ํ๋ COMMIT๊ณผ ์คํจํ์๋ COMMINT ์์ ์ผ๋ก ๋ค์ ๋๋์๊ฐ๋ ROLLBACK์ด ์๋ค.
spring์์ ํธ๋์ญ์ ์ฒ๋ฆฌ๋ฅผํ๋ ค๋ฉด AOP๋ฅผ ์์์ผํ๋ค.
JDBC๋ฅผ ์ด์ฉํ ์ง์ ํธ๋์ญ์ ์ฒ๋ฆฌ
// AutoCommit์ ๊บผ์ ํด๋ฆฌ ํธ๋์ญ์
์ ํ๋๋ก ๋ฌถ์ ๋ค ์์
์ด ์ ์ ์ข
๋ฃ๋๋ฉด ๋ค์ ํจ๋ค.
connection.setAutoCommit(false);
.. excute ๋์
connection.setAutoCommit(true);
// AutoCommit์ด ์ผ์ง๊ธฐ์ ์ ์คํจํ๋ฉด rollbackํ connection ๋๊ธฐ
connection.rollback();
connection.close();
์ถ์ฒ - ํด๋ฆฌ : SpringBoot Part1
'Back-end ๋ฐ๋ธ์ฝ์ค > week 03 - 05 TIL (Spring)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TIL] 221114 - SpringBoot Part3 : ์น ๊ธฐ์ Overview, Servelt (0) | 2022.11.22 |
---|---|
[TIL] 221111 - SpringBoot Part2 : ํธ๋์ญ์ ๊ณผ AoP (1) | 2022.11.22 |
[TIL] 221109 - SpringBoot Part2 : Spring์ JDBC์ง์ (0) | 2022.11.10 |
[TIL] 221108 - SpringBoot Part2 : JDBC (0) | 2022.11.08 |
[TIL] 221107 - SpringBoot Part2 : Spring Test ์์ํ๊ธฐ (0) | 2022.11.08 |
๋๊ธ