DATACOUCH-190 - Document SpEL support example.

This commit is contained in:
Simon Baslé
2016-01-15 18:35:04 +01:00
parent 15d0d27d71
commit 6876e179f3
4 changed files with 169 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
package org.springframework.data.couchbase.repository.spel;
import java.util.Collections;
import java.util.Map;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.ExtensionAwareEvaluationContextProvider;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
import org.springframework.data.repository.query.spi.EvaluationContextExtensionSupport;
@Configuration
@EnableCouchbaseRepositories
public class SpelConfig extends IntegrationTestApplicationConfig {
@Bean
public EvaluationContextExtension customSpelExtension() {
return new CustomSpelExtension();
}
public static class CustomSpelExtension extends EvaluationContextExtensionSupport {
/**
* Returns the identifier of the extension. The id can be leveraged by users to fully qualify property lookups and
* thus overcome ambiguities in case multiple extensions expose properties with the same name.
*
* @return the extension id, must not be {@literal null}.
*/
@Override
public String getExtensionId() {
return "custom";
}
@Override
public Map<String, Object> getProperties() {
return Collections.<String, Object>singletonMap("oneCustomer", "uname-3");
}
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.data.couchbase.repository.spel;
import java.util.List;
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.repository.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@N1qlPrimaryIndexed
public interface SpelRepository extends CrudRepository<User, String> {
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND username = \"#{oneCustomer}\"")
List<User> findCustomUsers();
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 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.data.couchbase.repository.spel;
import static org.junit.Assert.*;
import java.util.List;
import com.couchbase.client.java.Bucket;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.repository.SimpleCouchbaseRepositoryListener;
import org.springframework.data.couchbase.repository.User;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Simon Baslé
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpelConfig.class)
@TestExecutionListeners(SimpleCouchbaseRepositoryListener.class)
public class SpelRepositoryTests {
@Autowired
private Bucket client;
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
@Autowired
private SpelRepository repository;
@Test
public void testSpelExtensionResolved() {
List<User> users = repository.findCustomUsers();
assertEquals(1, users.size());
assertEquals("testuser-3", users.get(0).getKey());
assertEquals("uname-3", users.get(0).getUsername());
}
}

View File

@@ -146,6 +146,47 @@ A few N1QL-specific values are provided through SpEL:
Another example: "`#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND test = $1`", which is equivalent to
`SELECT #{#n1ql.fields} FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} AND test = $1`".
.A practical application of SpEL with Spring Security
****
SpEL can be useful when you want to do a query depending on data injected by other Spring components, like Spring Security.
Here is what you need to do to extend the SpEL context to get access to such external data.
First, you need to implement an `EvaluationContextExtension` (use the support class as below):
[source,java]
----
class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {
@Override
public String getExtensionId() {
return "security";
}
@Override
public SecurityExpressionRoot getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {};
}
}
----
Then all you need to do for Spring Data Couchbase to be able to access associated SpEL values is to declare a corresponding bean in your configuration:
[source,java]
----
@Bean
EvaluationContextExtension securityExtension() {
return new SecurityEvaluationContextExtension();
}
----
This could be useful to craft a query according to the role of the connected user for instance:
[source,java]
----
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND " +
"role = '?#{hasRole('ROLE_ADMIN') ? 'public_admin' : 'admin'}'")
List<UserInfo> findAllAdmins(); //only ROLE_ADMIN users will see hidden admins
----
****
The second method uses Spring-Data's query derivation mechanism to build a N1QL query from the method name and parameters. This will produce a query looking like this: `SELECT ... FROM ... WHERE firstName = "valueOfFnameAtRuntime"`. You can combine these criteria, even do a count with a name like `countByFirstname` or a limit with a name like `findFirst3ByLastname`...
NOTE: Actually the generated N1QL query will also contain an additional N1QL criteria in order to only select documents that match the repository's entity class.