LDAP-269: Basic QueryDSL support.

This commit is contained in:
Mattias Hellborg Arthursson
2013-10-22 08:06:12 +02:00
parent 8d1d5f9759
commit 7823b329b4
17 changed files with 856 additions and 43 deletions

View File

@@ -18,7 +18,8 @@ dependencies {
"com.sun:ldapbp:1.0",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-orm:$springVersion"
"org.springframework:spring-orm:$springVersion",
"com.mysema.querydsl:querydsl-apt:$queryDslVersion"
testCompile "junit:junit:$junitVersion",
"commons-lang:commons-lang:$commonsLangVersion",

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2005-2013 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.ldap.repository.support;
import com.mysema.query.apt.DefaultConfiguration;
import org.springframework.ldap.odm.annotations.Id;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.VariableElement;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Map;
/**
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
class DefaultLdapAnnotationProcessorConfiguration extends DefaultConfiguration {
public DefaultLdapAnnotationProcessorConfiguration(
RoundEnvironment roundEnv,
Map<String, String> options,
Collection<String> keywords,
Class<? extends Annotation> entitiesAnn,
Class<? extends Annotation> entityAnn,
Class<? extends Annotation> superTypeAnn,
Class<? extends Annotation> embeddableAnn,
Class<? extends Annotation> embeddedAnn,
Class<? extends Annotation> skipAnn) {
super(roundEnv, options, keywords, entitiesAnn, entityAnn, superTypeAnn, embeddableAnn, embeddedAnn, skipAnn);
}
@Override
public boolean isBlockedField(VariableElement field) {
return super.isBlockedField(field) || field.getAnnotation(Id.class) != null;
}
@Override
public boolean isValidField(VariableElement field) {
return super.isValidField(field) && field.getAnnotation(Id.class) == null;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2005-2013 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.ldap.repository.support;
import com.mysema.query.annotations.QueryEntities;
import com.mysema.query.apt.AbstractQuerydslProcessor;
import com.mysema.query.apt.Configuration;
import com.mysema.query.apt.DefaultConfiguration;
import org.springframework.ldap.odm.annotations.Entry;
import org.springframework.ldap.odm.annotations.Transient;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.tools.Diagnostic;
import java.util.Collections;
/**
* QueryDSL Annotation Processor to generate QueryDSL classes for entity classes annotated with {@link Entry}.
*
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
@SupportedAnnotationTypes("org.springframework.ldap.odm.annotations.*")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class LdapAnnotationProcessor extends AbstractQuerydslProcessor {
@Override
protected Configuration createConfiguration(RoundEnvironment roundEnv) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Running " + getClass().getSimpleName());
DefaultConfiguration configuration = new DefaultLdapAnnotationProcessorConfiguration(roundEnv, processingEnv.getOptions(),
Collections.<String>emptySet(), QueryEntities.class, Entry.class, null, null, null, Transient.class);
configuration.setUseFields(true);
configuration.setUseGetters(false);
return configuration;
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.ldap.repository.support;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
@@ -30,6 +31,8 @@ import org.springframework.ldap.repository.query.PartTreeLdapRepositoryQuery;
import java.io.Serializable;
import java.lang.reflect.Method;
import static org.springframework.data.querydsl.QueryDslUtils.QUERY_DSL_PRESENT;
/**
* Factory to create {@link org.springframework.ldap.repository.LdapRepository} instances.
* @author Mattias Hellborg Arthursson
@@ -50,15 +53,32 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new SimpleLdapRepository(
ldapOperations,
ldapOperations.getObjectDirectoryMapper(),
metadata.getDomainType());
if(!isQueryDslRepository(metadata.getRepositoryInterface())) {
return new SimpleLdapRepository(
ldapOperations,
ldapOperations.getObjectDirectoryMapper(),
metadata.getDomainType());
} else {
return new QueryDslLdapRepository(
ldapOperations,
ldapOperations.getObjectDirectoryMapper(),
metadata.getDomainType());
}
}
private static boolean isQueryDslRepository(Class<?> repositoryInterface) {
return QUERY_DSL_PRESENT && QueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return SimpleLdapRepository.class;
if(!isQueryDslRepository(metadata.getRepositoryInterface())) {
return SimpleLdapRepository.class;
} else {
return QueryDslLdapRepository.class;
}
}
@Override

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2005-2013 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.ldap.repository.support;
import com.mysema.query.types.Constant;
import com.mysema.query.types.Expression;
import com.mysema.query.types.FactoryExpression;
import com.mysema.query.types.Operation;
import com.mysema.query.types.Operator;
import com.mysema.query.types.Ops;
import com.mysema.query.types.ParamExpression;
import com.mysema.query.types.Path;
import com.mysema.query.types.SubQueryExpression;
import com.mysema.query.types.TemplateExpression;
import com.mysema.query.types.Visitor;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.Filter;
import org.springframework.ldap.filter.GreaterThanOrEqualsFilter;
import org.springframework.ldap.filter.LessThanOrEqualsFilter;
import org.springframework.ldap.filter.LikeFilter;
import org.springframework.ldap.filter.NotFilter;
import org.springframework.ldap.filter.OrFilter;
import org.springframework.ldap.filter.PresentFilter;
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
/**
* Helper class for generating LDAP filters from QueryDSL Expressions.
*
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
class LdapSerializer implements Visitor<Object, Void> {
private final ObjectDirectoryMapper odm;
private final Class<?> clazz;
public LdapSerializer(ObjectDirectoryMapper odm, Class<?> clazz) {
this.odm = odm;
this.clazz = clazz;
}
public Filter handle(Expression<?> expression) {
return (Filter) expression.accept(this, null);
}
@Override
public Object visit(Constant<?> expr, Void context) {
return expr.getConstant().toString();
}
@Override
public Object visit(FactoryExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(Operation<?> expr, Void context) {
Operator<?> operator = expr.getOperator();
if (operator == Ops.EQ) {
return new EqualsFilter(attribute(expr), value(expr));
} else if (operator == Ops.AND) {
return new AndFilter()
.and(handle(expr.getArg(0)))
.and(handle(expr.getArg(1)));
} else if (operator == Ops.OR) {
return new OrFilter()
.or(handle(expr.getArg(0)))
.or(handle(expr.getArg(1)));
} else if (operator == Ops.NOT) {
return new NotFilter(handle(expr.getArg(0)));
} else if (operator == Ops.LIKE) {
return new LikeFilter(attribute(expr), value(expr));
} else if (operator == Ops.STARTS_WITH || operator == Ops.STARTS_WITH_IC) {
return new LikeFilter(attribute(expr), value(expr) + "*");
} else if (operator == Ops.ENDS_WITH || operator == Ops.ENDS_WITH_IC) {
return new LikeFilter(attribute(expr), "*" + value(expr));
} else if (operator == Ops.STRING_CONTAINS || operator == Ops.STRING_CONTAINS_IC) {
return new LikeFilter(attribute(expr), "*" + value(expr) + "*");
} else if (operator == Ops.IS_NOT_NULL) {
return new PresentFilter(attribute(expr));
} else if (operator == Ops.IS_NULL) {
return new NotFilter(new PresentFilter(attribute(expr)));
} else if (operator == Ops.GOE) {
return new GreaterThanOrEqualsFilter(attribute(expr), value(expr));
} else if (operator == Ops.LOE) {
return new LessThanOrEqualsFilter(attribute(expr), value(expr));
}
throw new UnsupportedOperationException("Unsupported operator " + operator.toString());
}
private String value(Operation<?> expr) {
return (String) expr.getArg(1).accept(this, null);
}
private String attribute(Operation<?> expr) {
return odm.attributeFor(clazz, (String) expr.getArg(0).accept(this, null));
}
@Override
public Object visit(ParamExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(Path<?> expr, Void context) {
return expr.getMetadata().getName();
}
@Override
public Object visit(SubQueryExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(TemplateExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2005-2013 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.ldap.repository.support;
import com.mysema.query.DefaultQueryMetadata;
import com.mysema.query.FilteredClause;
import com.mysema.query.support.QueryMixin;
import com.mysema.query.types.EntityPath;
import com.mysema.query.types.Predicate;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.query.LdapQuery;
import java.util.List;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
/**
* Spring LDAP specific {@link FilteredClause} implementation.
*
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
public class QueryDslLdapQuery<K> implements FilteredClause<QueryDslLdapQuery<K>> {
private final LdapOperations ldapOperations;
private final Class<? extends K> clazz;
private QueryMixin<QueryDslLdapQuery<K>> queryMixin =
new QueryMixin<QueryDslLdapQuery<K>>(this, new DefaultQueryMetadata().noValidate());
private final LdapSerializer filterGenerator;
@SuppressWarnings("unchecked")
public QueryDslLdapQuery(LdapOperations ldapOperations, EntityPath<K> entityPath) {
this(ldapOperations, (Class<K>) entityPath.getType());
}
public QueryDslLdapQuery(LdapOperations ldapOperations, Class<K> clazz) {
this.ldapOperations = ldapOperations;
this.clazz = clazz;
this.filterGenerator = new LdapSerializer(ldapOperations.getObjectDirectoryMapper(), clazz);
}
@Override
public QueryDslLdapQuery<K> where(Predicate... o) {
return queryMixin.where(o);
}
@SuppressWarnings("unchecked")
public List<K> list() {
return (List<K>) ldapOperations.find(buildQuery(), clazz);
}
public K uniqueResult() {
return ldapOperations.findOne(buildQuery(), clazz);
}
LdapQuery buildQuery() {
return query().filter(filterGenerator.handle(queryMixin.getMetadata().getWhere()));
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2005-2013 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.ldap.repository.support;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
import java.util.List;
/**
* Base repository implementation for QueryDSL support.
*
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
public class QueryDslLdapRepository<T> extends SimpleLdapRepository<T> implements QueryDslPredicateExecutor<T> {
public QueryDslLdapRepository(LdapOperations ldapOperations,
ObjectDirectoryMapper odm,
Class<T> clazz) {
super(ldapOperations, odm, clazz);
}
@Override
public T findOne(Predicate predicate) {
return queryFor(predicate).uniqueResult();
}
@Override
public List<T> findAll(Predicate predicate) {
return queryFor(predicate).list();
}
@Override
public long count(Predicate predicate) {
return findAll(predicate).size();
}
private QueryDslLdapQuery<T> queryFor(Predicate predicate) {
return new QueryDslLdapQuery<T>(getLdapOperations(), getClazz())
.where(predicate);
}
@Override
public Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
throw new UnsupportedOperationException();
}
@Override
public Page<T> findAll(Predicate predicate, Pageable pageable) {
throw new UnsupportedOperationException();
}
}

View File

@@ -30,6 +30,7 @@ import org.springframework.util.Assert;
import javax.naming.Name;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
@@ -51,6 +52,14 @@ public class SimpleLdapRepository<T> implements LdapRepository<T> {
this.clazz = clazz;
}
protected LdapOperations getLdapOperations() {
return ldapOperations;
}
protected Class<T> getClazz() {
return clazz;
}
@Override
public long count() {
Filter filter = odm.filterFor(clazz, null);
@@ -63,7 +72,7 @@ public class SimpleLdapRepository<T> implements LdapRepository<T> {
private <S extends T> boolean isNew(S entity, Name id) {
if (entity instanceof Persistable) {
Persistable persistable = (Persistable) entity;
Persistable<?> persistable = (Persistable<?>) entity;
return persistable.isNew();
} else {
return id == null;
@@ -119,7 +128,7 @@ public class SimpleLdapRepository<T> implements LdapRepository<T> {
}
@Override
public Iterable<T> findAll(LdapQuery ldapQuery) {
public List<T> findAll(LdapQuery ldapQuery) {
Assert.notNull(ldapQuery, "LdapQuery must not be null");
return ldapOperations.find(ldapQuery, clazz);
}
@@ -141,12 +150,12 @@ public class SimpleLdapRepository<T> implements LdapRepository<T> {
}
@Override
public Iterable<T> findAll() {
public List<T> findAll() {
return ldapOperations.findAll(clazz);
}
@Override
public Iterable<T> findAll(final Iterable<Name> names) {
public List<T> findAll(final Iterable<Name> names) {
Iterable<T> found = new TransformingIterable<Name, T>(names, new Function<Name, T>() {
@Override
public T transform(Name name) {

View File

@@ -0,0 +1,45 @@
package org.springframework.ldap.repository.support;
import com.mysema.query.types.Path;
import com.mysema.query.types.PathMetadata;
import com.mysema.query.types.path.EntityPathBase;
import com.mysema.query.types.path.ListPath;
import com.mysema.query.types.path.PathInits;
import com.mysema.query.types.path.StringPath;
import org.springframework.ldap.odm.core.impl.UnitTestPerson;
import javax.annotation.Generated;
import static com.mysema.query.types.PathMetadataFactory.forVariable;
/**
* QPerson is a Querydsl query type for Person
*/
@Generated("com.mysema.query.codegen.EntitySerializer")
public class QPerson extends EntityPathBase<UnitTestPerson> {
private static final long serialVersionUID = -1526737794;
public static final QPerson person = new QPerson("person");
public final StringPath fullName = createString("fullName");
public final ListPath<String, StringPath> description = this.<String, StringPath>createList("description", String.class, StringPath.class, PathInits.DIRECT2);
public final StringPath lastName = createString("lastName");
public QPerson(String variable) {
super(UnitTestPerson.class, forVariable(variable));
}
public QPerson(Path<? extends UnitTestPerson> path) {
super(path.getType(), path.getMetadata());
}
public QPerson(PathMetadata<?> metadata) {
super(UnitTestPerson.class, metadata);
}
}

View File

@@ -0,0 +1,106 @@
package org.springframework.ldap.repository.support;
import com.mysema.query.types.Expression;
import org.junit.Before;
import org.junit.Test;
import org.springframework.ldap.filter.Filter;
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper;
import org.springframework.ldap.odm.core.impl.UnitTestPerson;
import static org.junit.Assert.assertEquals;
/**
* @author Mattias Hellborg Arthursson
*/
public class QueryDslFilterGeneratorTest {
private LdapSerializer tested;
private QPerson person;
@Before
public void prepareTestedInstance() {
ObjectDirectoryMapper odm = new DefaultObjectDirectoryMapper();
tested = new LdapSerializer(odm, UnitTestPerson.class);
person = QPerson.person;
}
@Test
public void testEqualsFilter() {
Expression<?> expression = person.fullName.eq("John Doe");
Filter result = tested.handle(expression);
assertEquals("(cn=John Doe)", result.toString());
}
@Test
public void testAndFilter() {
Expression<?> expression = person.fullName.eq("John Doe").and(person.lastName.eq("Doe"));
Filter result = tested.handle(expression);
assertEquals("(&(cn=John Doe)(sn=Doe))", result.toString());
}
@Test
public void testOrFilter() {
Expression<?> expression = person.fullName.eq("John Doe").or(person.lastName.eq("Doe"));
Filter result = tested.handle(expression);
assertEquals("(|(cn=John Doe)(sn=Doe))", result.toString());
}
@Test
public void testOr() {
Expression<?> expression = person.fullName.eq("John Doe")
.and(person.lastName.eq("Doe").or(person.lastName.eq("Die")));
Filter result = tested.handle(expression);
assertEquals("(&(cn=John Doe)(|(sn=Doe)(sn=Die)))", result.toString());
}
@Test
public void testNot() {
Expression<?> expression = person.fullName.eq("John Doe").not();
Filter result = tested.handle(expression);
assertEquals("(!(cn=John Doe))", result.toString());
}
@Test
public void testIsLike() {
Expression<?> expression = person.fullName.like("kalle*");
Filter result = tested.handle(expression);
assertEquals("(cn=kalle*)", result.toString());
}
@Test
public void testStartsWith() {
Expression<?> expression = person.fullName.startsWith("kalle");
Filter result = tested.handle(expression);
assertEquals("(cn=kalle*)", result.toString());
}
@Test
public void testEndsWith() {
Expression<?> expression = person.fullName.endsWith("kalle");
Filter result = tested.handle(expression);
assertEquals("(cn=*kalle)", result.toString());
}
@Test
public void testContains() {
Expression<?> expression = person.fullName.contains("kalle");
Filter result = tested.handle(expression);
assertEquals("(cn=*kalle*)", result.toString());
}
@Test
public void testNotNull() {
Expression<?> expression = person.fullName.isNotNull();
Filter result = tested.handle(expression);
assertEquals("(cn=*)", result.toString());
}
@Test
public void testNull() {
Expression<?> expression = person.fullName.isNull();
Filter result = tested.handle(expression);
assertEquals("(!(cn=*))", result.toString());
}
}

View File

@@ -14,6 +14,7 @@ ext.commonsLoggingVersion = '1.1.1'
ext.gsbaseVersion = '2.0.1'
ext.log4jVersion = '1.2.15'
ext.mockitoVersion = '1.9.5'
ext.queryDslVersion = '3.2.4'
repositories {
mavenCentral()

View File

@@ -245,7 +245,9 @@ public class PersonDaoImpl implements PersonDao {
Below is a list of the most important changes in Spring LDAP 2.0.
</para>
<itemizedlist>
<listitem>Java 1.6 is now required when using Spring LDAP. Spring versions starting at 2.0 and up are still supported.</listitem>
<listitem>
Java 1.6 is now required when using Spring LDAP. Spring versions starting at 2.0 and up are still supported.
</listitem>
<listitem>
The central API has been updated with Java 5 features such as generics and varargs. As a consequence,
the entire <literal>spring-ldap-tiger</literal> module has been deprecated and users are encouraged to migrate
@@ -262,6 +264,10 @@ public class PersonDaoImpl implements PersonDao {
A custom XML namespace is now provided to simplify configuration of Spring LDAP.
See <xref linkend="configuration" /> for more information.
</listitem>
<listitem>
Spring Data Repository and QueryDSL support is now included in Spring LDAP.
See <xref linkend="repositories" /> for more information.
</listitem>
<listitem>
<literal>DistinguishedName</literal> and associated classes have been deprecated in favor of standard
Java <literal>LdapName</literal>. See <xref linkend="ldap-names" /> for information on how the library

View File

@@ -1,34 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="repositories">
<title>Spring LDAP Repositories</title>
<title>Spring LDAP Repositories</title>
<sect1 id="repositories-overview">
<title>Overview</title>
<para>
Spring LDAP has built-in support for Spring Data repositories. The basic functionality and configuration is described <ulink
url="http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html">here</ulink>.
When working with Spring LDAP repositories, please note the following:
<itemizedlist>
<listitem>
Spring LDAP repositories can be enabled using an <literal>&lt;ldap:repositories&gt;</literal> tag in
your XML configuration or using an <literal>@EnableLdapRepositories</literal> annotation on a
configuration class.
</listitem>
<listitem>
All Spring LDAP repositories must work with entities annotated with the ODM annotations, as described
in <xref linkend="odm" />.
</listitem>
<listitem>
Since all ODM managed classes must have a Distinguished Name as ID, all Spring LDAP repositories must
have the ID type parameter set to <literal>javax.naming.Name</literal>. Indeed, the built-in
<literal>SpringLdapRepository</literal> only takes one type parameter; the managed entity class, defaulting
ID to <literal>javax.naming.Name</literal>.
</listitem>
<listitem>
Due to specifics of the LDAP protocol, paging and sorting is not supported for Spring LDAP repositories.
</listitem>
</itemizedlist>
</para>
</sect1>
<sect1 id="repositories-overview">
<title>Overview</title>
<para>
Spring LDAP has built-in support for Spring Data repositories. The basic functionality and configuration is described <ulink
url="http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html">here</ulink>.
When working with Spring LDAP repositories, please note the following:
<itemizedlist>
<listitem>
Spring LDAP repositories can be enabled using an <literal>&lt;ldap:repositories&gt;</literal> tag in
your XML configuration or using an <literal>@EnableLdapRepositories</literal> annotation on a
configuration class.
</listitem>
<listitem>
To include support for <literal>LdapQuery</literal> parameters in automatically generated repositories,
have your interface extend <literal>LdapRepository</literal> rather than <literal>CrudRepository</literal>.
</listitem>
<listitem>
All Spring LDAP repositories must work with entities annotated with the ODM annotations, as described
in <xref linkend="odm" />.
</listitem>
<listitem>
Since all ODM managed classes must have a Distinguished Name as ID, all Spring LDAP repositories must
have the ID type parameter set to <literal>javax.naming.Name</literal>. Indeed, the built-in
<literal>SpringLdapRepository</literal> only takes one type parameter; the managed entity class, defaulting
ID to <literal>javax.naming.Name</literal>.
</listitem>
<listitem>
Due to specifics of the LDAP protocol, paging and sorting is not supported for Spring LDAP repositories.
</listitem>
</itemizedlist>
</para>
</sect1>
<sect1 id="querydsl-repositories">
<title>QueryDSL support</title>
<para>
Basic QueryDSL support is included in Spring LDAP. This support includes the following:
<itemizedlist>
<listitem>
An Annotation Processor, <literal>LdapAnnotationProcessor</literal>, for generating QueryDSL classes
based on Spring LDAP ODM annotations. See <xref linkend="odm" /> for more information on the ODM annotations.
</listitem>
<listitem>
A Query implementation, <literal>QueryDslLdapQuery</literal>, for building and executing QueryDSL
queries in code.
</listitem>
<listitem>
Spring Data repository support for QueryDSL predicates. <literal>QueryDslPredicateExecutor</literal>
includes a number of additional methods with appropriate parameters; extend this interface along with
<literal>LdapRepository</literal> to include this support in your repository.
</listitem>
</itemizedlist>
</para>
</sect1>
</chapter>

View File

@@ -3,11 +3,29 @@ repositories {
}
apply from: JAVA_SCRIPT
idea.module.excludeDirs = [
file('.gradle'),
file('build/classes'),
file('build/tmp'),
file('build/dependency-cache'),
file('build/libs')]
sourceSets {
generated {
java {
srcDirs = ['build/generated-src']
}
}
}
ext.springSecurityVersion='3.0.5.RELEASE'
dependencies {
compile project(":spring-ldap-test"),
project(":spring-ldap-core-tiger"),
project(":spring-ldap-core-tiger")
compile "com.mysema.querydsl:querydsl-apt:$queryDslVersion"
compile("org.springframework.security:spring-security-core:$springSecurityVersion") {
exclude group: "org.springframework", module: "spring-expression"
exclude group: "org.springframework", module: "spring-core"
@@ -33,4 +51,38 @@ dependencies {
testCompile("org.springframework.security:spring-security-ldap:$springSecurityVersion") {
exclude group: "org.springframework.ldap", module: "spring-ldap-core"
}
}
}
task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') {
source = sourceSets.main.java
classpath = configurations.compile
options.compilerArgs = [
"-proc:only",
"-processor", "org.springframework.ldap.repository.support.LdapAnnotationProcessor"
]
destinationDir = sourceSets.generated.java.srcDirs.iterator().next()
}
compileJava {
dependsOn generateQueryDSL
source generateQueryDSL.destinationDir
}
compileGeneratedJava {
dependsOn generateQueryDSL
options.warnings = false
classpath += sourceSets.main.runtimeClasspath
}
clean {
delete sourceSets.generated.java.srcDirs
}
ideaModule.dependsOn generateQueryDSL
idea {
module {
sourceDirs += file('build/generated-src')
}
}

View File

@@ -0,0 +1,11 @@
package org.springframework.ldap.itest.repositories;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.ldap.itest.odm.Person;
import org.springframework.ldap.repository.LdapRepository;
/**
* @author Mattias Hellborg Arthursson
*/
public interface PersonQueryDslRepository extends LdapRepository<Person>, QueryDslPredicateExecutor<Person> {
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2005-2013 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.ldap.itest.odm;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.itest.AbstractLdapTemplateIntegrationTest;
import org.springframework.ldap.repository.support.QueryDslLdapQuery;
import org.springframework.test.context.ContextConfiguration;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Mattias Hellborg Arthursson
*/
@ContextConfiguration(locations = {"/conf/ldapTemplateTestContext.xml"})
public class LdapTemplateQueryDslLdapQueryITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapTemplate tested;
private QPerson qperson;
private QueryDslLdapQuery<Person> query;
@Before
public void prepareTestedInstance() {
qperson = QPerson.person;
query = new QueryDslLdapQuery<Person>(tested, qperson);
}
@Test
public void testUniqueResult() {
Person person = query.where(qperson.commonName.eq("Some Person3")).uniqueResult();
assertNotNull(person);
assertEquals("Some Person3", person.getCommonName());
assertEquals("Person3", person.getSurname());
assertEquals("Sweden, Company1, Some Person3", person.getDesc().get(0));
assertEquals("+46 555-123654", person.getTelephoneNumber());
}
@Test
public void testList() {
List<Person> persons = query.where(qperson.commonName.eq("Some Person3")).list();
Person person = persons.get(0);
assertEquals("Some Person3", person.getCommonName());
assertEquals("Person3", person.getSurname());
assertEquals("Sweden, Company1, Some Person3", person.getDesc().get(0));
assertEquals("+46 555-123654", person.getTelephoneNumber());
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2005-2013 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.ldap.itest.repository;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.itest.AbstractLdapTemplateIntegrationTest;
import org.springframework.ldap.itest.odm.Person;
import org.springframework.ldap.itest.odm.QPerson;
import org.springframework.ldap.itest.repositories.PersonQueryDslRepository;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.test.context.ContextConfiguration;
import javax.naming.Name;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Tests for Spring LDAP automatic repository scan functionality.
*
* @author Mattias Hellborg Arthursson
*/
@ContextConfiguration(locations = {"/conf/repositoryScanTestContext.xml"})
public class RepositoryScanQueryDslITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private PersonQueryDslRepository tested;
@Test
public void testFindOneWithPredicate() {
QPerson person = QPerson.person;
Person found = tested.findOne(person.commonName.eq("Some Person3"));
assertNotNull(found);
assertEquals("Some Person3", found.getCommonName());
assertEquals("Person3", found.getSurname());
assertEquals("Sweden, Company1, Some Person3", found.getDesc().get(0));
assertEquals("+46 555-123654", found.getTelephoneNumber());
}
@Test
public void testFindAllWithPredicate() {
QPerson person = QPerson.person;
Iterable<Person> foundPersons = tested.findAll(person.commonName.eq("Some Person3"));
Person found = foundPersons.iterator().next();
assertEquals("Some Person3", found.getCommonName());
assertEquals("Person3", found.getSurname());
assertEquals("Sweden, Company1, Some Person3", found.getDesc().get(0));
assertEquals("+46 555-123654", found.getTelephoneNumber());
}
@Test
public void testCountWithPredicate() {
QPerson person = QPerson.person;
long count = tested.count(person.commonName.eq("Some Person3"));
assertEquals(1, count);
}
}