Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1007,31 +1007,24 @@ public long updateAll(Map params) {

/**
* Return whether we should execute this query in memory.
* <p>
* Section 3.10.8 of the specification scopes the duty of a
* <code>FlushModeType.AUTO</code> query to the updates "which could potentially
* affect the result of the query", and lets the provider discharge it "by flushing
* those entities to the database or by some other means". The access path check
* below is that other means: only an instance of a type the query reads can change
* its result, so a query is never made to flush a transaction's unrelated writes.
* The same section forbids flushing when no transaction is active, which
* {@link Broker#isActive()} covers.
*/
private boolean isInMemory(int operation) {
// if there are any dirty instances in the current trans that are
// involved in this query, we have to execute in memory or flush
boolean inMem = !_storeQuery.supportsDataStoreExecution()
|| _collection != null;
int flush = _fc.getFlushBeforeQueries();

// Per JPA spec, FlushModeType.AUTO (FLUSH_TRUE) requires that
// pending changes are visible to queries. The access path check
// may miss dirty entities reachable via navigation (e.g.,
// o.customer.spouse), so when FLUSH_TRUE is set we check for
// ANY dirty objects rather than only those in the access path.
boolean dirty;
if (flush == FLUSH_TRUE && _broker.isActive()) {
dirty = !_broker.getPersistedTypes().isEmpty()
|| !_broker.getUpdatedTypes().isEmpty()
|| !_broker.getDeletedTypes().isEmpty();
} else {
dirty = isAccessPathDirty();
}

if (!inMem && (!_ignoreChanges || operation != OP_SELECT
|| flush == FLUSH_TRUE)
&& _broker.isActive() && dirty) {
if (!inMem && (!_ignoreChanges || operation != OP_SELECT)
&& _broker.isActive() && isAccessPathDirty()) {
if ((flush == FLUSH_TRUE
|| (flush == FLUSH_WITH_CONNECTION && _broker.hasConnection())
|| operation != OP_SELECT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,26 @@ protected ClassMetaData addAccessPath(ClassMetaData meta) {
return meta;
}

/**
* Register the element and key types of the given field in the query's access path.
* A collection or map field declares Collection/List/Map as its type, which has no
* metadata of its own, so the types such a field reads are its element and key
* types. Registering them keeps the access path complete for a predicate that uses
* the field without declaring a join variable - SIZE(x.coll), x.coll IS EMPTY - and
* mirrors what the Criteria builder records for the same field.
*/
protected void addElementAndKeyAccessPath(FieldMetaData fmd) {
if (fmd == null) {
return;
}
if (fmd.getElement() != null && fmd.getElement().getDeclaredTypeMetaData() != null) {
addAccessPath(fmd.getElement().getDeclaredTypeMetaData());
}
if (fmd.getKey() != null && fmd.getKey().getDeclaredTypeMetaData() != null) {
addAccessPath(fmd.getKey().getDeclaredTypeMetaData());
}
}

/**
* Return the recorded query access path.
*/
Expand Down Expand Up @@ -310,6 +330,12 @@ protected Value traversePath(Path path, String field, boolean pcOnly,
// ex2b). Resolve via the @MapsId @ManyToOne target entity.
meta = resolveMapsIdTargetMeta(fmd);
}
// A collection or map field has no declared type metadata of its own - its
// declared type is Collection/List/Map - so the types it actually reads are its
// element and key types. They have to be registered for predicates that use the
// field without declaring a join variable, such as SIZE(x.coll) or
// x.coll IS EMPTY; without them a dirty element would not be flushed.
addElementAndKeyAccessPath(fmd);
if (meta != null) {
addAccessPath(meta);
path.setMetaData(meta);
Expand Down
Loading
Loading