You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Hibernate中批量处理@ManyToMany关联的查询与插入?

Hibernate多对多关联的批量查询与批量插入优化

我定义了User和Role两个实体类,二者为单向@ManyToMany关联,实体代码如下:

User实体类

@Entity
@Table(name = "users")
@Getter
@Setter
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private String firstName;
    private String lastName;
    private String email;
    private String password;
    
    @ManyToMany
    @JoinTable(name = "users_roles",
               joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
               inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
    private Set<Role> roles = new HashSet<>();
}

Role实体类

@Entity
@Table(name = "roles")
@Getter
@Setter
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private String name;
}

在执行更新用户角色的测试方法时:

@Test
@Transactional
void updateUser(){
    User user = userRepository.findById(1L).get();

    List<Integer> roleIds = new LinkedList<>();
    roleIds.add(5);
    roleIds.add(6);
    roleIds.add(7);
    roleIds.add(8);
    roleIds.add(9);
    roleIds.add(10);
    roleIds.add(11);
    roleIds.add(12);
    roleIds.add(13);
    roleIds.add(14);
    roleIds.add(15);
    for(int roleId : roleIds){
        Role role1 = new Role();
        role1.setId(roleId);
        user.getRoles().add(role1);
    }
    userRepository.saveAndFlush(user);
}

调用saveAndFlush(user)后,Hibernate会针对每个roleId执行单独的SELECT语句验证角色存在,同时对关联表users_roles执行多条单独的INSERT语句。由于业务限制无法通过重写isNew方法规避查询,请问如何实现批量查询(用IN语句)和批量插入?


解决方案

1. 实现批量查询Role

当前代码手动创建Role对象并设置ID,Hibernate会将这些对象视为瞬态对象,为了确保关联的是数据库中存在的持久化对象,会逐个执行SELECT查询验证。

优化方式:通过JPA Repository的批量查询方法一次性获取所有目标Role,替代逐个创建对象的逻辑:

步骤1:在RoleRepository中添加批量查询方法

public interface RoleRepository extends JpaRepository<Role, Long> {
    List<Role> findByIdIn(List<Long> roleIds);
}

步骤2:修改测试方法中的角色添加逻辑

@Test
@Transactional
void updateUser(){
    User user = userRepository.findById(1L).get();

    List<Long> roleIds = Arrays.asList(5L,6L,7L,8L,9L,10L,11L,12L,13L,14L,15L);
    // 批量查询所有目标Role,仅执行一条带IN的SELECT语句
    List<Role> targetRoles = roleRepository.findByIdIn(roleIds);
    user.getRoles().addAll(targetRoles);
    
    userRepository.saveAndFlush(user);
}

这样Hibernate只会执行一条SELECT * FROM roles WHERE id IN (5,6,...15)的语句,避免了多次单条查询。

2. 实现关联表的批量插入

默认情况下Hibernate会逐个插入关联表记录,需要通过配置开启JDBC批量操作:

在application.properties(或application.yml)中添加以下配置:

# 开启批量操作,设置批量大小
spring.jpa.properties.hibernate.jdbc.batch_size=20
# 对INSERT语句排序,确保同表的INSERT能被批量处理
spring.jpa.properties.hibernate.order_inserts=true
# 对UPDATE语句排序(可选,按需开启)
spring.jpa.properties.hibernate.order_updates=true
# 批量处理带版本控制的数据(如果实体用了@Version乐观锁则开启)
spring.jpa.properties.hibernate.batch_versioned_data=true

配置后,Hibernate会将多条INSERT INTO users_roles(user_id, role_id) VALUES (?,?)合并成批量语句执行,减少数据库交互次数。


内容的提问来源于stack exchange,提问作者Anand Kadhi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 14:25:12