์ด ๋ฌธ์ ํด๊ฒฐํ๋ ค๊ณ ๊ฑฐ์ 5์๊ฐ๋์ ๋๋ฒ๊น ํด์ ์์ธ ์ฐพ์๋๋๋ฐ, ์๊ณ ๋ณด๋๊น 1์ 26์ผ์ธ๊ฐ(๋ถ๊ณผ ํ๋ฌ ์ ) ํด๊ฒฐ๋์ด ์๋๋ผ๊ตฌ์..
๋ค๋ง ์์ง ์คํ๋ง๋ถํธ jpa dependency ๋ฒ์ ์ ๋ฐ์์ด ์๋์ด์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค๋ ๊ฒ์ ๋ณด๊ณ ๊ทธ๋ฅ ์ฌํผํ๋ ์ค์ ๋๋ค.
์๋ง ๊ณง ํด๊ฒฐ๋ ๊ฒ ๊ฐ์๋ฐ, ํด๋น ์ค๋ฅ๋ Hibernate 6.2 ์ด์ ๋ฒ์ ์์ ๋ฐ์ํฉ๋๋ค.
๐ง ๋ฌธ์ ์ํฉ ์ฌํ
์๋์ ๊ฐ์ด 1๋ ๋ค ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ฐ์ง One๊ณผ Many๋ฅผ ์ ์ํ์์ต๋๋ค.
@Entity
public class One {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "one")
private List<Many> manys = new ArrayList<>();
public Long id() {
return id;
}
public List<Many> manys() {
return manys;
}
public void saveMany() {
manys.add(new Many(this));
}
}
@Entity
public class Many {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "one_id")
private One one;
public Many() {
}
public Many(final One one) {
this.one = one;
}
public Many(final One one, final String d) {
this.one = one;
}
public Long id() {
return id;
}
public One one() {
return one;
}
}
application.yml ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
logging:
level:
root: info
org:
hibernate:
orm.jdbc.bind: trace
spring:
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
default_batch_fetch_size: 30
hibernate:
ddl-auto: create
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํด ์ฃผ๊ฒ ์ต๋๋ค.
@SpringBootTest
@Transactional
class ManyTest {
@Autowired
private EntityManager em;
@Autowired
private OneRepository oneRepository;
@Test
void ddd() {
One one1 = new One();
one1.saveMany();
oneRepository.save(one1);
em.flush();
em.clear();
System.out.println("========================");
one1 = oneRepository.findById(one1.id()).get();
System.out.println("Many ํฌ๊ธฐ: " + one1.manys().size());
}
}
์ ์ฝ๋์ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.

In ์ ์ BatchSize ๋งํผ์ ์ค๋ณต๋ ID๊ฐ ๋ฐ์ธ๋ฉ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
์ด์ ๊ด๋ จ๋ ๋ฌธ์ ๋ฅผ ์ฐพ๊ธฐ ์ํด ๊ตฌ๊ธ๋ง๊ณผ chatGPT๊น์ง ์ฌ์ฉํด ๋ณด์์ง๋ง ๊ฒฐ๊ตญ ํด๋ต์ ์ฐพ์ ์ ์์ด์ ๋๋ฒ๊น ์ ์๋ํ์ต๋๋ค.
(๋๋ฒ๊น ์ ์์ด๋ฆฌ ์คํต๋๋๊ฑด์ง... ์ง์ง ์ฐพ๋๋ฐ ๋๋ฌด ํ๋ค์์ด์ใ ใ )
์์ง๋ ์ด๋ป๊ฒ ์์ธ์ ์ฐพ์๋๋์ง๋ ๋ชจ๋ฅด๊ฒ ์ง๋ง, Hibernate ๊ด๋ จํด์ breakpoint๋ฅผ ์ด๊ณณ ์ ๊ณณ ๋ค ๊ฑธ๋ค ๋ณด๋ ๊ฒฐ๊ตญ
CollectionLoaderBatchKey์ ๋๋ฌํ ์ ์์์ต๋๋ค.
๐ง CollectionLoaderBatchKey
๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ํ๋ ํด๋์ค์ ๋๋ค.
A one-time use CollectionLoader for applying a batch fetch
ํด๋น ํด๋์ค์ load ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ๋์ด ์์ต๋๋ค.

์ค์ํ ๋ถ๋ถ์ ์ฃผํฉ์ ๋ฐ์ค๋ก ํ์ํด ๋์๋๋ฐ์,
์กฐํํ ID๊ฐ 1๊ฐ์ธ ๊ฒฝ์ฐ numberOfKeysToLoad๊ฐ batchSize๋งํผ ์ค์ ๋๊ธฐ ๋๋ฌธ์ ์์ ๊ฐ์ด In์ ์ ํ๋์ Id๊ฐ ์ค๋ณต๋์ด ๋ฐ์ํ๋ ๊ฒ์ ๋๋ค.
์ด๋ ํด๋น ์ปค๋ฐ์์ ํด๊ฒฐ๋์์ผ๋, ์คํ๋ง๋ถํธ jpa dependency ๋ฒ์ ์ผ๋ก๋ ์์ง ์ ์ฉ๋์ง ์์์ต๋๋ค.
https://github.com/hibernate/hibernate-orm/pull/5991
HHH-16043 Hibernate 6.x breaks collection batch fetching by mbladel ยท Pull Request #5991 ยท hibernate/hibernate-orm
https://hibernate.atlassian.net/browse/HHH-16043
github.com
์๋ง ๊ณง ํด๊ฒฐ๋ ๊ฒ ๊ฐ์ผ๋, ํน์ ์ง๊ธ ๋ฐ๋ก ํด๊ฒฐํ๊ณ ์ถ์ผ์๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์์กด์ฑ์ ์ถ๊ฐํด ์ฃผ์๋ฉด ํด๊ฒฐ ๊ฐ๋ฅํฉ๋๋ค.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// ์ถ๊ฐ
implementation 'org.hibernate.orm:hibernate-core:6.2.0.CR2'
// ์ด์ธ ์์กด์ฑ์ ์๋ต
...
}
์ถ๊ฐ ์ดํ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ ์๋ํฉ๋๋ค.

'๐๏ธ Spring > JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ด ๋ฌธ์ ํด๊ฒฐํ๋ ค๊ณ ๊ฑฐ์ 5์๊ฐ๋์ ๋๋ฒ๊น ํด์ ์์ธ ์ฐพ์๋๋๋ฐ, ์๊ณ ๋ณด๋๊น 1์ 26์ผ์ธ๊ฐ(๋ถ๊ณผ ํ๋ฌ ์ ) ํด๊ฒฐ๋์ด ์๋๋ผ๊ตฌ์..
๋ค๋ง ์์ง ์คํ๋ง๋ถํธ jpa dependency ๋ฒ์ ์ ๋ฐ์์ด ์๋์ด์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค๋ ๊ฒ์ ๋ณด๊ณ ๊ทธ๋ฅ ์ฌํผํ๋ ์ค์ ๋๋ค.
์๋ง ๊ณง ํด๊ฒฐ๋ ๊ฒ ๊ฐ์๋ฐ, ํด๋น ์ค๋ฅ๋ Hibernate 6.2 ์ด์ ๋ฒ์ ์์ ๋ฐ์ํฉ๋๋ค.
๐ง ๋ฌธ์ ์ํฉ ์ฌํ
์๋์ ๊ฐ์ด 1๋ ๋ค ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ฐ์ง One๊ณผ Many๋ฅผ ์ ์ํ์์ต๋๋ค.
@Entity
public class One {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "one")
private List<Many> manys = new ArrayList<>();
public Long id() {
return id;
}
public List<Many> manys() {
return manys;
}
public void saveMany() {
manys.add(new Many(this));
}
}
@Entity
public class Many {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "one_id")
private One one;
public Many() {
}
public Many(final One one) {
this.one = one;
}
public Many(final One one, final String d) {
this.one = one;
}
public Long id() {
return id;
}
public One one() {
return one;
}
}
application.yml ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
logging:
level:
root: info
org:
hibernate:
orm.jdbc.bind: trace
spring:
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
default_batch_fetch_size: 30
hibernate:
ddl-auto: create
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํด ์ฃผ๊ฒ ์ต๋๋ค.
@SpringBootTest
@Transactional
class ManyTest {
@Autowired
private EntityManager em;
@Autowired
private OneRepository oneRepository;
@Test
void ddd() {
One one1 = new One();
one1.saveMany();
oneRepository.save(one1);
em.flush();
em.clear();
System.out.println("========================");
one1 = oneRepository.findById(one1.id()).get();
System.out.println("Many ํฌ๊ธฐ: " + one1.manys().size());
}
}
์ ์ฝ๋์ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.

In ์ ์ BatchSize ๋งํผ์ ์ค๋ณต๋ ID๊ฐ ๋ฐ์ธ๋ฉ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
์ด์ ๊ด๋ จ๋ ๋ฌธ์ ๋ฅผ ์ฐพ๊ธฐ ์ํด ๊ตฌ๊ธ๋ง๊ณผ chatGPT๊น์ง ์ฌ์ฉํด ๋ณด์์ง๋ง ๊ฒฐ๊ตญ ํด๋ต์ ์ฐพ์ ์ ์์ด์ ๋๋ฒ๊น ์ ์๋ํ์ต๋๋ค.
(๋๋ฒ๊น ์ ์์ด๋ฆฌ ์คํต๋๋๊ฑด์ง... ์ง์ง ์ฐพ๋๋ฐ ๋๋ฌด ํ๋ค์์ด์ใ ใ )
์์ง๋ ์ด๋ป๊ฒ ์์ธ์ ์ฐพ์๋๋์ง๋ ๋ชจ๋ฅด๊ฒ ์ง๋ง, Hibernate ๊ด๋ จํด์ breakpoint๋ฅผ ์ด๊ณณ ์ ๊ณณ ๋ค ๊ฑธ๋ค ๋ณด๋ ๊ฒฐ๊ตญ
CollectionLoaderBatchKey์ ๋๋ฌํ ์ ์์์ต๋๋ค.
๐ง CollectionLoaderBatchKey
๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ํ๋ ํด๋์ค์ ๋๋ค.
A one-time use CollectionLoader for applying a batch fetch
ํด๋น ํด๋์ค์ load ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ๋์ด ์์ต๋๋ค.

์ค์ํ ๋ถ๋ถ์ ์ฃผํฉ์ ๋ฐ์ค๋ก ํ์ํด ๋์๋๋ฐ์,
์กฐํํ ID๊ฐ 1๊ฐ์ธ ๊ฒฝ์ฐ numberOfKeysToLoad๊ฐ batchSize๋งํผ ์ค์ ๋๊ธฐ ๋๋ฌธ์ ์์ ๊ฐ์ด In์ ์ ํ๋์ Id๊ฐ ์ค๋ณต๋์ด ๋ฐ์ํ๋ ๊ฒ์ ๋๋ค.
์ด๋ ํด๋น ์ปค๋ฐ์์ ํด๊ฒฐ๋์์ผ๋, ์คํ๋ง๋ถํธ jpa dependency ๋ฒ์ ์ผ๋ก๋ ์์ง ์ ์ฉ๋์ง ์์์ต๋๋ค.
https://github.com/hibernate/hibernate-orm/pull/5991
HHH-16043 Hibernate 6.x breaks collection batch fetching by mbladel ยท Pull Request #5991 ยท hibernate/hibernate-orm
https://hibernate.atlassian.net/browse/HHH-16043
github.com
์๋ง ๊ณง ํด๊ฒฐ๋ ๊ฒ ๊ฐ์ผ๋, ํน์ ์ง๊ธ ๋ฐ๋ก ํด๊ฒฐํ๊ณ ์ถ์ผ์๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์์กด์ฑ์ ์ถ๊ฐํด ์ฃผ์๋ฉด ํด๊ฒฐ ๊ฐ๋ฅํฉ๋๋ค.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// ์ถ๊ฐ
implementation 'org.hibernate.orm:hibernate-core:6.2.0.CR2'
// ์ด์ธ ์์กด์ฑ์ ์๋ต
...
}
์ถ๊ฐ ์ดํ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ ์๋ํฉ๋๋ค.
