allArguments = new ArrayList<>(nameArguments);
+
+			if (hasArguments) {
+				allArguments.add(argsBuilder.build());
+			}
+
+			builder.add(name, allArguments.toArray());
+		}
+	}
+
+	/**
+	 * An extended variant of {@link CodeBlock.Builder} that supports building statements in a fluent way and extended for
+	 * functional {@link #addStatement(Consumer) statement creation}.
+	 * 
+	 * This builder provides additional methods for creating and managing code blocks, including support for control flow,
+	 * named arguments, and conditional statements. It is designed to enhance the readability and flexibility of code
+	 * block construction.
+	 * 
+	 * Use this builder to create complex code structures in a fluent and intuitive manner.
+	 *
+	 * @see CodeBlock.Builder
+	 */
+	public static class CodeBlockBuilder {
+
+		private final CodeBlock.Builder builder;
+
+		CodeBlockBuilder(CodeBlock.Builder builder) {
+			this.builder = builder;
+		}
+
+		/**
+		 * Determine whether this builder is empty.
+		 *
+		 * @return {@code true} if the builder is empty; {@code false} otherwise.
+		 * @see CodeBlock.Builder#isEmpty()
+		 */
+		public boolean isEmpty() {
+			return builder.isEmpty();
+		}
+
+		/**
+		 * Add a formatted statement to the code block.
+		 *
+		 * @param format the format string.
+		 * @param args the arguments for the format string.
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#add(String, Object...)
+		 */
+		@Contract("_, _ -> this")
+		public CodeBlockBuilder add(String format, @Nullable Object... args) {
+
+			builder.add(format, args);
+			return this;
+		}
+
+		/**
+		 * Add a {@link CodeBlock} as a statement to the code block.
+		 *
+		 * @param codeBlock the {@link CodeBlock} to add.
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#addStatement(CodeBlock)
+		 */
+		@Contract("_ -> this")
+		public CodeBlockBuilder addStatement(CodeBlock codeBlock) {
+
+			builder.addStatement(codeBlock);
+			return this;
+		}
+
+		/**
+		 * Add a statement to the code block using a {@link Consumer} to configure it.
+		 *
+		 * @param consumer the {@link Consumer} to configure the statement.
+		 * @return {@code this} builder.
+		 */
+		@Contract("null -> fail; _ -> this")
+		public CodeBlockBuilder addStatement(Consumer consumer) {
+
+			Assert.notNull(consumer, "Consumer must not be null");
+
+			StatementBuilder statementBuilder = new StatementBuilder();
+			consumer.accept(statementBuilder);
+
+			if (!statementBuilder.isEmpty()) {
+
+				this.add("$[");
+
+				for (CodeBlock block : statementBuilder.blocks) {
+					builder.add(block);
+				}
+
+				this.add(";\n$]");
+
+			}
+			return this;
+		}
+
+		/**
+		 * Add a {@link CodeBlock} to the code block.
+		 *
+		 * @param codeBlock the {@link CodeBlock} to add.
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#addStatement(CodeBlock)
+		 */
+		@Contract("_ -> this")
+		public CodeBlockBuilder add(CodeBlock codeBlock) {
+
+			builder.add(codeBlock);
+			return this;
+		}
+
+		/**
+		 * Add a formatted statement to the code block.
+		 *
+		 * @param format the format string.
+		 * @param args the arguments for the format string.
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#addStatement(String, Object...)
+		 */
+		@Contract("_, _ -> this")
+		public CodeBlockBuilder addStatement(String format, @Nullable Object... args) {
+
+			builder.addStatement(format, args);
+			return this;
+		}
+
+		/**
+		 * Add named arguments to the code block.
+		 *
+		 * @param format the format string.
+		 * @param arguments the named arguments.
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#addNamed(String, Map)
+		 */
+		@Contract("_, _ -> this")
+		public CodeBlockBuilder addNamed(String format, Map arguments) {
+
+			builder.addNamed(format, arguments);
+			return this;
+		}
+
+		/**
+		 * Begin a control flow block with the specified format and arguments.
+		 *
+		 * @param controlFlow the control flow format string.
+		 * @param args the arguments for the control flow format string.
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#beginControlFlow(String, Object...)
+		 */
+		@Contract("_, _ -> this")
+		public CodeBlockBuilder beginControlFlow(String controlFlow, @Nullable Object... args) {
+
+			builder.beginControlFlow(controlFlow, args);
+			return this;
+		}
+
+		/**
+		 * End the current control flow block with the specified format and arguments.
+		 *
+		 * @param controlFlow the control flow format string.
+		 * @param args the arguments for the control flow format string.
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#endControlFlow(String, Object...)
+		 */
+		@Contract("_, _ -> this")
+		public CodeBlockBuilder endControlFlow(String controlFlow, @Nullable Object... args) {
+
+			builder.endControlFlow(controlFlow, args);
+			return this;
+		}
+
+		/**
+		 * End the current control flow block.
+		 *
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#endControlFlow()
+		 */
+		@Contract("-> this")
+		public CodeBlockBuilder endControlFlow() {
+
+			builder.endControlFlow();
+			return this;
+		}
+
+		/**
+		 * Begin the next control flow block with the specified format and arguments.
+		 *
+		 * @param controlFlow the control flow format string.
+		 * @param args the arguments for the control flow format string.
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#nextControlFlow(String, Object...)
+		 */
+		@Contract("_, _ -> this")
+		public CodeBlockBuilder nextControlFlow(String controlFlow, @Nullable Object... args) {
+
+			builder.nextControlFlow(controlFlow, args);
+			return this;
+		}
+
+		/**
+		 * Indent the current code block.
+		 *
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#indent()
+		 */
+		@Contract("-> this")
+		public CodeBlockBuilder indent() {
+
+			builder.indent();
+			return this;
+		}
+
+		/**
+		 * Unindent the current code block.
+		 *
+		 * @return {@code this} builder.
+		 * @see CodeBlock.Builder#unindent()
+		 */
+		@Contract("-> this")
+		public CodeBlockBuilder unindent() {
+
+			builder.unindent();
+			return this;
+		}
+
+		/**
+		 * Build the {@link CodeBlock} from the current state of the builder.
+		 *
+		 * @return the constructed {@link CodeBlock}.
+		 */
+		@CheckReturnValue
+		public CodeBlock build() {
+			return builder.build();
+		}
+
+		/**
+		 * Clear the current state of the builder.
+		 *
+		 * @return {@code this} builder.
+		 */
+		@Contract("-> this")
+		public CodeBlockBuilder clear() {
+
+			builder.clear();
+			return this;
+		}
+
+	}
+
+	/**
+	 * Builder for creating statements including conditional and concatenated variants.
+	 * 
+	 * This builder allows for the creation of complex statements with conditional logic and concatenated elements. It is
+	 * designed to simplify the construction of dynamic code blocks.
+	 * 
+	 * Use this builder to handle conditional inclusion in a structured and fluent manner instead of excessive conditional
+	 * nesting that would be required otherwise in the calling code.
+	 */
+	public static class StatementBuilder {
+
+		private final List blocks = new ArrayList<>();
+
+		StatementBuilder() {}
+
+		/**
+		 * Determine whether this builder is empty.
+		 *
+		 * @return {@code true} if the builder is empty; {@code false} otherwise.
+		 */
+		public boolean isEmpty() {
+			return blocks.isEmpty();
+		}
+
+		/**
+		 * Add a conditional statement to the builder if the condition is  met.
+		 *
+		 * @param state the condition to evaluate.
+		 * @return a {@link ConditionalStatementStep} for further configuration.
+		 */
+		public ConditionalStatementStep when(boolean state) {
+			return whenNot(!state);
+		}
+
+		/**
+		 * Add a conditional statement to the builder if the condition is not  met.
+		 *
+		 * @param state the condition to evaluate.
+		 * @return a {@link ConditionalStatementStep} for further configuration.
+		 */
+		public ConditionalStatementStep whenNot(boolean state) {
+
+			return (format, args) -> {
+
+				if (!state) {
+					add(format, args);
+				}
+				return this;
+			};
+		}
+
+		/**
+		 * Add a formatted statement to the builder.
+		 *
+		 * @param format the format string.
+		 * @param args the arguments for the format string.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_, _ -> this")
+		public StatementBuilder add(String format, @Nullable Object... args) {
+			return add(CodeBlock.of(format, args));
+		}
+
+		/**
+		 * Add a {@link CodeBlock} to the statement builder.
+		 *
+		 * @param codeBlock the code block to add.
+		 * @return {@code this} builder.
+		 */
+		@Contract("null -> fail; _ -> this")
+		public StatementBuilder add(CodeBlock codeBlock) {
+
+			Assert.notNull(codeBlock, "CodeBlock must not be null");
+			blocks.add(codeBlock);
+			return this;
+		}
+
+		/**
+		 * Concatenate elements into the builder with a delimiter.
+		 *
+		 * @param elements the elements to concatenate.
+		 * @param delim the delimiter to use between elements.
+		 * @param mapper the mapping function to apply to each element returning a {@link CodeBlock} to add.
+		 * @param  the type of the elements.
+		 * @return {@code this} builder.
+		 */
+		@Contract("null, _ -> fail; _, _ -> this")
+		public  StatementBuilder addAll(Iterable extends T> elements, String delim,
+				Function super T, CodeBlock> mapper) {
+			return addAll(elements, t -> delim, mapper);
+		}
+
+		/**
+		 * Concatenate elements into the builder with a custom delimiter function.
+		 *
+		 * @param elements the elements to concatenate.
+		 * @param delim the function to determine the delimiter for each element. Delimiters are applied beginning with the
+		 *          second iteration element and obtain from the current element.
+		 * @param mapper the mapping function to apply to each element returning a {@link CodeBlock} to add.
+		 * @param  the type of the elements.
+		 * @return {@code this} builder.
+		 */
+		@Contract("null, _, _ -> fail; _, _, _ -> this")
+		public  StatementBuilder addAll(Iterable extends T> elements, Function super T, String> delim,
+				Function super T, CodeBlock> mapper) {
+
+			Assert.notNull(elements, "Elements must not be null");
+
+			boolean first = true;
+			for (T element : elements) {
+
+				if (first) {
+					first = false;
+				} else {
+					blocks.add(CodeBlock.of(delim.apply(element)));
+				}
+
+				add(mapper.apply(element));
+			}
+
+			return this;
+		}
+
+		/**
+		 * Functional interface for conditional statement steps.
+		 */
+		public interface ConditionalStatementStep {
+
+			/**
+			 * Add a statement to the builder if the condition is met.
+			 *
+			 * @param format the format string.
+			 * @param args the arguments for the format string.
+			 * @return the {@link StatementBuilder}.
+			 */
+			StatementBuilder then(String format, @Nullable Object... args);
+
+		}
+
+	}
+
+	/**
+	 * Builder for constructing return statements based on the target return type. The resulting {@link #build()
+	 * CodeBlock} must be added as a {@link CodeBlock.Builder#addStatement(CodeBlock)}.
+	 */
+	public abstract static class ReturnBuilderSupport {
+
+		private final List rules = new ArrayList<>();
+		private final List fallback = new ArrayList<>();
+
+		/**
+		 * Create a new builder.
+		 */
+		ReturnBuilderSupport() {}
+
+		/**
+		 * Add a return statement if the given condition is {@code true}.
+		 *
+		 * @param condition the condition to evaluate.
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_, _, _ -> this")
+		public ReturnBuilderSupport when(boolean condition, String format, @Nullable Object... args) {
+			this.rules.add(ruleOf(condition, format, args));
+			return this;
+		}
+
+		/**
+		 * Add a fallback return statement if no previous return statement was added.
+		 *
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_, _ -> this")
+		public ReturnBuilderSupport otherwise(String format, @Nullable Object... args) {
+			this.fallback.add(ruleOf(true, format, args));
+			return this;
+		}
+
+		/**
+		 * Add a fallback return statement if no previous return statement was added.
+		 *
+		 * @param builderConsumer the code block builder consumer to apply.
+		 * @return {@code this} builder.
+		 */
+		ReturnBuilderSupport otherwise(Consumer builderConsumer) {
+			this.fallback.add(new ReturnRule(true, "", new Object[] {}, builderConsumer));
+			return this;
+		}
+
+		/**
+		 * Build the code block representing the return statement.
+		 *
+		 * @return the resulting {@code CodeBlock}
+		 */
+		@CheckReturnValue
+		public CodeBlock build() {
+
+			CodeBlock.Builder builder = CodeBlock.builder();
+
+			for (ReturnRule rule : (Iterable extends ReturnRule>) () -> Stream
+					.concat(this.rules.stream(), this.fallback.stream()).iterator()) {
+				if (rule.condition()) {
+					builder.add("return");
+					rule.accept(builder);
+					return builder.build();
+				}
+			}
+
+			return builder.build();
+		}
+
+		/**
+		 * Add a return statement if the given condition is {@code true}.
+		 *
+		 * @param condition the condition to evaluate.
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		static ReturnRule ruleOf(boolean condition, String format, @Nullable Object... args) {
+
+			Assert.notNull(format, "Format must not be null");
+
+			if (format.startsWith("return")) {
+				throw new IllegalArgumentException("Return value format '%s' must not contain 'return'".formatted(format));
+			}
+
+			return new ReturnRule(condition, format, args, null);
+		}
+
+	}
+
+	record ReturnRule(boolean condition, String format, @Nullable Object[] args,
+			@Nullable Consumer builderCustomizer) {
+
+		public void accept(CodeBlock.Builder builder) {
+
+			if (StringUtils.hasText(format()) || builderCustomizer() != null) {
+
+				builder.add(" ");
+
+				if (StringUtils.hasText(format())) {
+					builder.add(format(), args());
+				}
+
+				if (builderCustomizer() != null) {
+					builderCustomizer().accept(builder);
+				}
+			}
+		}
+
+	}
+
+	/**
+	 * Builder for constructing return statements based conditionally on the target return type. The resulting
+	 * {@link #build() CodeBlock} must be added as a {@link CodeBlock.Builder#addStatement(CodeBlock)}.
+	 */
+	public static class TypedReturnBuilder extends ReturnBuilderSupport {
+
+		private final ResolvableType returnType;
+
+		/**
+		 * Create a new builder for the given return type.
+		 *
+		 * @param returnType the method return type
+		 */
+		TypedReturnBuilder(ResolvableType returnType) {
+
+			Assert.notNull(returnType, "Return type must not be null");
+
+			this.returnType = returnType;
+
+			// consider early return cases for Void and void.
+			whenBoxed(Void.class, "null");
+			when(ReflectionUtils.isVoid(returnType.toClass()), "");
+		}
+
+		/**
+		 * Add return statements for numeric types if the given {@code resultToReturn} points to a {@link Number}. Considers
+		 * primitive and boxed {@code int} and {@code long} type return paths and that {@code resultToReturn} can be
+		 * {@literal null}.
+		 *
+		 * @param resultToReturn the argument or variable name holding the result.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_ -> this")
+		public TypedReturnBuilder number(String resultToReturn) {
+			return whenBoxedLong("$1L != null ? $1L.longValue() : null", resultToReturn)
+					.whenLong("$1L != null ? $1L.longValue() : 0L", resultToReturn)
+					.whenBoxedInteger("$1L != null ? $1L.intValue() : null", resultToReturn)
+					.whenInt("$1L != null ? $1L.intValue() : 0", resultToReturn);
+		}
+
+		/**
+		 * Add a return statement if the return type is boolean (primitive or box type).
+		 *
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_, _ -> this")
+		public TypedReturnBuilder whenBoolean(String format, @Nullable Object... args) {
+			return when(returnType.isAssignableFrom(boolean.class) || returnType.isAssignableFrom(Boolean.class), format,
+					args);
+		}
+
+		/**
+		 * Add a return statement if the return type is {@link Long} (boxed {@code long} type).
+		 *
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_, _ -> this")
+		public TypedReturnBuilder whenBoxedLong(String format, @Nullable Object... args) {
+			return whenBoxed(long.class, format, args);
+		}
+
+		/**
+		 * Add a return statement if the return type is a primitive {@code long} type.
+		 *
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_, _ -> this")
+		public TypedReturnBuilder whenLong(String format, @Nullable Object... args) {
+			return when(returnType.toClass() == long.class, format, args);
+		}
+
+		/**
+		 * Add a return statement if the return type is {@link Integer} (boxed {@code int} type).
+		 *
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_, _ -> this")
+		public TypedReturnBuilder whenBoxedInteger(String format, @Nullable Object... args) {
+			return whenBoxed(int.class, format, args);
+		}
+
+		/**
+		 * Add a return statement if the return type is a primitive {@code int} type.
+		 *
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_, _ -> this")
+		public TypedReturnBuilder whenInt(String format, @Nullable Object... args) {
+			return when(returnType.toClass() == int.class, format, args);
+		}
+
+		/**
+		 * Add a return statement if the return type matches the given boxed wrapper type.
+		 *
+		 * @param primitiveOrWrapper the primitive or wrapper type.
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("null, _, _ -> fail; _, _, _ -> this")
+		public TypedReturnBuilder whenBoxed(Class> primitiveOrWrapper, String format, @Nullable Object... args) {
+
+			Class> primitiveWrapper = ClassUtils.resolvePrimitiveIfNecessary(primitiveOrWrapper);
+			return when(returnType.toClass() == primitiveWrapper, format, args);
+		}
+
+		/**
+		 * Add a return statement if the return type matches the given primitive or boxed wrapper type.
+		 *
+		 * @param primitiveType the primitive or wrapper type.
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("null, _, _ -> fail; _, _, _ -> this")
+		public TypedReturnBuilder whenPrimitiveOrBoxed(Class> primitiveType, String format, @Nullable Object... args) {
+
+			Class> primitiveWrapper = ClassUtils.resolvePrimitiveIfNecessary(primitiveType);
+			return when(
+					ClassUtils.isAssignable(ClassUtils.resolvePrimitiveIfNecessary(returnType.toClass()), primitiveWrapper),
+					format, args);
+		}
+
+		/**
+		 * Add a return statement if the declared return type is assignable from the given {@code returnType}.
+		 *
+		 * @param returnType the candidate return type.
+		 * @param format the code format string.
+		 * @param args the format arguments
+		 * @return {@code this} builder.
+		 */
+		@Contract("null, _, _ -> fail; _, _, _ -> this")
+		public TypedReturnBuilder when(Class> returnType, String format, @Nullable Object... args) {
+
+			Assert.notNull(returnType, "Return type must not be null");
+			return when(this.returnType.isAssignableFrom(returnType), format, args);
+		}
+
+		/**
+		 * Add a return statement if the given condition is {@code true}.
+		 *
+		 * @param condition the condition to evaluate.
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Override
+		@Contract("_, _, _ -> this")
+		public TypedReturnBuilder when(boolean condition, String format, @Nullable Object... args) {
+			super.when(condition, format, args);
+			return this;
+		}
+
+		/**
+		 * Add a fallback return statement considering that the returned value might be nullable and apply conditionally
+		 * {@link Optional#ofNullable(Object)} wrapping if the return type is {@code Optional}.
+		 *
+		 * @param codeBlock the code block result to be returned.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_ -> this")
+		public TypedReturnBuilder optional(CodeBlock codeBlock) {
+			return optional("$L", codeBlock);
+		}
+
+		/**
+		 * Add a fallback return statement considering that the returned value might be nullable and apply conditionally
+		 * {@link Optional#ofNullable(Object)} wrapping if the return type is {@code Optional}.
+		 *
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Contract("null, _ -> fail; _, _ -> this")
+		public TypedReturnBuilder optional(String format, @Nullable Object... args) {
+
+			if (Optional.class.isAssignableFrom(returnType.toClass())) {
+
+				Assert.hasText(format, "Format must not be null or empty");
+				if (format.startsWith("return")) {
+					throw new IllegalArgumentException("Return value format '%s' must not contain 'return'".formatted(format));
+				}
+
+				otherwise(builder -> {
+
+					builder.add("$T.ofNullable(", Optional.class);
+					builder.add(format, args);
+					builder.add(")");
+				});
+
+				return this;
+			}
+
+			return otherwise(format, args);
+		}
+
+		/**
+		 * Add a fallback return statement if no previous return statement was added.
+		 *
+		 * @param codeBlock the code block result to be returned.
+		 * @return {@code this} builder.
+		 */
+		@Contract("_ -> this")
+		public TypedReturnBuilder otherwise(CodeBlock codeBlock) {
+			return otherwise("$L", codeBlock);
+		}
+
+		/**
+		 * Add a fallback return statement if no previous return statement was added.
+		 *
+		 * @param format the code format string.
+		 * @param args the format arguments.
+		 * @return {@code this} builder.
+		 */
+		@Override
+		@Contract("_, _ -> this")
+		public TypedReturnBuilder otherwise(String format, @Nullable Object... args) {
+			super.otherwise(format, args);
+			return this;
+		}
+
+	}
+
+	record CodeTuple(String format, @Nullable Object... args) {
+
+	}
+
+}
diff --git a/src/main/java/org/springframework/data/javapoet/TypeNames.java b/src/main/java/org/springframework/data/javapoet/TypeNames.java
new file mode 100644
index 0000000000..eb9db1a9b9
--- /dev/null
+++ b/src/main/java/org/springframework/data/javapoet/TypeNames.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2025 the original author or 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.springframework.data.javapoet;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.javapoet.TypeName;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Collection of {@link org.springframework.javapoet.TypeName} transformation utilities.
+ * 
+ * This class delivers some simple functionality that should be provided by the JavaPoet framework. It also provides
+ * easy-to-use methods to convert between types.
+ * 
+ * Mainly for internal use within the framework
+ *
+ * @author Mark Paluch
+ * @since 4.0
+ */
+public abstract class TypeNames {
+
+	/**
+	 * Obtain a {@link TypeName class name} for the given type, resolving primitive wrappers as necessary.
+	 *
+	 * @param type the class to use.
+	 * @return the corresponding {@link TypeName}.
+	 */
+	public static TypeName classNameOrWrapper(Class> type) {
+		return ClassUtils.isPrimitiveOrWrapper(type) ? TypeName.get(ClassUtils.resolvePrimitiveIfNecessary(type))
+				: TypeName.get(type);
+	}
+
+	/**
+	 * Obtain a {@link TypeName class name} for the given {@link ResolvableType}, resolving primitive wrappers as
+	 * necessary. Ideal to represent a type name used as {@code Class} value as generic parameters are not considered.
+	 *
+	 * @param resolvableType the resolvable type to use.
+	 * @return the corresponding {@link TypeName}.
+	 */
+	public static TypeName classNameOrWrapper(ResolvableType resolvableType) {
+		return classNameOrWrapper(resolvableType.toClass());
+	}
+
+	/**
+	 * Obtain a {@link TypeName} for the given {@link ResolvableType}. Ideal to represent a type name used as
+	 * {@code Class} value as generic parameters are not considered.
+	 *
+	 * @param resolvableType the resolvable type to use.
+	 * @return the corresponding {@link TypeName}.
+	 */
+	public static TypeName className(ResolvableType resolvableType) {
+		return TypeName.get(resolvableType.toClass());
+	}
+
+	/**
+	 * Obtain a {@link TypeName} for the underlying type of the given {@link ResolvableType}. Can render a class name, a
+	 * type signature or a generic type variable.
+	 *
+	 * @param resolvableType the resolvable type represent.
+	 * @return the corresponding {@link TypeName}.
+	 */
+	public static TypeName typeName(ResolvableType resolvableType) {
+		return TypeName.get(resolvableType.getType());
+	}
+
+	/**
+	 * Obtain a {@link TypeName} for the given type, resolving primitive wrappers as necessary. Ideal to represent a type
+	 * parameter for parametrized types as primitive boxing is considered.
+	 *
+	 * @param type the class to be represented.
+	 * @return the corresponding {@link TypeName}.
+	 */
+	public static TypeName typeNameOrWrapper(Class> type) {
+		return typeNameOrWrapper(ResolvableType.forClass(type));
+	}
+
+	/**
+	 * Obtain a {@link TypeName} for the given {@link ResolvableType}, resolving primitive wrappers as necessary. Can
+	 * render a class name, a type signature or a generic type variable. Ideal to represent a type parameter for
+	 * parametrized types as primitive boxing is considered.
+	 *
+	 * @param resolvableType the resolvable type to be represented.
+	 * @return the corresponding {@link TypeName}.
+	 */
+	public static TypeName typeNameOrWrapper(ResolvableType resolvableType) {
+		return ClassUtils.isPrimitiveOrWrapper(resolvableType.toClass())
+				? TypeName.get(ClassUtils.resolvePrimitiveIfNecessary(resolvableType.toClass()))
+				: typeName(resolvableType);
+	}
+
+	private TypeNames() {}
+
+}
diff --git a/src/main/java/org/springframework/data/javapoet/package-info.java b/src/main/java/org/springframework/data/javapoet/package-info.java
new file mode 100644
index 0000000000..c0a6b8c24e
--- /dev/null
+++ b/src/main/java/org/springframework/data/javapoet/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Opinionated extensions to JavaPoet to support Spring Data specific use cases.
+ */
+@org.jspecify.annotations.NullMarked
+package org.springframework.data.javapoet;
diff --git a/src/main/java/org/springframework/data/repository/aot/generate/AotQueryMethodGenerationContext.java b/src/main/java/org/springframework/data/repository/aot/generate/AotQueryMethodGenerationContext.java
index 34fc7f8ff9..2720f992a6 100644
--- a/src/main/java/org/springframework/data/repository/aot/generate/AotQueryMethodGenerationContext.java
+++ b/src/main/java/org/springframework/data/repository/aot/generate/AotQueryMethodGenerationContext.java
@@ -47,6 +47,7 @@ public class AotQueryMethodGenerationContext {
 	private final QueryMethod queryMethod;
 	private final RepositoryInformation repositoryInformation;
 	private final AotRepositoryFragmentMetadata targetTypeMetadata;
+	private final MethodReturn methodReturn;
 	private final MethodMetadata targetMethodMetadata;
 	private final VariableNameFactory variableNameFactory;
 	private final ExpressionMarker expressionMarker;
@@ -60,6 +61,8 @@ protected AotQueryMethodGenerationContext(RepositoryInformation repositoryInform
 		this.repositoryInformation = repositoryInformation;
 		this.targetTypeMetadata = new AotRepositoryFragmentMetadata();
 		this.targetMethodMetadata = new MethodMetadata(repositoryInformation, method);
+		this.methodReturn = new MethodReturn(queryMethod.getResultProcessor().getReturnedType(),
+				targetMethodMetadata.getReturnType());
 		this.variableNameFactory = LocalVariableNameFactory.forMethod(targetMethodMetadata);
 		this.expressionMarker = new ExpressionMarker();
 	}
@@ -73,6 +76,8 @@ protected AotQueryMethodGenerationContext(RepositoryInformation repositoryInform
 		this.repositoryInformation = repositoryInformation;
 		this.targetTypeMetadata = targetTypeMetadata;
 		this.targetMethodMetadata = new MethodMetadata(repositoryInformation, method);
+		this.methodReturn = new MethodReturn(queryMethod.getResultProcessor().getReturnedType(),
+				targetMethodMetadata.getReturnType());
 		this.variableNameFactory = LocalVariableNameFactory.forMethod(targetMethodMetadata);
 		this.expressionMarker = new ExpressionMarker();
 	}
@@ -135,6 +140,13 @@ public Class> getDomainType() {
 		return getRepositoryInformation().getDomainType();
 	}
 
+	/**
+	 * @return the method return information.
+	 */
+	public MethodReturn getMethodReturn() {
+		return methodReturn;
+	}
+
 	/**
 	 * @return the returned type without considering dynamic projections.
 	 */
@@ -146,6 +158,7 @@ public ReturnedType getReturnedType() {
 	 * @return the actual returned domain type.
 	 * @see org.springframework.data.repository.core.RepositoryMetadata#getReturnedDomainClass(Method)
 	 */
+	@Deprecated(forRemoval = true)
 	public ResolvableType getActualReturnType() {
 		return targetMethodMetadata.getActualReturnType();
 	}
@@ -154,6 +167,7 @@ public ResolvableType getActualReturnType() {
 	 * @return the query method return type.
 	 * @see org.springframework.data.repository.core.RepositoryMetadata#getReturnType(Method)
 	 */
+	@Deprecated(forRemoval = true)
 	public ResolvableType getReturnType() {
 		return targetMethodMetadata.getReturnType();
 	}
@@ -161,6 +175,7 @@ public ResolvableType getReturnType() {
 	/**
 	 * @return the {@link TypeName} representing the method return type.
 	 */
+	@Deprecated(forRemoval = true)
 	public TypeName getReturnTypeName() {
 		return TypeName.get(getReturnType().getType());
 	}
@@ -168,6 +183,7 @@ public TypeName getReturnTypeName() {
 	/**
 	 * @return the {@link TypeName} representing the actual (component) method return type.
 	 */
+	@Deprecated(forRemoval = true)
 	public TypeName getActualReturnTypeName() {
 		return TypeName.get(getActualReturnType().getType());
 	}
diff --git a/src/main/java/org/springframework/data/repository/aot/generate/MethodReturn.java b/src/main/java/org/springframework/data/repository/aot/generate/MethodReturn.java
new file mode 100644
index 0000000000..c8b4c4f296
--- /dev/null
+++ b/src/main/java/org/springframework/data/repository/aot/generate/MethodReturn.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2025 the original author or 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.springframework.data.repository.aot.generate;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.data.javapoet.TypeNames;
+import org.springframework.data.repository.query.ReturnedType;
+import org.springframework.data.util.ReflectionUtils;
+import org.springframework.data.util.TypeInformation;
+import org.springframework.javapoet.TypeName;
+
+/**
+ * Value object that encapsulates introspection of a method's return type, providing convenient access to its
+ * characteristics such as projection, optionality, array status, and actual type information.
+ * 
+ * Designed to support repository method analysis in the context of Ahead-of-Time (AOT) processing, this class leverages
+ * {@link ReturnedType}, {@link ResolvableType}, and {@link TypeInformation} to expose both the declared and actual
+ * return types, including handling of wrapper types, projections, and primitive types.
+ * 
+ * Typical usage involves querying the return type characteristics to drive code generation or runtime behavior in
+ * repository infrastructure.
+ *
+ * @author Mark Paluch
+ * @since 4.0
+ */
+public class MethodReturn {
+
+	private final ReturnedType returnedType;
+	private final Class> actualReturnClass;
+	private final ResolvableType returnType;
+	private final ResolvableType actualType;
+	private final TypeName typeName;
+	private final TypeName className;
+	private final TypeName actualTypeName;
+	private final TypeName actualClassName;
+
+	/**
+	 * Create a new {@code MethodReturn} instance based on the given {@link ReturnedType} and its {@link ResolvableType
+	 * method return type}.
+	 *
+	 * @param returnedType the returned type to inspect.
+	 * @param returnType the method return type.
+	 */
+	public MethodReturn(ReturnedType returnedType, ResolvableType returnType) {
+
+		this.returnedType = returnedType;
+		this.returnType = returnType;
+		this.typeName = TypeNames.typeName(returnType);
+		this.className = TypeNames.className(returnType);
+
+		Class> returnClass = returnType.toClass();
+		TypeInformation> typeInformation = TypeInformation.of(returnType);
+		TypeInformation> actualType = typeInformation.isMap() ? typeInformation
+				: (typeInformation.getType().equals(Stream.class) ? typeInformation.getComponentType()
+						: typeInformation.getActualType());
+
+		if (actualType != null) {
+			this.actualType = actualType.toResolvableType();
+			this.actualTypeName = TypeNames.typeName(this.actualType);
+			this.actualClassName = TypeNames.className(this.actualType);
+			this.actualReturnClass = actualType.getType();
+		} else {
+			this.actualType = returnType;
+			this.actualTypeName = typeName;
+			this.actualClassName = className;
+			this.actualReturnClass = returnClass;
+		}
+	}
+
+	/**
+	 * Returns whether the method return type is a projection. Query projections (e.g. returning {@code String} or
+	 * {@code int} are not considered.
+	 *
+	 * @return {@literal true} if the return type is a projection.
+	 */
+	public boolean isProjecting() {
+		return returnedType.isProjecting();
+	}
+
+	/**
+	 * Returns whether the method return type is an interface-based projection.
+	 *
+	 * @return {@literal true} if the return type is an interface-based projection.
+	 */
+	public boolean isInterfaceProjection() {
+		return isProjecting() && returnedType.getReturnedType().isInterface();
+	}
+
+	/**
+	 * Returns whether the method return type is {@code Optional}.
+	 *
+	 * @return {@literal true} if the return type is {@code Optional}.
+	 */
+	public boolean isOptional() {
+		return Optional.class.isAssignableFrom(toClass());
+	}
+
+	/**
+	 * Returns whether the method return type is an array.
+	 *
+	 * @return {@literal true} if the return type is an array.
+	 */
+	public boolean isArray() {
+		return toClass().isArray();
+	}
+
+	/**
+	 * Returns whether the method return type is {@code void}. Considers also {@link Void} and Kotlin's {@code Unit}.
+	 *
+	 * @return {@literal true} if the return type is {@code void}.
+	 */
+	public boolean isVoid() {
+		return ReflectionUtils.isVoid(toClass());
+	}
+
+	/**
+	 * Returns the {@link Class} representing the declared return type.
+	 *
+	 * @return the declared return class.
+	 */
+	public Class> toClass() {
+		return returnType.toClass();
+	}
+
+	/**
+	 * Returns the actual type (i.e. component type of a collection).
+	 *
+	 * @return the actual type.
+	 */
+	public ResolvableType getActualType() {
+		return actualType;
+	}
+
+	/**
+	 * Returns the {@link TypeName} representing the declared return type.
+	 *
+	 * @return the declared return type name.
+	 */
+	public TypeName getTypeName() {
+		return typeName;
+	}
+
+	/**
+	 * Returns the {@link TypeName} representing the declared return class (i.e. without generics).
+	 *
+	 * @return the declared return class name.
+	 */
+	public TypeName getClassName() {
+		return className;
+	}
+
+	/**
+	 * Returns the actual {@link TypeName} representing the declared return type (component type of collections).
+	 *
+	 * @return the actual return type name.
+	 */
+	public TypeName getActualTypeName() {
+		return actualTypeName;
+	}
+
+	/**
+	 * Returns the actual {@link TypeName} representing the declared return class (component type of collections).
+	 *
+	 * @return the actual return class name.
+	 */
+	public TypeName getActualClassName() {
+		return actualClassName;
+	}
+
+	/**
+	 * Returns the {@link Class} representing the actual return type.
+	 *
+	 * @return the actual return class.
+	 */
+	public Class> getActualReturnClass() {
+		return actualReturnClass;
+	}
+
+}
diff --git a/src/test/java/org/springframework/data/javapoet/JavaPoetUnitTests.java b/src/test/java/org/springframework/data/javapoet/JavaPoetUnitTests.java
new file mode 100644
index 0000000000..c549b91a56
--- /dev/null
+++ b/src/test/java/org/springframework/data/javapoet/JavaPoetUnitTests.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2025 the original author or 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.springframework.data.javapoet;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.javapoet.CodeBlock;
+
+/**
+ * Unit tests for {@link LordOfTheStrings}.
+ *
+ * @author Mark Paluch
+ */
+class JavaPoetUnitTests {
+
+	@Test // GH-3357
+	void shouldConsiderConditionals() {
+
+		assertThat(LordOfTheStrings.builder().addStatement(statementBuilder -> {
+			statementBuilder.when(true).then("return $S", "The Return of the King");
+			statementBuilder.when(false).then("return $S", "The Two Towers");
+		}).build()).hasToString("return \"The Return of the King\";\n");
+
+		assertThat(LordOfTheStrings.builder().addStatement(statementBuilder -> {
+			assertThat(statementBuilder.isEmpty()).isTrue();
+			statementBuilder.whenNot(true).then("return $S", "The Return of the King");
+			assertThat(statementBuilder.isEmpty()).isTrue();
+
+			statementBuilder.whenNot(false).then("return $S", "The Two Towers");
+			assertThat(statementBuilder.isEmpty()).isFalse();
+		}).build()).hasToString("return \"The Two Towers\";\n");
+
+		assertThat(LordOfTheStrings.builder().addStatement(statementBuilder -> {
+
+			assertThat(statementBuilder.isEmpty()).isTrue();
+			statementBuilder.add("foo");
+			assertThat(statementBuilder.isEmpty()).isFalse();
+		}).build()).hasToString("foo;\n");
+	}
+
+	@Test // GH-3357
+	void shouldConcatenateCollections() {
+
+		assertThat(LordOfTheStrings.builder().addStatement(statementBuilder -> {
+			statementBuilder.addAll(Arrays.asList("foo", "bar"), ", ", it -> CodeBlock.of(it + " $S", it));
+		}).build()).hasToString("foo \"foo\", bar \"bar\";\n");
+
+		assertThat(LordOfTheStrings.builder().addStatement(statementBuilder -> {
+			statementBuilder.addAll(Arrays.asList("foo", "barrrr"), it -> "" + it.length(),
+					it -> CodeBlock.of(it + " $S", it));
+		}).build()).hasToString("foo \"foo\"6barrrr \"barrrr\";\n");
+	}
+
+	@Test // GH-3357
+	void youShallNotPass() {
+
+		// without expecting arguments, the second $L is superfluous.
+		assertThatIllegalArgumentException().isThrownBy(() -> LordOfTheStrings.invoke("$L.run($L)", "runnable").build());
+	}
+
+	@Test // GH-3357
+	void shouldRenderMethodCall() {
+
+		CodeBlock block = LordOfTheStrings.invoke("$L.run()", "runnable").build();
+		assertThat(block).hasToString("runnable.run()");
+	}
+
+	@Test // GH-3357
+	void shouldRenderMethodCallWithArguments() {
+
+		CodeBlock block = LordOfTheStrings.invoke("$L.run($L)", "runnable").argument("foo").build();
+		assertThat(block).hasToString("runnable.run(foo)");
+
+		block = LordOfTheStrings.invoke("$L.run($L)", "runnable").argument("foo").argument("bar").build();
+		assertThat(block).hasToString("runnable.run(foo, bar)");
+
+		block = LordOfTheStrings.invoke("$L.run($L)", "runnable").argument("$L", "foo").argument("bar").build();
+		assertThat(block).hasToString("runnable.run(foo, bar)");
+
+		block = LordOfTheStrings.invoke("$L.run($L)", "runnable").arguments(Arrays.asList("foo", "bar")).build();
+		assertThat(block).hasToString("runnable.run(foo, bar)");
+
+		block = LordOfTheStrings.invoke("$L.run($L)", "runnable").arguments(List.of()).build();
+		assertThat(block).hasToString("runnable.run()");
+
+		block = LordOfTheStrings.invoke("$L.run($L)", "runnable")
+				.arguments(List.of("foo", "bar"), it -> CodeBlock.of("$S", it)).build();
+		assertThat(block).hasToString("runnable.run(\"foo\", \"bar\")");
+	}
+
+	@Test // GH-3357
+	void shouldRenderAssignTo() {
+
+		CodeBlock block = LordOfTheStrings.invoke("$L.run()", "runnable").assignTo("$T result", String.class);
+		assertThat(block).hasToString("java.lang.String result = runnable.run()");
+	}
+
+	@Test // GH-3357
+	void shouldRenderSimpleReturn() {
+
+		CodeBlock block = LordOfTheStrings.returning(Long.class).otherwise("1L").build();
+		assertThat(block).hasToString("return 1L");
+	}
+
+	@Test // GH-3357
+	void shouldRenderConditionalLongReturn() {
+
+		CodeBlock block = LordOfTheStrings.returning(Long.class).whenLong("1L").whenBoxedLong("$T.valueOf(1)", Long.class)
+				.otherwise("😫").build();
+		assertThat(block).hasToString("return java.lang.Long.valueOf(1)");
+
+		block = LordOfTheStrings.returning(Long.class).whenBoxedLong("$T.valueOf(1)", Long.class).whenLong("1L")
+				.otherwise("😫").build();
+		assertThat(block).hasToString("return java.lang.Long.valueOf(1)");
+
+		block = LordOfTheStrings.returning(long.class).whenBoxedLong("$T.valueOf(1)", Long.class).whenLong("1L")
+				.otherwise("😫").build();
+		assertThat(block).hasToString("return 1L");
+
+		block = LordOfTheStrings.returning(Long.class).whenBoxed(Long.class, "$T.valueOf(1)", Long.class).otherwise("😫")
+				.build();
+		assertThat(block).hasToString("return java.lang.Long.valueOf(1)");
+
+		block = LordOfTheStrings.returning(Long.class).whenBoxed(long.class, "$T.valueOf(1)", Long.class).otherwise("😫")
+				.build();
+		assertThat(block).hasToString("return java.lang.Long.valueOf(1)");
+	}
+
+	@Test // GH-3357
+	void shouldRenderConditionalIntReturn() {
+
+		CodeBlock block = LordOfTheStrings.returning(Integer.class).whenBoxed(long.class, "$T.valueOf(1)", Long.class)
+				.otherwise("😫").build();
+		assertThat(block).hasToString("return 😫");
+
+		block = LordOfTheStrings.returning(Integer.class).whenBoxedInteger("$T.valueOf(1)", Integer.class).otherwise("😫")
+				.build();
+		assertThat(block).hasToString("return java.lang.Integer.valueOf(1)");
+
+		block = LordOfTheStrings.returning(int.class).whenBoxedInteger("$T.valueOf(1)", Integer.class).whenInt("1")
+				.otherwise("😫").build();
+		assertThat(block).hasToString("return 1");
+	}
+
+	@Test // GH-3357
+	void shouldRenderConditionalBooleanReturn() {
+
+		CodeBlock block = LordOfTheStrings.returning(boolean.class).whenBoolean("$L", true).otherwise("😫").build();
+		assertThat(block).hasToString("return true");
+
+		block = LordOfTheStrings.returning(Boolean.class).whenBoolean("$L", true).otherwise("😫").build();
+		assertThat(block).hasToString("return true");
+	}
+
+	@Test // GH-3357
+	void shouldRenderConditionalNumericReturn() {
+
+		CodeBlock block = LordOfTheStrings.returning(boolean.class).number("someNumericVariable").otherwise("😫").build();
+		assertThat(block).hasToString("return 😫");
+
+		block = LordOfTheStrings.returning(long.class).number("someNumericVariable").otherwise("😫").build();
+		assertThat(block).hasToString("return someNumericVariable != null ? someNumericVariable.longValue() : 0L");
+
+		block = LordOfTheStrings.returning(Long.class).number("someNumericVariable").otherwise("😫").build();
+		assertThat(block).hasToString("return someNumericVariable != null ? someNumericVariable.longValue() : null");
+
+		block = LordOfTheStrings.returning(int.class).number("someNumericVariable").otherwise("😫").build();
+		assertThat(block).hasToString("return someNumericVariable != null ? someNumericVariable.intValue() : 0");
+
+		block = LordOfTheStrings.returning(Integer.class).number("someNumericVariable").otherwise("😫").build();
+		assertThat(block).hasToString("return someNumericVariable != null ? someNumericVariable.intValue() : null");
+	}
+
+	@Test // GH-3357
+	void shouldRenderConditionalOptional() {
+
+		CodeBlock block = LordOfTheStrings.returning(Optional.class).optional(CodeBlock.of("$S", "foo")).build();
+		assertThat(block).hasToString("return java.util.Optional.ofNullable(\"foo\")");
+
+		block = LordOfTheStrings.returning(String.class).optional(CodeBlock.of("$S", "foo")).build();
+		assertThat(block).hasToString("return \"foo\"");
+	}
+
+}
diff --git a/src/test/java/org/springframework/data/repository/aot/generate/AotRepositoryCreatorUnitTests.java b/src/test/java/org/springframework/data/repository/aot/generate/AotRepositoryCreatorUnitTests.java
index 3dbff067fe..5ed0352f7d 100644
--- a/src/test/java/org/springframework/data/repository/aot/generate/AotRepositoryCreatorUnitTests.java
+++ b/src/test/java/org/springframework/data/repository/aot/generate/AotRepositoryCreatorUnitTests.java
@@ -27,6 +27,7 @@
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.mockito.Answers;
 
 import org.springframework.aot.generate.Generated;
 import org.springframework.aot.hint.TypeReference;
@@ -153,7 +154,7 @@ void appliesQueryMethodContributor() {
 
 		repositoryCreator.contributeMethods((method) -> {
 
-			return new MethodContributor<>(mock(QueryMethod.class), null) {
+			return new MethodContributor<>(mock(QueryMethod.class, Answers.RETURNS_MOCKS), null) {
 
 				@Override
 				public MethodSpec contribute(AotQueryMethodGenerationContext context) {