elunez/eladmin · error · EntityExistException

Menu with componentName {} existed

Error message

Menu with componentName {} existed

What it means

EntityExistException thrown in MenuServiceImpl.create when componentName is non-blank and another menu already uses it. componentName is the Vue keep-alive route name, so eladmin enforces global uniqueness to prevent two routes registered under the same component name, which would break tag-view caching and navigation.

Source

Thrown at eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MenuServiceImpl.java:128

        if (CollUtil.isEmpty(menus)){
            List<RoleSmallDto> roles = roleService.findByUsersId(currentUserId);
            Set<Long> roleIds = roles.stream().map(RoleSmallDto::getId).collect(Collectors.toSet());
            LinkedHashSet<Menu> data = menuRepository.findByRoleIdsAndTypeNot(roleIds, 2);
            menus = new ArrayList<>(data);
            redisUtils.set(key, menus, 1, TimeUnit.DAYS);
        }
        return menus.stream().map(menuMapper::toDto).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(Menu resources) {
        if(menuRepository.findByTitle(resources.getTitle()) != null){
            throw new EntityExistException(Menu.class,"title",resources.getTitle());
        }
        if(StringUtils.isNotBlank(resources.getComponentName())){
            if(menuRepository.findByComponentName(resources.getComponentName()) != null){
                throw new EntityExistException(Menu.class,"componentName",resources.getComponentName());
            }
        }
        if (Long.valueOf(0L).equals(resources.getPid())) {
            resources.setPid(null);
        }
        if(resources.getIFrame()){
            if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE)||resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) {
                throw new BadRequestException(BAD_REQUEST);
            }
        }
        menuRepository.save(resources);
        // 计算子节点数目
        resources.setSubCount(0);
        // 更新父节点菜单数目
        updateSubCnt(resources.getPid());
    }

    @Override

View on GitHub (pinned to 55fbf70595)

Solutions

  1. Give the new menu a distinct componentName matching the actual Vue component's name in the front-end project.
  2. Leave componentName blank if the page does not need keep-alive caching — the check is skipped for blank values.
  3. Search existing menus (GET /api/menus) for the componentName and rename or delete the conflicting entry first.

Example fix

// before: duplicated keep-alive name
menu.setComponentName("UserManagement"); // already used

// after: unique name aligned with the Vue page
menu.setComponentName("OnlineUserManagement");
Defensive patterns

Strategy: validation

Validate before calling

if (StringUtils.isNotBlank(form.componentName)) {
    Menu dup = menuRepository.findByComponentName(form.componentName);
    if (dup != null) throw new IllegalArgumentException("componentName taken: " + dup.getTitle());
}

Try / catch

catch (EntityExistException e) { if (e.getMessage().startsWith("Menu with componentName")) { formErrors.componentName = '已被其他菜单使用'; } }

Prevention

When it happens

Trigger: POST /api/menus with a non-blank componentName equal to an existing menu's componentName (only checked when StringUtils.isNotBlank).

Common situations: Copy-pasting an existing menu definition and editing only the title/path; front-end developers naming two pages with the same Vue component name; seeded menus from SQL dumps colliding with manually added ones.

Related errors


AI-assisted analysis of elunez/eladmin@55fbf70595 (2026-08-14). Data as JSON: /api/errors/bbbd84259ca1d659. Report an issue: GitHub.