Skip to content

Commit

Permalink
third solution for result set mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
gavinking committed Dec 20, 2024
1 parent f6c5bab commit da79526
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 98 deletions.
15 changes: 15 additions & 0 deletions api/src/main/java/jakarta/persistence/EntityManagerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.sql.ResultSetMapping;

/**
* Interface used to interact with the persistence unit, and to
Expand Down Expand Up @@ -371,6 +372,20 @@ public interface EntityManagerFactory extends AutoCloseable {
*/
<E> Map<String, EntityGraph<? extends E>> getNamedEntityGraphs(Class<E> entityType);

/**
* A map keyed by {@linkplain SqlResultSetMapping#name name}, containing
* {@linkplain ResultSetMapping result set mappings} to every result set
* mapping defined in annotations whose inferred result type is assignable
* to the given Java type.
* @param resultType any Java type, including {@code Object.class}
* meaning all result set mappings
* @return a map keyed by query name
* @param <R> the specified upper bound on the query result types
*
* @since 4.0
*/
<R> Map<String, ResultSetMapping<R>> getResultSetMappings(Class<R> resultType);

/**
* Create a new application-managed {@link EntityManager} with an active
* transaction, and execute the given function, passing the {@code EntityManager}
Expand Down
15 changes: 3 additions & 12 deletions api/src/main/java/jakarta/persistence/sql/ColumnMapping.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
package jakarta.persistence.sql;

import jakarta.persistence.ColumnResult;

import java.lang.annotation.Annotation;

public record ColumnMapping<T>(String name, Class<T> type)
implements ColumnResult, MappingElement<T> {
implements MappingElement<T> {

public static ColumnMapping<Object> map(String name) {
public static ColumnMapping<Object> of(String name) {
return new ColumnMapping<>(name, Object.class);
}

public static <T> ColumnMapping<T> map(String name, Class<T> type) {
public static <T> ColumnMapping<T> of(String name, Class<T> type) {
return new ColumnMapping<>(name, type);
}

@Override
public Class<? extends Annotation> annotationType() {
return ColumnResult.class;
}
}
16 changes: 3 additions & 13 deletions api/src/main/java/jakarta/persistence/sql/ConstructorMapping.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
package jakarta.persistence.sql;

import jakarta.persistence.ColumnResult;
import jakarta.persistence.ConstructorResult;
public record ConstructorMapping<T>(Class<T> targetClass, ColumnMapping<?>[] columns)
implements MappingElement<T> {

import java.lang.annotation.Annotation;

public record ConstructorMapping<T>(Class<T> targetClass, ColumnResult[] columns)
implements ConstructorResult, MappingElement<T> {

public static <T> ConstructorMapping<T> map(Class<T> targetClass, ColumnResult... columns) {
public static <T> ConstructorMapping<T> of(Class<T> targetClass, ColumnMapping<?>... columns) {
return new ConstructorMapping<>(targetClass, columns);
}

@Override
public Class<? extends Annotation> annotationType() {
return ConstructorResult.class;
}
}
17 changes: 17 additions & 0 deletions api/src/main/java/jakarta/persistence/sql/EmbeddableMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package jakarta.persistence.sql;

import jakarta.persistence.metamodel.SingularAttribute;

public record EmbeddableMapping<C,T>(Class<C> container, Class<T> embeddableClass, String name, MemberMapping<?>[] fields)
implements MemberMapping<C> {

@SafeVarargs
public static <C,T> EmbeddableMapping<C,T> of(Class<C> container, Class<T> embeddableClass, String name, MemberMapping<T>... fields) {
return new EmbeddableMapping<>(container, embeddableClass, name, fields);
}

@SafeVarargs
public static <C,T> EmbeddableMapping<C,T> of(SingularAttribute<C,T> embedded, MemberMapping<T>... fields) {
return new EmbeddableMapping<>(embedded.getDeclaringType().getJavaType(), embedded.getJavaType(), embedded.getName(), fields);
}
}
19 changes: 5 additions & 14 deletions api/src/main/java/jakarta/persistence/sql/EntityMapping.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
package jakarta.persistence.sql;

import jakarta.persistence.EntityResult;
import jakarta.persistence.FieldResult;
import jakarta.persistence.LockModeType;

import java.lang.annotation.Annotation;

public record EntityMapping<T>(Class<T> entityClass, LockModeType lockMode, String discriminatorColumn, FieldResult[] fields)
implements EntityResult, MappingElement<T> {
public record EntityMapping<T>(Class<T> entityClass, LockModeType lockMode, String discriminatorColumn, MemberMapping<?>[] fields)
implements MappingElement<T> {

@SafeVarargs
public static <T> EntityMapping<T> map(Class<T> entityClass, FieldMapping<T>... fields) {
public static <T> EntityMapping<T> of(Class<T> entityClass, MemberMapping<T>... fields) {
return new EntityMapping<>(entityClass, LockModeType.NONE, "", fields);
}

@SafeVarargs
public static <T> EntityMapping<T> map(Class<T> entityClass, String discriminatorColumn, FieldMapping<T>... fields) {
public static <T> EntityMapping<T> of(Class<T> entityClass, String discriminatorColumn, MemberMapping<T>... fields) {
return new EntityMapping<>(entityClass, LockModeType.NONE, discriminatorColumn, fields);
}

@SafeVarargs
public static <T> EntityMapping<T> map(Class<T> entityClass, LockModeType lockMode, String discriminatorColumn, FieldMapping<T>... fields) {
public static <T> EntityMapping<T> of(Class<T> entityClass, LockModeType lockMode, String discriminatorColumn, MemberMapping<T>... fields) {
return new EntityMapping<>(entityClass, lockMode, discriminatorColumn, fields);
}

@Override
public Class<? extends Annotation> annotationType() {
return EntityResult.class;
}
}
15 changes: 5 additions & 10 deletions api/src/main/java/jakarta/persistence/sql/FieldMapping.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
package jakarta.persistence.sql;

import jakarta.persistence.FieldResult;
import jakarta.persistence.metamodel.SingularAttribute;

import java.lang.annotation.Annotation;
public record FieldMapping<C,T>(Class<C> container, Class<T> type, String name, String column) implements MemberMapping<C> {

public record FieldMapping<T>(String name, String column)
implements FieldResult {

public static <T> FieldMapping<T> map(SingularAttribute<T,?> attribute, String column) {
return new FieldMapping<>(attribute.getName(), column);
public static <C,T> FieldMapping<C,T> of(Class<C> container, Class<T> type, String name, String column) {
return new FieldMapping<>(container, type, name, column);
}

@Override
public Class<? extends Annotation> annotationType() {
return FieldResult.class;
public static <C,T> FieldMapping<C,T> of(SingularAttribute<C,T> attribute, String column) {
return new FieldMapping<>(attribute.getDeclaringType().getJavaType(), attribute.getJavaType(), attribute.getName(), column);
}
}

13 changes: 0 additions & 13 deletions api/src/main/java/jakarta/persistence/sql/MappingElement.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
package jakarta.persistence.sql;

import java.lang.reflect.Array;
import java.util.Arrays;

public interface MappingElement<T> {

@SuppressWarnings("unchecked")
private static <R> R[] newArrayInstance(Class<R> type, int length) {
return (R[]) Array.newInstance(type, length);
}
static <R> R[] extract(Class<R> type, MappingElement[] mappingElements) {
return Arrays.stream(mappingElements)
.filter(type::isInstance)
.toArray(length -> newArrayInstance(type, length));
}
}
4 changes: 4 additions & 0 deletions api/src/main/java/jakarta/persistence/sql/MemberMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package jakarta.persistence.sql;

public interface MemberMapping<T> {
}
91 changes: 55 additions & 36 deletions api/src/main/java/jakarta/persistence/sql/ResultSetMapping.java
Original file line number Diff line number Diff line change
@@ -1,48 +1,67 @@
package jakarta.persistence.sql;

import jakarta.persistence.ColumnResult;
import jakarta.persistence.ConstructorResult;
import jakarta.persistence.EntityResult;
import jakarta.persistence.SqlResultSetMapping;
/**
* Specifies a mapping of the columns of a result set of a SQL query or stored procedure
* to {@linkplain EntityMapping entities}, {@linkplain ColumnMapping scalar values}, and
* {@linkplain ConstructorMapping Java class constructors}.
*
* <p>This class may be instantiated programmatically, for example:
* {@snippet :
* var entityMapping =
* ResultSetMapping.of(
* EntityMapping.of(Author.class,
* FieldMapping.of(Author_.ssn, "author_ssn"),
* EmbeddableMapping.of(Author_.name,
* FieldMapping.of(Name_.first, "author_first_name"),
* FieldMapping.of(Name_.last, "author_last_name"))));
*
* var constructorMapping =
* ResultSetMapping.of(
* ConstructorMapping.of(Summary.class,
* ColumnMapping.of("book_isbn"),
* ColumnMapping.of("book_title"),
* ColumnMapping.of("book_author")));
*
* var mixedMapping =
* ResultSetMapping.of(
* EntityMapping.of(Author.class),
* EntityMapping.of(Book.class,
* FieldMapping.of(Book_.isbn, "book_isbn")),
* ColumnMapping.of("sales", BigDecimal.class),
* ConstructorMapping.of(Summary.class,
* ColumnMapping.of("book_isbn"),
* ColumnMapping.of("book_title")));
* }
*
* <p>Alternatively, an instance representing a
* {@linkplain jakarta.persistence.SqlResultSetMapping result set mapping defined using annotations}
* may be obtained via {@link jakarta.persistence.EntityManagerFactory#getResultSetMappings}.
*
* <p>A {@code ResultSetMapping} may be used to
* {@linkplain jakarta.persistence.EntityManager#createNativeQuery(String, ResultSetMapping) obtain}
* and execute a {@link jakarta.persistence.TypedQuery TypedQuery}.
*
* @since 4.0
*/
public record ResultSetMapping<T>(Class<T> resultType, MappingElement<?>[] elements) {

import java.lang.annotation.Annotation;

import static jakarta.persistence.sql.MappingElement.extract;

public record ResultSetMapping<T>(String name, EntityResult[] entities, ConstructorResult[] classes, ColumnResult[] columns)
implements SqlResultSetMapping {

public static ResultSetMapping<Object[]> create(MappingElement<?>... mappings) {
return new ResultSetMapping<>("",
extract(EntityResult.class, mappings),
extract(ConstructorResult.class, mappings),
extract(ColumnResult.class, mappings));
}

public static <T> ResultSetMapping<T> create(EntityMapping<T> entityMapping) {
return new ResultSetMapping<>("",
new EntityResult[]{entityMapping},
new ConstructorResult[0],
new ColumnResult[0]);
public static ResultSetMapping<Object[]> of(MappingElement<?>... mappings) {
return new ResultSetMapping<>(Object[].class, mappings);
}

public static <T> ResultSetMapping<T> create(ConstructorMapping<T> constructorMapping) {
return new ResultSetMapping<>("",
new EntityResult[0],
new ConstructorResult[]{constructorMapping},
new ColumnResult[0]);
public static <T> ResultSetMapping<T> of(EntityMapping<T> entityMapping) {
return new ResultSetMapping<>(entityMapping.entityClass(),
new EntityMapping[]{entityMapping});
}

public static <T> ResultSetMapping<T> create(ColumnMapping<T> columnResult) {
return new ResultSetMapping<>("",
new EntityResult[0],
new ConstructorResult[0],
new ColumnResult[]{columnResult});
public static <T> ResultSetMapping<T> of(ConstructorMapping<T> constructorMapping) {
return new ResultSetMapping<>(constructorMapping.targetClass(),
new ConstructorMapping[]{constructorMapping});
}

@Override
public Class<? extends Annotation> annotationType() {
return SqlResultSetMapping.class;
public static <T> ResultSetMapping<T> of(ColumnMapping<T> columnMapping) {
return new ResultSetMapping<>(columnMapping.type(),
new ColumnMapping[]{columnMapping});
}
}

0 comments on commit da79526

Please sign in to comment.