From 8923a6650977acd093e7f9f7e2744dc094a618c8 Mon Sep 17 00:00:00 2001 From: Julia Gustafsson Date: Thu, 29 Jun 2023 13:07:20 +0200 Subject: [PATCH] Add reordering optimizations, fix #35 --- .../pom.xml | 8 + ...IntermediateOperationOptimizerFactory.java | 8 +- .../MoveAnonymousLambdaOperations.java | 129 ++++++++++ .../src/main/java/module-info.java | 1 + .../strategy/InternalOptimizerTest.java | 117 +++++++++ .../MoveAnonymousLambdaOperationsTest.java | 224 ++++++++++++++++++ .../internal/strategy/model/Film$.java | 115 +++++++++ .../internal/strategy/model/Film.java | 210 ++++++++++++++++ 8 files changed, 806 insertions(+), 6 deletions(-) create mode 100644 jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperations.java create mode 100644 jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/InternalOptimizerTest.java create mode 100644 jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperationsTest.java create mode 100644 jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/model/Film$.java create mode 100644 jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/model/Film.java diff --git a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/pom.xml b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/pom.xml index 8cd7e81d8..29cefe960 100644 --- a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/pom.xml +++ b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/pom.xml @@ -52,6 +52,14 @@ pipeline-standard test + + com.speedment.jpastreamer + field + + + com.speedment.jpastreamer + field + diff --git a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/InternalIntermediateOperationOptimizerFactory.java b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/InternalIntermediateOperationOptimizerFactory.java index 10fe4226c..bfa4bf44e 100644 --- a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/InternalIntermediateOperationOptimizerFactory.java +++ b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/InternalIntermediateOperationOptimizerFactory.java @@ -16,12 +16,7 @@ import com.speedment.jpastreamer.interopoptimizer.IntermediateOperationOptimizer; import com.speedment.jpastreamer.interopoptimizer.IntermediateOperationOptimizerFactory; -import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.RemoveOrderAffectingOperations; -import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.SquashDistinct; -import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.SquashFilter; -import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.SquashLimit; -import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.SquashSkip; -import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.SquashSorted; +import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.*; import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; import com.speedment.jpastreamer.rootfactory.RootFactory; @@ -53,6 +48,7 @@ public InternalIntermediateOperationOptimizerFactory() { registerOptimizer(new SquashFilter<>(intermediateOperationFactory)); registerOptimizer(new SquashSorted<>(intermediateOperationFactory)); registerOptimizer(new SquashDistinct(intermediateOperationFactory)); + registerOptimizer(new MoveAnonymousLambdaOperations(), Priority.LOWEST); } @Override diff --git a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperations.java b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperations.java new file mode 100644 index 000000000..d3ce31a8e --- /dev/null +++ b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperations.java @@ -0,0 +1,129 @@ +package com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy; + +import com.speedment.jpastreamer.field.Field; +import com.speedment.jpastreamer.field.predicate.SpeedmentPredicate; +import com.speedment.jpastreamer.interopoptimizer.IntermediateOperationOptimizer; +import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperation; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationType; + +import java.util.LinkedList; + +import static com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationType.*; + +public class MoveAnonymousLambdaOperations implements IntermediateOperationOptimizer { + + @Override + public Pipeline optimize(Pipeline pipeline) { + + LinkedList> intermediateOperations = pipeline.intermediateOperations(); + LinkedList> optimizedOperations = new LinkedList<>(); + for (int i = 0; i < intermediateOperations.size(); i++) { + final IntermediateOperation intermediateOperation = intermediateOperations.get(i); + if (i == 0 || !movable(intermediateOperation.type())) { + optimizedOperations.add(intermediateOperation); + continue; + } + int position = optimizedOperations.size(); // default - end of list + for(int j = optimizedOperations.size() - 1; j >= 0; j--) { + final IntermediateOperation currentOperation = optimizedOperations.get((j)); + if(swappable(intermediateOperation.type(), currentOperation.type()) && anonymousLambda(currentOperation)) { + position = j; + } else { + break; + } + } + if (position < optimizedOperations.size()) { + optimizedOperations.add(position, intermediateOperation); + } else { + optimizedOperations.add(intermediateOperation); + } + } + + // Update pipeline + for (int k = 0; k < intermediateOperations.size(); k++) { + final IntermediateOperation intermediateOperation = optimizedOperations.get(k); + intermediateOperations.set(k, intermediateOperation); + } + + return pipeline; + } + + public Pipeline optimize2(Pipeline pipeline) { + + LinkedList> intermediateOperations = pipeline.intermediateOperations(); + + int i = 0; + while(i < intermediateOperations.size() - 1) { + final IntermediateOperation intermediateOperation = intermediateOperations.get(i); + final IntermediateOperationType iot = intermediateOperation.type(); + if (movable(iot) && !anonymousLambda(intermediateOperation) ) { + // We only move movable operations with anonymous lambdas + int j = i + 1; + int currentPos = i; + while (j < intermediateOperations.size()) { + final IntermediateOperation next = intermediateOperations.get(j); + final IntermediateOperationType iotNext = next.type(); + if (swappable(iot, iotNext)) { // check if current lambda can be swapped with next operation + if (iotNext == DISTINCT || anonymousLambda(next)) { + i = -1; + currentPos = swapOperations(intermediateOperations, currentPos, j); + } else { + j++; + i++; + continue; + } + } else { + break; + } + j++; + } + } + i++; + } + + return pipeline; + } + + + private boolean movable(final IntermediateOperationType type) { + return type == FILTER || type == SORTED || type == DISTINCT; + } + + private boolean swappable(final IntermediateOperationType type, final IntermediateOperationType nextType) { + // Anonymous sorts and lambdas can be swapped with a distinct operator to allow the inclusion + // of the distinct operation in the query. + if (type == FILTER) { + return nextType == FILTER || nextType == SORTED || nextType == DISTINCT; + } else if (type == SORTED) { + return nextType == FILTER || nextType == DISTINCT; + } else if (type == DISTINCT) { + return nextType == FILTER || nextType == SORTED; + } + return false; + } + + private int swapOperations(final LinkedList> intermediateOperations, + final int index1, final int index2) + { + if (0 <= index1 && index1 < intermediateOperations.size() && 0 <= index2 && index2 < intermediateOperations.size()) { + final IntermediateOperation io1 = intermediateOperations.get(index1); + final IntermediateOperation io2 = intermediateOperations.get(index2); + + intermediateOperations.set(index1, io2); + intermediateOperations.set(index2, io1); + + return index2; + } + return -1; + } + + private boolean anonymousLambda(final IntermediateOperation operation) { + if (operation.type() == DISTINCT){ + return true; + } + final Object[] arguments = operation.arguments(); + return !(arguments != null && (arguments[0] instanceof SpeedmentPredicate || arguments[0] instanceof Field)); + } + +} diff --git a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/module-info.java b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/module-info.java index 499e062bc..c45de983b 100644 --- a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/module-info.java +++ b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/main/java/module-info.java @@ -18,6 +18,7 @@ module jpastreamer.interopoptimizer.standard { requires transitive jpastreamer.interopoptimizer; requires jpastreamer.rootfactory; + requires jpastreamer.field; exports com.speedment.jpastreamer.interopoptimizer.standard; diff --git a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/InternalOptimizerTest.java b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/InternalOptimizerTest.java new file mode 100644 index 000000000..af30bf312 --- /dev/null +++ b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/InternalOptimizerTest.java @@ -0,0 +1,117 @@ +package com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy; + +import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.pipeline.PipelineFactory; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperation; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; +import com.speedment.jpastreamer.rootfactory.RootFactory; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +abstract class InternalOptimizerTest { + + protected final PipelineFactory pipelineFactory = RootFactory.getOrThrow(PipelineFactory.class, ServiceLoader::load); + protected final IntermediateOperationFactory iof = RootFactory.getOrThrow(IntermediateOperationFactory.class, ServiceLoader::load); + protected final MoveAnonymousLambdaOperations optimizer = new MoveAnonymousLambdaOperations(); + + abstract Class getEntityClass(); + + @TestFactory + Stream modify() { + return pipelines().map(testCase -> dynamicTest(testCase.getName(), () -> { + this.optimizer.optimize(testCase.getPipeline()); + + assertTestCase(testCase); + })); + } + + protected abstract Stream> pipelines(); + + protected Pipeline createPipeline(final IntermediateOperation... operations) { + final Pipeline pipeline = pipelineFactory.createPipeline(getEntityClass()); + + for (IntermediateOperation operation : operations) { + pipeline.intermediateOperations().add(operation); + } + + return pipeline; + } + + private void assertTestCase(final PipelineTestCase testCase) { + final Pipeline unoptimized = testCase.getPipeline(); + final Pipeline optimized = testCase.getExpectedPipeline(); + + final List> unoptimizedIntermediateOperations = unoptimized.intermediateOperations(); + final List> optimizedIntermediateOperations = optimized.intermediateOperations(); + + assertEquals(optimizedIntermediateOperations.size(), unoptimizedIntermediateOperations.size()); + + for (int i = 0; i < unoptimizedIntermediateOperations.size(); i++) { + final IntermediateOperation unoptimizedOperation = unoptimizedIntermediateOperations.get(i); + final IntermediateOperation optimizedOperation = optimizedIntermediateOperations.get(i); + + assertEquals(optimizedOperation.type(), unoptimizedOperation.type()); + assertArguments(optimizedOperation.arguments(), unoptimizedOperation.arguments()); + } + } + + protected void assertArguments(final Object[] expected, final Object[] actual) { + if (expected != actual) { + assertNotNull(expected); + assertNotNull(actual); + assertEquals(expected.length, actual.length); + + for(int i = 0; i < expected.length; ++i) { + Object expectedElement = expected[i]; + Object actualElement = actual[i]; + if (expectedElement != actualElement) { + final Field[] actualDeclaredFields = actualElement.getClass().getDeclaredFields(); + final Field[] expectedDeclaredFields = expectedElement.getClass().getDeclaredFields(); + for (int j = 0; j < expectedDeclaredFields.length; ++j) { + assertEquals(actualDeclaredFields[i], expectedDeclaredFields[i]); + } + } + } + } + } + + protected static final class PipelineTestCase { + + private final String name; + private final Pipeline pipeline; + private final Pipeline expectedPipeline; + + protected PipelineTestCase( + final String name, + final Pipeline pipeline, + final Pipeline expectedPipeline + ) { + this.name = name; + this.pipeline = pipeline; + this.expectedPipeline = expectedPipeline; + } + + public String getName() { + return name; + } + + public Pipeline getPipeline() { + return pipeline; + } + + public Pipeline getExpectedPipeline() { + return expectedPipeline; + } + } + + +} diff --git a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperationsTest.java b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperationsTest.java new file mode 100644 index 000000000..9cc00797c --- /dev/null +++ b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperationsTest.java @@ -0,0 +1,224 @@ +package com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy; + +import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.model.Film; +import com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.model.Film$; +import com.speedment.jpastreamer.pipeline.Pipeline; + +import java.util.Comparator; +import java.util.stream.Stream; + +public class MoveAnonymousLambdaOperationsTest extends InternalOptimizerTest { + + @Override + Class getEntityClass() { + return Film.class; + } + + @Override + protected Stream> pipelines() { + return Stream.of( + test1(), + test2(), + test3(), + test4(), + test5(), + test6(), + test7(), + test8(), + test9(), + test10() + ); + } + + private PipelineTestCase test1() { + // No optimizations can be made + final Pipeline unoptimized = createPipeline( + iof.createLimit(1) + ); + + final Pipeline optimized = createPipeline( + iof.createLimit(1) + ); + + return new PipelineTestCase<>("Reorder Test 1", unoptimized, optimized); + } + + private PipelineTestCase test2() { + // Pipeline can be fully optimized + final Pipeline unoptimized = createPipeline( + iof.createFilter(f -> f.equals("")), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createSorted(Film$.length) + ); + + final Pipeline optimized = createPipeline( + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createSorted(Film$.length), + iof.createFilter(f -> f.equals("")) + ); + + return new PipelineTestCase<>("Reorder Test 2", unoptimized, optimized); + } + + private PipelineTestCase test3() { + // Pipeline can be fully optimized + final Pipeline unoptimized = createPipeline( + iof.createFilter(f -> f.equals("a")), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createFilter(f -> f.equals("b")), + iof.createSorted(Film$.length) + ); + + final Pipeline optimized = createPipeline( + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createSorted(Film$.length), + iof.createFilter(f -> f.equals("b")), + iof.createFilter(f -> f.equals("a")) + ); + + return new PipelineTestCase<>("Reorder Test 3", unoptimized, optimized); + } + + private PipelineTestCase test4() { + // The map blocks the anonymous filter from being moved up the pipeline + final Pipeline unoptimized = createPipeline( + iof.createFilter(f -> f.equals("a")), + iof.createMap(Film$.length.asInt()), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createFilter(f -> f.equals("b")), + iof.createSorted(Film$.length) + ); + + final Pipeline optimized = createPipeline( + iof.createFilter(f -> f.equals("a")), + iof.createMap(Film$.length.asInt()), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createSorted(Film$.length), + iof.createFilter(f -> f.equals("b")) + ); + + return new PipelineTestCase<>("Reorder Test 4", unoptimized, optimized); + } + + private PipelineTestCase test5() { + // Anonymous lambdas are moved to the end of the pipeline + final Pipeline unoptimized = createPipeline( + iof.createMap(Film$.length.asInt()), + iof.createFilter(f -> f.equals("a")), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createFilter(f -> f.equals("b")), + iof.createSorted(Film$.length) + ); + + final Pipeline optimized = createPipeline( + iof.createMap(Film$.length.asInt()), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createSorted(Film$.length), + iof.createFilter(f -> f.equals("b")), + iof.createFilter(f -> f.equals("a")) + ); + + return new PipelineTestCase<>("Reorder Test 5", unoptimized, optimized); + } + + + private PipelineTestCase test6() { + // Unchanged as sorts cannot be rearranged + final Pipeline unoptimized = createPipeline( + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createSorted(Comparator.comparing(Object::toString)), + iof.createSorted(Film$.length) + ); + + final Pipeline optimized = createPipeline( + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createSorted(Comparator.comparing(Object::toString)), + iof.createSorted(Film$.length) + ); + + return new PipelineTestCase<>("Reorder Test 6", unoptimized, optimized); + } + + private PipelineTestCase test7() { + // The sorts cannot be reordered even though the first sort uses an anonymous lambda + final Pipeline unoptimized = createPipeline( + iof.createFilter(f -> f.equals("b")), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.acquireDistinct(), + iof.createSorted(Comparator.comparing(Object::toString)), + iof.createSorted(Film$.length) + ); + + final Pipeline optimized = createPipeline( + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createSorted(Comparator.comparing(Object::toString)), + iof.createSorted(Film$.length), + iof.acquireDistinct(), + iof.createFilter(f -> f.equals("b")) + ); + + return new PipelineTestCase<>("Reorder Test 7", unoptimized, optimized); + } + + private PipelineTestCase test8() { + // The two sorts cannot be reordered, but both operations can be moved past the distinct() and filter() + final Pipeline unoptimized = createPipeline( + iof.createSorted(Comparator.comparing(Object::toString)), + iof.createSorted(Comparator.comparing(Object::toString)), + iof.acquireDistinct(), + iof.createFilter(Film$.length.greaterThan((short) 120)) + ); + + final Pipeline optimized = createPipeline( + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.acquireDistinct(), + iof.createSorted(Comparator.comparing(Object::toString)), + iof.createSorted(Comparator.comparing(Object::toString)) + ); + + return new PipelineTestCase<>("Reorder Test 8", unoptimized, optimized); + } + + private PipelineTestCase test9() { + // No operations can be moved past the map + final Pipeline unoptimized = createPipeline( + iof.createSorted(Comparator.comparing(Object::toString)), + iof.acquireDistinct(), + iof.createMap(Film$.length.asInt()), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createLimit(100), + iof.createSkip(10) + ); + + final Pipeline optimized = createPipeline( + iof.acquireDistinct(), + iof.createSorted(Comparator.comparing(Object::toString)), + iof.createMap(Film$.length.asInt()), + iof.createFilter(Film$.length.greaterThan((short) 120)), + iof.createLimit(100), + iof.createSkip(10) + ); + + return new PipelineTestCase<>("Reorder Test 9", unoptimized, optimized); + } + + private PipelineTestCase test10() { + // No operations can be moved past the limit + final Pipeline unoptimized = createPipeline( + iof.createSorted(Comparator.comparing(Object::toString)), + iof.acquireDistinct(), + iof.createLimit(100), + iof.createFilter(Film$.length.greaterThan((short) 120)) + ); + + final Pipeline optimized = createPipeline( + iof.acquireDistinct(), + iof.createSorted(Comparator.comparing(Object::toString)), + iof.createLimit(100), + iof.createFilter(Film$.length.greaterThan((short) 120)) + ); + + return new PipelineTestCase<>("Reorder Test 10", unoptimized, optimized); + } + +} diff --git a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/model/Film$.java b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/model/Film$.java new file mode 100644 index 000000000..8938d861d --- /dev/null +++ b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/model/Film$.java @@ -0,0 +1,115 @@ +/* + * JPAstreamer - Express JPA queries with Java Streams + * Copyright (c) 2020-2022, Speedment, Inc. All Rights Reserved. + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * See: https://github.com/speedment/jpa-streamer/blob/master/LICENSE + */ +package com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.model; + +import com.speedment.jpastreamer.field.ComparableField; +import com.speedment.jpastreamer.field.ShortField; +import com.speedment.jpastreamer.field.StringField; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +public class Film$ { + + /** + * This Field corresponds to the {@link Film} field "length". + */ + public static final ComparableField length = ComparableField.create( + Film.class, + "length", + Film::getLength, + false + ); + /** + * This Field corresponds to the {@link Film} field "filmId". + */ + public static final ShortField filmId = ShortField.create( + Film.class, + "filmId", + Film::getFilmId, + false + ); + /** + * This Field corresponds to the {@link Film} field "replacementCost". + */ + public static final ComparableField replacementCost = ComparableField.create( + Film.class, + "replacementCost", + Film::getReplacementCost, + false + ); + + /** + * This Field corresponds to the {@link Film} field "rating". + */ + public static final StringField rating = StringField.create( + Film.class, + "rating", + Film::getRating, + false + ); + /** + * This Field corresponds to the {@link Film} field "title". + */ + public static final StringField title = StringField.create( + Film.class, + "title", + Film::getTitle, + false + ); + /** + * This Field corresponds to the {@link Film} field "languageId". + */ + public static final ShortField languageId = ShortField.create( + Film.class, + "languageId", + Film::getLanguageId, + false + ); + /** + * This Field corresponds to the {@link Film} field "description". + */ + public static final StringField description = StringField.create( + Film.class, + "description", + Film::getDescription, + false + ); + /** + * This Field corresponds to the {@link Film} field "rentalRate". + */ + public static final ComparableField rentalRate = ComparableField.create( + Film.class, + "rentalRate", + Film::getRentalRate, + false + ); + /** + * This Field corresponds to the {@link Film} field "rentalDuration". + */ + public static final ShortField rentalDuration = ShortField.create( + Film.class, + "rentalDuration", + Film::getRentalDuration, + false + ); + /** + * This Field corresponds to the {@link Film} field "lastUpdate". + */ + public static final ComparableField lastUpdate = ComparableField.create( + Film.class, + "lastUpdate", + Film::getLastUpdate, + false + ); +} diff --git a/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/model/Film.java b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/model/Film.java new file mode 100644 index 000000000..77b54bca4 --- /dev/null +++ b/jpastreamer.provider/jpastreamer.interopoptimizer.standard/src/test/java/com/speedment/jpastreamer/interopoptimizer/standard/internal/strategy/model/Film.java @@ -0,0 +1,210 @@ +/* + * JPAstreamer - Express JPA queries with Java Streams + * Copyright (c) 2020-2022, Speedment, Inc. All Rights Reserved. + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * See: https://github.com/speedment/jpa-streamer/blob/master/LICENSE + */ +package com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy.model; + +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +@Entity +@Table(name = "film", schema = "sakila") +public class Film { + public Film() { + } + + public Film(short filmId) { + this.filmId = filmId; + } + + public Film(String description) { + this.description = description; + } + + public Film(short filmId, String title, String description) { + this.filmId = filmId; + this.title = title; + this.description = description; + } + + public Film(short filmId, String description) { + this.filmId = filmId; + this.description = description; + } + + @Id + @Column(name = "film_id", columnDefinition = "SMALLINT UNSIGNED") + private short filmId; + + @Basic + @Column(name = "title", columnDefinition = "VARCHAR(255)") + private String title; + + @Basic + @Column(name = "description", columnDefinition = "TEXT") + private String description; + @Basic + @Column(name = "rental_duration", columnDefinition = "YEAR") + private short rentalDuration; + @Basic + @Column(name = "language_id", columnDefinition = "SMALLINT UNSIGNED") + private short languageId; + @Basic + @Column(name = "rental_rate", columnDefinition = "DECIMAL(4,2)") + private BigDecimal rentalRate; + + @Basic + @Column(name = "length", columnDefinition = "SMALL UNSIGNED") + private Short length; + + @Basic + @Column(name = "rating", columnDefinition = "enum('G','PG','PG-13','R','NC-17')") + private String rating; + + @Basic + @Column(name = "replacement_cost") + private BigDecimal replacementCost; + + @Basic + @Column(name = "last_update") + private Timestamp lastUpdate; + + public short getFilmId() { + return filmId; + } + + public void setFilmId(short filmId) { + this.filmId = filmId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public short getLanguageId() { + return languageId; + } + + public void setLanguageId(short languageId) { + this.languageId = languageId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public short getRentalDuration() { + return rentalDuration; + } + + public void setRentalDuration(short rentalDuration) { + this.rentalDuration = rentalDuration; + } + + public String getRating() { + return rating; + } + + public void setRating(String rating) { + this.rating = rating; + } + + public BigDecimal getRentalRate() { + return rentalRate; + } + + public void setRentalRate(BigDecimal rentalRate) { + this.rentalRate = rentalRate; + } + + public Short getLength() { + return length; + } + + public void setLength(Short length) { + this.length = length; + } + + public BigDecimal getReplacementCost() { + return replacementCost; + } + + public void setReplacementCost(BigDecimal replacementCost) { + this.replacementCost = replacementCost; + } + + public Timestamp getLastUpdate() { + return lastUpdate; + } + + public void setLastUpdate(Timestamp lastUpdate) { + this.lastUpdate = lastUpdate; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Film film = (Film) o; + + if (filmId != film.filmId) return false; + if (rentalDuration != film.rentalDuration) return false; + if (title != null ? !title.equals(film.title) : film.title != null) return false; + if (description != null ? !description.equals(film.description) : film.description != null) return false; + if (rentalRate != null ? !rentalRate.equals(film.rentalRate) : film.rentalRate != null) return false; + if (length != null ? !length.equals(film.length) : film.length != null) return false; + if (replacementCost != null ? !replacementCost.equals(film.replacementCost) : film.replacementCost != null) + return false; + if (lastUpdate != null ? !lastUpdate.equals(film.lastUpdate) : film.lastUpdate != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) filmId; + result = 31 * result + (title != null ? title.hashCode() : 0); + result = 31 * result + (description != null ? description.hashCode() : 0); + result = 31 * result + (int) rentalDuration; + result = 31 * result + (rentalRate != null ? rentalRate.hashCode() : 0); + result = 31 * result + (length != null ? length.hashCode() : 0); + result = 31 * result + (replacementCost != null ? replacementCost.hashCode() : 0); + result = 31 * result + (lastUpdate != null ? lastUpdate.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Film{" + + "filmId=" + filmId + + ", title='" + title + '\'' + + ", description='" + description + '\'' + + ", rentalDuration=" + rentalDuration + + ", languageId=" + languageId + + ", rentalRate=" + rentalRate + + ", length=" + length + + ", rating='" + rating + '\'' + + ", replacementCost=" + replacementCost + + ", lastUpdate=" + lastUpdate + + '}'; + } +}