Skip to content

Commit 24a01c3

Browse files
authored
Switch to EntityRef for improved entity handling (#1283)
2 parents 74ebe52 + 140dd0e commit 24a01c3

File tree

6 files changed

+91
-40
lines changed

6 files changed

+91
-40
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Doma Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.seasar.doma.jdbc;
17+
18+
import java.util.Objects;
19+
20+
public class EntityRef {
21+
22+
private Object entity;
23+
24+
public EntityRef(Object entity) {
25+
this.entity = Objects.requireNonNull(entity);
26+
}
27+
28+
public Object getEntity() {
29+
return entity;
30+
}
31+
32+
public void setEntity(Object entity) {
33+
this.entity = Objects.requireNonNull(entity);
34+
}
35+
}

doma-core/src/main/java/org/seasar/doma/jdbc/aggregate/AggregateCommand.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.seasar.doma.internal.util.Combinations;
2727
import org.seasar.doma.internal.util.Pair;
2828
import org.seasar.doma.jdbc.EntityId;
29+
import org.seasar.doma.jdbc.EntityRef;
2930
import org.seasar.doma.jdbc.command.Command;
3031
import org.seasar.doma.jdbc.command.SelectCommand;
3132
import org.seasar.doma.jdbc.entity.EntityType;
@@ -59,7 +60,7 @@ public AggregateCommand(
5960
@Override
6061
public RESULT execute() {
6162
Set<EntityId> rootEntityIds = new LinkedHashSet<>();
62-
Map<EntityId, Object> entityCache = new HashMap<>();
63+
Map<EntityId, EntityRef> entityCache = new HashMap<>();
6364
Combinations<AssociationEntityKey> combinations = new Combinations<>();
6465

6566
SelectCommand<List<AssociationEntityPool>> command =
@@ -73,14 +74,15 @@ public RESULT execute() {
7374
entityCache));
7475
List<AssociationEntityPool> entityPools = command.execute();
7576
for (AssociationEntityPool entityPool : entityPools) {
76-
associate(entityCache, combinations, entityPool);
77+
associate(combinations, entityPool);
7778
}
7879

7980
@SuppressWarnings("unchecked")
8081
Stream<ENTITY> stream =
8182
(Stream<ENTITY>)
8283
rootEntityIds.stream()
8384
.map(entityCache::get)
85+
.map(EntityRef::getEntity)
8486
.filter(Objects::nonNull)
8587
.filter(rootEntityType.getEntityClass()::isInstance);
8688

@@ -92,9 +94,7 @@ public RESULT execute() {
9294
* of linkage rules and updates the cache with newly associated entities.
9395
*/
9496
private void associate(
95-
Map<EntityId, Object> entityCache,
96-
Combinations<AssociationEntityKey> combinations,
97-
AssociationEntityPool entityPool) {
97+
Combinations<AssociationEntityKey> combinations, AssociationEntityPool entityPool) {
9898

9999
for (AssociationLinkerType<?, ?> linkerType :
100100
aggregateStrategyType.getAssociationLinkerTypes()) {
@@ -114,10 +114,14 @@ private void associate(
114114
@SuppressWarnings("unchecked")
115115
BiFunction<Object, Object, Object> linker =
116116
(BiFunction<Object, Object, Object>) linkerType.getLinker();
117-
Object newEntity = linker.apply(source.entity(), target.entity());
118-
if (newEntity != null && newEntity != source.entity()) {
119-
entityCache.replace(source.entityId(), newEntity);
120-
entityPool.replace(new AssociationEntityPoolEntry(source.entityKey(), newEntity));
117+
EntityRef sourceEntityRef = source.entityRef();
118+
EntityRef targetEntityRef = target.entityRef();
119+
Object sourceEntity = sourceEntityRef.getEntity();
120+
Object targetEntity = targetEntityRef.getEntity();
121+
Object resultEntity = linker.apply(sourceEntity, targetEntity);
122+
if (resultEntity != null && resultEntity != sourceEntity) {
123+
// Update a reference to an immutable entity
124+
sourceEntityRef.setEntity(resultEntity);
121125
}
122126
}
123127
}

doma-core/src/main/java/org/seasar/doma/jdbc/aggregate/AssociationEntityPoolEntry.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717

1818
import java.util.Objects;
1919
import org.seasar.doma.jdbc.EntityId;
20+
import org.seasar.doma.jdbc.EntityRef;
2021

21-
public record AssociationEntityPoolEntry(AssociationEntityKey entityKey, Object entity) {
22+
public record AssociationEntityPoolEntry(AssociationEntityKey entityKey, EntityRef entityRef) {
2223
public AssociationEntityPoolEntry {
2324
Objects.requireNonNull(entityKey);
24-
Objects.requireNonNull(entity);
25+
Objects.requireNonNull(entityRef);
2526
}
2627

2728
public AssociationPathKey pathKey() {

doma-core/src/main/java/org/seasar/doma/jdbc/aggregate/AssociationEntityPoolIterationHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.seasar.doma.internal.jdbc.command.AbstractIterationHandler;
2323
import org.seasar.doma.internal.jdbc.command.ResultListCallback;
2424
import org.seasar.doma.jdbc.EntityId;
25+
import org.seasar.doma.jdbc.EntityRef;
2526
import org.seasar.doma.jdbc.ObjectProvider;
2627
import org.seasar.doma.jdbc.entity.EntityType;
2728
import org.seasar.doma.jdbc.query.SelectQuery;
@@ -37,14 +38,14 @@ public class AssociationEntityPoolIterationHandler
3738
private final AggregateStrategyType aggregateStrategyType;
3839
private final boolean resultMappingEnsured;
3940
private final Set<EntityId> rootEntityIds;
40-
private final Map<EntityId, Object> entityCache;
41+
private final Map<EntityId, EntityRef> entityCache;
4142

4243
public AssociationEntityPoolIterationHandler(
4344
EntityType<?> rootEntityType,
4445
AggregateStrategyType aggregateStrategyType,
4546
boolean resultMappingEnsured,
4647
Set<EntityId> rootEntityIds,
47-
Map<EntityId, Object> entityCache) {
48+
Map<EntityId, EntityRef> entityCache) {
4849
super(new ResultListCallback<>());
4950
this.rootEntityType = Objects.requireNonNull(rootEntityType);
5051
this.aggregateStrategyType = Objects.requireNonNull(aggregateStrategyType);

doma-core/src/main/java/org/seasar/doma/jdbc/aggregate/AssociationEntityPoolProvider.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.seasar.doma.internal.jdbc.command.FetchSupport;
3232
import org.seasar.doma.internal.jdbc.command.MappingSupport;
3333
import org.seasar.doma.jdbc.EntityId;
34+
import org.seasar.doma.jdbc.EntityRef;
3435
import org.seasar.doma.jdbc.Naming;
3536
import org.seasar.doma.jdbc.entity.AssociationPropertyType;
3637
import org.seasar.doma.jdbc.entity.EntityPropertyType;
@@ -43,7 +44,7 @@ public class AssociationEntityPoolProvider extends AbstractObjectProvider<Associ
4344
private final EntityType<?> rootEntityType;
4445
private final Query query;
4546
private final Set<EntityId> rootEntityIds;
46-
private final Map<EntityId, Object> entityCache;
47+
private final Map<EntityId, EntityRef> entityCache;
4748
private Map<Integer, MappingSupport.PropType> indexMap;
4849
private final MappingSupport mappingSupport;
4950
private final FetchSupport fetchSupport;
@@ -56,7 +57,7 @@ public AssociationEntityPoolProvider(
5657
Query query,
5758
boolean resultMappingEnsured,
5859
Set<EntityId> rootEntityIds,
59-
Map<EntityId, Object> entityCache) {
60+
Map<EntityId, EntityRef> entityCache) {
6061
assertNotNull(rootEntityType, aggregateStrategyType, query, rootEntityIds, entityCache);
6162
this.rootEntityType = rootEntityType;
6263
this.query = query;
@@ -169,8 +170,8 @@ private AssociationEntityPool createEntityPool(
169170
}
170171
AssociationEntityKey entityKey = createEntityKey(pathKey, props);
171172
EntityId entityId = entityKey.entityId();
172-
Object entity = entityCache.computeIfAbsent(entityId, id -> createEntity(id, props));
173-
entityPool.add(new AssociationEntityPoolEntry(entityKey, entity));
173+
EntityRef entityRef = entityCache.computeIfAbsent(entityId, id -> createEntityRef(id, props));
174+
entityPool.add(new AssociationEntityPoolEntry(entityKey, entityRef));
174175
if (pathKey.isRoot()) {
175176
rootEntityIds.add(entityId);
176177
}
@@ -192,16 +193,16 @@ private static AssociationEntityKey createEntityKey(
192193
return entityKey;
193194
}
194195

195-
private static Object createEntity(EntityId entityId, List<MappingSupport.Prop> props) {
196+
private static EntityRef createEntityRef(EntityId entityId, List<MappingSupport.Prop> props) {
196197
@SuppressWarnings("unchecked")
197198
EntityType<Object> entityType = (EntityType<Object>) entityId.entityType();
198199
Map<String, Property<Object, ?>> states =
199200
props.stream()
200201
.collect(Collectors.toMap(MappingSupport.Prop::name, MappingSupport.Prop::property));
201-
Object newEntity = entityType.newEntity(states);
202+
Object entity = entityType.newEntity(states);
202203
if (!entityType.isImmutable()) {
203-
entityType.saveCurrentStates(newEntity);
204+
entityType.saveCurrentStates(entity);
204205
}
205-
return newEntity;
206+
return new EntityRef(entity);
206207
}
207208
}

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/command/AssociateCommand.java

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.seasar.doma.internal.util.Combinations;
2929
import org.seasar.doma.internal.util.Pair;
3030
import org.seasar.doma.jdbc.EntityId;
31+
import org.seasar.doma.jdbc.EntityRef;
3132
import org.seasar.doma.jdbc.command.Command;
3233
import org.seasar.doma.jdbc.command.SelectCommand;
3334
import org.seasar.doma.jdbc.criteria.context.SelectContext;
@@ -52,7 +53,7 @@ public AssociateCommand(
5253
@SuppressWarnings("unchecked")
5354
public List<ENTITY> execute() {
5455
Set<EntityId> rootEntityIds = new LinkedHashSet<>();
55-
Map<EntityId, Object> cache = new HashMap<>();
56+
Map<EntityId, EntityRef> cache = new HashMap<>();
5657
Combinations<EntityKey> combinations = new Combinations<>();
5758
SelectCommand<List<EntityPool>> command =
5859
new SelectCommand<>(
@@ -61,51 +62,59 @@ public List<ENTITY> execute() {
6162
entityMetamodel, rootEntityIds, context.getProjectionEntityMetamodels()));
6263
List<EntityPool> entityPools = command.execute();
6364
for (EntityPool entityPool : entityPools) {
64-
Map<EntityMetamodel<?>, Pair<EntityKey, Object>> associationCandidate = new LinkedHashMap<>();
65+
Map<EntityMetamodel<?>, Pair<EntityKey, EntityRef>> associationCandidate =
66+
new LinkedHashMap<>();
6567
for (Map.Entry<EntityKey, EntityData> e : entityPool.entrySet()) {
6668
EntityKey key = e.getKey();
6769
EntityData data = e.getValue();
6870
EntityId entityId = key.asEntityId();
69-
Object entity =
71+
EntityRef entityRef =
7072
cache.computeIfAbsent(
7173
entityId,
7274
k -> {
7375
EntityType<Object> entityType = (EntityType<Object>) k.entityType();
74-
Object newEntity = entityType.newEntity(data.getStates());
76+
Object entity = entityType.newEntity(data.getStates());
7577
if (!entityType.isImmutable()) {
76-
entityType.saveCurrentStates(newEntity);
78+
entityType.saveCurrentStates(entity);
7779
}
78-
return newEntity;
80+
return new EntityRef(entity);
7981
});
80-
associationCandidate.put(key.getEntityMetamodel(), new Pair<>(key, entity));
82+
associationCandidate.put(key.getEntityMetamodel(), new Pair<>(key, entityRef));
8183
}
82-
associate(cache, combinations, associationCandidate);
84+
associate(combinations, associationCandidate);
8385
}
8486
return (List<ENTITY>)
85-
rootEntityIds.stream().map(cache::get).filter(Objects::nonNull).collect(toList());
87+
rootEntityIds.stream()
88+
.map(cache::get)
89+
.map(EntityRef::getEntity)
90+
.filter(Objects::nonNull)
91+
.collect(toList());
8692
}
8793

8894
private void associate(
89-
Map<EntityId, Object> cache,
9095
Combinations<EntityKey> combinations,
91-
Map<EntityMetamodel<?>, Pair<EntityKey, Object>> associationCandidate) {
96+
Map<EntityMetamodel<?>, Pair<EntityKey, EntityRef>> associationCandidate) {
9297
for (Map.Entry<Pair<EntityMetamodel<?>, EntityMetamodel<?>>, BiFunction<Object, Object, Object>>
9398
e : context.associations.entrySet()) {
9499
Pair<EntityMetamodel<?>, EntityMetamodel<?>> metamodelPair = e.getKey();
95100
BiFunction<Object, Object, Object> associator = e.getValue();
96-
Pair<EntityKey, Object> keyAndEntity1 = associationCandidate.get(metamodelPair.fst);
97-
Pair<EntityKey, Object> keyAndEntity2 = associationCandidate.get(metamodelPair.snd);
98-
if (keyAndEntity1 == null || keyAndEntity2 == null) {
101+
Pair<EntityKey, EntityRef> source = associationCandidate.get(metamodelPair.fst);
102+
Pair<EntityKey, EntityRef> target = associationCandidate.get(metamodelPair.snd);
103+
if (source == null || target == null) {
99104
continue;
100105
}
101-
Pair<EntityKey, EntityKey> keyPair = new Pair<>(keyAndEntity1.fst, keyAndEntity2.fst);
106+
Pair<EntityKey, EntityKey> keyPair = new Pair<>(source.fst, target.fst);
102107
if (combinations.contains(keyPair)) {
103108
continue;
104109
}
105-
Object newEntity = associator.apply(keyAndEntity1.snd, keyAndEntity2.snd);
106-
if (newEntity != null) {
107-
cache.replace(keyAndEntity1.fst.asEntityId(), newEntity);
108-
associationCandidate.replace(metamodelPair.fst, new Pair<>(keyAndEntity1.fst, newEntity));
110+
EntityRef sourceEntityRef = source.snd;
111+
EntityRef targetEntityRef = target.snd;
112+
Object sourceEntity = sourceEntityRef.getEntity();
113+
Object targetEntity = targetEntityRef.getEntity();
114+
Object resultEntity = associator.apply(sourceEntity, targetEntity);
115+
if (resultEntity != null && resultEntity != sourceEntity) {
116+
// Update a reference to an immutable entity
117+
sourceEntityRef.setEntity(resultEntity);
109118
}
110119
combinations.add(keyPair);
111120
}

0 commit comments

Comments
 (0)