Spring DB Part II

Spring DB Part II

์˜ํ•œ๋‹˜์˜ ์Šคํ”„๋ง DB 2ํŽธ - ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ํ™œ์šฉ ๊ธฐ์ˆ  ๊ฐ•์˜๋ฅผ ์š”์•ฝํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

Project

Intro

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

project settings

Profiles

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ…Œ์ŠคํŠธํ•˜์ž

  • ๊ธฐ๋ณธ์ ์œผ๋กœ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๋ฉด ๊ตฌํ˜„์ฒด๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ ๊ฐ™์€ ํ…Œ์ŠคํŠธ๋กœ ํ•ด๋‹น ๊ตฌํ˜„์ฒด๊ฐ€ ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€ ๊ฒ€์ฆ ๊ฐ€๋Šฅ

์‹๋ณ„์ž ์„ ํƒ ์ „๋žต

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ณธํ‚ค๊ฐ€ ๋งŒ์กฑํ•ด์•ผํ•˜๋Š” ์กฐ๊ฑด

    • null ๊ฐ’์€ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

    • ์œ ์ผํ•ด์•ผ ํ•œ๋‹ค.

    • ๋ณ€ํ•ด์„  ์•ˆ ๋œ๋‹ค.

  • ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธํ‚ค๋ฅผ ์„ ํƒํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ์ „๋žต

    • ์ž์—ฐํ‚ค(natural key)

      • ๋น„์ฆˆ๋‹ˆ์Šค์— ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ํ‚ค (ex. ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ, ์ด๋ฉ”์ผ, ์ „ํ™”๋ฒˆํ˜ธ)

    • ๋Œ€๋ฆฌํ‚ค, ๋Œ€์ฒดํ‚ค(surrogate key)

      • ๋น„์ฆˆ๋‹ˆ์Šค์™€ ๊ด€๋ จ ์—†๋Š” ์ž„์˜๋กœ ๋งŒ๋“ค์–ด์ง„ ํ‚ค (ex, ์˜ค๋ผํด ์‹œํ€€์Šค, auto_increment, identity, ํ‚ค์ƒ์„ฑ ํ…Œ์ด๋ธ” ์‚ฌ์šฉ)

  • ์ž์—ฐํ‚ค๋ณด๋‹ค๋Š” ๋Œ€๋ฆฌํ‚ค ๊ถŒ์žฅ

    • ๊ธฐ๋ณธํ‚ค์˜ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋ ค๋ฉด ๋Œ€๋ฆฌํ‚ค๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ข‹์€ ์„ ํƒ

    • ๋น„์ฆˆ๋‹ˆ์Šค ํ™˜๊ฒฝ์€ ์–ธ์  ๊ฐ€ ๋ณ€ํ•œ๋‹ค..

Spring JdbcTemplate

๊ฐ„๋‹จํ•˜๊ณ  ์‹ค์šฉ์ ์ธ ๋ฐฉ๋ฒ•

์žฅ์ 

  • ์„ค์ •์ด ํŽธ๋ฆฌ

    • spring-boot-starter-jdbc ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ณ„๋„์˜ ์ถ”๊ฐ€ ์„ค์ •์€ ๋ถˆํ•„์š”

  • ๋ฐ˜๋ณต ๋ฌธ์ œ ํ•ด๊ฒฐ

    • ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด์ด ๋Œ€๋ถ€๋ถ„์˜ ๋ฐ˜๋ณต ์ž‘์—…์„ ๋Œ€์‹  ์ฒ˜๋ฆฌ

    • SQL ์ž‘์„ฑ, ํŒŒ๋ฆฌ๋ฏธํ„ฐ ์ •์˜, ์‘๋‹ต ๊ฐ’ ๋งคํ•‘๋งŒ ํ•„์š”

    • ๋Œ€์‹  ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๋ฐ˜๋ณต ์ž‘์—…

      • ์ปค๋„ฅ์…˜ ํš๋“

      • statement ์ค€๋น„/์‹คํ–‰

      • ๊ฒฐ๊ณผ ๋ฐ˜๋ณต ๋ฃจํ”„ ์‹คํ–‰

      • ์ปค๋„ฅ์…˜/statement/resultset ์ข…๋ฃŒ

      • ํŠธ๋žœ์žญ์…˜์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ์ปค๋„ฅ์…˜ ๋™๊ธฐํ™”

      • ์˜ˆ์™ธ ๋ฐœ์ƒ์‹œ ์Šคํ”„๋ง ์˜ˆ์™ธ ๋ณ€ํ™˜๊ธฐ ์‹คํ–‰...

๋‹จ์ 

  • ๋™์  ์ฟผ๋ฆฌ ์ž‘์„ฑ์˜ ์–ด๋ ค์›€(๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ž‘์„ฑํ•ด ์ฃผ์–ด์•ผ ํ•จ..)

JdbcTemplate

์ˆœ์„œ ๊ธฐ๋ฐ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ

commit

Memory to JdbcTemplate

NamedParameterJdbcTemplate

์ด๋ฆ„ ๊ธฐ๋ฐ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ

  • ๋ฐ”์ธ๋”ฉ์œผ๋กœ ์ธํ•œ ๋ฌธ์ œ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด NamedParameterJdbcTemplate๋Š” SQL์—์„œ ? ๋Œ€์‹  :parameterName ์„ ์‚ฌ์šฉ

  • ์ฝ”๋“œ๋ฅผ ์ค„์ด๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ๋ชจํ˜ธํ•จ์„ ์ œ๊ฑฐํ•ด์„œ ์ฝ”๋“œ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์œ ์ง€๋ณด์ˆ˜ ๊ด€์ ์—์„œ ๋งค์šฐ ์ค‘์š”

commit

SqlParameterSource

BeanPropertySqlParameterSource

  • ์ž๋™์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ

  • getXXX()๋ฅผ ํ™œ์šฉํ•ด ์ž๋™ ์ƒ์„ฑ

String sql = "insert into item (item_name, price, quantity) " +
                "values (:itemName, :price, :quantity)";

SqlParameterSource param = new BeanPropertySqlParameterSource(item);
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(sql, param, keyHolder);

MapSqlParameterSource

  • SQL์— ๋” ํŠนํ™”๋œ ๊ธฐ๋Šฅ ์ œ๊ณต

String sql = "update item " +
        "set item_name=:itemName, price=:price, quantity=:quantity " +
        "where id=:id";

SqlParameterSource param = new MapSqlParameterSource()
        .addValue("itemName", updateParam.getItemName())
        .addValue("price", updateParam.getPrice())
        .addValue("quantity", updateParam.getQuantity())
        .addValue("id", itemId); // ๋ณ„๋„๋กœ ํ•„์š”

Map

String sql = "select id, item_name, price, quantity from item where id = :id ";

Map<String, Object> param = Map.of("id", id);
Item item = template.queryForObject(sql, param, itemRowMapper());

BeanPropertyRowMapper

  • ResultSet ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„์„œ ์ž๋ฐ”๋นˆ ๊ทœ์•ฝ์— ๋งž์ถฐ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜

  • ์–ธ๋”์Šค์ฝ”์–ด ํ‘œ๊ธฐ๋ฒ•์„ ์นด๋ฉœ๋กœ ์ž๋™ ๋ณ€ํ™˜

BeanPropertyRowMapper<Item> rowMapper = BeanPropertyRowMapper.newInstance(Item.class);

SimpleJdbcInsert

INSERT SQL์„ ํŽธ์˜๊ธฐ๋Šฅ ์ œ๊ณต

์ƒ์„ฑ ์‹œ์ ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์˜ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด์„œ ํ…Œ์ด๋ธ”์— ์–ด๋–ค ์ปฌ๋Ÿผ์ด ์žˆ๋Š”์ง€ ํ™•์ธ

  • withTableName : ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ํ…Œ์ด๋ธ”๋ช… ์ง€์ •

  • usingGeneratedKeyColumns : key๋ฅผ ์ƒ์„ฑํ•˜๋Š” PK ์ปฌ๋Ÿผ๋ช… ์ง€์ •

  • usingColumns : INSERT SQL์— ์‚ฌ์šฉํ•  ์ปฌ๋Ÿผ ์ง€์ •(์ƒ๋Ÿ‰ ๊ฐ€๋Šฅ)

    • ํŠน์ • ์ปฌ๋Ÿผ๋งŒ ์ง€์ •ํ•ด์„œ ์ €์žฅํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ ์‚ฌ์šฉ

private final NamedParameterJdbcTemplate template;
private final SimpleJdbcInsert jdbcInsert;

public JdbcTemplateItemRepositoryV3(DataSource dataSource) {
    this.template = new NamedParameterJdbcTemplate(dataSource);
    this.jdbcInsert = new SimpleJdbcInsert(dataSource)
            .withTableName("item")
            .usingGeneratedKeyColumns("id");
            .usingColumns("item_name", "price", "quantity");
}

@Override
public Item save(Item item) {
    SqlParameterSource param = new BeanPropertySqlParameterSource(item);
    Number key = jdbcInsert.executeAndReturnKey(param);
    item.setId(key.longValue());
    return item;
}

SimpleJdbcCall

์Šคํ† ์–ด๋“œ ํ”„๋กœ์‹œ์ €๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ˜ธ์ถœ

Calling a Stored Procedure with SimpleJdbcCall

Using JdbcTemplate

Using JdbcTemplate

์กฐํšŒ

๋‹จ๊ฑด: .queryForObject, ๋ชฉ๋ก: .query

  • ๋‹จ๊ฑด ์กฐํšŒ (์ˆซ์ž)

int rowCount = jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
  • ๋‹จ๊ฑด ์กฐํšŒ (์ˆซ์ž ์กฐํšŒ, ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ)

int countOfActorsNamedJoe = jdbcTemplate.queryForObject("select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
  • ๋‹จ๊ฑด ์กฐํšŒ (๋ฌธ์ž ์กฐํšŒ)

String lastName = jdbcTemplate.queryForObject("select last_name from t_actor where id = ?", String.class, 1212L);
  • ๋‹จ๊ฑด ์กฐํšŒ (๊ฐ์ฒด ์กฐํšŒ)

Actor actor = jdbcTemplate.queryForObject(
			"select first_name, last_name from t_actor where id = ?",
			(resultSet, rowNum) -> { // ๊ฒฐ๊ณผ ๊ฐ์ฒด ๋งคํ•‘์„ ์œ„ํ•ด RowMapper ์‚ฌ์šฉ
				Actor newActor = new Actor();
				newActor.setFirstName(resultSet.getString("first_name"));
				newActor.setLastName(resultSet.getString("last_name"));
				return newActor;
			},
			1212L);
  • ๋ชฉ๋ก ์กฐํšŒ (๊ฐ์ฒด)

List<Actor> actors = jdbcTemplate.query(
			"select first_name, last_name from t_actor",
			(resultSet, rowNum) -> {
				Actor actor = new Actor();
				actor.setFirstName(resultSet.getString("first_name"));
				actor.setLastName(resultSet.getString("last_name"));
				return actor;
			});

๋ณ€๊ฒฝ(INSERT, UPDATE, DELETE)

.update(), (๋ฐ˜ํ™˜๊ฐ’ int๋Š” SQL ์‹คํ–‰ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ๋ฐ›์€ ๋กœ์šฐ ์ˆ˜)

  • ๋“ฑ๋ก

jdbcTemplate.update(
			"insert into t_actor (first_name, last_name) values (?, ?)",
			"Leonor", "Watling");
  • ์ˆ˜์ •

jdbcTemplate.update(
			"update t_actor set last_name = ? where id = ?",
			"Banjo", 5276L);
  • ์‚ญ์ œ

jdbcTemplate.update(
			"delete from t_actor where id = ?",
			Long.valueOf(actorId));

๊ธฐํƒ€ ๊ธฐ๋Šฅ

  • DDL

jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
  • ์Šคํ† ์–ด๋“œ ํ”„๋กœ์‹œ์ € ํ˜ธ์ถœ

jdbcTemplate.update(
		"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
		Long.valueOf(unionId));

๐ŸŒž JDBC TEST

@SpringBootTest๋Š” @SpringBootApplication์„ ์ฐพ๊ณ  ํ•ด๋‹น ์„ค์ •์„ ์‚ฌ์šฉ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ถ„๋ฆฌ

DB ์ƒ์„ฑ: jdbc:h2:~/testcase 
DB ์ ‘์†: jdbc:h2:tcp://localhost/~/testcase

ํ…Œ์ŠคํŠธ์˜ ์ค‘์š”ํ•œ ์›์น™

  • ํ…Œ์ŠคํŠธ๋Š” ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์™€ ๊ฒฉ๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

  • ํ…Œ์ŠคํŠธ๋Š” ๋ฐ˜๋ณตํ•ด์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค

๋ฐ์ดํ„ฐ ๋กค๋ฐฑ

  • ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ์ž๋Š” PlatformTransactionManager๋ฅผ ์ฃผ์ž… ๋ฐ›์•„์„œ ์‚ฌ์šฉ

    • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์ ์ ˆํ•œ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ์ž๋™ ๋“ฑ๋ก

  • @BeforeEach: ๊ฐ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰ ์ง์ „์— ํ˜ธ์ถœ(ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘ ์œ„์น˜)

    • transactionManager.getTransaction(new DefaultTransactionDefinition())

  • @AfterEach: ๊ฐ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์™„๋ฃŒ ์งํ›„์— ํ˜ธ์ถœ(ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ ์œ„์น˜)

    • transactionManager.rollback(status)

commit

๐ŸŒž @Transactionanl

  • Spring @Transactional์€ ๋กœ์ง์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜๋ฉด ์ปค๋ฐ‹์ด ๋™์ž‘ํ•˜์ง€๋งŒ

  • ํ…Œ์ŠคํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์‹คํ–‰ํ•˜๊ณ , ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ์ž๋™์œผ๋กœ ๋กค๋ฐฑ

  • ๊ฐ•์ œ๋กœ ์ปค๋ฐ‹์„ ํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ์—๋Š”, @Commit ๋˜๋Š” @Rollback(value = false)๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉ

commit

๐ŸŒž Embedded mode DB

  • H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” JVM ์•ˆ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณต

    • DB๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋‚ด์žฅํ•ด์„œ ํ•จ๊ป˜ ์‹คํ–‰

    • Spring Boot๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์„ค์ •์ด ์—†์œผ๋ฉด ์ž„๋ฒ ๋””๋“œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉ (commit)

  • dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1");

    • jdbc:h2:mem:db: ์ž„๋ฒ ๋””๋“œ(๋ฉ”๋ชจ๋ฆฌ) ๋ชจ๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ

    • DB_CLOSE_DELAY=-1: ์ž„๋ฒ ๋””๋“œ ๋ชจ๋“œ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ์—ฐ๊ฒฐ์ด ๋ชจ๋‘ ๋Š์–ด์ง€๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋„ ์ข…๋ฃŒ๋˜๋Š” ํ˜„์ƒ์„ ๋ฐฉ์ง€

Initialize a Database Using Basic SQL Scripts

Embedded Database Support

MyBatis

  • ๊ธฐ๋ณธ์ ์œผ๋กœ JdbcTemplate์ด ์ œ๊ณตํ•˜๋Š” ๋Œ€๋ถ€๋ถ„ ๊ธฐ๋Šฅ ์ œ๊ณต

  • SQL์„ XML์— ์ž‘์„ฑํ•˜๊ณ  ๋™์  ์ฟผ๋ฆฌ๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ 

  • ๋™์  ์ฟผ๋ฆฌ์™€ ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๊ฐ€ ๋งŽ๋‹ค๋ฉด MyBatis, ๋‹จ์ˆœํ•œ ์ฟผ๋ฆฌ๊ฐ€ ๋งŽ์œผ๋ฉด JdbcTemplate ์„ ํƒ

Mybatis

์„ค์ •

mybatis.type-aliases-package

  • ํƒ€์ž… ์ •๋ณด ์‚ฌ์šฉ ์‹œ ํŒจํ‚ค์ง€ ์ด๋ฆ„ ์ƒ๋žต์„ ์œ„ํ•œ ์„ค์ • (์ง€์ •ํ•œ ํŒจํ‚ค์ง€์™€ ๊ทธ ํ•˜์œ„ ํŒจํ‚ค์ง€๊ฐ€ ์ž๋™์œผ๋กœ ์ธ์‹)

    • ์—ฌ๋Ÿฌ ์œ„์น˜ ์ง€์ • ์‹œ ,, ; ๋กœ ๊ตฌ๋ถ„

mybatis.configuration.map-underscore-to-camel-case

  • JdbcTemplate#BeanPropertyRowMapper์ฒ˜๋Ÿผ ์–ธ๋”๋ฐ”๋ฅผ ์นด๋ฉœ๋กœ ์ž๋™ ๋ณ€๊ฒฝํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ ํ™œ์„ฑํ™”

logging.level.hello.itemservice.repository.mybatis=trace

  • MyBatis์—์„œ ์‹คํ–‰๋˜๋Š” ์ฟผ๋ฆฌ ๋กœ๊ทธ ํ™•์ธ์„ ์œ„ํ•œ ์„ค์ •

commit

์ ์šฉ

  • XML ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•  ๊ฒฝ์šฐ

    • resources/mapper ๋ฅผ ํฌํ•จํ•œ ๊ทธ ํ•˜์œ„ ํด๋”์— ์žˆ๋Š” XML

    • application.properties

      mybatis.mapper-locations=classpath:mapper/**/*.xml
  • INSERT <insert>

    • id: Mapper Class์— ์„ค์ •ํ•œ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์ง€์ •

    • ํŒŒ๋ผ๋ฏธํ„ฐ: #{} ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๊ณ  ๋งคํผ์—์„œ ๋„˜๊ธด ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„์„ ๊ธฐ์ž…

    • #{} : PreparedStatement ๋ฅผ ์‚ฌ์šฉ(like. JDBC ? ์น˜ํ™˜)

    • useGeneratedKeys: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ํ‚ค๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ๋Š” IDENTITY ์ „๋žต์ผ ๋•Œ ์‚ฌ์šฉ

    • keyProperty: ์ƒ์„ฑ๋˜๋Š” ํ‚ค์˜ ์†์„ฑ ์ด๋ฆ„ ์ง€์ •

  • UPDATE <update>

    • ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํ•œ ๊ฐœ๋งŒ ์žˆ์œผ๋ฉด @Param์„ ์ง€์ •ํ•˜์ง€ ์•Š์•„๋„ ๋˜์ง€๋งŒ, ๋‘ ๊ฐœ ์ด์ƒ์ด๋ฉด @Param์œผ๋กœ ์ด๋ฆ„์„ ์ง€์ •ํ•ด์„œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ตฌ๋ถ„

  • SELECT <select>

    • resultType: ๋ฐ˜ํ™˜ ํƒ€์ž… ๋ช…์‹œ(๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด์— ๋งคํ•‘) -> ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋กœ ๋ฐ”๋กœ ๋ณ€ํ™˜

    • ๋ฐ˜ํ™˜ ๊ฐ์ฒด๊ฐ€ ํ•˜๋‚˜์ด๋ฉด Item, Optional ์‚ฌ์šฉ, ํ•˜๋‚˜ ์ด์ƒ์ด๋ฉด ์ปฌ๋ ‰์…˜ ์‚ฌ์šฉ

  • ๋™์  ์ฟผ๋ฆฌ

    • <if>: ํ•ด๋‹น ์กฐ๊ฑด์ด ๋งŒ์กฑํ•˜๋ฉด ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€

    • <where>: ์ ์ ˆํ•˜๊ฒŒ where ๋ฌธ์žฅ ์ƒ์„ฑ

  • ํŠน์ˆ˜๋ฌธ์ž

    < : &lt;
    > : &gt;
    & : &amp;
    
    and price <![CDATA[<=]]> #{maxPrice}  

commit

์‹คํ–‰

  • ItemRepository ๋ฅผ ๊ตฌํ˜„ํ•œ MyBatisItemRepository ์ƒ์„ฑ

    • ๋‹จ์ˆœํžˆ ItemMapper ์— ๊ธฐ๋Šฅ์„ ์œ„์ž„

commit

Mapper Interface ์˜ ๋™์ž‘

  1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๋”ฉ ์‹œ์ ์— MyBatis ์Šคํ”„๋ง ์—ฐ๋™ ๋ชจ๋“ˆ์€ @Mapper๊ฐ€ ๋ถ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํƒ์ƒ‰

  2. ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋ฉด ๋™์  ํ”„๋ก์‹œ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•ด์„œ Mapper Interface์˜ ๊ตฌํ˜„์ฒด ์ƒ์„ฑ(class com.sun.proxy.$Proxy66)

  3. ์ƒ์„ฑ๋œ ๊ตฌํ˜„์ฒด๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก

MyBatis ์Šคํ”„๋ง ์—ฐ๋™ ๋ชจ๋“ˆ

  • ์ธํ„ฐํŽ˜์ด์Šค๋งŒ์œผ๋กœ XML ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•„์„œ ํ˜ธ์ถœ (Mapper ๊ตฌํ˜„์ฒด ์‚ฌ์šฉ)

  • Mapper ๊ตฌํ˜„์ฒด๋Š” ์Šคํ”„๋ง ์˜ˆ์™ธ ์ถ”์ƒํ™”๋„ ํ•จ๊ป˜ ์ ์šฉ

    • MyBatis์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ DataAccessException(์Šคํ”„๋ง ์˜ˆ์™ธ ์ถ”์ƒํ™”)์— ๋งž๊ฒŒ ๋ณ€ํ™˜

  • JdbcTemplate์˜ ๊ธฐ๋ณธ์ ์ธ ์„ค์ •๋“ค์€ ๋ชจ๋‘ ์ž๋™์œผ๋กœ ์„ค์ • (๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜, ํŠธ๋žœ์žญ์…˜ ๊ด€๋ จ ๊ธฐ๋Šฅ ๋“ฑ..)

MyBatis ์Šคํ”„๋ง ์—ฐ๋™ ๋ชจ๋“ˆ์ด ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•ด์ฃผ๋Š” ๋ถ€๋ถ„์€ MybatisAutoConfiguration class ์ฐธ๊ณ 

๊ธฐ๋Šฅ

MyBatis

MyBatis-Spring

if

 <select id="findActiveBlogWithTitleLike" resultType="Blog">
    SELECT * FROM BLOG
    WHERE state = โ€˜ACTIVEโ€™
    <if test="title != null">
        AND title like #{title}
    </if>
</select>

choose (when, otherwise)

<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = โ€˜ACTIVEโ€™
    <choose>
        <when test="title != null">
            AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
            AND author_name like #{author.name}
        </when>
        <otherwise>
            AND featured = 1
        </otherwise>
    </choose>
</select>

trim (where, set)

<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG
    <where>
        <if test="state != null">
            state = #{state}
        </if>
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
            AND author_name like #{author.name}
        </if>
    </where>
</select>
  • <where>๋Š” ๋ฌธ์žฅ์ด ์žˆ์œผ๋ฉด where๋ฅผ ์ถ”๊ฐ€ (๋งŒ์•ฝ and๊ฐ€ ๋จผ์ € ์‹œ์ž‘๋œ๋‹ค๋ฉด and๋ฅผ ์ œ๊ฑฐ)

foreach

<select id="selectPostIn" resultType="domain.blog.Post">
    SELECT *
    FROM POST P
    <where>
        <foreach item="item" index="index" collection="list"
                  open="ID in (" separator="," close=")" nullable="true">
            #{item}
        </foreach>
    </where>
</select>

MyBatis ๋™์  SQL

MyBatis Annotation SQL

์žฌ์‚ฌ์šฉ์„ ์œ„ํ•œ SQL ์กฐ๊ฐ

<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

<select id="selectUsers" resultType="map">
    select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
    from some_table t1
    cross join some_table t2
</select>

Result Maps

  • ์ปฌ๋Ÿผ๋ช…๊ณผ ๊ฐ์ฒด ํ”„๋กœํผํ‹ฐ๋ช…์ด ๋‹ค๋ฅผ ๊ฒฝ์šฐ ์œ ์šฉ

<resultMap id="userResultMap" type="User">
    <id property="id" column="user_id" />
    <result property="username" column="username"/>
    <result property="password" column="password"/>
</resultMap>
<select id="selectUsers" resultMap="userResultMap">
    select user_id, user_name, hashed_password
    from some_table
    where id = #{id}
</select>

Result Maps

JPA

SQL ์ค‘์‹ฌ์ ์ธ ๊ฐœ๋ฐœ์˜ ๋ฌธ์ œ์ 

  • SQL์— ์˜์กด์ ์ธ ๊ฐœ๋ฐœ

    • CRUD ์ฝ”๋“œ์˜ ๋ฐ˜๋ณต

    • ํ•„๋“œ ์ˆ˜์ • ์‹œ ๋งŽ์€ ์–‘์˜ SQL ์ˆ˜์ •์ด ํ•„์š”

  • ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ„์˜ ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜

    • ๊ฐ์ฒด <-> SQL ๋งคํ•‘ ์ž‘์—…์— ๋งŽ์€ ๋…ธ๋ ฅ์ด ํ•„์š”

  • ๊ณ„์ธต๋ถ„ํ• ์˜ ์–ด๋ ค์›€

    • ์ฒ˜์Œ ์‹คํ–‰ํ•˜๋Š” SQL์— ๋”ฐ๋ผ ํƒ์ƒ‰ ๋ฒ”์œ„ ๊ฒฐ์ •

    • ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰์—์„œ ์—”ํ‹ฐํ‹ฐ ์‹ ๋ขฐ ๋ฌธ์ œ

JPA(Java Persistence API)

  • ์ž๋ฐ” ์ง„์˜์˜ ORM(Object-relational mapping) ๊ธฐ์ˆ  ํ‘œ์ค€

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ JDBC ์‚ฌ์ด์—์„œ ๋™์ž‘

  • ์žฅ์ 

    • ๊ฐ์ฒด ์ค‘์‹ฌ ๊ฐœ๋ฐœ(์ƒ์‚ฐ์„ฑ, ์œ ์ง€๋ณด์ˆ˜)

      • ์ƒ์†, ์—ฐ๊ด€๊ด€๊ณ„

    • ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ๋ถˆ์ผ์น˜ ํ•ด๊ฒฐ

      • ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰

    • ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋Šฅ

      • 1์ฐจ ์บ์‹œ์™€ ๋™์ผ์„ฑ ๋ณด์žฅ

      • ์“ฐ๊ธฐ ์ง€์—ฐ(insert query ๋ชจ์œผ๊ธฐ)

      • ์ง€์—ฐ ๋กœ๋”ฉ(๊ฐ์ฒด ์‹ค์ œ ์‚ฌ์šฉ ์‹œ ๋กœ๋”ฉ)

    • ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์ถ”์ƒํ™” ๋…๋ฆฝ์„ฑ

    • ํ‘œ์ค€

JPA INTRO

์„ค์ •

application.properties

  • org.hibernate.SQL=DEBUG : hibernate๊ฐ€ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” SQL ํ™•์ธ

  • org.hibernate.type.descriptor.sql.BasicBinder=TRACE : SQL์— ๋ฐ”์ธ๋”ฉ ๋˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ํ™•์ธ

commit

๊ฐœ๋ฐœ

  • JPA์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ๊ฐ์ฒด์™€ ํ…Œ์ด๋ธ”์„ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ

commit

์˜ˆ์™ธ

  • EntityManager๋Š” ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด JPA ๊ด€๋ จ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ

    • @Repository๋ฅผ ํ†ตํ•ด ์Šคํ”„๋ง์ด ์˜ˆ์™ธ ๋ณ€ํ™˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” AOP ์ƒ์„ฑ

  • JPA๋Š” PersistenceException ๊ณผ ๊ทธ ํ•˜์œ„ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ

    • ์ถ”๊ฐ€๋กœ IllegalStateException , IllegalArgumentException ๋ฐœ์ƒ

@Repository์˜ ๊ธฐ๋Šฅ

  • ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์˜ ๋Œ€์ƒ + ์˜ˆ์™ธ ๋ณ€ํ™˜ AOP ์ ์šฉ ๋Œ€์ƒ

  • ์Šคํ”„๋ง + JPA ์‚ฌ์šฉ ์‹œ ์Šคํ”„๋ง์€ JPA ์˜ˆ์™ธ ๋ณ€ํ™˜๊ธฐ(PersistenceExceptionTranslator) ๋“ฑ๋ก

  • ์˜ˆ์™ธ ๋ณ€ํ™˜ AOP Proxy๋Š” JPA ๊ด€๋ จ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด JPA ์˜ˆ์™ธ ๋ณ€ํ™˜๊ธฐ๋ฅผ ํ†ตํ•ด ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์˜ˆ์™ธ๋กœ ๋ณ€ํ™˜ (PersistenceException -> DataAccessException)

  • ์‹ค์ œ JPA ์˜ˆ์™ธ๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ์ฝ”๋“œ: EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible()

Result

Spring Data JPA

JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๋Œ€ํ‘œ์ ์ธ ๊ธฐ๋Šฅ

  • ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋Šฅ

  • ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ๊ธฐ๋Šฅ

์ฐธ๊ณ 

  • Spring Data JPA๊ฐ€ Repository ๊ตฌํ˜„ ํด๋ž˜์Šค(Proxy)๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก

  • ์Šคํ”„๋ง ์˜ˆ์™ธ ์ถ”์ƒํ™” ์ง€์› (Spring Data JPA๊ฐ€ ๋งŒ๋“ค์–ด์ฃผ๋Š” Proxy์—์„œ ์˜ˆ์™ธ ๋ณ€ํ™˜์„ ์ฒ˜๋ฆฌ)

์ ์šฉ

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  • JPA, hibernate, Spring Data JPA, Spring JDBC ๊ธฐ๋Šฅ์ด ๋ชจ๋‘ ํฌํ•จ

Spring Data

  • Repository interface

@Indexed
public interface Repository<T, ID> {
}
  • CrudRepository interface

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
	<S extends T> S save(S entity);
	<S extends T> Iterable<S> saveAll(Iterable<S> entities);
	Optional<T> findById(ID id);
	boolean existsById(ID id);
	Iterable<T> findAll();
	Iterable<T> findAllById(Iterable<ID> ids);
	long count();
	void deleteById(ID id);
	void delete(T entity);
	void deleteAllById(Iterable<? extends ID> ids);
	void deleteAll(Iterable<? extends T> entities);
	void deleteAll();
}
  • PagingAndSortingRepository interface

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
	Iterable<T> findAll(Sort sort);
	Page<T> findAll(Pageable pageable);
}

Spring Data JPA

  • JpaRepository interface

    • ๊ธฐ๋ณธ์ ์ธ CRUD ๊ธฐ๋Šฅ ์ œ๊ณต

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

	@Override
	List<T> findAll();

	@Override
	List<T> findAll(Sort sort);

	@Override
	List<T> findAllById(Iterable<ID> ids);

	@Override
	<S extends T> List<S> saveAll(Iterable<S> entities);
  
  void flush();

	<S extends T> S saveAndFlush(S entity);

	<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);

	void deleteAllInBatch(Iterable<T> entities);

	void deleteAllByIdInBatch(Iterable<ID> ids);

	void deleteAllInBatch();

	T getOne(ID id);

	T getById(ID id);

	@Override
	<S extends T> List<S> findAll(Example<S> example);

	@Override
	<S extends T> List<S> findAll(Example<S> example, Sort sort);
}

Using JpaRepository example

public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long> {

    List<Item> findByItemNameLike(String itemName);

    List<Item> findByPriceLessThanEqual(Integer price);

    // Query Method (์•„๋ž˜ ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ ์ˆ˜ํ–‰)
    List<Item> findByItemNameLikeAndPriceLessThanEqual(String itemName, Integer price);

    // JPQL
    @Query("select i from Item i where i.itemName like :itemName and i.price <=:price")
    List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);
}

Spring Data

Query Creation

Limiting Query Results

QueryDSL

๊ธฐ์กด ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ 

  • ์ผ๋ฐ˜ ์ฟผ๋ฆฌ๋Š” ๋ฌธ์ž์ด๋ฏ€๋กœ Type-check๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ , ์‹คํ–‰ ์ „๊นŒ์ง€๋Š” ์ž‘๋™ ์—ฌ๋ถ€ ํ™•์ธ ๋ถˆ๊ฐ€

  • ์ฟผ๋ฆฌ๋ฅผ Java๋กœ type-safeํ•˜๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ QueryDSL -> ์ฃผ๋กœ JPQL์— ์‚ฌ์šฉ

ํ•ด๊ฒฐ

  • DSL(DomainSpecificLanguage) : ํŠน์ • ๋„๋ฉ”์ธ์— ํŠนํ™”๋˜์–ด ์ œํ•œ์ ์ธ ํ‘œํ˜„๋ ฅ์„ ๊ฐ€์ง„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด

  • QueryDSL(QueryDomainSpecificLanguage) : ์ฟผ๋ฆฌ์— ํŠนํ™”๋œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด

    • JPA, MongoDB, SQL ๊ฐ™์€ ๊ธฐ์ˆ ๋“ค์„ ์œ„ํ•ด type-safe SQL์„ ๋งŒ๋“œ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ

    • type-safe, ๋‹จ์ˆœ, ์‰ฌ์šด ์žฅ์ 

    • Q์ฝ”๋“œ ์ƒ์„ฑ์„ ์œ„ํ•œ APT(Annotation Processing Tool) ์„ค์ •์ด ํ•„์š”

์„ค์ •

Build Tool์— ๋”ฐ๋ฅธ QClass ์ƒ์„ฑ ๋ฐฉ๋ฒ•

  • Gradle : Gradle์„ ํ†ตํ•ด ๋นŒ๋“œ

    • Gradle IntelliJ

      • Gradle -> Tasks -> build -> clean

      • Gradle -> Tasks -> other -> compileJava

    • Gradle Console

      • ./gradlew clean compileJava

    • build/generated/sources/annotationProcessor ํ•˜์œ„์— ์ƒ์„ฑ

  • IntelliJ IDEA : IntelliJ๊ฐ€ ์ง์ ‘ ์ž๋ฐ”๋ฅผ ์‹คํ–‰ํ•ด์„œ ๋นŒ๋“œ

    • Build Project / Start

    • src/main/generated ํ•˜์œ„์— ์ƒ์„ฑ

commit

์ ์šฉ

  • Querydsl ์‚ฌ์šฉ์„ ์œ„ํ•ด JPAQueryFactory ํ•„์š”

    • JPAQueryFactory ๋Š” JPA ์ฟผ๋ฆฌ์ธ JPQL์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด EntityManager ํ•„์š”

  • JdbcTemplate ์„ค์ •๊ณผ ์œ ์‚ฌ

commit

ํ™œ์šฉ ๋ฐฉ์•ˆ

๊ตฌ์กฐ์˜ ์•ˆ์ •์„ฑ vs ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ์™€ ๊ฐœ๋ฐœ์˜ ํŽธ๋ฆฌ์„ฑ

  • Trade Off

    • DI, OCP ๋ฅผ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด ์–ด๋Œ‘ํ„ฐ๋ฅผ ๋„์ž…ํ•˜๊ณ , ๋” ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ์œ ์ง€

    • ์–ด๋Œ‘ํ„ฐ ์ œ๊ฑฐ๋กœ ๊ตฌ์กฐ๊ฐ€ ๋‹จ์ˆœํ•ด ์ง€์ง€๋งŒ, DI, OCP๋ฅผ ํฌ๊ธฐํ•˜๊ณ , Service ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝ

  • ๋‹ค๋งŒ, ์ƒํ™ฉ์— ๋”ฐ๋ผ์„œ ๊ตฌ์กฐ์˜ ์•ˆ์ •์„ฑ์ด ์ค‘์š”ํ•  ์ˆ˜๋„ ์žˆ๊ณ , ๋‹จ์ˆœํ•จ์ด ๋” ๋‚˜์€ ์„ ํƒ์ผ ์ˆ˜ ์žˆ๋‹ค.

    • ์ถ”์ƒํ™” ๋น„์šฉ์„ ๋„˜์–ด์„ค ๋งŒํผ ํšจ๊ณผ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์ถ”์ƒํ™” ๋„์ž…์ด ์‹ค์šฉ์ 

    • ์ƒํ™ฉ์— ๋งž๋Š” ์„ ํƒ์ด ์ค‘์š”

    • ๋จผ์ €, ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜๊ณ , ์ดํ›„ ๋ฆฌํŽ™ํ† ๋ง์„ ์ถ”์ฒœ

์‹ค์šฉ์ ์ธ ๊ตฌ์กฐ

  • SpringDataJPA์™€ QueryDSL Repository๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ๊ธฐ๋ณธ CRUD์™€ ๋‹จ์ˆœ ์กฐํšŒ๋Š” SpringDataJPA ๋‹ด๋‹น, ๋ณต์žกํ•œ ์กฐํšŒ ์ฟผ๋ฆฌ๋Š” Querydsl ๋‹ด๋‹น

commit

๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ธฐ์ˆ  ์กฐํ•ฉ

  • JPA, SpringDataJPA, Querydsl ์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ํ•ด๋‹น ๋ถ€๋ถ„์—๋Š” JdbcTemplate ์ด๋‚˜ MyBatis ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉ

  • ํŠธ๋žœ์žญ์…˜ ๋งค๋„ˆ์ง€์˜ ๊ฒฝ์šฐ JpaTransactionManager ํ•˜๋‚˜๋งŒ ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋กํ•˜๋ฉด, JPA, JdbcTemplate, MyBatis ๋ฅผ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์–ด์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

  • JPA, JdbcTemplate์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ JPA์˜ ํ”Œ๋Ÿฌ์‹œ ํƒ€์ด๋ฐ์ด ๋‹ค๋ฅด๋‹ค๋ฉด ๋ณ€๊ฒฝํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์Œ

    • JPA๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋˜๋Š” ์‹œ์ ์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜

    • JPA ํ˜ธ์ถœ์ด ๋๋‚œ ์‹œ์ ์— ํ”Œ๋Ÿฌ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , JdbcTemplate ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ

Spring Transaction

  • Spring Transaction ์ถ”์ƒํ™”

    • PlatformTransactionManager ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜ ์ถ”์ƒํ™”

    • ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ธฐ์ˆ ๋งˆ๋‹ค ๋ชจ๋‘ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์„ ์ถ”์ƒํ™”

package org.springframework.transaction;

public interface PlatformTransactionManager extends TransactionManager {

	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;
}
  • Spring์€ Transaction์„ ์ถ”์ƒํ™”ํ•ด์„œ ์ œ๊ณตํ•˜๊ณ , ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ธฐ์ˆ ์— ๋Œ€ํ•œ TransactionManager์˜ ๊ตฌํ˜„์ฒด๋„ ์ œ๊ณต

    • ์‚ฌ์šฉ์ž๋Š” ํ•„์š”ํ•œ ๊ตฌํ˜„์ฒด๋ฅผ Spring Bean์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ , ์ฃผ์ž… ๋ฐ›์•„์„œ ์‚ฌ์šฉ

  • Spring Boot๋Š” ์–ด๋–ค ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜๋Š”์ง€๋ฅผ ์ž๋™์œผ๋กœ ์ธ์‹ํ•ด์„œ ์ ์ ˆํ•œ TransactionManager ์„ ํƒ ๋ฐ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก (์„ ํƒ, ๋“ฑ๋ก ๊ณผ์ • ์ƒ๋žต)

    • JdbcTemplate, MyBatis ์‚ฌ์šฉ ์‹œ DataSourceTransactionManager(JdbcTransactionManager)๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก

    • JPA ์‚ฌ์šฉ ์‹œ JpaTransactionManager์„ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก

์‚ฌ์šฉ ๋ฐฉ์‹

์„ ์–ธ์  ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ vs ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ

์„ ์–ธ์  ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ(Declarative Transaction Management)

  • @Transactional ํ•˜๋‚˜๋งŒ ์„ ์–ธํ•˜์—ฌ ํŽธ๋ฆฌํ•˜๊ฒŒ ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉ(๊ณผ๊ฑฐ์—๋Š” XML์— ์„ค์ •)

  • ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ "ํ•ด๋‹น ๋กœ์ง์— ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•˜๊ฒ ๋‹ค."๋ผ๊ณ  ์„ ์–ธํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์ด ์ ์šฉ๋˜๋Š” ๋ฐฉ์‹

  • ๊ธฐ๋ณธ์ ์œผ๋กœ ํ”„๋ก์‹œ ๋ฐฉ์‹์˜ AOP ์ ์šฉ

  • ํŠธ๋žœ์žญ์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ์ฒด์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์„œ๋น„์Šค ๊ฐ์ฒด๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ

Result
  • ํŠธ๋žœ์žญ์…˜์€ ์ปค๋„ฅ์…˜์— setAutocommit(false) ์ง€์ •์œผ๋กœ ์‹œ์ž‘

  • ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์Šคํ”„๋ง ๋‚ด๋ถ€์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๋งค๋‹ˆ์ €๋ฅผ ์‚ฌ์šฉ

  • JdbcTemplate์„ ํฌํ•จํ•œ ๋Œ€๋ถ€๋ถ„์˜ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ธฐ์ˆ ๋“ค์€ ํŠธ๋žœ์žญ์…˜์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€์—์„œ ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค(์ปค๋„ฅ์…˜)๋ฅผ ๋™๊ธฐํ™”

์ฐธ๊ณ 

ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์˜ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ(programmatic transaction management)

  • TransactionManager ๋˜๋Š” TransactionTemplate ๋“ฑ์„ ์‚ฌ์šฉํ•ด์„œ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ž‘์„ฑ

  • ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์˜ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๊ฐ€ ํŠธ๋žœ์žญ์…˜์ด๋ผ๋Š” ๊ธฐ์ˆ  ์ฝ”๋“œ์™€ ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜๋Š” ๋‹จ์ 

  • ์„ ์–ธ์  ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๊ฐ€ ํ›จ์”ฌ ๊ฐ„ํŽธํ•˜๊ณ  ์‹ค์šฉ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์‹ค๋ฌด์—์„œ๋Š” ๋Œ€๋ถ€๋ถ„ ์„ ์–ธ์  ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๋ฅผ ์‚ฌ์šฉ

์ ์šฉ

AOP ์ ์šฉ ๋ฐฉ์‹์— ๋”ฐ๋ผ์„œ ์ธํ„ฐํŽ˜์ด์Šค์— @Transactional ์„ ์–ธ ์‹œ AOP๊ฐ€ ์ ์šฉ์ด ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์œผ๋ฏ€๋กœ, ๊ฐ€๊ธ‰์  ๊ตฌ์ฒด ํด๋ž˜์Šค์— @Transactional ์‚ฌ์šฉ ๊ถŒ์žฅ

  • Transaction ์ ์šฉ ํ™•์ธ

TransactionSynchronizationManager.isActualTransactionActive();

TransactionSynchronizationManager.isCurrentTransactionReadOnly();
  • ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ํŠธ๋žœ์žญ์…˜ ๋กœ๊ทธ ํ™•์ธ์„ ์œ„ํ•œ ์„ค์ •

logging.level.org.springframework.transaction.interceptor=TRACE
Getting transaction for [hello.springtx.apply...BasicService.tx]

.. ์‹ค์ œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ

.. ํŠธ๋žœ์ ์…˜ ๋กœ์ง ์ปค๋ฐ‹ ๋˜๋Š” ๋กค๋ฐฑ

Completing transaction for [hello.springtx.apply...BasicService.tx]
 
Result
  • @Transactional ์ด ํŠน์ • ํด๋ž˜์Šค๋‚˜ ๋ฉ”์„œ๋“œ์— ์žˆ๋‹ค๋ฉด, Transaction AOP๋Š” ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก -> ์‹ค์ œ ๊ฐ์ฒด ๋Œ€์‹  ํ”„๋ก์‹œ๋ฅผ ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋ก๋˜๊ณ  ํ”„๋ก์‹œ๋Š” ๋‚ด๋ถ€์— ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐ

  • ํ”„๋ก์‹œ๋Š” ๊ฐ์ฒด๋ฅผ ์ƒ์†ํ•ด์„œ ๋งŒ๋“ค์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉ

commit

์ ์šฉ ์œ„์น˜

  • ์Šคํ”„๋ง์—์„œ ์šฐ์„ ์ˆœ์œ„๋Š” ํ•ญ์ƒ ๋” ๊ตฌ์ฒด์ ์ด๊ณ  ์ž์„ธํ•œ ๊ฒƒ์ด ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง.

  • ํด๋ž˜์Šค์— ์ ์šฉํ•˜๋ฉด ๋ฉ”์„œ๋“œ๋Š” ์ž๋™ ์ ์šฉ

commit

์ฃผ์˜์‚ฌํ•ญ

  • @Transactional์„ ์„ ์–ธํ•˜๋ฉด ์Šคํ”„๋ง ํŠธ๋žœ์žญ์…˜ AOP ์ ์šฉ

    • ํŠธ๋žœ์žญ์…˜ AOP๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ”„๋ก์‹œ ๋ฐฉ์‹์˜ AOP ์‚ฌ์šฉ

  • ์Šคํ”„๋ง์€ ๋Œ€์ƒ ๊ฐ์ฒด ๋Œ€์‹  ํ”„๋ก์‹œ๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋ฏ€๋กœ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์š”์ฒญ์„ ๋จผ์ € ๋ฐ›๊ณ , ํ”„๋ก์‹œ ๊ฐ์ฒด์—์„œ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ์™€ ์‹ค์ œ ๊ฐ์ฒด ํ˜ธ์ถœ

  • ๋”ฐ๋ผ์„œ, ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•˜๋ ค๋ฉด ํ•ญ์ƒ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด์„œ ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•จ

  • โญ๏ธ ๋งŒ์•ฝ, ํ”„๋ก์‹œ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด AOP๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๊ณ , ํŠธ๋žœ์žญ์…˜๋„ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.

    • ๋Œ€์ƒ ๊ฐ์ฒด์˜ ๋‚ด๋ถ€์—์„œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์ด ๋ฐœ์ƒํ•˜๋ฉด ํ”„๋ก์‹œ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ

ํ”„๋ก์‹œ ํ˜ธ์ถœ

@Transactional
public void internal() {
    log.info("call internal");
}
Result
  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ service.internal()์„ ํ˜ธ์ถœํ•˜๋ฉด service์˜ ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ ํ˜ธ์ถœ

  2. internal() ๋ฉ”์„œ๋“œ์— @Transactional์ด ์„ ์–ธ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ๋Š” ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉ

  3. ํŠธ๋žœ์žญ์…˜ ์ ์šฉ ํ›„ ์‹ค์ œ service ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค์˜ internal() ํ˜ธ์ถœ

  4. ์‹ค์ œ service๊ฐ€ ์ฒ˜๋ฆฌ ์™„๋ฃŒ๋˜๋ฉด ์‘๋‹ต์ด ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ๋กœ ๋Œ์•„์˜ค๊ณ , ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ๋Š” ํŠธ๋žœ์žญ์…˜์„ ์™„๋ฃŒ

๋Œ€์ƒ ๊ฐ์ฒด ์ง์ ‘ ํ˜ธ์ถœ

public void external() {
    log.info("call external");
    internal();
}

@Transactional
public void internal() {
    log.info("call internal");
}
Result
  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ service.external()์„ ํ˜ธ์ถœํ•˜๋ฉด service์˜ ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ ํ˜ธ์ถœ

  2. external() ๋ฉ”์„œ๋“œ์—๋Š” @Transactional์ด ์—†์œผ๋ฏ€๋กœ ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ๋Š” ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•˜์ง€ ์•Š๊ณ , ์‹ค์ œ service ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค์˜ external() ํ˜ธ์ถœ

  3. external()์€ ๋‚ด๋ถ€์—์„œ (this.)internal() ์ง์ ‘ ํ˜ธ์ถœ

  4. ๋‚ด๋ถ€ ํ˜ธ์ถœ์€ ํ”„๋ก์‹œ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํŠธ๋žœ์žญ์…˜ ์ ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅ

@Transactional์„ ์‚ฌ์šฉํ•˜๋Š” ํŠธ๋žœ์žญ์…˜ AOP๋Š” ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€ ํ˜ธ์ถœ์— ํ”„๋ก์‹œ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

  • ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋‚ด๋ถ€ ํ˜ธ์ถœ์„ ์™ธ๋ถ€ ํ˜ธ์ถœ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด internal()๋ฅผ ๋ณ„๋„ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ

commit

๋Œ€์ƒ ๊ฐ์ฒด ์™ธ๋ถ€ ํ˜ธ์ถœ

Result
  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ service.external()์„ ํ˜ธ์ถœํ•˜๋ฉด ์‹ค์ œ service ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค ํ˜ธ์ถœ

  2. service๋Š” ์ฃผ์ž… ๋ฐ›์€ internalService.internal() ํ˜ธ์ถœ

  3. internalService๋Š” ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ์ด๋ฏ€๋กœ(@Transactional) ํŠธ๋žœ์žญ์…˜ ์ ์šฉ

  4. ํŠธ๋žœ์žญ์…˜ ์ ์šฉ ํ›„ ์‹ค์ œ internalService ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค์˜ internal() ํ˜ธ์ถœ

commit

์ฐธ๊ณ 

์Šคํ”„๋ง ํŠธ๋žœ์žญ์…˜ AOP ๊ธฐ๋Šฅ์€ ๊ณผ๋„ํ•œ ํŠธ๋žœ์žญ์…˜ ์ ์šฉ์„ ๋ง‰๊ธฐ ์œ„ํ•ด public ๋ฉ”์„œ๋“œ์—๋งŒ ์ ์šฉ๋˜๋„๋ก ๊ธฐ๋ณธ ์„ค์ •

public ์ด ์•„๋‹Œ๊ณณ์— @Transactional ์ด ๋ถ™์œผ๋ฉด ํŠธ๋žœ์žญ์…˜ ์ ์šฉ ๋ฌด์‹œ

์ดˆ๊ธฐํ™” ์‹œ์ 

  • ์ดˆ๊ธฐํ™” ์ฝ”๋“œ(ex.@PostConstruct)์™€ @Transactional์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜ ์ ์šฉ ๋ถˆ๊ฐ€

    • ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๊ฐ€ ๋จผ์ € ํ˜ธ์ถœ๋˜๊ณ  ์ดํ›„ ํŠธ๋žœ์žญ์…˜ AOP๊ฐ€ ์ ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ

@PostConstruct
@Transactional
public void initV1() {
    boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
    log.info("Hello init @PostConstruct tx active={}", isActive); // false
}
  • ๋Œ€์•ˆ์œผ๋กœ @ApplicationReadyEvent ์‚ฌ์šฉ

    • ApplicationReadyEvent๋Š” ํŠธ๋žœ์žญ์…˜ AOP๋ฅผ ํฌํ•จํ•œ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์™„์ „ํžˆ ์ƒ์„ฑ๋œ ์ดํ›„ ์ด๋ฒคํŠธ๊ฐ€ ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ

@EventListener(value = ApplicationReadyEvent.class)
@Transactional
public void init2() {
    boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
    log.info("Hello init ApplicationReadyEvent tx active={}", isActive); // true
}

comomit

์˜ต์…˜

  • String value() default "";

  • String transactionManager() default "";

    • @Transactional ์—์„œ ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ๊ฐ€ ์‚ฌ์šฉํ•  ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ € ์ง€์ •

    • ์ƒ๋žต ์‹œ ๊ธฐ๋ณธ์œผ๋กœ ๋“ฑ๋ก๋œ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ € ์‚ฌ์šฉ

    • ์‚ฌ์šฉ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๊ฐ€ ๋‘˜ ์ด์ƒ์ด๋ผ๋ฉด, ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ € ์ด๋ฆ„์„ ์ง€์ •ํ•ด์„œ ๊ตฌ๋ถ„

@Transactional("memberTxManager")
@Transactional("orderTxManager")
  • Class<? extends Throwable>[] rollbackFor() default {};

    • ํŠน์ • ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ์„ ํ•˜๋„๋ก ์ง€์ •

    • Exception(์ฒดํฌ ์˜ˆ์™ธ)์ด ๋ฐœ์ƒํ•ด๋„ ๋กค๋ฐฑํ•˜๋„๋ก ์„ค์ • ๊ฐ€๋Šฅ

@Transactional(rollbackFor = Exception.class)
  • Class<? extends Throwable>[] noRollbackFor() default {};

    • rollbackFor ์™€ ๋ฐ˜๋Œ€๋กœ ํŠน์ • ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ์„ ํ•˜์ง€ ์•Š๋„๋ก ์ง€์ •

  • Propagation propagation() default Propagation.REQUIRED;

    • ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ ์˜ต์…˜

  • Isolation isolation() default Isolation.DEFAULT;

    • ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€ ์ง€์ •

    • ๊ธฐ๋ณธ๊ฐ’์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ • ๊ธฐ์ค€(DEFAULT)

    • ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ์ง์ ‘ ์ง€์ •ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋“œ๋ฌพ (์ฐธ๊ณ )

      • DEFAULT : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์„ค์ •ํ•œ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ๋”ฐ๋ฅธ๋‹ค.

      • READ_UNCOMMITTED : ์ปค๋ฐ‹๋˜์ง€ ์•Š์€ ์ฝ๊ธฐ

      • READ_COMMITTED : ์ปค๋ฐ‹๋œ ์ฝ๊ธฐ

      • REPEATABLE_READ : ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ์ฝ๊ธฐ

      • SERIALIZABLE : ์ง๋ ฌํ™” ๊ฐ€๋Šฅ

  • int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    • ํŠธ๋žœ์žญ์…˜ ์ˆ˜ํ–‰ ์‹œ๊ฐ„์— ๋Œ€ํ•œ ํƒ€์ž„์•„์›ƒ์„ ์ดˆ ๋‹จ์œ„๋กœ ์ง€์ •

    • ๊ธฐ๋ณธ ๊ฐ’์€ ํŠธ๋žœ์žญ์…˜ ์‹œ์Šคํ…œ์˜ ํƒ€์ž„์•„์›ƒ

  • String[] label() default {};

    • ํŠธ๋žœ์žญ์…˜ ์• ๋…ธํ…Œ์ด์…˜์— ์žˆ๋Š” ๊ฐ’์„ ์ฝ์–ด์„œ ํŠน์ • ๋™์ž‘์„ ํ•  ๊ฒฝ์šฐ ์‚ฌ์šฉ

    • ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ

  • boolean readOnly() default false;

    • readOnly=true ์˜ต์…˜ ์‚ฌ์šฉ ์‹œ ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ

      • ๋“œ๋ผ์ด๋ฒ„๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋”ฐ๋ผ ์ •์ƒ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์Œ.

    • ์ฝ๊ธฐ์—์„œ ๋‹ค์–‘ํ•œ ์„ฑ๋Šฅ ์ตœ์ ํ™”

    • ํฌ๊ฒŒ ์„ธ ๊ณณ์—์„œ ์ ์šฉ

      • ํ”„๋ ˆ์ž„์›Œํฌ

        • JdbcTemplate: ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ๋ณ€๊ฒฝ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•˜๋ฉด ์˜ˆ์™ธ

        • JPA: ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฝ์šฐ ์ปค๋ฐ‹ ์‹œ์ ์— ํ”Œ๋Ÿฌ์‹œ๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ , ๋ณ€๊ฒฝ์ด ๋ถˆํ•„์š”ํ•˜๋‹ˆ ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ์œ„ํ•œ ์Šค๋ƒ…์ƒท ๊ฐ์ฒด๋„ ์ƒ์„ฑํ•˜์ง€ ์•Š์Œ

      • JDBC ๋“œ๋ผ์ด๋ฒ„

        • ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์˜ˆ์™ธ

        • ์ฝ๊ธฐ, ์“ฐ๊ธฐ(master, slave) ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ตฌ๋ถ„ํ•ด์„œ ์š”์ฒญ

        • DB / ๋“œ๋ผ์ด๋ฒ„ ๋ฒ„์ „์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘

      • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

        • ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฝ์šฐ ์ฝ๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๋ฏ€๋กœ, ๋‚ด๋ถ€์—์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ฐœ์ƒ

์˜ˆ์™ธ์™€ ๋กค๋ฐฑ

๋‚ด๋ถ€์—์„œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„(@Transactional ์ ์šฉ AOP) ๋ฐ–์œผ๋กœ ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ ๊ฒฝ์šฐ -> ์Šคํ”„๋ง ํŠธ๋žœ์žญ์…˜ AOP๋Š” ์˜ˆ์™ธ์˜ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๊ฑฐ๋‚˜ ๋กค๋ฐฑ

  • ์–ธ์ฒดํฌ ์˜ˆ์™ธ(RuntimeException, Error, ๊ทธ ํ•˜์œ„ ์˜ˆ์™ธ) ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ

  • ์ฒดํฌ ์˜ˆ์™ธ(Exception, ๊ทธ ํ•˜์œ„ ์˜ˆ์™ธ) ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹

  • ์ •์ƒ ์‘๋‹ต(๋ฆฌํ„ด) ์‹œ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹

์ฐธ๊ณ 

ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹/๋กค๋ฐฑ ๋กœ๊ทธ ํ™•์ธ์„ ์œ„ํ•œ ์„ค์ •

# ์‚ฌ์šฉ์ค‘์ธ TransactionManager
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG #JPA log
logging.level.org.hibernate.resource.transaction=DEBUG
  • ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ ๋กœ๊ทธ(Creating new transaction with name)์™€ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹/๋กค๋ฐฑ ๋กœ๊ทธ(Committing JPA transaction on EntityManager) ํ™•์ธ

commit

์Šคํ”„๋ง ๊ธฐ๋ณธ์ ์œผ๋กœ ์˜ˆ์™ธ๋ฅผ ์•„๋ž˜ ์ •์ฑ…์— ๋”ฐ๋ฆ„

  • ์ฒดํฌ ์˜ˆ์™ธ / Commit : ๋น„์ฆˆ๋‹ˆ์Šค ์˜ˆ์™ธ (ex. ์ž”๊ณ  ๋ถ€์กฑ ..)

    • ๋น„์ฆˆ๋‹ˆ์Šค ์˜ˆ์™ธ๋Š” ๋ฐ˜๋“œ์‹œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์œผ๋ฏ€๋กœ ์ค‘์š”ํ•˜๊ณ , ์ฒดํฌ ์˜ˆ์™ธ๋ฅผ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Œ.

    • rollbackFor ์˜ต์…˜์„ ์‚ฌ์šฉํ•ด์„œ ๋น„์ฆˆ๋‹ˆ์Šค ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋กค๋ฐฑ ์„ ํƒ ๊ฐ€๋Šฅ

  • ์–ธ์ฒดํฌ ์˜ˆ์™ธ / Rollback : ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์˜ˆ์™ธ (ex. DB ์ ‘๊ทผ ์˜ค๋ฅ˜, SQL ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ ..)

commit

ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ

Spring Transaction Propagation commit and rollback

Spring Transaction Propagation Use transaction twice

Result
  • ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด ํŠธ๋žœ์žญ์…˜1,2๊ฐ€ ๊ฐ™์€ conn0 ์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉ์ค‘์ธ๋ฐ, ์ด๊ฒƒ์€ ์ปค๋„ฅ์…˜ ํ’€ ๋•Œ๋ฌธ์— ๊ทธ๋Ÿฐ ๊ฒƒ

  • ํŠธ๋žœ์žญ์…˜1์€ conn0์„ ๋ชจ๋‘ ์‚ฌ์šฉ ํ›„ ์ปค๋„ฅ์…˜ ํ’€์— ๋ฐ˜๋‚ฉํ•˜๊ณ , ์ดํ›„ ํŠธ๋žœ์žญ์…˜2๊ฐ€ conn0์„ ์ปค๋„ฅ์…˜ ํ’€์—์„œ ํš๋“

  • ํžˆ์นด๋ฆฌ ์ปค๋„ฅ์…˜ ํ’€์—์„œ ์ปค๋„ฅ์…˜์„ ํš๋“ํ•˜๋ฉด ์‹ค์ œ ์ปค๋„ฅ์…˜์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋‚ด๋ถ€ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ํžˆ์นด๋ฆฌ ํ”„๋ก์‹œ ์ปค๋„ฅ์…˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ์ด ๊ฐ์ฒด์˜ ์ฃผ์†Œ๋ฅผ ํ™•์ธํ•˜๋ฉด ์ปค๋„ฅ์…˜ ํ’€์—์„œ ํš๋“ํ•œ ์ปค๋„ฅ์…˜ ๊ตฌ๋ถ„์ด ๊ฐ€๋Šฅ

    • HikariProxyConnection@2120431435 wrapping conn0: ...

    • HikariProxyConnection@1567077043 wrapping conn0: ...

    • ์ปค๋„ฅ์…˜์ด ์žฌ์‚ฌ์šฉ ๋˜์—ˆ์ง€๋งŒ, ๊ฐ๊ฐ ์ปค๋„ฅ์…˜ ํ’€์—์„œ ์ปค๋„ฅ์…˜์„ ์กฐํšŒ

๊ธฐ๋ณธ

Result
  • ํŠธ๋žœ์žญ์…˜์ด ์ง„ํ–‰์ค‘์ธ ์ƒํƒœ์—์„œ ๋‚ด๋ถ€์— ์ถ”๊ฐ€๋กœ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ์Šคํ”„๋ง์€ ์™ธ๋ถ€/๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜์„ ๋ฌถ์–ด์„œ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์„ ์ƒ์„ฑ

    • ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ์˜ ๊ธฐ๋ณธ ์˜ต์…˜์ธ REQUIRED ๊ธฐ์ค€

    • ๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜์€ ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜์— ์ฐธ์—ฌ(์™ธ/๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜์ด ํ•˜๋‚˜์˜ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์ž„)

    • ์˜ต์…˜์„ ํ†ตํ•ด ๋‹ค๋ฅธ ๋™์ž‘๋ฐฉ์‹ ์„ ํƒ ๊ฐ€๋Šฅ

  • ๋…ผ๋ฆฌ ํŠธ๋žœ์žญ์…˜๋“ค์€ ํ•˜๋‚˜์˜ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์ž„

    • ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜: ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ์šฉ๋˜๋Š” ํŠธ๋žœ์žญ์…˜(์‹œ์ž‘, ์ปค๋ฐ‹, ๋กค๋ฐฑ ๋‹จ์œ„)

    • ๋…ผ๋ฆฌ ํŠธ๋žœ์žญ์…˜: ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋‹จ์œ„

  • ๋ชจ๋“  ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๊ฐ€ ์ปค๋ฐ‹๋˜์–ด์•ผ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹

    • ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ผ๋„ ๋กค๋ฐฑ๋˜๋ฉด ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์€ ๋กค๋ฐฑ

  • ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜๋งŒ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘/์ปค๋ฐ‹

    • ์ฒ˜์Œ ์‹œ์ž‘ ํŠธ๋žœ์žญ์…˜์ด ์‹ค์ œ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌ

์›์น™

  • ๋ชจ๋“  ๋…ผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋˜์–ด์•ผ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹

  • ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์ด๋ผ๋„ ๋กค๋ฐฑ๋˜๋ฉด ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์€ ๋กค๋ฐฑ

ํ๋ฆ„

์š”์ฒญ ํ๋ฆ„

Result

์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜

\1. txManager.getTransaction() ํ˜ธ์ถœ๋กœ ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘

\2. ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋Š” ๋ฐ์ดํ„ฐ์†Œ์Šค๋ฅผ ํ†ตํ•ด ์ปค๋„ฅ์…˜ ์ƒ์„ฑ

\3. ์ƒ์„ฑ ์ปค๋„ฅ์…˜์„ ์ˆ˜๋™ ์ปค๋ฐ‹ ๋ชจ๋“œ๋กœ ์„ค์ •(๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘)

\4. ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋Š” ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๋งค๋‹ˆ์ €์— ์ปค๋„ฅ์…˜์„ ๋ณด๊ด€

\5. ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋Š” ํŠธ๋žœ์žญ์…˜์„ ์ƒ์„ฑํ•œ ๊ฒฐ๊ณผ๋ฅผ TransactionStatus์— ๋‹ด์•„์„œ ๋ฐ˜ํ™˜

  • isNewTransaction๋กœ ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜ ์—ฌ๋ถ€ ํ™•์ธ, true

\6. ๋กœ์ง1์ด ์‹คํ–‰๋˜๊ณ  ์ปค๋„ฅ์…˜์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜์ด ์ ์šฉ๋œ ์ปค๋„ฅ์…˜ ํš๋“ ํ›„ ์‚ฌ์šฉ

๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜

\7. txManager.getTransaction() ํ˜ธ์ถœ๋กœ ๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘

\8. ํŠธ๋žœ์žญ์…˜ ๋งค๋„ˆ์ €๋Š” ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ ์กด์žฌ ํ™•์ธ

\9. ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋ฏ€๋กœ ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์— ์ฐธ์—ฌ

  • ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์•„๋ฌด ํ–‰๋™์„ ํ•˜์ง€ ์•Š๊ณ , ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๋งค๋‹ˆ์ €์— ๋ณด๊ด€๋œ ๊ธฐ์กด ์ปค๋„ฅ์…˜ ์‚ฌ์šฉ

\10. isNewTransaction = false

\11. ๋กœ์ง2๊ฐ€ ์‹คํ–‰๋˜๊ณ , ์ปค๋„ฅ์…˜์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜์ด ๋ณด๊ด€ํ•œ ๊ธฐ์กด ์ปค๋„ฅ์…˜ ํš๋“ ํ›„ ์‚ฌ์šฉ

์‘๋‹ต ํ๋ฆ„

Result

๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜

\12. ๋กœ์ง2๊ฐ€ ๋๋‚˜๊ณ  ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹

\13. ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋Š” ์ปค๋ฐ‹ ์‹œ์ ์— ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘

  • ์—ฌ๊ธฐ์„œ๋Š” ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜์ด ์•„๋‹ˆ๋ฏ€๋กœ ์‹ค์ œ ๋ฌผ๋ฆฌ ์ปค๋ฐ‹ ํ˜ธ์ถœ์„ ํ•˜์ง€ ์•Š์Œ

  • ์•„์ง ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜์ง€ ์•Š์Œ

์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜

\14. ๋กœ์ง1์ด ๋๋‚˜๊ณ  ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹

\15. ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋Š” ์ปค๋ฐ‹ ์‹œ์ ์— ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘

  • ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜์€ ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜์ด๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์— ์‹ค์ œ ์ปค๋ฐ‹ ํ˜ธ์ถœ

\16. ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ปค๋ฐ‹์ด ๋ฐ˜์˜๋˜๊ณ , ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜๋„ ๋.

  • ๋…ผ๋ฆฌ์ ์ธ ์ปค๋ฐ‹: ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €์— ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์ด ๋…ผ๋ฆฌ์ ์ธ ์ปค๋ฐ‹์ด๋ผ๋ฉด,

  • ๋ฌผ๋ฆฌ ์ปค๋ฐ‹: ์‹ค์ œ ์ปค๋„ฅ์…˜์— ์ปค๋ฐ‹

ํ๋ฆ„ ํ•ต์‹ฌ

  • ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €์— ์ปค๋ฐ‹์„ ํ•œ๋‹ค๊ณ  ํ•ญ์ƒ ์‹ค์ œ ์ปค๋„ฅ์…˜์— ๋ฌผ๋ฆฌ ์ปค๋ฐ‹์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ

  • ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜์ธ ๊ฒฝ์šฐ์—๋งŒ ์‹ค์ œ ์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉํ•ด์„œ ๋ฌผ๋ฆฌ ์ปค๋ฐ‹/๋กค๋ฐฑ ์ˆ˜ํ–‰

commit

์™ธ/๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ

์™ธ๋ถ€ ๋กค๋ฐฑ

Result
  • ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜์—์„œ ์‹œ์ž‘ํ•œ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์˜ ๋ฒ”์œ„๊ฐ€ ๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜๊นŒ์ง€ ์‚ฌ์šฉ

  • ์ดํ›„ ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋˜๋ฉด์„œ ์ „์ฒด ๋‚ด์šฉ์€ ๋ชจ๋‘ ๋กค๋ฐฑ

commit

๋‚ด๋ถ€ ๋กค๋ฐฑ

Result
  • ๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•˜๋ฉด ์‹ค์ œ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋˜์ง€๋Š” ์•Š๊ณ , ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์— ๋กค๋ฐฑ ์ „์šฉ ๋งˆํฌ ํ‘œ์‹œ

    • Participating transaction failed - marking existing transaction as rollbackonly

  • ์ดํ›„ ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹์„ ํ˜ธ์ถœํ–ˆ์ง€๋งŒ, ์ „์ฒด ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ ์ „์šฉ์œผ๋กœ ํ‘œ์‹œ๋˜์–ด ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ -> UnexpectedRollbackException

    • Global transaction is marked as rollback-only

commit

์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜๊ณผ ๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ

  • REQUIRES_NEW ์˜ต์…˜ ์‚ฌ์šฉ

  • ์™ธ๋ถ€/๋‚ด๋ถ€ ํŠธ๋žœ์žญ์…˜์ด ๊ฐ๊ฐ ๋ณ„๋„์˜ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜(๋ณ„๋„์˜ DB connection ์‚ฌ์šฉ)์„ ๊ฐ€์ง

  • ๊ฐ ํŠธ๋žœ์žญ์…˜์˜ ๋กค๋ฐฑ์ด ์„œ๋กœ์—๊ฒŒ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ

  • ์š”์ฒญ ํ๋ฆ„

  • ์‘๋‹ต ํ๋ฆ„

commit

์ „ํŒŒ ์˜ต์…˜

์‹ค๋ฌด์—์„œ ๋Œ€๋ถ€๋ถ„ REQUIRED ์˜ต์…˜ ์‚ฌ์šฉ, ์•„์ฃผ ๊ฐ€๋” REQUIRES_NEW ์‚ฌ์šฉ

  • ๋‚˜๋จธ์ง€๋Š” ๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋‹ˆ ์ฐธ๊ณ ๋งŒ..

REQUIRED

  • ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ ์„ค์ •(ํŠธ๋žœ์žญ์…˜ ํ•„์ˆ˜)

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ X: ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ O: ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์— ์ฐธ์—ฌ

REQUIRES_NEW

  • ํ•ญ์ƒ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ X: ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ O: ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ

SUPPORT

  • ํŠธ๋žœ์žญ์…˜ ์ง€์›

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ X: ํŠธ๋žœ์žญ์…˜ ์—†์ด ์ง„ํ–‰

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ O: ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ ์ฐธ์—ฌ

NOT_SUPPORT

  • ํŠธ๋žœ์žญ์…˜ ์ง€์›์„ ํ•˜์ง€ ์•Š์Œ

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ X: ํŠธ๋žœ์žญ์…˜ ์—†์ด ์ง„ํ–‰

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ O: ํŠธ๋žœ์žญ์…˜ ์—†์ด ์ง„ํ–‰(๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์€ ๋ณด๋ฅ˜)

MANDATORY

  • ํŠธ๋žœ์žญ์…˜์ด ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•จ

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ X: IllegalTransactionStateException ์˜ˆ์™ธ ๋ฐœ์ƒ

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ O: ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ ์ฐธ์—ฌ

NEVER

  • ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ X: ํŠธ๋žœ์žญ์…˜ ์—†์ด ์ง„ํ–‰

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ O: IllegalTransactionStateException ์˜ˆ์™ธ ๋ฐœ์ƒ

NESTED

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ X: ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ

  • ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜ O: ์ค‘์ฒฉ ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ

  • ์ค‘์ฒฉ ํŠธ๋žœ์žญ์…˜์€ ์™ธ๋ถ€ ํŠธ๋žœ์žญ์…˜์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€๋งŒ, ์ค‘์ฒฉ ํŠธ๋žœ์žญ์…˜์€ ์™ธ๋ถ€์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ

isolation, timeout, readOnly ๋Š” ํŠธ๋žœ์žญ์…˜ ์ฒ˜์Œ ์‹œ์ž‘ ์‹œ์—๋งŒ ์ ์šฉ(์ฐธ์—ฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ ์šฉ๋˜์ง€ ์•Š์Œ)

ํ™œ์šฉ

  • ์˜ˆ์ œ ํ”„๋กœ์ ํŠธ : commit

  • ์ปค๋ฐ‹๊ณผ ๋กค๋ฐฑ : commit

  • ๋‹จ์ผ ํŠธ๋žœ์žญ์…˜ : commit

  • ์ „ํŒŒ ์ปค๋ฐ‹(default REQUIRED) : commit

  • ์ „ํŒŒ ๋กค๋ฐฑ : commit

โญ๏ธ ๋ณต๊ตฌ

REQUIRED์™€ UnexpectedRollbackException

Result
  • LogRepository ์—์„œ ์˜ˆ์™ธ ๋ฐœ์ƒ

  • ์˜ˆ์™ธ๋ฅผ ํ† ์Šคํ•˜๋ฉด LogRepository ์˜ ํŠธ๋žœ์žญ์…˜ AOP๊ฐ€ ํ•ด๋‹น ์˜ˆ์™ธ ์ „๋‹ฌ๋ฐ›์Œ

  • ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜์ด ์•„๋‹ˆ๋ฏ€๋กœ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•˜์ง€ ์•Š๊ณ , ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๋งค๋‹ˆ์ €์— rollbackOnly=true ํ‘œ์‹œ

  • ์ดํ›„ ํŠธ๋žœ์žญ์…˜ AOP๋Š” ์ „๋‹ฌ ๋ฐ›์€ ์˜ˆ์™ธ๋ฅผ ๋ฐ–์œผ๋กœ ํ† ์Šค

  • ์˜ˆ์™ธ๊ฐ€ MemberService ์— ํ† ์Šค๋˜๊ณ , MemberService ๋Š” ํ•ด๋‹น ์˜ˆ์™ธ ๋ณต๊ตฌ ๋ฐ ์ •์ƒ ๋ฆฌํ„ด

  • ์ •์ƒ ํ๋ฆ„์ด ๋˜์—ˆ์œผ๋ฏ€๋กœ MemberService ์˜ ํŠธ๋žœ์žญ์…˜ AOP๋Š” ์ปค๋ฐ‹ ํ˜ธ์ถœ

  • ์ปค๋ฐ‹ ํ˜ธ์ถœ ์‹œ ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜์ด๋ฏ€๋กœ ์‹ค์ œ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹. ์ด๋•Œ!! rollbackOnly ์ฒดํฌ

  • rollbackOnly=true ์ƒํƒœ์ด๋ฏ€๋กœ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ

  • ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋Š” UnexpectedRollbackException ์˜ˆ์™ธ ํ† ์Šค

  • ํŠธ๋žœ์žญ์…˜ AOP๋„ ์ „๋‹ฌ๋ฐ›์€ UnexpectedRollbackException ์„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ํ† ์Šค

commit

REQUIRES_NEW

Result
  • LogRepository ์—์„œ ์˜ˆ์™ธ ๋ฐœ์ƒ

  • ์˜ˆ์™ธ๋ฅผ ํ† ์Šคํ•˜๋ฉด LogRepository ์˜ ํŠธ๋žœ์žญ์…˜ AOP๊ฐ€ ํ•ด๋‹น ์˜ˆ์™ธ ์ „๋‹ฌ๋ฐ›์Œ

  • REQUIRES_NEW ๋ฅผ ์‚ฌ์šฉํ•œ ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜์ด๋ฏ€๋กœ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ ๋ฐ ๋ฐ˜ํ™˜(rollbackOnly ํ‘œ์‹œ X)

  • ์ดํ›„ ํŠธ๋žœ์žญ์…˜ AOP๋Š” ์ „๋‹ฌ ๋ฐ›์€ ์˜ˆ์™ธ๋ฅผ ๋ฐ–์œผ๋กœ ํ† ์Šค

  • ์˜ˆ์™ธ๊ฐ€ MemberService ์— ํ† ์Šค๋˜๊ณ , MemberService ๋Š” ํ•ด๋‹น ์˜ˆ์™ธ ๋ณต๊ตฌ ๋ฐ ์ •์ƒ ๋ฆฌํ„ด

  • ์ •์ƒ ํ๋ฆ„์ด ๋˜์—ˆ์œผ๋ฏ€๋กœ MemberService ์˜ ํŠธ๋žœ์žญ์…˜ AOP๋Š” ์ปค๋ฐ‹ ํ˜ธ์ถœ

  • ์ปค๋ฐ‹ ํ˜ธ์ถœ ์‹œ ์‹ ๊ทœ ํŠธ๋žœ์žญ์…˜์ด๋ฏ€๋กœ ์‹ค์ œ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹. rollbackOnly๊ฐ€ ์ฒดํฌ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ๋ฐ ์ •์ƒ ํ๋ฆ„ ๋ฐ˜ํ™˜

commit

REQUIRED ์ ์šฉ ์‹œ ๋…ผ๋ฆฌ ํŠธ๋žœ์žญ์…˜์ด ํ•˜๋‚˜๋ผ๋„ ๋กค๋ฐฑ๋˜๋ฉด ๊ด€๋ จ ๋ฌผ๋ฆฌ ํŠธ๋žœ์žญ์…˜ ๋ชจ๋‘ ๋กค๋ฐฑ

-> REQUIRES_NEW๋ฅผ ํ†ตํ•œ ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ๋กœ ํ•ด๊ฒฐ (๋‹จ, ํ•˜๋‚˜์˜ HTTP ์š”์ฒญ์— 2๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋‹จ์ )

-> ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•˜๋‹ค๋ฉด ํŠธ๋žœ์žญ์…˜์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ์ˆ˜๋„ ์žˆ์Œ

Last updated