Skip to content

Commit 1fe2928

Browse files
committed
Add FluentQuery support to QuerydslLdapRepository.
Closes #269.
1 parent 7fa477c commit 1fe2928

File tree

8 files changed

+719
-18
lines changed

8 files changed

+719
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2015-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.ldap.repository.query;
17+
18+
import org.springframework.core.convert.converter.Converter;
19+
import org.springframework.data.mapping.PersistentEntity;
20+
import org.springframework.data.mapping.PersistentProperty;
21+
import org.springframework.data.mapping.PersistentPropertyAccessor;
22+
import org.springframework.data.mapping.PreferredConstructor;
23+
import org.springframework.data.mapping.PreferredConstructor.Parameter;
24+
import org.springframework.data.mapping.SimplePropertyHandler;
25+
import org.springframework.data.mapping.context.MappingContext;
26+
import org.springframework.data.mapping.model.EntityInstantiator;
27+
import org.springframework.data.mapping.model.EntityInstantiators;
28+
import org.springframework.data.mapping.model.ParameterValueProvider;
29+
import org.springframework.util.Assert;
30+
31+
/**
32+
* {@link Converter} to instantiate DTOs from fully equipped domain objects.
33+
*
34+
* @author Mark Paluch
35+
*/
36+
public class DtoInstantiatingConverter implements Converter<Object, Object> {
37+
38+
private final Class<?> targetType;
39+
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context;
40+
private final EntityInstantiator instantiator;
41+
42+
/**
43+
* Creates a new {@link Converter} to instantiate DTOs.
44+
*
45+
* @param dtoType must not be {@literal null}.
46+
* @param context must not be {@literal null}.
47+
* @param entityInstantiators must not be {@literal null}.
48+
*/
49+
public DtoInstantiatingConverter(Class<?> dtoType,
50+
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context,
51+
EntityInstantiators entityInstantiators) {
52+
53+
Assert.notNull(dtoType, "DTO type must not be null!");
54+
Assert.notNull(context, "MappingContext must not be null!");
55+
Assert.notNull(entityInstantiators, "EntityInstantiators must not be null!");
56+
57+
this.targetType = dtoType;
58+
this.context = context;
59+
this.instantiator = entityInstantiators.getInstantiatorFor(context.getRequiredPersistentEntity(dtoType));
60+
}
61+
62+
/*
63+
* (non-Javadoc)
64+
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
65+
*/
66+
@Override
67+
@SuppressWarnings({ "rawtypes", "unchecked" })
68+
public Object convert(Object source) {
69+
70+
if (targetType.isInterface()) {
71+
return source;
72+
}
73+
74+
PersistentEntity<?, ?> sourceEntity = context.getRequiredPersistentEntity(source.getClass());
75+
PersistentPropertyAccessor<?> sourceAccessor = sourceEntity.getPropertyAccessor(source);
76+
PersistentEntity<?, ?> targetEntity = context.getRequiredPersistentEntity(targetType);
77+
PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = targetEntity.getPersistenceConstructor();
78+
79+
Object dto = instantiator.createInstance(targetEntity, new ParameterValueProvider() {
80+
81+
@Override
82+
public Object getParameterValue(Parameter parameter) {
83+
return sourceAccessor.getProperty(sourceEntity.getPersistentProperty(parameter.getName()));
84+
}
85+
});
86+
87+
PersistentPropertyAccessor<?> dtoAccessor = targetEntity.getPropertyAccessor(dto);
88+
89+
targetEntity.doWithProperties((SimplePropertyHandler) property -> {
90+
91+
if (constructor.isConstructorParameter(property)) {
92+
return;
93+
}
94+
95+
dtoAccessor.setProperty(property,
96+
sourceAccessor.getProperty(sourceEntity.getPersistentProperty(property.getName())));
97+
});
98+
99+
return dto;
100+
}
101+
}

Diff for: src/main/java/org/springframework/data/ldap/repository/support/LdapRepositoryFactory.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@
2020
import java.lang.reflect.Method;
2121
import java.util.Optional;
2222

23+
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
2324
import org.springframework.data.ldap.repository.query.AnnotatedLdapRepositoryQuery;
2425
import org.springframework.data.ldap.repository.query.LdapQueryMethod;
2526
import org.springframework.data.ldap.repository.query.PartTreeLdapRepositoryQuery;
27+
import org.springframework.data.mapping.PersistentEntity;
28+
import org.springframework.data.mapping.PersistentProperty;
29+
import org.springframework.data.mapping.context.MappingContext;
2630
import org.springframework.data.projection.ProjectionFactory;
2731
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
2832
import org.springframework.data.repository.core.EntityInformation;
@@ -50,6 +54,7 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
5054

5155
private final LdapQueryLookupStrategy queryLookupStrategy;
5256
private final LdapOperations ldapOperations;
57+
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext;
5358

5459
/**
5560
* Creates a new {@link LdapRepositoryFactory}.
@@ -62,6 +67,24 @@ public LdapRepositoryFactory(LdapOperations ldapOperations) {
6267

6368
this.queryLookupStrategy = new LdapQueryLookupStrategy(ldapOperations);
6469
this.ldapOperations = ldapOperations;
70+
this.mappingContext = new LdapMappingContext();
71+
}
72+
73+
/**
74+
* Creates a new {@link LdapRepositoryFactory}.
75+
*
76+
* @param ldapOperations must not be {@literal null}.
77+
* @param mappingContext must not be {@literal null}.
78+
*/
79+
LdapRepositoryFactory(LdapOperations ldapOperations,
80+
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
81+
82+
Assert.notNull(ldapOperations, "LdapOperations must not be null!");
83+
Assert.notNull(mappingContext, "LdapMappingContext must not be null!");
84+
85+
this.queryLookupStrategy = new LdapQueryLookupStrategy(ldapOperations);
86+
this.ldapOperations = ldapOperations;
87+
this.mappingContext = mappingContext;
6588
}
6689

6790
/* (non-Javadoc)
@@ -92,7 +115,8 @@ protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
92115
*/
93116
@Override
94117
protected Object getTargetRepository(RepositoryInformation information) {
95-
return getTargetRepositoryViaReflection(information, ldapOperations, ldapOperations.getObjectDirectoryMapper(),
118+
return getTargetRepositoryViaReflection(information, ldapOperations, mappingContext,
119+
ldapOperations.getObjectDirectoryMapper(),
96120
information.getDomainType());
97121
}
98122

Diff for: src/main/java/org/springframework/data/ldap/repository/support/LdapRepositoryFactoryBean.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import javax.naming.Name;
1919

2020
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
21+
import org.springframework.data.mapping.PersistentEntity;
22+
import org.springframework.data.mapping.PersistentProperty;
2123
import org.springframework.data.mapping.context.MappingContext;
2224
import org.springframework.data.repository.Repository;
2325
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
@@ -39,6 +41,7 @@ public class LdapRepositoryFactoryBean<T extends Repository<S, Name>, S>
3941

4042
private @Nullable LdapOperations ldapOperations;
4143
private boolean mappingContextConfigured = false;
44+
private @Nullable MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext;
4245

4346
/**
4447
* Creates a new {@link LdapRepositoryFactoryBean} for the given repository interface.
@@ -64,6 +67,7 @@ public void setLdapOperations(LdapOperations ldapOperations) {
6467
public void setMappingContext(MappingContext<?, ?> mappingContext) {
6568

6669
super.setMappingContext(mappingContext);
70+
this.mappingContext = mappingContext;
6771
this.mappingContextConfigured = true;
6872
}
6973

@@ -76,7 +80,8 @@ protected RepositoryFactorySupport createRepositoryFactory() {
7680

7781
Assert.state(ldapOperations != null, "LdapOperations must be set");
7882

79-
return new LdapRepositoryFactory(ldapOperations);
83+
return mappingContext != null ? new LdapRepositoryFactory(ldapOperations, mappingContext)
84+
: new LdapRepositoryFactory(ldapOperations);
8085
}
8186

8287
/*

Diff for: src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapQuery.java

+38-6
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818
import static org.springframework.ldap.query.LdapQueryBuilder.*;
1919

2020
import java.util.List;
21+
import java.util.function.Consumer;
2122

23+
import org.springframework.ldap.core.ContextMapper;
2224
import org.springframework.ldap.core.LdapOperations;
2325
import org.springframework.ldap.filter.AbsoluteTrueFilter;
2426
import org.springframework.ldap.query.LdapQuery;
27+
import org.springframework.ldap.query.LdapQueryBuilder;
2528
import org.springframework.util.Assert;
2629

2730
import com.querydsl.core.DefaultQueryMetadata;
@@ -40,8 +43,9 @@
4043
public class QuerydslLdapQuery<K> implements FilteredClause<QuerydslLdapQuery<K>> {
4144

4245
private final LdapOperations ldapOperations;
43-
private final Class<? extends K> entityType;
46+
private final Class<K> entityType;
4447
private final LdapSerializer filterGenerator;
48+
private final Consumer<LdapQueryBuilder> queryCustomizer;
4549

4650
private QueryMixin<QuerydslLdapQuery<K>> queryMixin = new QueryMixin<>(this, new DefaultQueryMetadata().noValidate());
4751

@@ -52,7 +56,7 @@ public class QuerydslLdapQuery<K> implements FilteredClause<QuerydslLdapQuery<K>
5256
* @param entityPath must not be {@literal null}.
5357
*/
5458
public QuerydslLdapQuery(LdapOperations ldapOperations, EntityPath<K> entityPath) {
55-
this(ldapOperations, entityPath.getType());
59+
this(ldapOperations, (Class<K>) entityPath.getType());
5660
}
5761

5862
/**
@@ -61,13 +65,30 @@ public QuerydslLdapQuery(LdapOperations ldapOperations, EntityPath<K> entityPath
6165
* @param ldapOperations must not be {@literal null}.
6266
* @param entityType must not be {@literal null}.
6367
*/
64-
public QuerydslLdapQuery(LdapOperations ldapOperations, Class<? extends K> entityType) {
68+
public QuerydslLdapQuery(LdapOperations ldapOperations, Class<K> entityType) {
69+
this(ldapOperations, entityType, it -> {
70+
71+
});
72+
}
73+
74+
/**
75+
* Creates a new {@link QuerydslLdapQuery}.
76+
*
77+
* @param ldapOperations must not be {@literal null}.
78+
* @param entityType must not be {@literal null}.
79+
* @param queryCustomizer must not be {@literal null}.
80+
* @since 2.6
81+
*/
82+
public QuerydslLdapQuery(LdapOperations ldapOperations, Class<K> entityType,
83+
Consumer<LdapQueryBuilder> queryCustomizer) {
6584

6685
Assert.notNull(ldapOperations, "LdapOperations must not be null!");
6786
Assert.notNull(entityType, "Type must not be null!");
87+
Assert.notNull(queryCustomizer, "Query customizer must not be null!");
6888

6989
this.ldapOperations = ldapOperations;
7090
this.entityType = entityType;
91+
this.queryCustomizer = queryCustomizer;
7192
this.filterGenerator = new LdapSerializer(ldapOperations.getObjectDirectoryMapper(), this.entityType);
7293
}
7394

@@ -89,10 +110,17 @@ public List<K> list() {
89110

90111
LdapQuery ldapQuery = buildQuery();
91112
if (ldapQuery.filter() instanceof AbsoluteTrueFilter) {
92-
return (List<K>) ldapOperations.findAll(entityType);
113+
return ldapOperations.findAll(entityType);
93114
}
94115

95-
return (List<K>) ldapOperations.find(ldapQuery, entityType);
116+
return ldapOperations.find(ldapQuery, entityType);
117+
}
118+
119+
<T> List<T> search(ContextMapper<T> mapper) {
120+
121+
LdapQuery ldapQuery = buildQuery();
122+
123+
return ldapOperations.search(ldapQuery, mapper);
96124
}
97125

98126
public K uniqueResult() {
@@ -102,6 +130,10 @@ public K uniqueResult() {
102130
LdapQuery buildQuery() {
103131

104132
Predicate where = queryMixin.getMetadata().getWhere();
105-
return where != null ? query().filter(filterGenerator.handle(where)) : query().filter(new AbsoluteTrueFilter());
133+
134+
LdapQueryBuilder builder = query();
135+
queryCustomizer.accept(builder);
136+
137+
return where != null ? builder.filter(filterGenerator.handle(where)) : builder.filter(new AbsoluteTrueFilter());
106138
}
107139
}

0 commit comments

Comments
 (0)