使用Spring Security与JPA审计时,如何提升批量插入性能?
JPA批量插入审计字段性能优化方案
针对你遇到的批量插入时getCurrentAuditor()重复调用导致的性能问题,以下是几种实用的优化方案:
方案一:线程内缓存审计用户
利用ThreadLocal在同一个请求线程内缓存查询到的当前用户,避免批量插入时重复查询数据库。
修改后的AuditorAwareImpl实现:
@Component @RequiredArgsConstructor public class AuditorAwareImpl implements AuditorAware<User> { private final UserRepository userRepository; private final ThreadLocal<Optional<User>> currentAuditorCache = new ThreadLocal<>(); @Override public Optional<User> getCurrentAuditor() { // 优先从线程缓存获取,无缓存时再查询数据库 Optional<User> cachedAuditor = currentAuditorCache.get(); if (cachedAuditor != null) { return cachedAuditor; } Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Optional<User> auditor = Optional.of(authentication) .map(Authentication::getName) .flatMap(userRepository::findByUsername); // 将查询结果存入线程缓存 currentAuditorCache.set(auditor); return auditor; } // 线程结束后清理缓存,避免线程复用导致的脏数据 @PreDestroy public void clearCache() { currentAuditorCache.remove(); } }
优势:无需改动业务代码,兼容单条和批量操作,同一个线程内仅执行一次用户查询,性能提升明显。
方案二:手动赋值审计字段
在批量导入前主动查询一次当前用户,手动为所有待插入实体设置审计字段,绕过AuditorAware的自动调用逻辑。
示例代码:
@Service @RequiredArgsConstructor public class BatchImportService { private final UserRepository userRepository; private final EntityManager entityManager; public void batchImport(List<YourEntity> entities) { // 仅查询一次当前用户 User currentUser = userRepository.findByUsername( SecurityContextHolder.getContext().getAuthentication().getName() ).orElseThrow(() -> new RuntimeException("当前用户不存在")); // 为所有实体手动设置审计字段 entities.forEach(entity -> { entity.setLastCreatedBy(currentUser); entity.setLastModifiedBy(currentUser); entity.setLastCreatedDate(LocalDateTime.now()); entity.setLastModifiedDate(LocalDateTime.now()); }); // 结合EntityManager实现高效批量插入 int batchSize = 500; for (int i = 0; i < entities.size(); i++) { entityManager.persist(entities.get(i)); if (i % batchSize == 0 && i > 0) { entityManager.flush(); entityManager.clear(); } } entityManager.flush(); entityManager.clear(); } }
优势:完全控制审计字段赋值逻辑,配合EntityManager的批量操作优化,能最大化插入性能,适合明确的批量导入场景。
方案三:缓存用户查询结果
通过Spring Cache缓存findByUsername的查询结果,即使getCurrentAuditor()多次调用,也不会重复查询数据库。
- 启用Spring Cache:在配置类添加
@EnableCaching注解 - 修改
UserRepository添加缓存注解:
@Repository public interface UserRepository extends JpaRepository<User, Long> { @Cacheable(value = "userCache", key = "#username") Optional<User> findByUsername(String username); }
优势:无需修改AuditorAwareImpl代码,通用型优化,同时能提升其他场景下的用户查询性能。
内容的提问来源于stack exchange,提问作者alexsaleh
相关产品推荐
相关产品推荐

