-
-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable mapping of arbitrary SQL results to aggregate entities
- Loading branch information
1 parent
b5cee08
commit e621d3e
Showing
47 changed files
with
1,927 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* Copyright Doma Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.seasar.doma; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* Indicates an association between entities. | ||
* | ||
* <p>This annotation is applied to fields that represent a relationship between entities. | ||
*/ | ||
@Target(ElementType.FIELD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface Association {} |
46 changes: 46 additions & 0 deletions
46
doma-core/src/main/java/org/seasar/doma/AssociationLinker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright Doma Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.seasar.doma; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import java.util.function.BiFunction; | ||
|
||
/** | ||
* Associates a field in an entity class with properties in a related object for the purpose of | ||
* creating object relationships when mapping between database tables and entities. | ||
* | ||
* <p>This class can only be annotated on {@code public static final} fields of type {@link | ||
* BiFunction}. | ||
*/ | ||
@Target(java.lang.annotation.ElementType.FIELD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface AssociationLinker { | ||
/** | ||
* Specifies the path to the property in the related object. | ||
* | ||
* @return the property path as a string | ||
*/ | ||
String propertyPath(); | ||
|
||
/** | ||
* Defines the prefix for the column in the database table that is linked to the field. | ||
* | ||
* @return the column prefix as a string | ||
*/ | ||
String columnPrefix(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
doma-core/src/main/java/org/seasar/doma/jdbc/aggregate/AggregateCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* | ||
* Copyright Doma Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.seasar.doma.jdbc.aggregate; | ||
|
||
import java.util.Comparator; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.function.BiFunction; | ||
import java.util.stream.Stream; | ||
import org.seasar.doma.internal.util.Combinations; | ||
import org.seasar.doma.internal.util.Pair; | ||
import org.seasar.doma.jdbc.command.Command; | ||
import org.seasar.doma.jdbc.command.SelectCommand; | ||
import org.seasar.doma.jdbc.entity.EntityType; | ||
import org.seasar.doma.jdbc.query.Query; | ||
import org.seasar.doma.jdbc.query.SelectQuery; | ||
|
||
public class AggregateCommand<RESULT, ENTITY> implements Command<RESULT> { | ||
private final SelectQuery query; | ||
private final EntityType<ENTITY> entityType; | ||
private final StreamReducer<RESULT, ENTITY> streamReducer; | ||
private final List<AssociationLinkerType<?, ?>> associationLinkerTypes; | ||
|
||
public AggregateCommand( | ||
SelectQuery query, | ||
EntityType<ENTITY> entityType, | ||
StreamReducer<RESULT, ENTITY> streamReducer, | ||
List<AssociationLinkerType<?, ?>> associationLinkerTypes) { | ||
this.query = Objects.requireNonNull(query); | ||
this.entityType = Objects.requireNonNull(entityType); | ||
this.streamReducer = Objects.requireNonNull(streamReducer); | ||
Objects.requireNonNull(associationLinkerTypes); | ||
this.associationLinkerTypes = sortAssociationLinkerTypes(associationLinkerTypes); | ||
} | ||
|
||
private static List<AssociationLinkerType<?, ?>> sortAssociationLinkerTypes( | ||
List<AssociationLinkerType<?, ?>> associationLinkerTypes) { | ||
Comparator<AssociationLinkerType<?, ?>> reversedComparator = | ||
Comparator.<AssociationLinkerType<?, ?>>comparingInt(AssociationLinkerType::getDepth) | ||
.reversed(); | ||
return associationLinkerTypes.stream().sorted(reversedComparator).toList(); | ||
} | ||
|
||
@Override | ||
@SuppressWarnings("unchecked") | ||
public RESULT execute() { | ||
Map<LinkableEntityKey, Object> cache = new LinkedHashMap<>(); | ||
Combinations<LinkableEntityKey> combinations = new Combinations<>(); | ||
SelectCommand<List<LinkableEntityPool>> command = | ||
new SelectCommand<>( | ||
query, | ||
new LinkableEntityPoolIterationHandler( | ||
entityType, associationLinkerTypes, query.isResultMappingEnsured())); | ||
List<LinkableEntityPool> entityPools = command.execute(); | ||
for (LinkableEntityPool entityPool : entityPools) { | ||
Map<String, KeyAndEntity> associationCandidate = new LinkedHashMap<>(); | ||
for (Map.Entry<LinkableEntityKey, LinkableEntityData> e : entityPool.entrySet()) { | ||
LinkableEntityKey key = e.getKey(); | ||
LinkableEntityData data = e.getValue(); | ||
Object entity = | ||
cache.computeIfAbsent( | ||
key, | ||
k -> { | ||
EntityType<Object> entityType = (EntityType<Object>) k.entityType(); | ||
Object newEntity = entityType.newEntity(data.getStates()); | ||
if (!entityType.isImmutable()) { | ||
entityType.saveCurrentStates(newEntity); | ||
} | ||
return newEntity; | ||
}); | ||
associationCandidate.put(key.propertyPath(), new KeyAndEntity(key, entity)); | ||
} | ||
associate(cache, combinations, associationCandidate); | ||
} | ||
Stream<ENTITY> stream = | ||
(Stream<ENTITY>) | ||
cache.entrySet().stream() | ||
.filter(e -> e.getKey().belongsToRootEntity()) | ||
.map(Map.Entry::getValue); | ||
return streamReducer.reduce(stream); | ||
} | ||
|
||
private void associate( | ||
Map<LinkableEntityKey, Object> cache, | ||
Combinations<LinkableEntityKey> combinations, | ||
Map<String, KeyAndEntity> associationCandidate) { | ||
for (AssociationLinkerType<?, ?> associationLinkerType : associationLinkerTypes) { | ||
KeyAndEntity source = associationCandidate.get(associationLinkerType.getSourceName()); | ||
KeyAndEntity target = associationCandidate.get(associationLinkerType.getTargetName()); | ||
if (source == null || target == null) { | ||
continue; | ||
} | ||
Pair<LinkableEntityKey, LinkableEntityKey> keyPair = new Pair<>(source.key(), target.key()); | ||
if (combinations.contains(keyPair)) { | ||
continue; | ||
} | ||
@SuppressWarnings("unchecked") | ||
BiFunction<Object, Object, Object> linker = | ||
(BiFunction<Object, Object, Object>) associationLinkerType.getLinker(); | ||
Object newEntity = linker.apply(source.entity(), target.entity()); | ||
if (newEntity != null) { | ||
cache.replace(source.key(), newEntity); | ||
associationCandidate.replace( | ||
associationLinkerType.getSourceName(), new KeyAndEntity(source.key(), newEntity)); | ||
} | ||
combinations.add(keyPair); | ||
} | ||
} | ||
|
||
@Override | ||
public Query getQuery() { | ||
return query; | ||
} | ||
|
||
private record KeyAndEntity(LinkableEntityKey key, Object entity) {} | ||
} |
26 changes: 26 additions & 0 deletions
26
doma-core/src/main/java/org/seasar/doma/jdbc/aggregate/AssociationIdentifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright Doma Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.seasar.doma.jdbc.aggregate; | ||
|
||
import java.util.Objects; | ||
import org.seasar.doma.jdbc.entity.EntityType; | ||
|
||
record AssociationIdentifier(String propertyPath, EntityType<?> entityType) { | ||
AssociationIdentifier { | ||
Objects.requireNonNull(propertyPath); | ||
Objects.requireNonNull(entityType); | ||
} | ||
} |
Oops, something went wrong.