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
- @OneToMany, @ManyToMany, @ManyToOne, @OneToOne Option
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
Dirty Read VS Phantom Read VS Non-repeatable Read
Dirty Read | Phantom 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)
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 JPAEntity ๋ด๋ถ์ @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
- After we enable caching, for the minimal setup, we must register a
@Aspect
- to use this proxy, declare
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 : ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ (๋ฐ์ดํฐ ๊ฐ์ด ์ ํํ ์ํ)
Comments powered by Disqus.