I have the following entities.
Entity Models:
{code:java}@Entity @Getter @Setter public class Article {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id;
private String categoryId;
@BatchSize(size = 20) @ManyToMany private List<Tag> tags = new ArrayList<>();
}{code}
{code:java}@Entity @Getter @NoArgsConstructor public class Tag { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@Column(unique = true) private String name;
public Tag (String name) { this.name = name; } } {code}
And DTO:
{noformat}@Getter public class ArticleResponseDto {
private final int id; private final List<TagDto> tags;
public ArticleResponseDto(Article article) { this.id = article.getId(); this.tags = article.getTags().stream() .map(TagDto::new) .toList(); } }
@Getter class TagDto {
private final String name;
public TagDto(Tag tag) { this.name = tag.getName(); } }{noformat}
In Hibernate 5.6.14, when querying articles, it generated queries :
{noformat}Hibernate: select article0_.id as id1_0_, article0_.category_id as category2_0_ from article article0_ order by article0_.id DESC limit ? Hibernate: select tags0_.article_id as article_1_1_1_, tags0_.tags_id as tags_id2_1_1_, tag1_.id as id1_2_0_, tag1_.name as name2_2_0_ from article_tags tags0_ inner join tag tag1_ on tags0_.tags_id=tag1_.id where tags0_.article_id in (?, ?, ?, ?, ?){noformat}
However In Hibernate 6.1.6:
{noformat}Hibernate: select t1_0.article_id,t1_1.id,t1_1.name from article_tags t1_0 join tag t1_1 on t1_1.id=t1_0.tags_id where t1_0.article_id in(?,?,?,?,?) Hibernate: select t1_0.article_id,t1_1.id,t1_1.name from article_tags t1_0 join tag t1_1 on t1_1.id=t1_0.tags_id where t1_0.article_id in(?,?) Hibernate: select t1_0.article_id,t1_1.id,t1_1.name from article_tags t1_0 join tag t1_1 on t1_1.id=t1_0.tags_id where t1_0.article_id in(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?){noformat}
Hibernate 6.1.6 generated queries 2 more and binded data in each query were article-id that had empty tag list. And the values in IN clause at the last query was always generated as much as the size of {{@BatchSize}}.
Test Code:
{noformat}@SpringBootTest @Transactional class TestServiceTest {
@Autowired private EntityManager entityManager;
@BeforeEach void fixture() { List<Tag> tags = List.of(new Tag("t1"), new Tag("t2"), new Tag("t3")); List<Tag> tags2 = List.of(new Tag("t4"), new Tag("t5"));
Article article = new Article(); Article article2 = new Article(); Article article3 = new Article(); Article article4 = new Article(); Article article5 = new Article();
article.setTags(tags); article3.setTags(tags2);
tags.forEach(entityManager::persist); tags2.forEach(entityManager::persist);
entityManager.persist(article); entityManager.persist(article2); entityManager.persist(article3); entityManager.persist(article4); entityManager.persist(article5);
entityManager.flush(); entityManager.clear(); }
@Test void test() { Query query = entityManager.createQuery("select a from Article a"); List<Article> tech = query.setMaxResults(20).getResultList(); tech.stream() .map(ArticleResponseDto::new) .toList(); }
}{noformat}
I expected executing queries same in both version, it wasn't.
h3. Reproducer
reproducer: [https://github.com/chlrbgh89/hibernate-batch-size-bug|https://github.com/chlrbgh89/hibernate-batch-size-bug|smart-link]
reproducer Hibernate version is {{6.1.6.Final}}
If you want to downgrade version to {{5.6.14.Final}}, uncomment this code in {{build.gradle}}
{code:groovy}//configurations.configureEach { // resolutionStrategy.eachDependency {details -> // if(details.requested.name == 'hibernate-core') { // details.useTarget group: 'org.hibernate', name:'hibernate-core-jakarta', version: '5.6.14.Final' // } // } //}{code}
Envrionment
* Spring Boot 3.0.1 * Hibernate 6.1.6.Final |
|