greenrobot/greenDAO · error · DaoException

JOINs are not supported for DELETE queries

Error message

JOINs are not supported for DELETE queries

What it means

DELETE statements in SQL cannot reference joined tables, so QueryBuilder.buildDelete() rejects any builder that has joins registered. The library throws a DaoException early rather than emitting invalid SQL. DeleteQuery only supports single-table deletes with where conditions.

Solutions

  1. Rewrite the delete using a subquery-style condition (e.g. where(Property.in(...query with join...))) — build the joined select as a separate query collecting keys, then delete with where(key.in(keys)).
  2. Execute raw SQL via the database handle if a correlated multi-table delete is required.
  3. Loop over the joined SELECT results and delete each entity individually (slower but simple).

Example fix

// before
QueryBuilder<User> qb = userDao.queryBuilder();
qb.join(DeptDao.class, UserDao.Properties.DeptId)
  .where(DeptDao.Properties.Name.eq("old"));
qb.buildDelete(); // DaoException
// after
List<Long> ids = userDao.queryBuilder()
  .join(DeptDao.class, UserDao.Properties.DeptId)
  .where(DeptDao.Properties.Name.eq("old"))
  .list() // collect matching users
  .stream().map(User::getId).collect(toList());
userDao.deleteByKeyInTx(ids);
Defensive patterns

Strategy: fallback

Try / catch

try { qb.buildDelete().executeDeleteWithoutDetachingEntities(); } catch (DaoException e) { deleteByKeyInTx(collectKeysViaJoinedSelect()); }

Prevention

When it happens

Trigger: Calling queryBuilder.join(...)...buildDelete() — i.e. buildDelete() on a QueryBuilder where joins is non-empty (from join(Class, Property) calls).

Common situations: Developers write a SELECT with a JOIN to filter rows, then call buildDelete() on the same builder expecting it to delete the matched rows; joins are valid for select queries only.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of greenrobot/greenDAO@0bbb338e17 (2026-09-08). Data as JSON: /api/errors/48e61bf4dec82f16. Report an issue: GitHub.

Appendix: source

Thrown at DaoCore/src/main/java/org/greenrobot/greendao/query/QueryBuilder.java:348

        int offsetPosition = -1;
        if (offset != null) {
            if (limit == null) {
                throw new IllegalStateException("Offset cannot be set without limit");
            }
            builder.append(" OFFSET ?");
            values.add(offset);
            offsetPosition = values.size() - 1;
        }
        return offsetPosition;
    }

    /**
     * Builds a reusable query object for deletion (Query objects can be executed more efficiently than creating a
     * QueryBuilder for each execution.
     */
    public DeleteQuery<T> buildDelete() {
        if (!joins.isEmpty()) {
            throw new DaoException("JOINs are not supported for DELETE queries");
        }
        String tablename = dao.getTablename();
        String baseSql = SqlUtils.createSqlDelete(tablename, null);
        StringBuilder builder = new StringBuilder(baseSql);

        // tablePrefix gets replaced by table name below. Don't use tableName here because it causes trouble when
        // table name ends with tablePrefix.
        appendJoinsAndWheres(builder, tablePrefix);

        String sql = builder.toString();
        // Remove table aliases, not supported for DELETE queries.
        // TODO(?): don't create table aliases in the first place.
        sql = sql.replace(tablePrefix + ".\"", '"' + tablename + "\".\"");
        checkLog(sql);

        return DeleteQuery.create(dao, sql, values.toArray());
    }

View on GitHub (pinned to 0bbb338e17)