1515 */
1616package org .springframework .data .jdbc .repository .query ;
1717
18- import java .util .ArrayList ;
19- import java .util .Collection ;
18+ import java .util .Collections ;
19+ import java .util .List ;
2020
2121import org .springframework .data .domain .Pageable ;
2222import org .springframework .data .domain .Sort ;
2323import org .springframework .data .jdbc .core .convert .JdbcConverter ;
24+ import org .springframework .data .mapping .PersistentPropertyPath ;
2425import org .springframework .data .mapping .context .MappingContext ;
2526import org .springframework .data .relational .core .dialect .Dialect ;
2627import org .springframework .data .relational .core .dialect .RenderContextFactory ;
28+ import org .springframework .data .relational .core .mapping .PersistentPropertyPathExtension ;
2729import org .springframework .data .relational .core .mapping .RelationalPersistentEntity ;
2830import org .springframework .data .relational .core .mapping .RelationalPersistentProperty ;
2931import org .springframework .data .relational .core .query .Criteria ;
32+ import org .springframework .data .relational .core .sql .Expression ;
3033import org .springframework .data .relational .core .sql .Select ;
3134import org .springframework .data .relational .core .sql .SelectBuilder ;
3235import org .springframework .data .relational .core .sql .SqlIdentifier ;
3538import org .springframework .data .relational .repository .query .RelationalEntityMetadata ;
3639import org .springframework .data .relational .repository .query .RelationalParameterAccessor ;
3740import org .springframework .data .relational .repository .query .RelationalQueryCreator ;
41+ import org .springframework .data .repository .query .Parameters ;
42+ import org .springframework .data .repository .query .parser .Part ;
3843import org .springframework .data .repository .query .parser .PartTree ;
3944import org .springframework .jdbc .core .namedparam .MapSqlParameterSource ;
4045import org .springframework .util .Assert ;
@@ -50,8 +55,6 @@ class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
5055 private final PartTree tree ;
5156 private final RelationalParameterAccessor accessor ;
5257 private final QueryMapper queryMapper ;
53-
54- private final MappingContext <RelationalPersistentEntity <?>, RelationalPersistentProperty > mappingContext ;
5558 private final RelationalEntityMetadata <?> entityMetadata ;
5659 private final RenderContextFactory renderContextFactory ;
5760
@@ -65,8 +68,8 @@ class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
6568 * @param entityMetadata relational entity metadata, must not be {@literal null}.
6669 * @param accessor parameter metadata provider, must not be {@literal null}.
6770 */
68- JdbcQueryCreator (PartTree tree , JdbcConverter converter , Dialect dialect ,
69- RelationalEntityMetadata <?> entityMetadata , RelationalParameterAccessor accessor ) {
71+ JdbcQueryCreator (PartTree tree , JdbcConverter converter , Dialect dialect , RelationalEntityMetadata <?> entityMetadata ,
72+ RelationalParameterAccessor accessor ) {
7073 super (tree , accessor );
7174
7275 Assert .notNull (converter , "JdbcConverter must not be null" );
@@ -76,12 +79,60 @@ class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
7679 this .tree = tree ;
7780 this .accessor = accessor ;
7881
79- this .mappingContext = (MappingContext ) converter .getMappingContext ();
8082 this .entityMetadata = entityMetadata ;
8183 this .queryMapper = new QueryMapper (dialect , converter );
8284 this .renderContextFactory = new RenderContextFactory (dialect );
8385 }
8486
87+ /**
88+ * Validate parameters for the derived query. Specifically checking that the query method defines scalar parameters
89+ * and collection parameters where required and that invalid parameter declarations are rejected.
90+ *
91+ * @param tree
92+ * @param parameters
93+ */
94+ public static void validate (PartTree tree , Parameters <?, ?> parameters ,
95+ MappingContext <? extends RelationalPersistentEntity <?>, ? extends RelationalPersistentProperty > context ) {
96+
97+ RelationalQueryCreator .validate (tree , parameters );
98+
99+ for (PartTree .OrPart parts : tree ) {
100+ for (Part part : parts ) {
101+
102+ PersistentPropertyPath <? extends RelationalPersistentProperty > propertyPath = context
103+ .getPersistentPropertyPath (part .getProperty ());
104+ PersistentPropertyPathExtension path = new PersistentPropertyPathExtension (context , propertyPath );
105+
106+ for (PersistentPropertyPathExtension pathToValidate = path ; path .getLength () > 0 ; path = path .getParentPath ()) {
107+ validateProperty (pathToValidate );
108+ }
109+ }
110+ }
111+ }
112+
113+ private static void validateProperty (PersistentPropertyPathExtension path ) {
114+
115+ if (!path .getParentPath ().isEmbedded () && path .getLength () > 1 ) {
116+ throw new IllegalArgumentException (
117+ String .format ("Cannot query by nested property: %s" , path .getRequiredPersistentPropertyPath ().toDotPath ()));
118+ }
119+
120+ if (path .isMultiValued () || path .isMap ()) {
121+ throw new IllegalArgumentException (String .format ("Cannot query by multi-valued property: %s" ,
122+ path .getRequiredPersistentPropertyPath ().getLeafProperty ().getName ()));
123+ }
124+
125+ if (!path .isEmbedded () && path .isEntity ()) {
126+ throw new IllegalArgumentException (
127+ String .format ("Cannot query by nested entity: %s" , path .getRequiredPersistentPropertyPath ().toDotPath ()));
128+ }
129+
130+ if (path .getRequiredPersistentPropertyPath ().getLeafProperty ().isReference ()) {
131+ throw new IllegalArgumentException (
132+ String .format ("Cannot query by reference: %s" , path .getRequiredPersistentPropertyPath ().toDotPath ()));
133+ }
134+ }
135+
85136 /**
86137 * Creates {@link ParametrizedQuery} applying the given {@link Criteria} and {@link Sort} definition.
87138 *
@@ -96,7 +147,12 @@ protected ParametrizedQuery complete(Criteria criteria, Sort sort) {
96147 Table table = Table .create (entityMetadata .getTableName ());
97148 MapSqlParameterSource parameterSource = new MapSqlParameterSource ();
98149
99- SelectBuilder .SelectFromAndJoin builder = Select .builder ().select (table .columns (getSelectProjection ())).from (table );
150+ List <? extends Expression > columns = table .columns (getSelectProjection ());
151+ if (columns .isEmpty ()) {
152+ columns = Collections .singletonList (table .asterisk ());
153+ }
154+
155+ SelectBuilder .SelectFromAndJoin builder = Select .builder ().select (columns ).from (table );
100156
101157 if (tree .isExistsProjection ()) {
102158 builder = builder .limit (1 );
@@ -132,27 +188,6 @@ private SqlIdentifier[] getSelectProjection() {
132188 return new SqlIdentifier [] { tableEntity .getIdColumn () };
133189 }
134190
135- Collection <SqlIdentifier > columnNames = unwrapColumnNames ("" , tableEntity );
136-
137- return columnNames .toArray (new SqlIdentifier [0 ]);
138- }
139-
140- private Collection <SqlIdentifier > unwrapColumnNames (String prefix , RelationalPersistentEntity <?> persistentEntity ) {
141-
142- Collection <SqlIdentifier > columnNames = new ArrayList <>();
143-
144- for (RelationalPersistentProperty property : persistentEntity ) {
145-
146- if (property .isEmbedded ()) {
147- columnNames .addAll (
148- unwrapColumnNames (prefix + property .getEmbeddedPrefix (), mappingContext .getPersistentEntity (property )));
149- }
150-
151- else {
152- columnNames .add (property .getColumnName ().transform (prefix ::concat ));
153- }
154- }
155-
156- return columnNames ;
191+ return new SqlIdentifier [0 ];
157192 }
158193}
0 commit comments