HHH-12945 Properly support entry() criteria expression
With additional changes from Gail.
This commit is contained in:
parent
e4c964fb36
commit
2e0976d8b6
|
@ -7,15 +7,17 @@
|
||||||
package org.hibernate.query.criteria.internal.expression;
|
package org.hibernate.query.criteria.internal.expression;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.persistence.criteria.Expression;
|
|
||||||
import javax.persistence.metamodel.MapAttribute;
|
import javax.persistence.criteria.CompoundSelection;
|
||||||
|
import javax.persistence.criteria.Selection;
|
||||||
|
|
||||||
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
|
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
|
||||||
import org.hibernate.query.criteria.internal.ParameterRegistry;
|
import org.hibernate.query.criteria.internal.ParameterRegistry;
|
||||||
import org.hibernate.query.criteria.internal.PathImplementor;
|
|
||||||
import org.hibernate.query.criteria.internal.Renderable;
|
|
||||||
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
|
import org.hibernate.query.criteria.internal.path.MapAttributeJoin;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,41 +27,41 @@ import org.hibernate.sql.ast.Clause;
|
||||||
*/
|
*/
|
||||||
public class MapEntryExpression<K,V>
|
public class MapEntryExpression<K,V>
|
||||||
extends ExpressionImpl<Map.Entry<K,V>>
|
extends ExpressionImpl<Map.Entry<K,V>>
|
||||||
implements Expression<Map.Entry<K,V>>, Serializable {
|
implements CompoundSelection<Map.Entry<K,V>>, Serializable {
|
||||||
|
|
||||||
private final PathImplementor origin;
|
private final MapAttributeJoin<?, K, V> original;
|
||||||
private final MapAttribute<?, K, V> attribute;
|
|
||||||
|
|
||||||
public MapEntryExpression(
|
public MapEntryExpression(
|
||||||
CriteriaBuilderImpl criteriaBuilder,
|
CriteriaBuilderImpl criteriaBuilder,
|
||||||
Class<Map.Entry<K, V>> javaType,
|
Class<Map.Entry<K, V>> javaType,
|
||||||
PathImplementor origin,
|
MapAttributeJoin<?, K, V> original) {
|
||||||
MapAttribute<?, K, V> attribute) {
|
super( criteriaBuilder, javaType );
|
||||||
super( criteriaBuilder, javaType);
|
this.original = original;
|
||||||
this.origin = origin;
|
|
||||||
this.attribute = attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapAttribute<?, K, V> getAttribute() {
|
|
||||||
return attribute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void registerParameters(ParameterRegistry registry) {
|
public void registerParameters(ParameterRegistry registry) {
|
||||||
// none to register
|
// none to register
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
|
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
|
||||||
return "entry(" + path( renderingContext ) + ")";
|
return "entry(" + original.render( renderingContext ) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't think this is valid outside of select clause...
|
// don't think this is valid outside of select clause...
|
||||||
throw new IllegalStateException( "illegal reference to map entry outside of select clause." );
|
throw new IllegalStateException( "illegal reference to map entry outside of select clause." );
|
||||||
}
|
}
|
||||||
|
|
||||||
private String path(RenderingContext renderingContext) {
|
@Override
|
||||||
return origin.getPathIdentifier()
|
public boolean isCompoundSelection() {
|
||||||
+ '.'
|
return true;
|
||||||
+ ( (Renderable) getAttribute() ).render( renderingContext );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Selection<?>> getCompoundSelectionItems() {
|
||||||
|
return Arrays.asList( original.key(), original.value() );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ public class MapAttributeJoin<O,K,V>
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "unchecked" })
|
@SuppressWarnings({ "unchecked" })
|
||||||
public Expression<Map.Entry<K, V>> entry() {
|
public Expression<Map.Entry<K, V>> entry() {
|
||||||
return new MapEntryExpression( criteriaBuilder(), Map.Entry.class, this, getAttribute() );
|
return new MapEntryExpression( criteriaBuilder(), Map.Entry.class, this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.jpa.test.criteria.mapjoin;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Customer {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private int id;
|
||||||
|
private String name;
|
||||||
|
@OneToMany(cascade = CascadeType.ALL)
|
||||||
|
private Map<String, CustomerOrder> orderMap;
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, CustomerOrder> getOrderMap() {
|
||||||
|
return orderMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderMap(Map<String, CustomerOrder> orderMap) {
|
||||||
|
this.orderMap = orderMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOrder(String orderType, String itemName, int qty) {
|
||||||
|
if ( orderMap == null ) {
|
||||||
|
orderMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
CustomerOrder order = new CustomerOrder();
|
||||||
|
order.setItem( itemName );
|
||||||
|
order.setQty( qty );
|
||||||
|
orderMap.put( orderType, order );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Customer{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
", orderMap=" + orderMap +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.jpa.test.criteria.mapjoin;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class CustomerOrder {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private long id;
|
||||||
|
private String item;
|
||||||
|
private int qty;
|
||||||
|
|
||||||
|
public String getItem() {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(String item) {
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getQty() {
|
||||||
|
return qty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQty(int qty) {
|
||||||
|
this.qty = qty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Order{" +
|
||||||
|
"id=" + id +
|
||||||
|
", item='" + item + '\'' +
|
||||||
|
", qty=" + qty +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.jpa.test.criteria.mapjoin;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.MapJoin;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class MapJoinEntryTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[]{ Customer.class, CustomerOrder.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
doInJPA( this::entityManagerFactory, em -> {
|
||||||
|
Customer customer = new Customer();
|
||||||
|
customer.setName( "Morgan Philips" );
|
||||||
|
customer.addOrder( "online", "AA Glass Cleaner", 3 );
|
||||||
|
|
||||||
|
em.persist( customer );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-12945")
|
||||||
|
public void testMapJoinEntryCriteria() {
|
||||||
|
doInJPA( this::entityManagerFactory, em -> {
|
||||||
|
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
|
||||||
|
|
||||||
|
CriteriaQuery<Map.Entry> query = criteriaBuilder.createQuery( Map.Entry.class );
|
||||||
|
Root<Customer> customer = query.from( Customer.class );
|
||||||
|
MapJoin<Customer, String, CustomerOrder> orderMap = customer.join( Customer_.orderMap );
|
||||||
|
query.select( orderMap.entry() );
|
||||||
|
|
||||||
|
TypedQuery<Map.Entry> typedQuery = em.createQuery( query );
|
||||||
|
List<Map.Entry> resultList = typedQuery.getResultList();
|
||||||
|
|
||||||
|
assertEquals( 1, resultList.size() );
|
||||||
|
assertEquals( "online", resultList.get( 0 ).getKey() );
|
||||||
|
assertEquals( "AA Glass Cleaner", ( (CustomerOrder) resultList.get( 0 ).getValue() ).getItem() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapJoinEntryJPQL() {
|
||||||
|
doInJPA( this::entityManagerFactory, em -> {
|
||||||
|
TypedQuery<Map.Entry> query = em.createQuery( "SELECT ENTRY(mp) FROM Customer c JOIN c.orderMap mp",
|
||||||
|
Map.Entry.class );
|
||||||
|
List<Map.Entry> resultList = query.getResultList();
|
||||||
|
|
||||||
|
assertEquals( 1, resultList.size() );
|
||||||
|
assertEquals( "online", resultList.get( 0 ).getKey() );
|
||||||
|
assertEquals( "AA Glass Cleaner", ( (CustomerOrder) resultList.get( 0 ).getValue() ).getItem() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue