DATACOUCH-74 - Initial Documentation
This commit is contained in:
68
src/docbkx/caching.xml
Normal file
68
src/docbkx/caching.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="couchbase.caching">
|
||||
<title>Caching</title>
|
||||
|
||||
<abstract>
|
||||
<para>This chapter describes additional support for caching and <code>@Cacheable</code>.</para>
|
||||
</abstract>
|
||||
|
||||
<section id="caching.usage">
|
||||
<title>Configuration & Usage</title>
|
||||
|
||||
<para>Technically, caching is not part of spring-data, but is implemented directly in the spring core. Most
|
||||
database implementations in the spring-data package can't support <code>@Cacheable</code>, because it is not
|
||||
possible to store arbitrary data.</para>
|
||||
|
||||
<para>Couchbase supports both binary and JSON data, so you can get both out of the same database.</para>
|
||||
|
||||
<para>To make it work, you need to add the <code>@EnableCaching</code> annotation and configure the
|
||||
<code>cacheManager</code> bean:</para>
|
||||
|
||||
<example>
|
||||
<title><code>AbstractCouchbaseConfiguration</code> for Caching</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class Config extends AbstractCouchbaseConfiguration {
|
||||
// general methods
|
||||
|
||||
@Bean
|
||||
public CouchbaseCacheManager cacheManager() throws Exception {
|
||||
HashMap<String, CouchbaseClient> instances = new HashMap<String, CouchbaseClient>();
|
||||
instances.put("persistent", couchbaseClient());
|
||||
return new CouchbaseCacheManager(instances);
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>The <code>persistent</code> identifier can then be used on the <code>@Cacheable</code> annotation to identify
|
||||
the cache manager to use (you can have more than one configured).</para>
|
||||
|
||||
<para>Once it is set up, you can annotate every method with the <code>@Cacheable</code> annotation to transparently
|
||||
cache it in your couchbase bucket. You can also customize how the key is generated.</para>
|
||||
|
||||
<example>
|
||||
<title>Caching example</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Cacheable(value="persistent", key="'longrunsim-'+#time")
|
||||
public String simulateLongRun(long time) {
|
||||
try {
|
||||
Thread.sleep(time);
|
||||
} catch(Exception ex) {
|
||||
System.out.println("This shouldnt happen...");
|
||||
}
|
||||
return "Ive slept " + time + " miliseconds.;
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>If you run the method multiple times, you'll see a set operation happening first, followed by multiple
|
||||
get operations and no sleep time (which fakes the expensive execution). You can store whatever you want, if it
|
||||
is JSON of course you can access it through views and look at it in the Web UI.</para>
|
||||
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
141
src/docbkx/configuration.xml
Normal file
141
src/docbkx/configuration.xml
Normal file
@@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="couchbase.configuration">
|
||||
<title>Installation & Configuration</title>
|
||||
|
||||
<abstract>
|
||||
<para>This chapter describes the common installation and configuration steps needed when working with the
|
||||
library.</para>
|
||||
</abstract>
|
||||
|
||||
<section id="installation">
|
||||
<title>Installation</title>
|
||||
|
||||
<para>All versions intented for production use are distributed across Maven Central and the Spring release
|
||||
repository. As a result, the library can be included like any other maven dependency:</para>
|
||||
|
||||
<example>
|
||||
<title>Including the dependency through maven</title>
|
||||
|
||||
<programlisting lang="xml"><![CDATA[
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-couchbase</artifactId>
|
||||
<version>1.0.0.RELEASE</version>
|
||||
</dependency>
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>This will pull in several dependencies, including the underlying Couchbase Java SDK, common Spring
|
||||
dependencies and also Jackson as the JSON mapping infrastructure.</para>
|
||||
|
||||
<para>You can also grab snapshots from the
|
||||
<ulink url="http://repo.spring.io/libs-snapshot">spring snapshot repository</ulink> and milestone releases
|
||||
from the <ulink url="http://repo.spring.io/libs-milestone">milestone repository</ulink>. Here is an example
|
||||
on how to use the current SNAPSHOT dependency:</para>
|
||||
|
||||
<example>
|
||||
<title>Using a snapshot version</title>
|
||||
|
||||
<programlisting lang="xml"><![CDATA[
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-couchbase</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<name>Spring Snapshot Repository</name>
|
||||
<url>http://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Once you have all needed dependencies on the classpath, you can start configuring it. Both Java and
|
||||
XML config are supported. The next sections describe both approaches in detail.</para>
|
||||
</section>
|
||||
|
||||
<section id="configuration-java">
|
||||
<title>Annotation-based Configuration ("JavaConfig")</title>
|
||||
|
||||
<para>The annotation based configuration approach is getting more and more popular. It allows you to get rid
|
||||
of XML configuration and treat configuration as part of your code directly. To get started, all you need
|
||||
to do is sublcass the <code>AbstractCouchbaseConfiguration</code> and implement the abstract methods.</para>
|
||||
|
||||
<para>Please make sure to have cglib support in the classpath so that the annotation based configuration works.</para>
|
||||
|
||||
<example>
|
||||
<title>Extending the <code>AbstractCouchbaseConfiguration</code></title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Configuration
|
||||
public class Config extends AbstractCouchbaseConfiguration {
|
||||
@Override
|
||||
protected List<String> bootstrapHosts() {
|
||||
return Collections.singletonList("127.0.0.1");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBucketName() {
|
||||
return "beer-sample";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBucketPassword() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>All you need to provide is a list of Couchbase nodes to bootstrap into (without any ports, just the IP
|
||||
address or hostname). Please note that while one host is sufficient in development, it is recommended to
|
||||
add 3 to 5 bootstrap nodes here. Couchbase will pick up all nodes from the cluster automatically, but it
|
||||
could be the case that the only node you've provided is experiencing issues while you are starting the
|
||||
application.</para>
|
||||
|
||||
<para>The <code>bucketName</code> and <code>password</code> should be the same as configured in Couchbase
|
||||
Server itself. In the example given, we are connecting to the <code>beer-sample</code> bucket which is one
|
||||
of the sample buckets shipped with Couchbase Server and has no password set by default.</para>
|
||||
|
||||
<para>Depending on how your environment is setup, the configuration will be automatically picked up by the
|
||||
context or you need to instantiate your own one. How to manage configurations is not scope of this manual,
|
||||
please refer to the spring documentation for more information on that topic.</para>
|
||||
|
||||
<para>While not immediately obvious, much more things can be customized and overriden as custom beans from
|
||||
this configuration - we'll touch them in the individual manual sections as needed (for example repositories,
|
||||
validation and custom converters).</para>
|
||||
</section>
|
||||
|
||||
<section id="configuration-xml">
|
||||
<title>XML-based Configuration</title>
|
||||
|
||||
<para>The library provides a custom namespace that you can use in your XML configuration:</para>
|
||||
|
||||
<example>
|
||||
<title>Basic XML configuration</title>
|
||||
<programlisting lang="xml"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/data/couchbase
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/data/couchbase
|
||||
http://www.springframework.org/schema/data/couchbase/spring-couchbase.xsd">
|
||||
|
||||
<couchbase:couchbase bucket="beer-sample" password="" host="127.0.0.1" />
|
||||
</beans:beans>
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>This code is equivalent to the java configuration approach shown above. It is also possible to
|
||||
configure templates and repositories, which is shown in the appropriate sections.</para>
|
||||
|
||||
<para>If you start your application, you should see Couchbase INFO level logging in the logs, indicating
|
||||
that the underlying Couchbase Java SDK is connecting to the database. If any errors are reported, make sure
|
||||
that the given credentials and host information is correct.</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
453
src/docbkx/entity.xml
Normal file
453
src/docbkx/entity.xml
Normal file
@@ -0,0 +1,453 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="couchbase.entity">
|
||||
<title>Modeling Entities</title>
|
||||
|
||||
<abstract>
|
||||
<para>This chapter describes how to model Entities and explains their counterpart representation in Couchbase
|
||||
Server itself.</para>
|
||||
</abstract>
|
||||
|
||||
<section id="basics">
|
||||
<title>Documents and Fields</title>
|
||||
|
||||
<para>All entities should be annotated with the <code>@Document</code> annotation. Also, every field in the entity
|
||||
should be annotated with the <code>@Field</code> annotation. While this is - strictly speaking - optional, it
|
||||
helps to reduce edge cases and clearly shows the intent and design of the entity.</para>
|
||||
|
||||
<para>There is also a special <code>@Id</code> annotation which needs to be always in place. Best practice is
|
||||
to also name the property <code>id</code>. Here is a very simple <code>User</code> entity:</para>
|
||||
|
||||
<example>
|
||||
<title>A simple Document with Fields</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.couchbase.core.mapping.Field;
|
||||
|
||||
@Document
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Field
|
||||
private String firstname;
|
||||
|
||||
@Field
|
||||
private String lastname;
|
||||
|
||||
public User(String id, String firstname, String lastname) {
|
||||
this.id = id;
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getFirstname() {
|
||||
return firstname;
|
||||
}
|
||||
|
||||
public String getLastname() {
|
||||
return lastname;
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Couchbase Server supports automatic expiration for documents. The library implements support for it through
|
||||
the <code>@Document</code> annotation. You can set a <code>expiry</code> value which translates to the number of
|
||||
seconds until the document gets removed automatically. If you want to make it expire in 10 seconds after mutation,
|
||||
set it like <code>@Document(expiry = 10)</code>.</para>
|
||||
|
||||
<para>If you want a different representation of the field name inside the document in contrast to the field
|
||||
name used in your entity, you can set a different name on the <code>@Field</code> annotation. For example if
|
||||
you want to keep your documents small you can set the firstname field to <code>@Field("fname")</code>. In the
|
||||
JSON document, you'll see <code>{"fname": ".."}</code> instead of <code>{"firstname": ".."}</code>.</para>
|
||||
|
||||
<para>The <code>@Id</code> annotation needs to be present because every document in Couchbase needs a unique
|
||||
key. This key needs to be any string with a length of maximum 250 characters. Feel free to use whatever fits
|
||||
your use case, be it a UUID, an email address or anything else.</para>
|
||||
</section>
|
||||
|
||||
<section id="datatypes">
|
||||
<title>Datatypes and Converters</title>
|
||||
|
||||
<para>The storage format of choice is JSON. It is great, but like many data representations it allows less
|
||||
datatypes than you could express in Java directly. Therefore, for all non-primitive types some form of
|
||||
conversion to and from supported types needs to happen.</para>
|
||||
|
||||
<para>For the following entity field types, you don't need to add special handling:</para>
|
||||
|
||||
<table>
|
||||
<title>Primitive Types</title>
|
||||
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Java Type</entry>
|
||||
<entry>JSON Representation</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>string</entry>
|
||||
<entry>string</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>boolean</entry>
|
||||
<entry>boolean</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>byte</entry>
|
||||
<entry>number</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>short</entry>
|
||||
<entry>number</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>int</entry>
|
||||
<entry>number</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>long</entry>
|
||||
<entry>number</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>float</entry>
|
||||
<entry>number</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>double</entry>
|
||||
<entry>number</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>null</entry>
|
||||
<entry>Ignored on write</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>Since JSON supports objects ("maps") and lists, <code>Map</code> and <code>List</code> types can be converted
|
||||
naturally. If they only contain primitive field types from the last paragraph, you don't need to add special
|
||||
handling too. Here is an example:</para>
|
||||
|
||||
<example>
|
||||
<title>A Document with Map and List</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Document
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Field
|
||||
private List<String> firstnames;
|
||||
|
||||
@Field
|
||||
private Map<String, Integer> childrenAges;
|
||||
|
||||
public User(String id, List<String> firstnames, Map<String, Integer> childrenAges) {
|
||||
this.id = id;
|
||||
this.firstnames = firstnames;
|
||||
this.childrenAges = childrenAges;
|
||||
}
|
||||
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Storing a user with some sample data could look like this as a JSON representation:</para>
|
||||
|
||||
<example>
|
||||
<title>A Document with Map and List - JSON</title>
|
||||
<programlisting lang="json"><![CDATA[
|
||||
{
|
||||
"_class": "foo.User",
|
||||
"childrenAges": {
|
||||
"Alice": 10,
|
||||
"Bob": 5
|
||||
},
|
||||
"firstnames": [
|
||||
"Foo",
|
||||
"Bar",
|
||||
"Baz"
|
||||
]
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>You don't need to break everything down to primitive types and Lists/Maps all the time. Of course, you can
|
||||
also compose other objects out of those primitive values. Let's modify the last example so that we want to
|
||||
store a <code>List</code> of <code>Children</code>:</para>
|
||||
|
||||
<example>
|
||||
<title>A Document with composed objects</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Document
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Field
|
||||
private List<String> firstnames;
|
||||
|
||||
@Field
|
||||
private List<Child> children;
|
||||
|
||||
public User(String id, List<String> firstnames, List<Child> children) {
|
||||
this.id = id;
|
||||
this.firstnames = firstnames;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
static class Child {
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
Child(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>A populated object can look like:</para>
|
||||
|
||||
<example>
|
||||
<title>A Document with composed objects - JSON</title>
|
||||
<programlisting lang="json"><![CDATA[
|
||||
{
|
||||
"_class": "foo.User",
|
||||
"children": [
|
||||
{
|
||||
"age": 4,
|
||||
"name": "Alice"
|
||||
},
|
||||
{
|
||||
"age": 3,
|
||||
"name": "Bob"
|
||||
}
|
||||
],
|
||||
"firstnames": [
|
||||
"Foo",
|
||||
"Bar",
|
||||
"Baz"
|
||||
]
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Most of the time, you also need to store a temporal value like a <code>Date</code>. Since it can't be stored
|
||||
directly in JSON, a conversion needs to happen. The library implements default converters for <code>Date</code>,
|
||||
<code>Calendar</code> and JodaTime types (if on the classpath). All of those are represented by default in the
|
||||
document as a unix timestamp (number). You can always override the default behavior with custom converters as
|
||||
shown later. Here is an example:</para>
|
||||
|
||||
<example>
|
||||
<title>A Document with Date and Calendar</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Document
|
||||
public class BlogPost {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Field
|
||||
private Date created;
|
||||
|
||||
@Field
|
||||
private Calendar updated;
|
||||
|
||||
@Field
|
||||
private String title;
|
||||
|
||||
public BlogPost(String id, Date created, Calendar updated, String title) {
|
||||
this.id = id;
|
||||
this.created = created;
|
||||
this.updated = updated;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>A populated object can look like:</para>
|
||||
|
||||
<example>
|
||||
<title>A Document with Date and Calendar - JSON</title>
|
||||
<programlisting lang="json"><![CDATA[
|
||||
{
|
||||
"title": "a blog post title",
|
||||
"_class": "foo.BlogPost",
|
||||
"updated": 1394610843,
|
||||
"created": 1394610843897
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
|
||||
<para>If you want to override a converter or implement your own one, this is also possible. The library implements
|
||||
the general Spring Converter pattern. You can plug in custom converters on bean creation time in your
|
||||
configuration. Here's how you can configure it (in your overriden <code>AbstractCouchbaseConfiguration</code>):</para>
|
||||
|
||||
<example>
|
||||
<title>Custom Converters</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Override
|
||||
public CustomConversions customConversions() {
|
||||
return new CustomConversions(Arrays.asList(FooToBarConverter.INSTANCE, BarToFooConverter.INSTANCE));
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
public static enum FooToBarConverter implements Converter<Foo, Bar> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Bar convert(Foo source) {
|
||||
return /* do your conversion here */;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
public static enum BarToFooConverter implements Converter<Bar, Foo> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Foo convert(Bar source) {
|
||||
return /* do your conversion here */;
|
||||
}
|
||||
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>There are a few things to keep in mind with custom conversions:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>To make it unambiguous, always use the <code>@WritingConverter</code> and <code>@ReadingConverter</code>
|
||||
annotations on your converters. Especially if you are dealing with primitive type conversions, this will
|
||||
help to reduce possible wrong conversions.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>If you implement a writing converter, make sure to decode into primitive types, maps and lists
|
||||
only. If you need more complex object types, use the <code>CouchbaseDocument</code> and <code>CouchbaseList</code>
|
||||
types, which are also understood by the underlying translation engine. Your best bet is to stick with
|
||||
as simple as possible conversions.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Always put more special converters before generic converters to avoid the case where the wrong
|
||||
converter gets executed.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="version">
|
||||
<title>Optimistic Locking</title>
|
||||
|
||||
<para>Couchbase Server does not support multi-document transactions or rollback. To implement optimistic locking,
|
||||
Couchbase uses a CAS (compare and swap) approach. When a document is mutated, the CAS value also changes. The
|
||||
CAS is opaque to the client, the only thing you need to know is that it changes when the content or a meta
|
||||
information changes too.</para>
|
||||
|
||||
<para>In other datastores, similar behavior can be achieved through an arbitrary version field whith a incrementing
|
||||
counter. Since Couchbase supports this in a much better fashion, it is easy to implement. If you want automatic
|
||||
optimistic locking support, all you need to do is add a <code>@Version</code> annotation on a long field like
|
||||
this:</para>
|
||||
|
||||
<example>
|
||||
<title>A Document with optimistic locking.</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Document
|
||||
public class User {
|
||||
|
||||
@Version
|
||||
private long version;
|
||||
|
||||
// constructor, getters, setters...
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>If you load a document through the template or repository, the version field will be automatically populated
|
||||
with the current CAS value. It is important to note that you shouldn't access the field or even change it
|
||||
on your own. Once you save the document back, it will either succeed or fail with a
|
||||
<code>OptimisticLockingFailureException</code>. If you get such an exception, the further approach depends
|
||||
on what you want to achieve application wise. You should either retry the complete load-update-write cycle
|
||||
or propagate the error to the upper layers for proper handling.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="validation">
|
||||
<title>Validation</title>
|
||||
|
||||
<para>The library supports JSR 303 validation, which is based on annotations directly in your entities. Of course
|
||||
you can add all kinds of validation in your service layer, but this way its nicely coupled to your actual
|
||||
entities.</para>
|
||||
|
||||
<para>To make it work, you need to include two additional dependencies. JSR 303 and a library that implements it,
|
||||
like the one supported by hibernate:</para>
|
||||
|
||||
<example>
|
||||
<title>Validation dependencies</title>
|
||||
<programlisting lang="xml"><![CDATA[
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Now you need to add two beans to your configuration:</para>
|
||||
|
||||
<example>
|
||||
<title>Validation beans</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Bean
|
||||
public LocalValidatorFactoryBean validator() {
|
||||
return new LocalValidatorFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ValidatingCouchbaseEventListener validationEventListener() {
|
||||
return new ValidatingCouchbaseEventListener(validator());
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Now you can annotate your fields with JSR303 annotations. If a validation on <code>save()</code> fails,
|
||||
a <code>ConstraintViolationException</code> is thrown.</para>
|
||||
|
||||
<example>
|
||||
<title>Sample Validation Annotation</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Size(min = 10)
|
||||
@Field
|
||||
private String name;
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
55
src/docbkx/index.xml
Normal file
55
src/docbkx/index.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<book>
|
||||
<bookinfo>
|
||||
<title>Spring Data Couchbase - Reference Documentation</title>
|
||||
|
||||
<releaseinfo>version;</releaseinfo>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Michael</firstname>
|
||||
|
||||
<surname>Nitschinger</surname>
|
||||
|
||||
<affiliation>
|
||||
<jobtitle>Software Engineer</jobtitle>
|
||||
|
||||
<orgname>Couchbase, Inc.</orgname>
|
||||
</affiliation>
|
||||
|
||||
<email>michael.nitschinger@couchbase.com</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<legalnotice>
|
||||
<para>Copies of this document may be made for your own use and for
|
||||
distribution to others, provided that you do not charge any fee for such
|
||||
copies and further provided that each copy contains this Copyright
|
||||
Notice, whether distributed in print or electronically.</para>
|
||||
</legalnotice>
|
||||
|
||||
<copyright>
|
||||
<year>2014</year>
|
||||
|
||||
<holder>The original authors.</holder>
|
||||
</copyright>
|
||||
|
||||
<productname>Spring Data Couchbase - Reference Documentation</productname>
|
||||
</bookinfo>
|
||||
|
||||
<toc/>
|
||||
|
||||
|
||||
<xi:include href="preface.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
<part id="reference">
|
||||
<title>Reference Documentation</title>
|
||||
<xi:include href="configuration.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
<xi:include href="entity.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
<xi:include href="repository.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
<xi:include href="template.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
<xi:include href="caching.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
</part>
|
||||
|
||||
</book>
|
||||
42
src/docbkx/preface.xml
Normal file
42
src/docbkx/preface.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<preface id="couchbase.preface">
|
||||
<title>Preface</title>
|
||||
|
||||
<abstract>
|
||||
<para>This reference documentation describes the general usage of the Spring Data Couchbase library.</para>
|
||||
</abstract>
|
||||
|
||||
<section id="metadata">
|
||||
<title>Project Information</title>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Version control - <ulink
|
||||
url="git://github.com/spring-projects/spring-data-couchbase.git">git://github.com/spring-projects/spring-data-couchbase.git</ulink></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Bugtracker - <ulink
|
||||
url="https://jira.springsource.org/browse/DATACOUCH">https://jira.springsource.org/browse/DATACOUCH</ulink></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Release repository - <ulink
|
||||
url="http://repo.spring.io/libs-release">http://repo.spring.io/libs-release</ulink></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Milestone repository - <ulink
|
||||
url="http://repo.spring.io/libs-milestone">http://repo.spring.io/libs-milestone</ulink></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Snapshot repository - <ulink
|
||||
url="http://repo.spring.io/libs-snapshot">http://repo.spring.io/libs-snapshot</ulink></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
</preface>
|
||||
253
src/docbkx/repository.xml
Normal file
253
src/docbkx/repository.xml
Normal file
@@ -0,0 +1,253 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="couchbase.repository">
|
||||
<title>Repositories</title>
|
||||
|
||||
<abstract>
|
||||
<para>The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code
|
||||
required to implement data access layers for various persistence stores.</para>
|
||||
</abstract>
|
||||
|
||||
<section id ="repository.configuration">
|
||||
<title>Configuration</title>
|
||||
|
||||
<para>While support for repositories is always present, you need to enable them in general or for a specific
|
||||
namespace. If you extend <code>AbstractCouchbaseConfiguration</code>, just use the
|
||||
<code>@EnableCouchbaseRepositories</code> annotation. It provides lots of possible options to narrow or
|
||||
customize the search path, one of the most common ones is <code>basePackages</code>.</para>
|
||||
|
||||
<example>
|
||||
<title>Annotation-Based Repository Setup</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
@Configuration
|
||||
@EnableCouchbaseRepositories(basePackages = {"com.couchbase.example.repos"})
|
||||
public class Config extends AbstractCouchbaseConfiguration {
|
||||
//...
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>XML-based configuration is also available:</para>
|
||||
|
||||
<example>
|
||||
<title>XML-Based Repository Setup</title>
|
||||
<programlisting lang="xml"><![CDATA[
|
||||
<couchbase:repositories base-package="com.couchbase.example.repos" />
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
</section>
|
||||
|
||||
<section id ="repository.usage">
|
||||
<title>Usage</title>
|
||||
|
||||
<para>In the simplest case, your repository will extend the <code>CrudRepository<T, String></code>, where
|
||||
T is the entity that you want to expose. Let's look at a repository for a user:</para>
|
||||
|
||||
<example>
|
||||
<title>A User repository</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface UserRepository extends CrudRepository<User, String> {
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Please note that this is just an interface and not an actual class. In the background, when your context
|
||||
gets initialized, actual implementations for your repository descriptions get created and you can access
|
||||
them through regular beans. This means you will save lots of boilerplate code while still exposing full
|
||||
CRUD semantics to your service layer and application.</para>
|
||||
|
||||
<para>Now, let's imagine we <code>@Autowrie</code> the <code>UserRepository</code> to a class that makes use of
|
||||
it. What methods do we have available?</para>
|
||||
|
||||
<table>
|
||||
<title>Exposed methods on the UserRepository</title>
|
||||
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Method</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>User save(User entity)</entry>
|
||||
<entry>Save the given entity.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>Iterable<User> save(Iterable<User> entity)</entry>
|
||||
<entry>Save the list of entities.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>User findOne(String id)</entry>
|
||||
<entry>Find a entity by its unique id.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>boolean exists(String id)</entry>
|
||||
<entry>Check if a given entity exists by its unique id.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>Iterable<User> findAll() (*)</entry>
|
||||
<entry>Find all entities by this type in the bucket.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>Iterable<User> findAll(Iterable<String> ids)</entry>
|
||||
<entry>Find all entities by this type and the given list of ids.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>long count() (*)</entry>
|
||||
<entry>Count the number of entities in the bucket.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>void delete(String id)</entry>
|
||||
<entry>Delete the entity by its id.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>void delete(User entity)</entry>
|
||||
<entry>Delete the entity.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>void delete(Iterable<User> entities)</entry>
|
||||
<entry>Delete all given entities.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>void deleteAll() (*)</entry>
|
||||
<entry>Delete all entities by type in the bucket.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>Now thats awesome! Just by defining an interface we get full CRUD functionality on top of our managed
|
||||
entity. All methods suffixed with (*) in the table are backed by Views, which is explained later.</para>
|
||||
|
||||
<para>If you are coming from other datastore implementations, you might want to implement the
|
||||
<code>PagingAndSortingRepository</code> as well. Note that as of now, it is not supported but will be in
|
||||
the future.</para>
|
||||
|
||||
<para>While the exposed methods provide you with a great variety of access patterns, very often you need to define
|
||||
custom ones. You can do this by adding method declarations to your interface, which will be automatically
|
||||
resolved to view requests in the background. Here is an example:</para>
|
||||
|
||||
<example>
|
||||
<title>An extended User repository</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
public interface UserRepository extends CrudRepository<User, String> {
|
||||
|
||||
List<User> findAllAdmins();
|
||||
|
||||
List<User> findByFirstname(Query query);
|
||||
}
|
||||
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Since we've came across views now multiple times and the <code>findByFirstname(Query query)</code> exposes
|
||||
a yet unknown parameter, let's cover that next.</para>
|
||||
</section>
|
||||
|
||||
<section id="repository.views">
|
||||
<title>Backing Views</title>
|
||||
|
||||
<para>As a rule of thumb, all repository access methods which are not "by a specific key" require a backing view
|
||||
to find the one or more matching entities. We'll only cover views to the extend which they are needed, if
|
||||
you need in-depth information about them please refer to the official Couchbase Server manual and the Couchbase
|
||||
Java SDK manual.</para>
|
||||
|
||||
<para>To cover the basic CRUD methods from the <code>CrudRepository</code>, 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 <code>_count</code>.</para>
|
||||
|
||||
<para>Since every view has a design document and view name, by convention we default to <code>all</code> as the
|
||||
view name and the lower-cased entity name as the design document name. So if your entity is named <code>User</code>, then
|
||||
the code expects the <code>all</code> view in the <code>user</code> design document. It needs to look like this: </para>
|
||||
|
||||
<example>
|
||||
<title>The all view map function</title>
|
||||
<programlisting lang="javascript"><![CDATA[
|
||||
// do not forget the _count reduce function!
|
||||
function (doc, meta) {
|
||||
if (doc._class == "namespace.to.entity.User") {
|
||||
emit(null, null);
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>Note that the important part in this map function is to only include the document IDs which correspond to
|
||||
our entity. Because the library always adds the <code>_class</code> property, this is a quick and easy way to do it.
|
||||
If you have another property in your JSON which does the same job (like a explicit <code>type</code> field), then
|
||||
you can use that as well - you don't have to stick to <code>_class</code> all the time.</para>
|
||||
|
||||
<para>Also make sure to publish your design documents into production so that they can be picked up by the
|
||||
library! Also, if you are curious why we use <code>emit(null, null)</code> in the view: the document id is
|
||||
always sent over to the client implicitly, so we can shave off a view bytes in our view by not duplicating
|
||||
the id. If you use <code>emit(meta.id, null)</code> it won't hurt much too.</para>
|
||||
|
||||
<para>Implementing your custom repository finder methods works the same way. The <code>findAllAdmins</code> calls
|
||||
the <code>allAdmins</code> view in the <code>user</code> design document. Imagine we have a field on our entity
|
||||
which looks like <code>boolean isAdmin</code>. We can write a view like this to expose them (we don't need
|
||||
a reduce function for this one):</para>
|
||||
|
||||
<example>
|
||||
<title>A custom view map function</title>
|
||||
<programlisting lang="javascript"><![CDATA[
|
||||
function (doc, meta) {
|
||||
if (doc._class == "namespace.to.entity.User" && doc.isAdmin) {
|
||||
emit(null, null);
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>By now, we've never actually customized our view at query time. This is where the special <code>Query</code>
|
||||
argument comes along - like in our <code>findByFirstname(Query query)</code> method.</para> By adding it we
|
||||
can customize the query at runtime. Let's write our view for this:
|
||||
|
||||
<example>
|
||||
<title>A parameterized view map function</title>
|
||||
<programlisting lang="javascript"><![CDATA[
|
||||
function (doc, meta) {
|
||||
if (doc._class == "namespace.to.entity.User") {
|
||||
emit(doc.firstname, null);
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>This view not only emits the document id, but also the firstname of every user as the key. We can now run
|
||||
a <code>Query</code> which returns us all users with a firstname of "Michael" or "Thomas".</para>
|
||||
|
||||
<example>
|
||||
<title>Query a repository method with custom params.</title>
|
||||
<programlisting lang="java"><![CDATA[
|
||||
// Load the bean, or @Autowire it
|
||||
UserRepository repo = ctx.getBean(UserRepository.class);
|
||||
|
||||
// Create the CouchbaseClient Query object
|
||||
Query query = new Query();
|
||||
|
||||
// Filter on those two keys
|
||||
query.setKeys(ComplexKey.of("Michael", "Thomas"));
|
||||
|
||||
// Run the query and get all matching users returned
|
||||
List<User> users = repo.findByFirstname(query));
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
<para>On all custom finder methods, you can use the <code>@View</code> annotation to both customize the design
|
||||
document and view name (to override the conventions).</para>
|
||||
|
||||
<para>Please keep in mind that by default, the <code>Stale.UPDATE_AFTER</code> mechanism is used. This means that
|
||||
whatever is in the index gets returned, and then the index gets updated. This strikes a good balance between
|
||||
performance and data freshness. You can tune the behavior through the <code>setStale()</code> method on the
|
||||
query object. For more details on behavior, please consult the Couchbase Server and Java SDK documentation
|
||||
directly.</para>
|
||||
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
38
src/docbkx/template.xml
Normal file
38
src/docbkx/template.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="couchbase.template">
|
||||
<title>Template & direct Operations</title>
|
||||
|
||||
<abstract>
|
||||
<para>The template provides lower level access to the underlying database and also serves as the foundation for
|
||||
repositories. Any time a repository is too high-level for you needs chances are good that the templates will
|
||||
serve you well.</para>
|
||||
</abstract>
|
||||
|
||||
<section id="template.ops">
|
||||
<title>Supported Operations</title>
|
||||
|
||||
<para>The template can be accessed through the <code>couchbaseTemplate</code> bean out of your context. Once
|
||||
you've got a reference to it, you can run all kinds of operations against it. Other than through a repository,
|
||||
in a template you need to always specifiy the target entity type which you want to get converted.</para>
|
||||
|
||||
<para>To mutate documents, you'll find <code>save</code>, <code>insert</code> and <code>update</code> methods
|
||||
exposed. Saving will insert or update the document, insert will fail if it has been created already and update
|
||||
only works against documents that have already been created.</para>
|
||||
|
||||
<para>Since Couchbase Server has different levels of persistence (by default you'll get a positive response if
|
||||
it has been acknowledged in the managed cache), you can provide higher durability options through the overloaded
|
||||
<code>PersistTo</code> and/or <code>ReplicateTo</code> options. The behaviour is part of the Couchbase Java SDK,
|
||||
please refer to the official documentation for more details.</para>
|
||||
|
||||
<para>Remvoing docouments through the <code>remove</code> methods works exactly the same.</para>
|
||||
|
||||
<para>If you want to load documents, you can do that through the <code>findById</code> method, which is the fastest
|
||||
and if possible your tool of choice. The find methods for views are <code>findByView</code> which converts it
|
||||
into the target entity, but also <code>querView</code> which exposes lower level semantics.</para>
|
||||
|
||||
<para>If you really need low-level semantics, the <code>couchbaseClient</code> bean is also always in scope.</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
Reference in New Issue
Block a user