/*
* Copyright 2016-2017 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
*
* http://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.convert;
import static org.hamcrest.core.IsEqual.*;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.domain.Example.*;
import java.lang.reflect.Member;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.persistence.Id;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute.PersistentAttributeType;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type;
import org.hamcrest.core.IsInstanceOf;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.util.ObjectUtils;
/**
* Unit tests for {@link QueryByExamplePredicateBuilder}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.Silent.class)
@SuppressWarnings({ "rawtypes", "unchecked" })
public class QueryByExamplePredicateBuilderUnitTests {
@Mock CriteriaBuilder cb;
@Mock Root root;
@Mock EntityType<Person> personEntityType;
@Mock Expression expressionMock;
@Mock Predicate truePredicate, dummyPredicate, andPredicate, orPredicate;
@Mock Path dummyPath;
Set<SingularAttribute<? super Person, ?>> personEntityAttribtues;
SingularAttribute<? super Person, Long> personIdAttribute;
SingularAttribute<? super Person, String> personFirstnameAttribute;
SingularAttribute<? super Person, Long> personAgeAttribute;
SingularAttribute<? super Person, Person> personFatherAttribute;
SingularAttribute<? super Person, Skill> personSkillAttribute;
SingularAttribute<? super Person, Address> personAddressAttribute;
public @Rule ExpectedException exception = ExpectedException.none();
@Before
public void setUp() {
personIdAttribute = new SingluarAttributeStub<Person, Long>("id", PersistentAttributeType.BASIC, Long.class);
personFirstnameAttribute = new SingluarAttributeStub<Person, String>("firstname", PersistentAttributeType.BASIC,
String.class);
personAgeAttribute = new SingluarAttributeStub<Person, Long>("age", PersistentAttributeType.BASIC, Long.class);
personFatherAttribute = new SingluarAttributeStub<Person, Person>("father", PersistentAttributeType.MANY_TO_ONE,
Person.class, personEntityType);
personSkillAttribute = new SingluarAttributeStub<Person, Skill>("skill", PersistentAttributeType.MANY_TO_ONE,
Skill.class);
personAddressAttribute = new SingluarAttributeStub<Person, Address>("address", PersistentAttributeType.EMBEDDED,
Address.class);
personEntityAttribtues = new LinkedHashSet<SingularAttribute<? super Person, ?>>();
personEntityAttribtues.add(personIdAttribute);
personEntityAttribtues.add(personFirstnameAttribute);
personEntityAttribtues.add(personAgeAttribute);
personEntityAttribtues.add(personFatherAttribute);
personEntityAttribtues.add(personAddressAttribute);
personEntityAttribtues.add(personSkillAttribute);
doReturn(dummyPath).when(root).get(any(SingularAttribute.class));
doReturn(personEntityType).when(root).getModel();
doReturn(personEntityAttribtues).when(personEntityType).getSingularAttributes();
doReturn(dummyPredicate).when(cb).equal(any(Expression.class), any(String.class));
doReturn(dummyPredicate).when(cb).equal(any(Expression.class), any(Long.class));
doReturn(dummyPredicate).when(cb).like(any(Expression.class), any(String.class));
doReturn(expressionMock).when(cb).literal(any(Boolean.class));
doReturn(truePredicate).when(cb).isTrue(eq(expressionMock));
doReturn(andPredicate).when(cb).and(ArgumentMatchers.any());
doReturn(orPredicate).when(cb).or(ArgumentMatchers.any());
}
@Test(expected = IllegalArgumentException.class) // DATAJPA-218
public void getPredicateShouldThrowExceptionOnNullRoot() {
QueryByExamplePredicateBuilder.getPredicate(null, cb, of(new Person()));
}
@Test(expected = IllegalArgumentException.class) // DATAJPA-218
public void getPredicateShouldThrowExceptionOnNullCriteriaBuilder() {
QueryByExamplePredicateBuilder.getPredicate(root, null, of(new Person()));
}
@Test(expected = IllegalArgumentException.class) // DATAJPA-218
public void getPredicateShouldThrowExceptionOnNullExample() {
QueryByExamplePredicateBuilder.getPredicate(root, null, null);
}
@Test // DATAJPA-218
public void emptyCriteriaListShouldResultTruePredicate() {
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(new Person())), equalTo(truePredicate));
}
@Test // DATAJPA-218
public void singleElementCriteriaShouldJustReturnIt() {
Person p = new Person();
p.firstname = "foo";
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p)), equalTo(dummyPredicate));
verify(cb, times(1)).equal(any(Expression.class), eq("foo"));
}
@Test // DATAJPA-937
public void unresolvableNestedAssociatedPathShouldFail() {
Person p = new Person();
Person father = new Person();
father.father = new Person();
p.father = father;
exception.expectCause(IsInstanceOf.<Throwable> instanceOf(IllegalArgumentException.class));
exception.expectMessage("Unexpected path type");
QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p));
}
@Test // DATAJPA-218
public void multiPredicateCriteriaShouldReturnCombinedOnes() {
Person p = new Person();
p.firstname = "foo";
p.age = 2L;
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p)), equalTo(andPredicate));
verify(cb, times(1)).equal(any(Expression.class), eq("foo"));
verify(cb, times(1)).equal(any(Expression.class), eq(2L));
}
@Test // DATAJPA-879
public void orConcatenatesPredicatesIfMatcherSpecifies() {
Person person = new Person();
person.firstname = "foo";
person.age = 2L;
Example<Person> example = of(person, ExampleMatcher.matchingAny());
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, example), equalTo(orPredicate));
verify(cb, times(1)).or(ArgumentMatchers.any());
}
static class Person {
@Id Long id;
String firstname;
Long age;
Person father;
Address address;
Skill skill;
}
static class Address {
String city;
String country;
}
static class Skill {
@Id Long id;
String name;
}
static class SingluarAttributeStub<X, T> implements SingularAttribute<X, T> {
private String name;
private PersistentAttributeType attributeType;
private Class<T> javaType;
private Type<T> type;
public SingluarAttributeStub(String name,
javax.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class<T> javaType) {
this(name, attributeType, javaType, null);
}
public SingluarAttributeStub(String name,
javax.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class<T> javaType, Type<T> type) {
this.name = name;
this.attributeType = attributeType;
this.javaType = javaType;
this.type = type;
}
@Override
public String getName() {
return name;
}
@Override
public javax.persistence.metamodel.Attribute.PersistentAttributeType getPersistentAttributeType() {
return attributeType;
}
@Override
public ManagedType<X> getDeclaringType() {
return null;
}
@Override
public Class<T> getJavaType() {
return javaType;
}
@Override
public Member getJavaMember() {
return null;
}
@Override
public boolean isAssociation() {
return !attributeType.equals(PersistentAttributeType.BASIC)
&& !attributeType.equals(PersistentAttributeType.EMBEDDED);
}
@Override
public boolean isCollection() {
return false;
}
@Override
public javax.persistence.metamodel.Bindable.BindableType getBindableType() {
return BindableType.SINGULAR_ATTRIBUTE;
}
@Override
public Class<T> getBindableJavaType() {
return javaType;
}
@Override
public boolean isId() {
return ObjectUtils.nullSafeEquals(name, "id");
}
@Override
public boolean isVersion() {
return false;
}
@Override
public boolean isOptional() {
return false;
}
@Override
public Type<T> getType() {
return type;
}
}
}