hs-web/hsweb-framework · error · IllegalArgumentException
join class [" + clazz + "] not found!
Error message
join class [" + clazz + "] not found!
What it means
DefaultQueryHelper's QuerySpec supports join-by-class: when you call join(Class) (or similar), it searches the list of already-registered joins for one whose mainClass equals the requested class. If no registered join matches, it throws this IllegalArgumentException instead of returning null, forcing the caller to fix the class reference.
Solutions
- Call join(TargetEntity.class) to register the join before referencing it by class.
- Verify the exact class (FQCN/identity) passed to the join-by-class lookup matches the class used when creating the join, including subclasses vs superclasses.
- Inspect the query spec's joins list (debug/toString) to confirm which classes were actually joined.
- If resolving dynamically, wrap the lookup and register the join lazily when the lookup fails.
Example fix
// before
QueryHelper wherever = helper.createQuery(UserEntity.class)
.join(RoleEntity.class);
// RoleEntity was never joined -> IllegalArgumentException
// after
QuerySpec<UserEntity> spec = helper.createQuery(UserEntity.class)
.leftJoin(RoleEntity.class, on -> on.eq("userId", "id"));
spec.join(RoleEntity.class); // now found among registered joins Defensive patterns
Strategy: try-catch
Validate before calling
// Java
boolean joined = spec.getJoins().stream()
.anyMatch(j -> Objects.equals(j.getMainClass(), TargetEntity.class));
if (!joined) { spec.leftJoin(TargetEntity.class, on -> on.eq("id", "targetId")); } Try / catch
// Java
try {
spec.join(TargetEntity.class);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("join class")) {
spec.leftJoin(TargetEntity.class, on -> on.eq("id", "targetId"));
} else { throw e; }
} Prevention
- Always declare joins explicitly before referencing them by class.
- Centralize join definitions in builder constants so class references stay in sync.
- After refactors, grep for join-by-class usages and verify against declared joins.
When it happens
Trigger: Calling QuerySpec.join(SomeEntity.class) (or an overload resolving joins by class) where SomeEntity.class was never registered as a join — e.g. the class was never passed to a previous join() call, or a different entity class (a subclass/superclass or a different type) was actually joined.
Common situations: Typo or refactor renamed the entity class so the queried class no longer matches the joined one; developer assumes joins are implicit via entity relations but they must be declared explicitly; copy-pasted query code referencing a join from another query builder instance.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- join alias [" + alias + "] not found!
- valuesList[" + valuesList + "] must have alias
- 不支持的验证规则:
- 不支持的授权请求:
- parentId
AI-assisted analysis of hs-web/hsweb-framework@b2cfc85a57 (2026-09-13).
Data as JSON: /api/errors/fc5e885ec662b8cb.
Report an issue: GitHub.
Appendix: source
Thrown at hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/query/DefaultQueryHelper.java:535
this.parent = parent;
logContext = Context.of(Logger.class, LoggerFactory.getLogger(clazz));
}
private List<JoinConditionalSpecImpl> joins() {
return joins == null ? joins = new ArrayList<>(3) : joins;
}
private JoinConditionalSpecImpl getJoinByClass(Class<?> clazz) {
if (joins != null) {
for (JoinConditionalSpecImpl join : joins) {
if (Objects.equals(join.mainClass, clazz)) {
return join;
}
}
}
throw new IllegalArgumentException("join class [" + clazz + "] not found!");
}
private JoinConditionalSpecImpl getJoinByAlias(String alias) {
if (joins != null) {
for (JoinConditionalSpecImpl join : joins) {
if (Objects.equals(join.alias, alias)) {
return join;
}
}
}
throw new IllegalArgumentException("join alias [" + alias + "] not found!");
}
@Override
public <From> FromSpec<R> from(Class<From> clazz) {
query = parent
.databaseView on GitHub (pinned to b2cfc85a57)