Home [Development] Transaction (@Transactional / JPA)
Post
Cancel

[Development] Transaction (@Transactional / JPA)

Spring @Transactional

  • Option
    • Isolation Level
    • Propagation
    • no-rollback for
  • Step transaction in Tasklet (job) of Spring batch

Spring JPA

  • Persistence Context
    • @OneToMany, @ManyToMany, @ManyToOne, @OneToOne Option
      • FetchType
      • CascadeType
      • orphanRemoval
    • @Embeddable, @Embedded
    • LockModeType

Spring @Transactional


Isolation Level

  • Default

  • READ_UNCOMMITTED
    can read uncommited datas.
    • allows dirty read (it can occur bad data consistency)
    • prevents nothing.
    • READ_UNCOMMITTED setting is the fastest.
  • READ_COMMITTED (often used)
    can read committed datas.
    • allows non-repeatable read
    • prevents just one, Dirty reads
  • REPEATABLE_READ
    • allows phantom read (= repeatable read)
    • prevents two anomalies: Dirty reads, Non-repeatable reads
  • SERIALIZABLE
    • prevents all three anomalies: Dirty reads, Non-repeatable reads and Phantom reads
    • makes transactions very slow
  • (weak) Read Uncommited < Read Committed < Repeatable Read < Serializable (strong)

  • if you want to use more high isolation level, use LockModeType

Screen Shot 2022-09-01 at 3 36 31 PM

isolation level


Dirty Read VS Phantom Read VS Non-repeatable Read

Dirty ReadPhantom Read (= repeatable read)Non-repeatable Read
read UNCOMMITED data from another transaction.read COMMITTED data from an UPDATE query from another transaction.read COMMITTED data from an INSERT or DELETE query from another transaction.

Dirty Checking

  • JPA์—์„œ๋Š” Entity๋ฅผ ์กฐํšŒํ•˜๋ฉด ํ•ด๋‹น Entity์˜ ์กฐํšŒ ์ƒํƒœ ๊ทธ๋Œ€๋กœ๋ฅผ Snapshot์„ ๋งŒ๋“ค์–ด ๋†“์Œ
  • ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜๋Š” ์‹œ์ ์— ํ•ด๋‹น Snapshot๊ณผ ๋น„๊ตํ•ด ๋‹ค๋ฅธ ์ ์ด ์žˆ๋‹ค๋ฉด Update Query๋ฅผ DB์— ์ „๋‹ฌํ•จ

reference : https://jojoldu.tistory.com/415


Propagation

1
2
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Transactional(propagation = Propagation.NEVER)
1
2
if any component or service will or will not participate in transaction 
and how will it behave if the calling component/service already has or does not have a transaction created already.
  • REQUIRED (default)
    Support a current transaction; create a new one if none exists.
    • ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰ํ•˜๋ฉฐ ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜์ด ์—†์„ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์ƒ์„ฑ
  • SUPPORTS
    Support a current transaction; execute non-transactionally if none exists.
    • ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰ํ•˜๋ฉฐ ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜์ด ์—†์„ ๊ฒฝ์šฐ nontransactionally๋กœ ์‹คํ–‰
  • MANDATORY
    Support a current transaction; throw an exception if no current transaction exists.
    • ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰๋˜๋ฉฐ ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜์ด ์—†์„ ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒ
  • REQUIRES_NEW
    Create a new transaction, suspending the current transaction if one exists.
    • ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜์„ ๋ฌด์‹œํ•˜๊ณ  ๋ฌด์กฐ๊ฑด ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์ด ์ƒ์„ฑ
  • NOT_SUPPORTED
    Do not support a current transaction; rather always execute non-transactionally.
    • nontransactionally๋กœ ์‹คํ–‰ํ•˜๋ฉฐ ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰๋  ๊ฒฝ์šฐ ์ผ์‹œ ์ •์ง€
  • NEVER
    Do not support a current transaction; throw an exception if a current transaction exists.
    • nontransactionally๋กœ ์‹คํ–‰๋˜๋ฉฐ ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•œ๋‹ค๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒ
  • NESTED
    Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED otherwise.
    • ๋‘˜๋Ÿฌ์‹ผ ํŠธ๋žœ์žญ์…˜์ด ์—†์„ ๊ฒฝ์šฐ REQUIRED์™€ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™. ํ•ด๋‹น ๋ฉ”์„œ๋“œ๊ฐ€ ๋ถ€๋ชจ ํŠธ๋žœ์žญ์…˜์—์„œ ์ง„ํ–‰๋  ๊ฒฝ์šฐ ๋ณ„๊ฐœ๋กœ ์ปค๋ฐ‹๋˜๊ฑฐ๋‚˜ ๋กค๋ฐฑ๋  ์ˆ˜ ์žˆ์Œ.

reference : https://jsonobject.tistory.com/467


no-rollback for

1
@Transactional(noRollbackFor = RuntimeException.class)
  • ํŠน์ • ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ๋กค๋ฐฑ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •

Read only

1
@Transactional(readOnly = true)
  • Entity๋ฅผ read only๋กœ ์กฐํšŒํ•˜๋ฉด, ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ์œ„ํ•œ snapshot์„ ์œ ์ง€ํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ณ , ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ flushํ•˜์ง€ ์•Š์•„๋„ ๋˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Œ.

  • other way : scala type in query

    1
    
    select o.id, o.name from Order o
    

Spring JPA


Persistence Context

1
the first-level cache where all the entities are fetched from the database or saved to the database.
  • Persistence context keeps track of any changes made into a managed entity.
  • If anything changes during a transaction, then the entity is marked as dirty. When the transaction completes, these changes are flushed into persistent storage.
  • An EntityManager is associated with a persistence context.
  • It sits between our application and persistent storage.

  • PersistenceContextType
    • Transaction-scoped persistence context (default)
    • If one exists, then it will be used. Otherwise, it will create a persistence context.
      1
      2
      
      @PersistenceContext
      private EntityManager em;
      
    • Extended-scoped persistence context
    • it can span across multiple transactions. We can persist the entity without the transaction but cannot flush it without a transaction.
    1
    
    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    

    Screen Shot 2022-09-01 at 2 16 51 PM Screen Shot 2022-09-01 at 2 16 45 PM

reference : https://www.baeldung.com/jpa-hibernate-persistence-context


EntityManager

1
2
3
4
5
6
7
8
@PersistenceContext
private EntityManager em;

Member member = em.find(Member.class, "member1"); // get entity
transaction.commit();
em.close(); // End persistence context


@OneToMany, @ManyToMany, @ManyToOne, @OneToOne Option

  • FetchType
    see next part
  • CASCADE
    to inherit persistence
    • CascadeType
      • ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH
  • orphanRemoval
    to remove child entity together when the parent entity is not related anymore
    • orpahnRemoval = true
      remove the related child entity automatically

example

1
2
3
4
5
6
7
8
9
10
11
12
// can add or remove child entity when the parent entity is added or removed
@OneToMany(mappedBy = "parentClassName", cascade = {CascadeType.ALL}, orphanRemoval = true)
private List<> ... = ...;


Parent parent = em.find(Parent.class, parentld);

// CascadeType.ALL =
parent.addChild(child); 

// orphanRemoval = true =
parent.getChildren().remove(object); 

FetchType

  • Lazy Loading (Recommend)
    delays the initialization of a resource. (get proxy)
    • the default of @OneToMany, @ManyToMany
      • (optional = false) : inner join
      • (optional = true) : outer join

example

1
2
3
4
5
6
7
8
9
10
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID", nullable = false)
private Team team;

Member member = em.find(Member.class, "member1");
Team team = member.getTeam(); // proxy object
team.getName(); // use real object (search data from DB on this time)

// proxy
Member member = em.getReference(Member.class, "member1");
  • find() : If there is no entity in the persistence context, search data from database.

  • Eager Loading
    initializes or loads a resource as soon as the code is executed. (get data immediately)
    • often use it with JOIN query
    • the default of @ManyToOne, @OneToOne
      • (optional = false) : outer join
      • (optional = true) : inner join

example

1
2
3
4
5
6
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID", nullable = false)
private Team team;

Member member = em.find(Member.class, "member1");
Team team = member.getTeam(); // get real object

@Embeddable, @Embedded

  • @Embeddable
    use on the defined value
  • @Embedded
    use on the being used value

example

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
public class Member {
  String name;

  @Embedded
  Address address;
}

@Embeddable
public class Address {
  String city;
  String street;
}
  • you can use @AttributeOverride if there are duplicated properties.

LockModeType

control DB concurrency

  • READ (= OPTIMISTIC)

  • WRITE (= OPTIMISTIC_FORCE_INCREMENT)

  • OPTIMISTIC (= READ)

  • OPTIMISTIC_FORCE_INCREMENT (= WRITE)

  • PESSIMISTIC_READ

  • PESSIMISTIC_WRITE

  • PESSIMISTIC_FORCE_INCREMENT

  • NONE

Optimistic

  • assume transaction will not be occured
  • how to apply : Use @Version in JPA

  • Entity ๋‚ด๋ถ€์— @Version์ด ๋ถ™์€ int, Integer, long, Long, short, Short, Timestamp Type์˜ ํ•„๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์ ์šฉ๋จ (ํ•œ ๊ฐœ์˜ ํ•„๋“œ๋งŒ ํ—ˆ์šฉ)

  • OPTIMISTIC
    • ์ฝ๊ธฐ ์‹œ์—๋„ ๏ผ Version ์†์„ฑ์„ ์ฒดํฌํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Œ์„ ๋ณด์žฅ (Dirty Read ๋ฐฉ์ง€)
  • OPTIMISTIC_FORCE_INCREMENT
    • @Version ์ •๋ณด๋ฅผ ๊ฐ•์ œ๋กœ ์ฆ๊ฐ€์‹œํ‚ด

Pessimistic

  • assume transaction will be occured
  • transaction will wait before getting Lock
  • how to apply : Use Lock on DB

  • PESSIMISTIC_WRITE (often used in PESSIMISTIC LOCK)
    • select for update
    • lock์ด ๊ฑธ๋ฆฐ row๋Š” ๋‹ค๋ฅธ transaction์ด read, update, delete ํ•  ์ˆ˜ ์—†์Œ (prevent non-repeatable read)
  • PESSIMISTIC_READ
    • only read / update, delete๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ / for share
  • PESSIMISTIC_FORCE_INCREMENT
    • Use @Version / ์ž ๊ธˆ์„ ํš๋“ํ•  ์‹œ @Version์ด ์—…๋ฐ์ดํŠธ๋จ / for update nowait

Recommend JPA Transaction

  • Read committed transaction + Optimistic Version management

reference : https://velog.io/@lsb156/JPA-Optimistic-Lock-Pessimistic-Lock


Primary Cache, Secondary Cache

  • primary cache
    in persistence context
  • secondary cache
    in application before terminating application
  • @Cacheable(cacheNames = "")
    enable caching
    • first check the cache before actually invoking the method and then caching the result.
    • must have the return value
  • @CacheEvict(cacheNames = {..., ...})
    to indicate the removal of one or more/all values so that fresh values can be loaded into the cache again.
    • the return value is not needed
    • @CacheEvict(value="...", allEntries=true)
      this will clear all the entries in the caches and prepare it for new data.
  • @CachePut
    we can update the content of the cache without interfering with the method execution. That is, the method will always be executed and the result cached:
    • @CachePut will actually run the method and then put its results in the cache, whereas @Cacheable will skip running the method and first check the cache.
  • @Caching
    • @Caching(evict = {โ€ฆ}), group multiple caching annotations
  • @CacheConfig
    we can streamline some of the cache configuration into a single place at the class level, so that we donโ€™t have to declare things multiple times
  • AopContext.currentProxy()
    • to use this proxy, declare @EnableAspectJAutoProxy(exposeProxy = true) in configuration
    • @EnableCaching in CachingConfig
      • After we enable caching, for the minimal setup, we must register a cacheManager
    • @Aspect
1
2
3
4
5
6
7
8
9
10
11
12
// @Cacheable
@Cacheable(cacheNames = "member")
public List<MemberDto> getMembers() {
  return api.getMembers();
}

// AopContext.currentProxy()
public List<MemberDto> getMembersOver30age() {
  return AopContext.currentProxy().getMembers()
    .stream().filter(age -> age > 30)
    .collect(Collectors.toList());
}
1
2
3
4
5
6
7
8
// Api
@CacheEvict(cacheNames = MEMBER)
@PostMapping
String registerMember(Member member);

@CacheEvict(cacheNames = MEMBER)
@PutMapping
void modifyMember(String memberCode, Member member);

reference : https://www.baeldung.com/spring-cache-tutorial


EntityTransaction

  • public void begin();

  • public void commit();

  • public void rollback();

  • public void setRollbackOnly();

  • public boolean getRollbackOnly();

  • public boolean isActive();


Word

  • Data Consistency : ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ (๋ฐ์ดํ„ฐ ๊ฐ’์ด ์„œ๋กœ ์ผ์น˜ํ•˜๋Š” ์ƒํƒœ)
  • Data Integrity : ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ (๋ฐ์ดํ„ฐ ๊ฐ’์ด ์ •ํ™•ํ•œ ์ƒํƒœ)
This post is licensed under CC BY 4.0 by the author.

[Development] Swagger

[Development] Kubernetes

Comments powered by Disqus.