From 2035a88dba3d8653d0f7114a006c334fb2190e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Basl=C3=A9?= Date: Thu, 30 Jul 2015 17:44:53 +0200 Subject: [PATCH] DATACOUCH-154 - Guess design doc as uncapitalized class name. In the SimpleCouchbaseRepository the guessed design document was lowercase of the entity class, whereas in ViewBasedCouchbaseQuery it is uncapitalized (only first letter is lowercase). This has been made consistent, using the uncapitalize method. The doc now reflects that and explicitly have examples with camel case classes (UserInfo instead of User). --- README.md | 34 ++++++------- src/main/asciidoc/repository.adoc | 50 +++++++++---------- .../support/SimpleCouchbaseRepository.java | 3 +- 3 files changed, 44 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 5c39926c..783ba3ed 100644 --- a/README.md +++ b/README.md @@ -75,27 +75,27 @@ To simplify the creation of data repositories Spring Data Couchbase provides a g will automatically create a repository proxy for you that adds implementations of finder methods you specify on an interface. -To create a repository on top of a `User` entity, all you need to write is: +To create a repository on top of a `UserInfo` entity, all you need to write is: ```java -public interface UserRepository extends CrudRepository { +public interface UserRepository extends CrudRepository { /** - * Return all users emitted by the view user/adults + * Return all users emitted by the view userInfo/adults */ @View - List findAdults(); + List findAdults(); /** * Find all users matching the last name. */ @View(viewName="lastNames") - List findByLastname(String lastName); + List findByLastname(String lastName); /** * Find all the users whose first name contains the word. */ - List findByFirstnameContains(String word); + List findByFirstnameContains(String word); } ``` @@ -104,7 +104,7 @@ Once you get a reference to that repository bean, you'll find a lot of methods t entity. In addition to the ones provided through the `CrudRepository`, you can add your own methods as well. In general, every CRUD method that does not depend on a single key (like `findById`) needs a backing View, `all` on the -server side (the design document is by default expected to be the uncapitalized name of the entity, like `user`). +server side (the design document is by default expected to be the uncapitalized name of the entity, like `userInfo`). ## Custom Repository Methods and Views Finder methods you define, if annotated with `@View`, also are backed by views. Either you want to return all items from @@ -112,7 +112,7 @@ these views and you can let the method name reflect the view name (like in `find `adults` view), or provide simple criteria (you explicitly specify the `viewName` and let the method name determine your criteria, like in `findByLastname`). -In the example above, it assumes you have a view named `findByLastname` in the `user` design document. You +In the example above, it assumes you have a view named `findByLastname` in the `userInfo` design document. You can customize the view and design document name through the `@View` annotation. Also make sure you publish them into production before accessing it. @@ -120,7 +120,7 @@ This is an example view for the `findByLastname` method: ```javascript function (doc, meta) { - if(doc._class == "com.example.entity.User" && doc.lastname) { + if(doc._class == "com.example.entity.UserInfo" && doc.lastname) { emit(doc.lastname, null); } } @@ -134,7 +134,7 @@ function): ```javascript function (doc, meta) { - if(doc._class == "com.example.entity.User") { + if(doc._class == "com.example.entity.UserInfo") { emit(null, null); } } @@ -148,13 +148,13 @@ This is the default repository query mechanism, so the associated `@Query` annot like: ```java -public interface UserRepository extends CrudRepository { +public interface UserRepository extends CrudRepository { /** * Advanced querying with N1QL derivation */ @View - List findByLastnameEqualsIgnoreCaseAndFirstnameStartsWithAndIsAdultTrue(String lastName, String fnamePrefix); + List findByLastnameEqualsIgnoreCaseAndFirstnameStartsWithAndIsAdultTrue(String lastName, String fnamePrefix); } ``` @@ -169,7 +169,7 @@ placeholder to make sure all necessary fields and metadata are selected by N1QL: ```java @Query("$SELECT_ENTITY$ WHERE firstname LIKE "%ck%" -List findPatrickAndJackAmongOthers(); +List findPatrickAndJackAmongOthers(); ``` ## Using The Repository @@ -232,12 +232,12 @@ public class MyService { public void doWork() { userRepository.deleteAll(); - User user = new User(); - user.setLastname("Jackson"); + UserInfo userInfo = new UserInfo(); + UserInfo.setLastname("Jackson"); - user = userRepository.save(user); + UserInfo = userRepository.save(userInfo); - List allJacksons = userRepository.findByLastname("Jackson"); + List allJacksons = userRepository.findByLastname("Jackson"); } } ``` diff --git a/src/main/asciidoc/repository.adoc b/src/main/asciidoc/repository.adoc index b09cff39..00507949 100644 --- a/src/main/asciidoc/repository.adoc +++ b/src/main/asciidoc/repository.adoc @@ -33,15 +33,15 @@ XML-based configuration is also available: [[couchbase.repository.usage]] == Usage -In the simplest case, your repository will extend the `CrudRepository`, where T is the entity that you want to expose. Let's look at a repository for a user: +In the simplest case, your repository will extend the `CrudRepository`, where T is the entity that you want to expose. Let's look at a repository for a UserInfo: -.A User repository +.A UserInfo repository ==== [source,java] ---- import org.springframework.data.repository.CrudRepository; -public interface UserRepository extends CrudRepository { +public interface UserRepository extends CrudRepository { } ---- ==== @@ -56,22 +56,22 @@ Now, let's imagine we `@Autowire` the `UserRepository` to a class that makes use | Method | Description -| User save(User entity) +| UserInfo save(UserInfo entity) | Save the given entity. -| Iterable save(Iterable entity) +| Iterable save(Iterable entity) | Save the list of entities. -| User findOne(String id) +| UserInfo findOne(String id) | Find a entity by its unique id. | boolean exists(String id) | Check if a given entity exists by its unique id. -| Iterable findAll() (*) +| Iterable findAll() (*) | Find all entities by this type in the bucket. -| Iterable findAll(Iterable ids) +| Iterable findAll(Iterable ids) | Find all entities by this type and the given list of ids. | long count() (*) @@ -80,10 +80,10 @@ Now, let's imagine we `@Autowire` the `UserRepository` to a class that makes use | void delete(String id) | Delete the entity by its id. -| void delete(User entity) +| void delete(UserInfo entity) | Delete the entity. -| void delete(Iterable entities) +| void delete(Iterable entities) | Delete all given entities. | void deleteAll() (*) @@ -106,16 +106,16 @@ WARNING: If it is detected at configuration time that the cluster doesn't suppor Here is an example: -.An extended User repository with N1QL queries +.An extended UserInfo repository with N1QL queries ==== [source,java] ---- -public interface UserRepository extends CrudRepository { +public interface UserRepository extends CrudRepository { @Query("$SELECT_ENTITY$ WHERE role = 'admin'") - List findAllAdmins(); + List findAllAdmins(); - List findByFirstname(String fname); + List findByFirstname(String fname); } ---- ==== @@ -174,7 +174,7 @@ IMPORTANT: This is only true for the methods directly defined by the `CrudReposi To cover the basic CRUD methods from the `CrudRepository`, one view needs to be implemented in Couchbase Server. It basically returns all documents for the specific entity and also adds the optional reduce function `_count`. -Since every view has a design document and view name, by convention we default to `all` as the view name and the lower-cased entity name as the design document name. So if your entity is named `User`, then the code expects the `all` view in the `user` design document. It needs to look like this: +Since every view has a design document and view name, by convention we default to `all` as the view name and the uncapitalized (lowercase first letter) entity name as the design document name. So if your entity is named `UserInfo`, then the code expects the `all` view in the `userInfo` design document. It needs to look like this: .The all view map function ==== @@ -182,7 +182,7 @@ Since every view has a design document and view name, by convention we default t ---- // do not forget the _count reduce function! function (doc, meta) { - if (doc._class == "namespace.to.entity.User") { + if (doc._class == "namespace.to.entity.UserInfo") { emit(null, null); } } @@ -204,29 +204,29 @@ In `2.0`, since N1QL has been introduced as a more powerful concept, view-backed - View based query derivation is limited to a few keywords and only works on simple keys (not compound keys like `[ age, fname ]`). - View based query derivation still needs you to include *one* valid property before keywords in the method name. -.An extended User repository with View queries +.An extended UserInfo repository with View queries ==== [source,java] ---- -public interface UserRepository extends CrudRepository { +public interface UserRepository extends CrudRepository { @View - List findAllAdmins(); + List findAllAdmins(); @View(viewName="firstNames") - List findByFirstnameStartingWith(String fnamePrefix); + List findByFirstnameStartingWith(String fnamePrefix); } ---- ==== -Implementing your custom repository finder methods also needs backing views. The `findAllAdmins` guesses to use the `allAdmins` view in the `user` design document, by convention. Imagine we have a field on our entity which looks like `boolean isAdmin`. We can write a view like this to expose them (we don't need a reduce function for this one, unless you plan to call one by prefixing your method with `count` instead of `find`!): +Implementing your custom repository finder methods also needs backing views. The `findAllAdmins` guesses to use the `allAdmins` view in the `userInfo` design document, by convention. Imagine we have a field on our entity which looks like `boolean isAdmin`. We can write a view like this to expose them (we don't need a reduce function for this one, unless you plan to call one by prefixing your method with `count` instead of `find`!): .The allAdmins map function ==== [source,javascript] ---- function (doc, meta) { - if (doc._class == "namespace.to.entity.User" && doc.isAdmin) { + if (doc._class == "namespace.to.entity.UserInfo" && doc.isAdmin) { emit(null, null); } } @@ -240,14 +240,14 @@ By now, we've never actually customized our view at query time. This is where th [source,javascript] ---- function (doc, meta) { - if (doc._class == "namespace.to.entity.User") { + if (doc._class == "namespace.to.entity.UserInfo") { emit(doc.firstname, null); } } ---- ==== -This view not only emits the document id, but also the firstname of every user as the key. We can now run a `ViewQuery` which returns us all users with a firstname of "Michael" or "Michele". +This view not only emits the document id, but also the firstname of every UserInfo as the key. We can now run a `ViewQuery` which returns us all users with a firstname of "Michael" or "Michele". .Query a repository method with custom params. ==== @@ -257,7 +257,7 @@ This view not only emits the document id, but also the firstname of every user a UserRepository repo = ctx.getBean(UserRepository.class); // Find all users with first name starting with "Mich" -List users = repo.findByFirstnameStartingWith("Mich"); +List users = repo.findByFirstnameStartingWith("Mich"); ---- ==== diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java index 50e97488..2e4183be 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java @@ -30,6 +30,7 @@ import org.springframework.data.couchbase.core.view.View; import org.springframework.data.couchbase.repository.CouchbaseRepository; import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Repository base implementation for Couchbase. @@ -205,7 +206,7 @@ public class SimpleCouchbaseRepository implements Co * @return ResolvedView containing the designDocument and viewName. */ private ResolvedView determineView() { - String designDocument = entityInformation.getJavaType().getSimpleName().toLowerCase(); + String designDocument = StringUtils.uncapitalize(entityInformation.getJavaType().getSimpleName()); String viewName = "all"; final View view = viewMetadataProvider.getView();