Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

programmatic result set mappings #691

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions api/src/main/java/jakarta/persistence/ColumnResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
/**
* Used in conjunction with the {@link SqlResultSetMapping},
* {@link NamedNativeQuery}, or {@link ConstructorResult}
* annotation to map a column of the SELECT list of a SQL query.
* annotation to map a column of the {@code SELECT} list of
* a SQL query to a scalar (non-entity) value.
*
* <p>The {@link #name} element references the name of a column
* in the SELECT list &#8212; i.e., column alias, if applicable.
* Scalar result types can be included in the query result by
* specifying this annotation in the metadata.
* in the {@code SELECT} list&mdash;that is, the column alias,
* if applicable. Scalar result types can be included in the
* query result by specifying this annotation in the metadata.
*
* <p>Example:
* {@snippet :
Expand Down Expand Up @@ -68,7 +69,7 @@
public @interface ColumnResult {

/**
* (Required) The name of a column in the SELECT clause of a SQL query
* (Required) The name of a column in the {@code SELECT} clause of a SQL query.
*/
String name();

Expand Down
33 changes: 20 additions & 13 deletions api/src/main/java/jakarta/persistence/ConstructorResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@

/**
* Used in conjunction with the {@link SqlResultSetMapping} or
* {@link NamedNativeQuery} annotation to map the SELECT clause
* of a SQL query to a constructor.
* {@link NamedNativeQuery} annotation to map the {@code SELECT}
* clause of a SQL query to the constructor of an arbitrary
* Java class.
*
* <p>Applies a constructor for the target class, passing in as
* arguments values from the specified columns. All columns
* corresponding to arguments of the intended constructor must
* be specified using the {@link #columns} element of the
* {@code ConstructorResult} annotation in the same order as that
* of the argument list of the constructor. Any entities returned
* as constructor results will be in either the new or detached
* state, depending on whether a primary key is retrieved for
* the constructed object.
* <p>When processing a result set, the provider instantiates
* the {@linkplain #targetClass target class} by calling a
* matching constructor of the class, passing as arguments the
* values of the specified {@linkplain #columns} of the result
* set. Columns must be explicitly listed by {@link #columns}
* in the same order as their corresponding parameters occur in
* the argument list of the constructor.
*
* <p>The target class need not be a managed type. Any instance
* of an entity class returned as a constructor result will be
* in either the new or detached state, depending on whether a
* primary key was assigned to the constructed object.
*
* <p>Example:
* {@snippet :
Expand Down Expand Up @@ -72,12 +76,15 @@

/**
* (Required) The class whose constructor is to be invoked.
* This may be any Java class with a constructor matching
* the specified {@linkplain #columns columns}.
*/
Class<?> targetClass();

/**
* (Required) The mapping of columns in the SELECT list
* to the arguments of the intended constructor, in order.
* (Required) The mapping of columns in the {@code SELECT}
* list to arguments of a constructor of the specified Java
* {@linkplain #targetClass target class}, in order.
*/
ColumnResult[] columns();
}
10 changes: 10 additions & 0 deletions api/src/main/java/jakarta/persistence/EntityManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.sql.ResultSetMapping;

/**
* Interface used to interact with the persistence context.
Expand Down Expand Up @@ -1138,6 +1139,15 @@ void refresh(Object entity,
*/
Query createNativeQuery(String sqlString, String resultSetMapping);

/**
* Create an instance of {@link TypedQuery} for executing
* a native SQL query.
* @param sqlString a native SQL query string
* @param resultSetMapping the result set mapping
* @return the new query instance
*/
<T> TypedQuery<T> createNativeQuery(String sqlString, ResultSetMapping<T> resultSetMapping);

/**
* Create an instance of {@link StoredProcedureQuery} for executing
* a stored procedure in the database.
Expand Down
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
32 changes: 20 additions & 12 deletions api/src/main/java/jakarta/persistence/EntityResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,22 @@

/**
* Used in conjunction with the {@link SqlResultSetMapping} or
* {@link NamedNativeQuery} annotation to map the SELECT clause
* of a SQL query to an entity result.
* {@link NamedNativeQuery} annotation to map the {@code SELECT}
* clause of a SQL query to an entity result.
*
* <p>If this annotation is used, the SQL statement should select
* all the columns that are mapped to the entity object.
* This should include foreign key columns to related entities.
* The results obtained when insufficient data is available
* are undefined.
* <p>The SQL {@code SELECT} statement should select every column
* mapped by the {@linkplain #entityClass entity class}, including
* foreign key columns of related entities. If a mapped column is
* missing from the SQL result set, the behavior is undefined.
*
* <p>If the names of the columns of the result set of the SQL
* statement exactly match the column names mapped by the entity
* class, then it is not necessary to explicitly specify mappings
* for the {@linkplain #fields} or {@linkplain #discriminatorColumn
* discriminator column} of the entity. Otherwise, if a column name
* of the SQL result set does not exactly match the column name
* mapped by the entity class, the {@link FieldResult} annotation
* must be used to explicitly specify the mapping.
*
* <p>Example:
* {@snippet :
Expand All @@ -57,7 +65,7 @@
*/
@Target({})
@Retention(RUNTIME)
public @interface EntityResult {
public @interface EntityResult {

/**
* The class of the result.
Expand All @@ -71,14 +79,14 @@
LockModeType lockMode() default LockModeType.OPTIMISTIC;

/**
* Maps the columns specified in the SELECT list of the
* query to the properties or fields of the entity class.
* Maps the columns specified in the {@code SELECT} list of
* the query to the properties or fields of the entity class.
*/
FieldResult[] fields() default {};

/**
* Specifies the column name (or alias) of the column in
* the SELECT list that is used to determine the type of
* Specifies the column name (or alias) of the column in the
* {@code SELECT} list that is used to determine the type of
* the entity instance.
*/
String discriminatorColumn() default "";
Expand Down
14 changes: 9 additions & 5 deletions api/src/main/java/jakarta/persistence/FieldResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@

/**
* Used in conjunction with the {@link EntityResult} annotation to map
* columns specified in the SELECT list of a SQL query to the properties
* or fields of an entity class.
* a column specified in the {@code SELECT} list of a SQL query to a
* property or field of an entity class.
*
* <p>The {@link #name} member specifies the name of the mapped field
* or property of the entity class. If the property or field is
* declared by a {@linkplain Embedded child embeddable object}, then
* {@link #name} specifies a qualified path.
*
* <p>Example:
* {@snippet :
Expand Down Expand Up @@ -59,13 +63,13 @@
public @interface FieldResult {

/**
* Name of the persistent field or property of the class.
* Name of the persistent field or property of an entity class.
*/
String name();

/**
* Name of the column in the SELECT clause - i.e., column
* aliases, if applicable.
* Name of the column in the {@code SELECT} clause&mdash;that is,
* the column alias, if applicable.
*/
String column();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Specifies a mapping of the columns of a result set of a native SQL query
* or stored procedure.
* Specifies a mapping of the columns of a result set of a native SQL
* query or stored procedure to {@linkplain EntityResult entities},
* {@linkplain ColumnResult scalar values}, and
* {@linkplain ConstructorResult Java class constructors}.
*
* <p>Example:
* {@snippet :
Expand Down Expand Up @@ -89,3 +92,5 @@
*/
ColumnResult[] columns() default {};
}


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

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

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

public static <T> ColumnMapping<T> of(String name, Class<T> type) {
return new ColumnMapping<>(name, type);
}
}
14 changes: 14 additions & 0 deletions api/src/main/java/jakarta/persistence/sql/CompoundMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package jakarta.persistence.sql;

public record CompoundMapping(MappingElement<?>[] elements)
implements ResultSetMapping<Object[]> {
public static CompoundMapping of(MappingElement<?>... elements) {
return new CompoundMapping(elements);
}

@Override
public Class<Object[]> type() {
return Object[].class;
}
}

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

public record ConstructorMapping<T>(Class<T> targetClass, ColumnMapping<?>[] columns)
implements MappingElement<T>, ResultSetMapping<T> {

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

@Override
public Class<T> type() {
return targetClass;
}
}
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);
}
}
27 changes: 27 additions & 0 deletions api/src/main/java/jakarta/persistence/sql/EntityMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package jakarta.persistence.sql;

import jakarta.persistence.LockModeType;

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

@SafeVarargs
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> of(Class<T> entityClass, String discriminatorColumn, MemberMapping<T>... fields) {
return new EntityMapping<>(entityClass, LockModeType.NONE, discriminatorColumn, fields);
}

@SafeVarargs
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<T> type() {
return entityClass;
}
}
15 changes: 15 additions & 0 deletions api/src/main/java/jakarta/persistence/sql/FieldMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package jakarta.persistence.sql;

import jakarta.persistence.metamodel.SingularAttribute;

public record FieldMapping<C,T>(Class<C> container, Class<T> type, String name, String column) implements MemberMapping<C> {

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);
}

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);
}
}

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

public interface MappingElement<T> {
}
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> {
}
Loading
Loading