Skip to content

DATAJDBC-340 - Using new SQL generation DSL. #147

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

Closed
wants to merge 3 commits into from
Closed
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
2 changes: 1 addition & 1 deletion mvnw
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# "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
# 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
Expand Down
4 changes: 2 additions & 2 deletions mvnw.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
Expand Down Expand Up @@ -122,7 +122,7 @@ set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain

set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)

@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.0.DATAJDBC-340-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data Relational Parent</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-jdbc-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.0.DATAJDBC-340-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-jdbc</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.0.DATAJDBC-340-SNAPSHOT</version>

<name>Spring Data JDBC</name>
<description>Spring Data module for JDBC repositories.</description>
Expand All @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.0.DATAJDBC-340-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
/*
* Copyright 2019 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.jdbc.core;

import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
* A wrapper around a {@link org.springframework.data.mapping.PersistentPropertyPath} for making common operations
* available used in SQL generation.
*
* @author Jens Schauder
* @since 1.1
*/
class PersistentPropertyPathExtension {

private final RelationalPersistentEntity<?> entity;
private final @Nullable PersistentPropertyPath<RelationalPersistentProperty> path;
private final MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> context;

PersistentPropertyPathExtension(MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> context,
RelationalPersistentEntity<?> entity) {

Assert.notNull(context, "Context must not be null.");
Assert.notNull(entity, "Entity must not be null.");

this.context = context;
this.entity = entity;
this.path = null;
}

PersistentPropertyPathExtension(MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> context,
PersistentPropertyPath<RelationalPersistentProperty> path) {

Assert.notNull(context, "Context must not be null.");
Assert.notNull(path, "Path must not be null.");
Assert.notNull(path.getBaseProperty(), "Path must not be empty.");

this.context = context;
this.entity = path.getBaseProperty().getOwner();
this.path = path;
}

/**
* Returns {@literal true} exactly when the path is non empty and the leaf property an embedded one.
*
* @return if the leaf property is embedded.
*/
boolean isEmbedded() {
return path != null && path.getRequiredLeafProperty().isEmbedded();
}

/**
* Returns the path that has the same beginning but is one segment shorter than this path.
*
* @return the parent path. Guaranteed to be not {@literal null}.
* @throws IllegalStateException when called on an empty path.
*/
PersistentPropertyPathExtension getParentPath() {

if (path == null) {
throw new IllegalStateException("The parent path of a root path is not defined.");
}

if (path.getLength() == 1) {
return new PersistentPropertyPathExtension(context, entity);
}

return new PersistentPropertyPathExtension(context, path.getParentPath());
}

/**
* Returns {@literal true} if there are multiple values for this path, i.e. if the path contains at least one element
* that is a collection and array or a map.
*
* @return {@literal true} if the path contains a multivalued element.
*/
boolean isMultiValued() {

return path != null && //
(path.getRequiredLeafProperty().isCollectionLike() //
|| path.getRequiredLeafProperty().isQualified() //
|| getParentPath().isMultiValued() //
);
}

/**
* The {@link RelationalPersistentEntity} associated with the leaf of this path.
*
* @return Might return {@literal null} when called on a path that does not represent an entity.
*/
@Nullable
RelationalPersistentEntity<?> getLeafEntity() {
return path == null ? entity : context.getPersistentEntity(path.getRequiredLeafProperty().getActualType());
}

/**
* @return {@literal true} when this is an empty path or the path references an entity.
*/
boolean isEntity() {
return path == null || path.getRequiredLeafProperty().isEntity();
}

/**
* @return {@literal true} when this is references a {@link java.util.List} or {@link java.util.Map}.
*/
boolean isQualified() {
return path != null && path.getRequiredLeafProperty().isQualified();
}

/**
* @return {@literal true} when this is references a {@link java.util.Collection} or an array.
*/
boolean isCollectionLike() {
return path != null && path.getRequiredLeafProperty().isCollectionLike();
}

/**
* The name of the column used to reference the id in the parent table.
*
* @throws IllegalStateException when called on an empty path.
*/
String getReverseColumnName() {

Assert.state(path != null, "Path is null");

return path.getRequiredLeafProperty().getReverseColumnName();
}

/**
* The alias used in select for the column used to reference the id in the parent table.
*
* @throws IllegalStateException when called on an empty path.
*/
String getReverseColumnNameAlias() {

return prefixWithTableAlias(getReverseColumnName());
}

/**
* The name of the column used to represent this property in the database.
*
* @throws IllegalStateException when called on an empty path.
*/
String getColumnName() {

Assert.state(path != null, "Path is null");

return assembleColumnName(path.getRequiredLeafProperty().getColumnName());
}

/**
* The alias for the column used to represent this property in the database.
*
* @throws IllegalStateException when called on an empty path.
*/
String getColumnAlias() {

return prefixWithTableAlias(getColumnName());
}

/**
* @return {@literal true} if this path represents an entity which has an Id attribute.
*/
boolean hasIdProperty() {

RelationalPersistentEntity<?> leafEntity = getLeafEntity();
return leafEntity != null && leafEntity.hasIdProperty();
}

PersistentPropertyPathExtension getIdDefiningParentPath() {

PersistentPropertyPathExtension parent = getParentPath();
if (parent.path == null) {
return parent;
}
if (parent.isEmbedded()) {
return getParentPath().getIdDefiningParentPath();
}
return parent;
}

/**
* The name of the table this path is tied to or of the longest ancestor path that is actually tied to a table.
*
* @return the name of the table. Guaranteed to be not {@literal null}.
*/
String getTableName() {
return getTableOwningAncestor().getRequiredLeafEntity().getTableName();
}

/**
* The alias used for the table on which this path is based.
*
* @return a table alias, {@literal null} if the table owning path is the empty path.
*/
@Nullable
String getTableAlias() {

PersistentPropertyPathExtension tableOwner = getTableOwningAncestor();

return tableOwner.path == null ? null : tableOwner.assembleTableAlias();

}

/**
* The column name of the id column of the ancestor path that represents an actual table.
*/
String getIdColumnName() {
return getTableOwningAncestor().getRequiredLeafEntity().getIdColumn();
}

/**
* If the table owning ancestor has an id the column name of that id property is returned. Otherwise the reverse
* column is returned.
*/
String getEffectiveIdColumnName() {

PersistentPropertyPathExtension owner = getTableOwningAncestor();
return owner.path == null ? owner.getRequiredLeafEntity().getIdColumn() : owner.getReverseColumnName();
}

/**
* The length of the path.
*/
int getLength() {
return path == null ? 0 : path.getLength();
}

/**
* Finds and returns the longest path with ich identical or an ancestor to the current path and maps directly to a
* table.
*
* @return a path. Guaranteed to be not {@literal null}.
*/
private PersistentPropertyPathExtension getTableOwningAncestor() {

return isEntity() && !isEmbedded() ? this : getParentPath().getTableOwningAncestor();
}

private String assembleTableAlias() {

Assert.state(path != null, "Path is null");

RelationalPersistentProperty leafProperty = path.getRequiredLeafProperty();
String prefix = isEmbedded() ? leafProperty.getEmbeddedPrefix() : leafProperty.getName();

if (path.getLength() == 1) {
Assert.notNull(prefix, "Prefix mus not be null.");
return prefix;
}

PersistentPropertyPathExtension parentPath = getParentPath();
return parentPath.isEmbedded() ? parentPath.assembleTableAlias() + prefix
: parentPath.assembleTableAlias() + "_" + prefix;
}

private String assembleColumnName(String suffix) {

Assert.state(path != null, "Path is null");

if (path.getLength() <= 1) {
return suffix;
}
PersistentPropertyPath<RelationalPersistentProperty> parentPath = path.getParentPath();
RelationalPersistentProperty parentLeaf = parentPath.getRequiredLeafProperty();
if (!parentLeaf.isEmbedded()) {
return suffix;
}
String embeddedPrefix = parentLeaf.getEmbeddedPrefix();
return getParentPath().assembleColumnName(embeddedPrefix + suffix);
}

@SuppressWarnings("unchecked")
private RelationalPersistentEntity<?> getRequiredLeafEntity() {
return path == null ? entity : context.getRequiredPersistentEntity(path.getRequiredLeafProperty().getActualType());
}

private String prefixWithTableAlias(String columnName) {

String tableAlias = getTableAlias();
return tableAlias == null ? columnName : tableAlias + "_" + columnName;
}

}
Loading