Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static Collection<String> getProjectionInputProperties(JpaEntityInformati
QueryUtils.checkSortExpression(it);
properties.add(it.getProperty());
});
properties.addAll(entity.getIdAttributeNames());
properties.addAll(entity.getIdAttributePaths());

return properties;
}
Expand Down Expand Up @@ -143,7 +143,7 @@ public Sort createSort(Sort sort, JpaEntityInformation<?, ?> entity) {
Collection<String> sortById;
Sort sortToUse;
if (entity.hasCompositeId()) {
sortById = new ArrayList<>(entity.getIdAttributeNames());
sortById = new ArrayList<>(entity.getIdAttributePaths());
} else {
sortById = new ArrayList<>(1);
sortById.add(entity.getRequiredIdAttribute().getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public Predicate compare(Order order, Expression<Comparable> propertyExpression,
return order.isAscending() ? cb.greaterThan(propertyExpression, compareValue)
: cb.lessThan(propertyExpression, compareValue);
}
return order.isAscending() ? cb.isNull(propertyExpression) : cb.isNotNull(propertyExpression);
return order.isAscending() ? cb.isNotNull(propertyExpression) : cb.isNull(propertyExpression);

}

Expand Down Expand Up @@ -161,7 +161,7 @@ public JpqlQueryBuilder.Predicate compare(Order order, JpqlQueryBuilder.Expressi

JpqlQueryBuilder.WhereStep where = JpqlQueryBuilder.where(propertyExpression);
if (value == null) {
return order.isAscending() ? where.isNull() : where.isNotNull();
return order.isAscending() ? where.isNotNull() : where.isNull();
}

QueryUtils.checkSortExpression(order);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ public interface JpaEntityInformation<T, ID> extends EntityInformation<T, ID>, J
*/
Collection<String> getIdAttributeNames();

/**
* Returns the attribute paths of the id attributes. If the entity has a composite id, then all id attribute paths are
* returned. If the entity has a single id attribute then this single attribute path is returned.
*/
Collection<String> getIdAttributePaths();

/**
* Extracts the value for the given id attribute from a composite id
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.springframework.data.jpa.util.JpaMetamodel;
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
* Implementation of {@link org.springframework.data.repository.core.EntityInformation} that uses JPA {@link Metamodel}
Expand Down Expand Up @@ -237,6 +238,11 @@ public Collection<String> getIdAttributeNames() {
return attributeNames;
}

@Override
public Collection<String> getIdAttributePaths() {
return idMetadata.attributePaths;
}

@Override
public @Nullable Object getCompositeIdAttributeValue(Object id, String idAttribute) {

Expand Down Expand Up @@ -266,7 +272,7 @@ public Map<String, Object> getKeyset(Iterable<String> propertyPaths, T entity) {
Map<String, Object> keyset = new LinkedHashMap<>();

if (hasCompositeId()) {
for (String idAttributeName : getIdAttributeNames()) {
for (String idAttributeName : getIdAttributePaths()) {
keyset.put(idAttributeName, getter.apply(idAttributeName));
}
} else {
Expand Down Expand Up @@ -304,15 +310,56 @@ private static class IdMetadata<T> implements Iterable<SingularAttribute<? super
private final IdentifiableType<T> type;
private final Set<SingularAttribute<? super T, ?>> idClassAttributes;
private final Set<SingularAttribute<? super T, ?>> attributes;
private final List<String> attributePaths;
private @Nullable Class<?> idType;

IdMetadata(IdentifiableType<T> source, PersistenceProvider persistenceProvider) {

this.type = source;
this.idClassAttributes = persistenceProvider.getIdClassAttributes(source);
this.attributes = source.hasSingleIdAttribute()
? Collections.singleton(source.getId(source.getIdType().getJavaType()))
: source.getIdClassAttributes();
this.attributes = findAttributes(source);
this.attributePaths = findAttributePaths(source, null);
}

private static <X> Set<SingularAttribute<? super X, ?>> findAttributes(IdentifiableType<X> source) {
if (source.hasSingleIdAttribute()) {
for (SingularAttribute<? super X, ?> attribute : source.getSingularAttributes()) {
if (attribute.isId()) {
return Collections.singleton(attribute);
}
}
}
return source.getIdClassAttributes();
}

private static <X> List<String> findAttributePaths(IdentifiableType<X> source, @Nullable String prefix) {
List<String> attributeNames = new ArrayList<>();

Set<SingularAttribute<? super X, ?>> attributes = findAttributes(source);
for (SingularAttribute<? super X, ?> attribute : attributes) {
final var name = prefix(prefix, attribute.getName());
if (attribute.isAssociation()) {
attributeNames.addAll(findAttributePaths(attribute.getType(), name));
} else {
attributeNames.add(name);
}
}

return attributeNames;
}

private static <X> List<String> findAttributePaths(Type<X> type, @Nullable String prefix) {
if (!(type instanceof IdentifiableType<?> identifiableType)) {
return Collections.emptyList();
}
return findAttributePaths(identifiableType, prefix);
}

private static String prefix(@Nullable String prefix, String value) {
if (prefix == null) {
return value;
}
return prefix + "." + value;
}

boolean hasSimpleId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ public Expression<?> createExpression(String property) {
public BooleanExpression compare(Order order, Expression<?> propertyExpression, @Nullable Object value) {

if (value == null) {
return Expressions.booleanOperation(order.isAscending() ? Ops.IS_NULL : Ops.IS_NOT_NULL, propertyExpression);
return Expressions.booleanOperation(order.isAscending() ? Ops.IS_NOT_NULL : Ops.IS_NULL, propertyExpression);
}

return Expressions.booleanOperation(order.isAscending() ? Ops.GT : Ops.LT, propertyExpression,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2016-present 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.jpa.domain.sample;

import jakarta.persistence.*;

import java.util.List;

/**
* @author James Bodkin
* @see <a href="https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#examples-of-derived-identities">Jakarta
* Persistence Specification: Derived Identities, Example 2</a>
*/
@Entity
@Table
public class Trade {

@Id
@GeneratedValue
@Column(name = "id")
private Integer id;

@OneToMany(mappedBy = "trade", cascade = CascadeType.ALL)
private List<TradeOrder> tradeOrders;

public Trade() {}

public Trade(List<TradeOrder> tradeOrders) {
this.tradeOrders = tradeOrders;
}

public Integer getId() {
return id;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2016-present 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.jpa.domain.sample;

import jakarta.persistence.*;

/**
* @author James Bodkin
* @see <a href="https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#examples-of-derived-identities">Jakarta
* Persistence Specification: Derived Identities, Example 2</a>
*/
@Entity
@Table
@IdClass(TradeItemId.class)
public class TradeItem {

@Id
@ManyToOne
@JoinColumn(name = "trade_id", referencedColumnName = "trade_id")
@JoinColumn(name = "trade_order_id", referencedColumnName = "number")
private TradeOrder tradeOrder;

@Id
@Column(name = "number")
private Integer number;

@Column(name = "type")
private String type;

public TradeItem() {}

public TradeItem(TradeOrder tradeOrder, Integer number, String type) {
this.tradeOrder = tradeOrder;
this.number = number;
this.type = type;
}

public TradeOrder getTradeOrder() {
return tradeOrder;
}

public Integer getNumber() {
return number;
}

public String getType() {
return type;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2016-present 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.jpa.domain.sample;

import java.util.Objects;

/**
* @author James Bodkin
* @see <a href="https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#examples-of-derived-identities">Jakarta
* Persistence Specification: Derived Identities, Example 2</a>
*/
public class TradeItemId {

private TradeOrderId tradeOrder;
private Integer number;

public TradeItemId() {}

public TradeItemId(TradeOrderId tradeOrder, Integer number) {
this.tradeOrder = tradeOrder;
this.number = number;
}

public TradeOrderId getTradeOrder() {
return tradeOrder;
}

public void setTradeOrder(TradeOrderId tradeOrder) {
this.tradeOrder = tradeOrder;
}

public Integer getNumber() {
return number;
}

public void setNumber(Integer number) {
this.number = number;
}

@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
TradeItemId that = (TradeItemId) o;
return Objects.equals(tradeOrder, that.tradeOrder) && Objects.equals(number, that.number);
}

@Override
public int hashCode() {
return Objects.hash(tradeOrder, number);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2016-present 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.jpa.domain.sample;

import jakarta.persistence.*;

import java.util.List;

/**
* @author James Bodkin
* @see <a href="https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#examples-of-derived-identities">Jakarta
* Persistence Specification: Derived Identities, Example 2</a>
*/
@Entity
@Table
@IdClass(TradeOrderId.class)
public class TradeOrder {

@Id
@ManyToOne
@JoinColumn(name = "trade_id", referencedColumnName = "id")
private Trade trade;

@Id
@Column(name = "number")
private Integer number;

@OneToMany(mappedBy = "tradeOrder", cascade = CascadeType.ALL)
private List<TradeItem> tradeItems;

public TradeOrder() {}

public TradeOrder(Trade trade, Integer number, List<TradeItem> tradeItems) {
this.trade = trade;
this.number = number;
this.tradeItems = tradeItems;
}

public Trade getTrade() {
return trade;
}

public Integer getNumber() {
return number;
}

}
Loading