DATACMNS-49 - Added infrastructure to read queries from a Properties file.
Added NamedQueries abstraction to hide a map of names to manually defined queries. Added PropertiesBasedNamedQueries to hold the named queries in a Properties instance. Extended resolveQuery(…) method in QueryLookupStrategy to take a NamedQuery instance to allow query creation callback use the named queries. Added optional 'named-queries-location' attribute to the repositories namespace the bean definition parser loads the properties from to populate a PropertiesBasedNamedQueries instance and pipe it into the repository factory. Store implementations have to default the name to lookup in their RepositoryConfiguraion class (see CommonRepositoryConfigInformation.getNamedQueriesLocation()).
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2010 the original author or authors.
|
||||
* Copyright 2008-2011 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.
|
||||
@@ -206,6 +206,8 @@ public abstract class AbstractRepositoryConfigDefinitionParser<S extends GlobalR
|
||||
context.getInterfaceName());
|
||||
builder.addPropertyValue("queryLookupStrategyKey",
|
||||
context.getQueryLookupStrategyKey());
|
||||
builder.addPropertyValue("namedQueries",
|
||||
new NamedQueriesBeanDefinitionParser(context.getNamedQueriesLocation()).parse(context.getSource(), parser));
|
||||
|
||||
String transactionManagerRef = context.getTransactionManagerRef();
|
||||
|
||||
|
||||
@@ -77,4 +77,10 @@ public interface CommonRepositoryConfigInformation {
|
||||
*/
|
||||
Key getQueryLookupStrategyKey();
|
||||
|
||||
/**
|
||||
* Returns the location of the properties file to contain named queries.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getNamedQueriesLocation();
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2011 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.repository.config;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} to create {@link BeanDefinition}s of {@link NamedQueries} instances looking up a
|
||||
* {@link Properties} file fom the given location.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class NamedQueriesBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String ATTRIBUTE = "named-queries-location";
|
||||
private final String defaultLocation;
|
||||
|
||||
/**
|
||||
* Creates a new {@link NamedQueriesBeanDefinitionParser} using the given default location.
|
||||
*
|
||||
* @param defaultLocation must be non-empty
|
||||
*/
|
||||
public NamedQueriesBeanDefinitionParser(String defaultLocation) {
|
||||
Assert.hasText(defaultLocation);
|
||||
this.defaultLocation = defaultLocation;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
|
||||
*/
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
|
||||
BeanDefinitionBuilder properties = BeanDefinitionBuilder.rootBeanDefinition(PropertiesFactoryBean.class);
|
||||
properties.addPropertyValue("locations", getDefaultedLocation(element));
|
||||
|
||||
if (isDefaultLocation(element)) {
|
||||
properties.addPropertyValue("ignoreResourceNotFound", true);
|
||||
}
|
||||
|
||||
AbstractBeanDefinition propertiesDefinition = properties.getBeanDefinition();
|
||||
propertiesDefinition.setSource(parserContext.extractSource(element));
|
||||
|
||||
BeanDefinitionBuilder namedQueries = BeanDefinitionBuilder.rootBeanDefinition(PropertiesBasedNamedQueries.class);
|
||||
namedQueries.addConstructorArgValue(propertiesDefinition);
|
||||
|
||||
AbstractBeanDefinition namedQueriesDefinition = namedQueries.getBeanDefinition();
|
||||
namedQueriesDefinition.setSource(parserContext.extractSource(element));
|
||||
|
||||
return namedQueriesDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we should use the default location.
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private boolean isDefaultLocation(Element element) {
|
||||
return !StringUtils.hasText(element.getAttribute(ATTRIBUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location to look for {@link Properties} if configured or the default one if not.
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private String getDefaultedLocation(Element element) {
|
||||
|
||||
String locations = element.getAttribute(ATTRIBUTE);
|
||||
return StringUtils.hasText(locations) ? locations : defaultLocation;
|
||||
}
|
||||
}
|
||||
@@ -189,4 +189,12 @@ public abstract class ParentDelegatingRepositoryConfigInformation<T extends Comm
|
||||
|
||||
return parent.getQueryLookupStrategyKey();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.CommonRepositoryConfigInformation#getNamedQueriesLocation()
|
||||
*/
|
||||
public String getNamedQueriesLocation() {
|
||||
return parent.getNamedQueriesLocation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2011 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.repository.core;
|
||||
|
||||
/**
|
||||
* Abstraction of a map of {@link NamedQueries} that can be looked up by their names.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface NamedQueries {
|
||||
|
||||
/**
|
||||
* Returns whether the map contains a named query for the given name. If this method returns {@literal true} you can
|
||||
* expect {@link #getQuery(String)} to return a non-{@literal null} query for the very same name.
|
||||
*
|
||||
* @param queryName
|
||||
* @return
|
||||
*/
|
||||
boolean hasQuery(String queryName);
|
||||
|
||||
/**
|
||||
* Returns the named query with the given name or {@literal null} if none exists.
|
||||
*
|
||||
* @param queryName
|
||||
* @return
|
||||
*/
|
||||
String getQuery(String queryName);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2011 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.repository.core.support;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link NamedQueries} implementation backed by a {@link Properties} instance.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PropertiesBasedNamedQueries implements NamedQueries {
|
||||
|
||||
public static NamedQueries EMPTY = new PropertiesBasedNamedQueries(new Properties());
|
||||
|
||||
private final Properties properties;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PropertiesBasedNamedQueries} for the given {@link Properties} instance.
|
||||
*
|
||||
* @param properties
|
||||
*/
|
||||
public PropertiesBasedNamedQueries(Properties properties) {
|
||||
Assert.notNull(properties);
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.NamedQueries#hasNamedQuery(java.lang.String)
|
||||
*/
|
||||
public boolean hasQuery(String queryName) {
|
||||
return properties.containsKey(queryName);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.NamedQueries#getNamedQuery(java.lang.String)
|
||||
*/
|
||||
public String getQuery(String queryName) {
|
||||
return properties.getProperty(queryName);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
@@ -43,6 +44,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
|
||||
private Key queryLookupStrategyKey;
|
||||
private Class<? extends T> repositoryInterface;
|
||||
private Object customImplementation;
|
||||
private NamedQueries namedQueries;
|
||||
|
||||
|
||||
/**
|
||||
@@ -79,6 +81,15 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
|
||||
this.customImplementation = customImplementation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Setter to inject a {@link NamedQueries} instance.
|
||||
*
|
||||
* @param namedQueries the namedQueries to set
|
||||
*/
|
||||
public void setNamedQueries(NamedQueries namedQueries) {
|
||||
this.namedQueries = namedQueries;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityMetadataProvider#getEntityMetadata()
|
||||
@@ -135,15 +146,16 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
this.factory = createRepositoryFactory();
|
||||
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
|
||||
this.factory.setNamedQueries(namedQueries);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
@@ -56,7 +57,7 @@ public abstract class RepositoryFactorySupport {
|
||||
private QueryLookupStrategy.Key queryLookupStrategyKey;
|
||||
private List<QueryCreationListener<?>> queryPostProcessors =
|
||||
new ArrayList<QueryCreationListener<?>>();
|
||||
|
||||
private NamedQueries namedQueries = PropertiesBasedNamedQueries.EMPTY;
|
||||
|
||||
/**
|
||||
* Sets the strategy of how to lookup a query to execute finders.
|
||||
@@ -67,6 +68,15 @@ public abstract class RepositoryFactorySupport {
|
||||
|
||||
this.queryLookupStrategyKey = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a {@link NamedQueries} instance to be handed to the {@link QueryLookupStrategy} for query creation.
|
||||
*
|
||||
* @param namedQueries the namedQueries to set
|
||||
*/
|
||||
public void setNamedQueries(NamedQueries namedQueries) {
|
||||
this.namedQueries = namedQueries == null ? PropertiesBasedNamedQueries.EMPTY : namedQueries;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -291,7 +301,7 @@ public abstract class RepositoryFactorySupport {
|
||||
|
||||
for (Method method : repositoryInformation.getQueryMethods()) {
|
||||
RepositoryQuery query =
|
||||
lookupStrategy.resolveQuery(method, repositoryInformation);
|
||||
lookupStrategy.resolveQuery(method, repositoryInformation, namedQueries);
|
||||
invokeListeners(query);
|
||||
queries.put(method, query);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.repository.query;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -56,7 +57,8 @@ public interface QueryLookupStrategy {
|
||||
*
|
||||
* @param method
|
||||
* @param metadata
|
||||
* @param namedQueries
|
||||
* @return
|
||||
*/
|
||||
RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata);
|
||||
RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries);
|
||||
}
|
||||
@@ -104,6 +104,19 @@ public class QueryMethod {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the named query this method belongs to.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getNamedQueryName() {
|
||||
|
||||
Class<?> domainClass = getDomainClass();
|
||||
return String.format("%s.%s", domainClass.getSimpleName(),
|
||||
method.getName());
|
||||
}
|
||||
|
||||
|
||||
protected Class<?> getDomainClass() {
|
||||
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="named-queries-location" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Defines the location to look for a Properties file containing externally defined queries.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="repository">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.repository.core.support;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -28,12 +28,11 @@ import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.QueryCreationListener;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
|
||||
|
||||
/**
|
||||
@@ -89,6 +88,7 @@ public class RepositoryFactorySupportUnitTests {
|
||||
verify(backingRepo, times(0)).findOne(1);
|
||||
}
|
||||
|
||||
|
||||
class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -123,7 +123,7 @@ public class RepositoryFactorySupportUnitTests {
|
||||
RepositoryQuery queryTwo = mock(RepositoryQuery.class);
|
||||
|
||||
QueryLookupStrategy strategy = mock(QueryLookupStrategy.class);
|
||||
when(strategy.resolveQuery(any(Method.class), any(RepositoryMetadata.class)))
|
||||
when(strategy.resolveQuery(any(Method.class), any(RepositoryMetadata.class), any(NamedQueries.class)))
|
||||
.thenReturn(queryOne, queryTwo);
|
||||
|
||||
return strategy;
|
||||
|
||||
Reference in New Issue
Block a user