You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/05/10 04:25:03 UTC
[camel] branch camel-3.x updated: CAMEL-19327: camel-jpa support singleResult option (#10032)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-3.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-3.x by this push:
new 55caf5d03a2 CAMEL-19327: camel-jpa support singleResult option (#10032)
55caf5d03a2 is described below
commit 55caf5d03a24be0a447154d2443d7dcd3f30b236
Author: jacekszymanski <ja...@gmail.com>
AuthorDate: Wed May 10 06:24:55 2023 +0200
CAMEL-19327: camel-jpa support singleResult option (#10032)
* support outputType JpaEndpoint option
* AdditionalQueryParameters no longer nested
* create JpaWithOptionsTestSupport, move support methods from JpaPagingTest
* outputType Query tests
* refactor tests
* add outputType support to processFind
* test outputType on finds
* refactor, the option is now boolean, named singleResult
---
.../camel/component/jpa/JpaEndpointConfigurer.java | 6 +
.../camel/component/jpa/JpaEndpointUriFactory.java | 3 +-
.../org/apache/camel/component/jpa/jpa.json | 1 +
.../apache/camel/component/jpa/JpaEndpoint.java | 14 +++
.../apache/camel/component/jpa/JpaProducer.java | 19 ++-
.../component/jpa/AdditionalQueryParameters.java | 30 +++++
.../camel/component/jpa/JpaOutputTypeTest.java | 140 +++++++++++++++++++++
.../apache/camel/component/jpa/JpaPagingTest.java | 105 ++--------------
.../component/jpa/JpaWithOptionsTestSupport.java | 119 ++++++++++++++++++
9 files changed, 341 insertions(+), 96 deletions(-)
diff --git a/components/camel-jpa/src/generated/java/org/apache/camel/component/jpa/JpaEndpointConfigurer.java b/components/camel-jpa/src/generated/java/org/apache/camel/component/jpa/JpaEndpointConfigurer.java
index 62e856b71f2..8155acd62a5 100644
--- a/components/camel-jpa/src/generated/java/org/apache/camel/component/jpa/JpaEndpointConfigurer.java
+++ b/components/camel-jpa/src/generated/java/org/apache/camel/component/jpa/JpaEndpointConfigurer.java
@@ -89,6 +89,8 @@ public class JpaEndpointConfigurer extends PropertyConfigurerSupport implements
case "sendEmptyMessageWhenIdle": target.setSendEmptyMessageWhenIdle(property(camelContext, boolean.class, value)); return true;
case "sharedentitymanager":
case "sharedEntityManager": target.setSharedEntityManager(property(camelContext, boolean.class, value)); return true;
+ case "singleresult":
+ case "singleResult": target.setSingleResult(property(camelContext, boolean.class, value)); return true;
case "skiplockedentity":
case "skipLockedEntity": target.setSkipLockedEntity(property(camelContext, boolean.class, value)); return true;
case "startscheduler":
@@ -179,6 +181,8 @@ public class JpaEndpointConfigurer extends PropertyConfigurerSupport implements
case "sendEmptyMessageWhenIdle": return boolean.class;
case "sharedentitymanager":
case "sharedEntityManager": return boolean.class;
+ case "singleresult":
+ case "singleResult": return boolean.class;
case "skiplockedentity":
case "skipLockedEntity": return boolean.class;
case "startscheduler":
@@ -270,6 +274,8 @@ public class JpaEndpointConfigurer extends PropertyConfigurerSupport implements
case "sendEmptyMessageWhenIdle": return target.isSendEmptyMessageWhenIdle();
case "sharedentitymanager":
case "sharedEntityManager": return target.isSharedEntityManager();
+ case "singleresult":
+ case "singleResult": return target.isSingleResult();
case "skiplockedentity":
case "skipLockedEntity": return target.isSkipLockedEntity();
case "startscheduler":
diff --git a/components/camel-jpa/src/generated/java/org/apache/camel/component/jpa/JpaEndpointUriFactory.java b/components/camel-jpa/src/generated/java/org/apache/camel/component/jpa/JpaEndpointUriFactory.java
index 7848eb3859a..7c748ec540a 100644
--- a/components/camel-jpa/src/generated/java/org/apache/camel/component/jpa/JpaEndpointUriFactory.java
+++ b/components/camel-jpa/src/generated/java/org/apache/camel/component/jpa/JpaEndpointUriFactory.java
@@ -21,7 +21,7 @@ public class JpaEndpointUriFactory extends org.apache.camel.support.component.En
private static final Set<String> SECRET_PROPERTY_NAMES;
private static final Set<String> MULTI_VALUE_PREFIXES;
static {
- Set<String> props = new HashSet<>(46);
+ Set<String> props = new HashSet<>(47);
props.add("backoffErrorThreshold");
props.add("backoffIdleThreshold");
props.add("backoffMultiplier");
@@ -60,6 +60,7 @@ public class JpaEndpointUriFactory extends org.apache.camel.support.component.En
props.add("schedulerProperties");
props.add("sendEmptyMessageWhenIdle");
props.add("sharedEntityManager");
+ props.add("singleResult");
props.add("skipLockedEntity");
props.add("startScheduler");
props.add("timeUnit");
diff --git a/components/camel-jpa/src/generated/resources/org/apache/camel/component/jpa/jpa.json b/components/camel-jpa/src/generated/resources/org/apache/camel/component/jpa/jpa.json
index 4c490ed1530..ced7dfba7aa 100644
--- a/components/camel-jpa/src/generated/resources/org/apache/camel/component/jpa/jpa.json
+++ b/components/camel-jpa/src/generated/resources/org/apache/camel/component/jpa/jpa.json
@@ -65,6 +65,7 @@
"firstResult": { "kind": "parameter", "displayName": "First Result", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": -1, "description": "Set the position of the first result to retrieve." },
"flushOnSend": { "kind": "parameter", "displayName": "Flush On Send", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Flushes the EntityManager after the entity bean has been persisted." },
"remove": { "kind": "parameter", "displayName": "Remove", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Indicates to use entityManager.remove(entity)." },
+ "singleResult": { "kind": "parameter", "displayName": "Single Result", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If enabled, a query or a find which would return no results or more than one result, will throw an exception instead." },
"useExecuteUpdate": { "kind": "parameter", "displayName": "Use Execute Update", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "description": "To configure whether to use executeUpdate() when producer executes a query. When you use INSERT, UPDATE or DELETE statement as a named query, you need to specify this option to 'true'." },
"usePersist": { "kind": "parameter", "displayName": "Use Persist", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Indicates to use entityManager.persist(entity) instead of entityManager.merge(entity). Note: entityManager.persist(entity) doesn't work for detached entities (where the EntityManager has to execute an UPDATE instead of an [...]
"lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
diff --git a/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaEndpoint.java b/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaEndpoint.java
index 46998eec4a2..d0041b49412 100644
--- a/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaEndpoint.java
+++ b/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaEndpoint.java
@@ -112,6 +112,8 @@ public class JpaEndpoint extends ScheduledPollEndpoint {
private Boolean useExecuteUpdate;
@UriParam(label = "producer")
private boolean findEntity;
+ @UriParam(label = "producer", defaultValue = "false")
+ private boolean singleResult;
@UriParam(label = "advanced", prefix = "emf.", multiValue = true)
private Map<String, Object> entityManagerProperties;
@@ -557,6 +559,18 @@ public class JpaEndpoint extends ScheduledPollEndpoint {
this.findEntity = findEntity;
}
+ public boolean isSingleResult() {
+ return singleResult;
+ }
+
+ /**
+ * If enabled, a query or a find which would return no results or more than one result, will throw an exception
+ * instead.
+ */
+ public void setSingleResult(boolean singleResult) {
+ this.singleResult = singleResult;
+ }
+
// Implementation methods
// -------------------------------------------------------------------------
diff --git a/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaProducer.java b/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaProducer.java
index 43287bf7766..1bf4a6cdbc5 100644
--- a/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaProducer.java
+++ b/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaProducer.java
@@ -22,6 +22,7 @@ import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
+import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.apache.camel.Exchange;
@@ -189,7 +190,16 @@ public class JpaProducer extends DefaultProducer {
} else {
target = exchange.getIn();
}
- Object answer = isUseExecuteUpdate() ? innerQuery.executeUpdate() : innerQuery.getResultList();
+
+ final Object answer;
+ if (isUseExecuteUpdate()) {
+ answer = innerQuery.executeUpdate();
+ } else if (getEndpoint().isSingleResult()) {
+ answer = innerQuery.getSingleResult();
+ } else {
+ answer = innerQuery.getResultList();
+ }
+
target.setBody(answer);
if (getEndpoint().isFlushOnSend()) {
@@ -244,6 +254,13 @@ public class JpaProducer extends DefaultProducer {
Object answer = entityManager.find(getEndpoint().getEntityType(), key);
LOG.debug("Find: {} -> {}", key, answer);
+ if (getEndpoint().isSingleResult() && answer == null) {
+ throw new NoResultException(
+ String.format(
+ "No results for key %s and singleResult requested",
+ key));
+ }
+
Message target;
if (ExchangeHelper.isOutCapable(exchange)) {
target = exchange.getMessage();
diff --git a/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/AdditionalQueryParameters.java b/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/AdditionalQueryParameters.java
new file mode 100644
index 00000000000..d966268b4b2
--- /dev/null
+++ b/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/AdditionalQueryParameters.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.camel.component.jpa;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target(value = { ElementType.METHOD })
+@interface AdditionalQueryParameters {
+
+ String value();
+
+}
diff --git a/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaOutputTypeTest.java b/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaOutputTypeTest.java
new file mode 100644
index 00000000000..25ae0e299e8
--- /dev/null
+++ b/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaOutputTypeTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.camel.component.jpa;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.examples.Customer;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class JpaOutputTypeTest extends JpaWithOptionsTestSupport {
+
+ private static final String ENDPOINT_URI = "jpa://" + Customer.class.getName();
+
+ private String queryOrFind;
+
+ @Test
+ @Query
+ @AdditionalQueryParameters("singleResult=true¶meters.seq=% 001")
+ public void testSingleCustomerOKQuery() throws Exception {
+ final Customer customer = runQueryTest(Customer.class);
+
+ assertNotNull(customer);
+ }
+
+ @Test
+ @Query("select c from Customer c")
+ @AdditionalQueryParameters("singleResult=true")
+ public void testTooMuchResults() throws Exception {
+ final Exchange result = doRunQueryTest();
+
+ Assertions.assertInstanceOf(NonUniqueResultException.class, getException(result));
+ }
+
+ @Test
+ @Query
+ @AdditionalQueryParameters("singleResult=true¶meters.seq=% xxx")
+ public void testNoCustomersQuery() throws Exception {
+ final Exchange result = doRunQueryTest();
+
+ Assertions.assertInstanceOf(NoResultException.class, getException(result));
+ }
+
+ @Test
+ @Find
+ @AdditionalQueryParameters("singleResult=true")
+ public void testSingleCustomerOKFind() throws Exception {
+ // ids in the db are not known, so query for a known element and use its id.
+ super.setUp(getEndpointUri());
+
+ final Customer fromDb = (Customer) entityManager
+ .createQuery("select c from Customer c where c.name like '% 001'")
+ .getSingleResult();
+
+ final Exchange result = template.send("direct:start", withBody(fromDb.getId()));
+
+ assertNotNull(result.getIn().getBody(Customer.class));
+ }
+
+ @Test
+ @Find
+ @AdditionalQueryParameters("singleResult=true")
+ public void testNoCustomerFind() throws Exception {
+ final Exchange result = doRunQueryTest(withBody(Long.MAX_VALUE));
+
+ Assertions.assertInstanceOf(NoResultException.class, getException(result));
+ }
+
+ @Override
+ protected String getEndpointUri() {
+ return String.format("%s?%s%s",
+ ENDPOINT_URI,
+ queryOrFind,
+ createAdditionalQueryParameters());
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ super.beforeEach(context);
+
+ // a query or a find is necessary - without the annotation test can't continue
+ final AnnotatedElement annotatedElement = context.getElement().get();
+
+ final Find find = annotatedElement.getAnnotation(Find.class);
+ final Query query = annotatedElement.getAnnotation(Query.class);
+
+ if ((find == null) == (query == null)) {
+ throw new IllegalStateException("Test must be annotated with EITHER Find OR Query");
+ }
+
+ if (find != null) {
+ queryOrFind = "findEntity=" + true;
+ } else { // query != null
+ queryOrFind = "query=" + query.value();
+ }
+
+ }
+
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface Find {
+ }
+
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface Query {
+ String value() default "select c from Customer c where c.name like :seq";
+ }
+
+ private static Exception getException(final Exchange exchange) {
+ final Exception exception = exchange.getException();
+
+ return exception != null ? exception : exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
+ }
+}
diff --git a/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaPagingTest.java b/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaPagingTest.java
index 824c9429c53..32aaf4d3d1a 100644
--- a/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaPagingTest.java
+++ b/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaPagingTest.java
@@ -16,47 +16,28 @@
*/
package org.apache.camel.component.jpa;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.AnnotatedElement;
import java.util.List;
-import java.util.Optional;
-import java.util.stream.IntStream;
-import org.apache.camel.Exchange;
-import org.apache.camel.Processor;
-import org.apache.camel.RoutesBuilder;
-import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.examples.Customer;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-public class JpaPagingTest extends AbstractJpaMethodSupport {
+public class JpaPagingTest extends JpaWithOptionsTestSupport {
private static final String ENDPOINT_URI = "jpa://" + Customer.class.getName() +
"?query=select c from Customer c order by c.name";
- // should be less than 1000 as numbers in entries' names are formatted for sorting with %03d (or change format)
- private static final int ENTRIES_COUNT = 30;
-
- private static final String ENTRY_SEQ_FORMAT = "%03d";
-
- // both should be less than ENTRIES_COUNT / 2
+ // both should be less than JpaWithOptionsTestSupport.ENTRIES_COUNT / 2
private static final int FIRST_RESULT = 5;
private static final int MAXIMUM_RESULTS = 10;
- protected String additionalQueryParameters = "";
-
@Test
public void testUnrestrictedQueryReturnsAll() throws Exception {
final List<Customer> customers = runQueryTest();
- assertEquals(ENTRIES_COUNT, customers.size());
+ assertEquals(JpaWithOptionsTestSupport.ENTRIES_COUNT, customers.size());
}
@Test
@@ -64,13 +45,13 @@ public class JpaPagingTest extends AbstractJpaMethodSupport {
public void testFirstResultInUri() throws Exception {
final List<Customer> customers = runQueryTest();
- assertEquals(ENTRIES_COUNT - FIRST_RESULT, customers.size());
+ assertEquals(JpaWithOptionsTestSupport.ENTRIES_COUNT - FIRST_RESULT, customers.size());
}
@Test
public void testMaxResultsInHeader() throws Exception {
- final List<Customer> customers
- = runQueryTest(exchange -> exchange.getIn().setHeader(JpaConstants.JPA_MAXIMUM_RESULTS, MAXIMUM_RESULTS));
+ final List<Customer> customers = runQueryTest(
+ withHeader(JpaConstants.JPA_MAXIMUM_RESULTS, MAXIMUM_RESULTS));
assertEquals(MAXIMUM_RESULTS, customers.size());
}
@@ -100,7 +81,7 @@ public class JpaPagingTest extends AbstractJpaMethodSupport {
final List<Customer> customers = runQueryTest(
withHeader(JpaConstants.JPA_FIRST_RESULT, FIRST_RESULT * 2));
- assertEquals(ENTRIES_COUNT - (FIRST_RESULT * 2), customers.size());
+ assertEquals(JpaWithOptionsTestSupport.ENTRIES_COUNT - (FIRST_RESULT * 2), customers.size());
assertFirstCustomerSequence(customers, FIRST_RESULT * 2);
}
@@ -115,7 +96,7 @@ public class JpaPagingTest extends AbstractJpaMethodSupport {
}
@Test
- @AdditionalQueryParameters("firstResult=" + ENTRIES_COUNT)
+ @AdditionalQueryParameters("firstResult=" + JpaWithOptionsTestSupport.ENTRIES_COUNT)
public void testFirstResultAfterTheEnd() throws Exception {
final List<Customer> customers = runQueryTest();
@@ -123,77 +104,13 @@ public class JpaPagingTest extends AbstractJpaMethodSupport {
}
private static void assertFirstCustomerSequence(final List<Customer> customers, final int firstResult) {
- assertTrue(customers.get(0).getName().endsWith(String.format(ENTRY_SEQ_FORMAT, firstResult)));
- }
-
- @SuppressWarnings("unchecked")
- protected List<Customer> runQueryTest(final Processor... preRun) throws Exception {
- setUp(getEndpointUri());
-
- final Exchange result = template.send("direct:start", exchange -> {
- for (Processor processor : preRun) {
- processor.process(exchange);
- }
- });
-
- return (List<Customer>) result.getMessage().getBody(List.class);
- }
-
- @Override
- public void beforeEach(ExtensionContext context) throws Exception {
- super.beforeEach(context);
-
- final Optional<AnnotatedElement> element = context.getElement();
-
- if (element.isPresent()) {
- final AnnotatedElement annotatedElement = element.get();
- final AdditionalQueryParameters annotation = annotatedElement.getAnnotation(AdditionalQueryParameters.class);
- if (annotation != null && !annotation.value().isBlank()) {
- additionalQueryParameters = annotation.value();
- }
- }
- }
-
- @Override
- protected void setUp(String endpointUri) throws Exception {
- super.setUp(endpointUri);
-
- createCustomers();
- assertEntitiesInDatabase(ENTRIES_COUNT, Customer.class.getName());
- }
-
- protected void createCustomers() {
- IntStream.range(0, ENTRIES_COUNT).forEach(idx -> {
- Customer customer = createDefaultCustomer();
- customer.setName(String.format("%s " + ENTRY_SEQ_FORMAT, customer.getName(), idx));
- save(customer);
- });
- }
-
- @Override
- protected RoutesBuilder createRouteBuilder() throws Exception {
- final String endpointUri = getEndpointUri();
- return new RouteBuilder() {
- public void configure() {
- from("direct:start")
- .to(endpointUri);
- }
- };
+ assertTrue(customers.get(0).getName().endsWith(
+ String.format(JpaWithOptionsTestSupport.ENTRY_SEQ_FORMAT, firstResult)));
}
protected String getEndpointUri() {
return ENDPOINT_URI +
- (additionalQueryParameters.isBlank() ? "" : "&" + additionalQueryParameters);
- }
-
- protected Processor withHeader(final String headerName, final Object headerValue) {
- return exchange -> exchange.getIn().setHeader(headerName, headerValue);
- }
-
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ ElementType.METHOD })
- private @interface AdditionalQueryParameters {
- String value();
+ createAdditionalQueryParameters();
}
}
diff --git a/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaWithOptionsTestSupport.java b/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaWithOptionsTestSupport.java
new file mode 100644
index 00000000000..0394799f362
--- /dev/null
+++ b/components/camel-jpa/src/test/java/org/apache/camel/component/jpa/JpaWithOptionsTestSupport.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.camel.component.jpa;
+
+import java.lang.reflect.AnnotatedElement;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.IntStream;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.examples.Customer;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+public abstract class JpaWithOptionsTestSupport extends AbstractJpaMethodSupport {
+
+ // should be less than 1000 as numbers in entries' names are formatted for sorting with %03d (or change format)
+ static final int ENTRIES_COUNT = 30;
+ static final String ENTRY_SEQ_FORMAT = "%03d";
+
+ private String additionalQueryParameters = "";
+
+ protected String createAdditionalQueryParameters() {
+ return additionalQueryParameters.isBlank() ? "" : "&" + additionalQueryParameters;
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ final String endpointUri = getEndpointUri();
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").to(endpointUri);
+ }
+ };
+ }
+
+ protected Exchange doRunQueryTest(final Processor... preRun) throws Exception {
+ setUp(getEndpointUri());
+
+ return template.send("direct:start", exchange -> {
+ for (Processor processor : preRun) {
+ processor.process(exchange);
+ }
+ });
+
+ }
+
+ protected <E> E runQueryTest(Class<E> type, final Processor... preRun) throws Exception {
+ final Exchange result = doRunQueryTest(preRun);
+
+ Assertions.assertNull(result.getException());
+ Assertions.assertNull(result.getProperty(Exchange.EXCEPTION_CAUGHT));
+
+ return result.getMessage().getBody(type);
+
+ }
+
+ @SuppressWarnings(value = "unchecked")
+ protected List<Customer> runQueryTest(final Processor... preRun) throws Exception {
+ return (List<Customer>) runQueryTest(List.class, preRun);
+ }
+
+ protected Processor withHeader(final String headerName, final Object headerValue) {
+ return exchange -> exchange.getIn().setHeader(headerName, headerValue);
+ }
+
+ protected Processor withBody(final Object body) {
+ return exchange -> exchange.getIn().setBody(body);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ super.beforeEach(context);
+
+ final Optional<AnnotatedElement> element = context.getElement();
+ if (element.isPresent()) {
+ final AnnotatedElement annotatedElement = element.get();
+ final AdditionalQueryParameters annotation = annotatedElement.getAnnotation(AdditionalQueryParameters.class);
+ if (annotation != null && !annotation.value().isBlank()) {
+ additionalQueryParameters = annotation.value();
+ }
+ }
+ }
+
+ protected abstract String getEndpointUri();
+
+ protected void createCustomers() {
+ IntStream.range(0, ENTRIES_COUNT).forEach(idx -> {
+ Customer customer = createDefaultCustomer();
+ customer.setName(String.format("%s " + ENTRY_SEQ_FORMAT, customer.getName(), idx));
+ save(customer);
+ });
+ }
+
+ @Override
+ protected void setUp(String endpointUri) throws Exception {
+ super.setUp(endpointUri);
+ createCustomers();
+ assertEntitiesInDatabase(ENTRIES_COUNT, Customer.class.getName());
+ }
+
+}