diff --git a/build.gradle b/build.gradle index 978b806..89e6bb2 100644 --- a/build.gradle +++ b/build.gradle @@ -114,6 +114,8 @@ allprojects { project -> 'version': project.version, 'imagesdir': 'images', 'sourcedir': "${project.projectDir}/src/main/groovy" + + baseDirFollowsSourceFile() } task copyDocs(type: Copy, dependsOn: asciidoctor) { diff --git a/developer/src/docs/asciidoc/gettingStarted.adoc b/developer/src/docs/asciidoc/gettingStarted.adoc index 7f970f9..19f7768 100644 --- a/developer/src/docs/asciidoc/gettingStarted.adoc +++ b/developer/src/docs/asciidoc/gettingStarted.adoc @@ -2,70 +2,46 @@ === Checking out and Building -The project is currently hosted on Github at [https://github.com/grails/grails-data-mapping]. +The project is https://github.com/grails/grails-data-mapping[hosted on GitHub]. You are free to fork the project from there or clone it anonymously using git: -[source,groovy] ---- -git clone git@github.com:grails/grails-data-mapping.git +git clone https://github.com/grails/grails-data-mapping.git cd grails-data-mapping ---- -The project has a https://gradle.org[Gradle] build. You will need Intellij 15 or greater to work with the source code. - -Use the Intellij 15 Gradle tooling to import the project. - +The project has a https://gradle.org[Gradle] build. To build the project you can run the `assemble` task: -[source,groovy] ---- ./gradlew assemble ---- To install the jar files for the various subprojects into your local Maven repository you can run: -[source,groovy] ---- -./gradlew install +./gradlew publishToMavenLocal ---- -To build all of the documentation run the command: - -[source,groovy] ----- -./gradlew allDocs ----- - -Documentation will produced in the `build/docs` directory. - -NOTE: If you experience PermGen errors when building documentation you may need to increase the JVM permgen inside GRADLE_OPTS - - === Project Structure - The project is essentially a multi-project Gradle build. There is a core API and then subprojects that implement that API. The core API subprojects include: * `grails-datastore-core` - The core API, this provides core interfaces for implementing a GORM provider -* `grails-datastore-gorm` - The runtime meta-programming and AST transformation infrastructure behind GORM. Also provides end users APIs like `grails.gorm.CriteriaBuilder` and `grails.gorm.DetachedCriteria` +* `grails-datastore-gorm` - The runtime meta-programming and AST transformation infrastructure behind GORM. This also provides end users with APIs like `grails.gorm.CriteriaBuilder` and `grails.gorm.DetachedCriteria` * `grails-datastore-gorm-support` - Support classes for easing the writing of a GORM plugin for Grails * `grails-datastore-gorm-tck` - The TCK that includes hundreds of Spock specifications that a GORM implementation will need to pass * `grails-datastore-web` - Classes required to integrate GORM into a web tier -Beyond these core subprojects there are implementations for various datastores. For example: - -* `grails-datastore-mongodb/grails-datastore-gorm-hibernate` - GORM for Hibernate -* `grails-datastore-mongodb/grails-datastore-gorm-mongo` - GORM for MongoDB project [https://grails.org/plugin/mongodb] -* `grails-datastore-neo4j` - GORM for Neo4j project [https://grails.org/plugin/neo4j] -* `grails-datastore-redis/grails-datastore-gorm-redis` - GORM for Redis project [https://grails.org/plugin/redis] -* `grails-datastore-cassandra/grails-datastore-gorm-cassandra` - GORM for Cassandra project [https://grails.org/plugin/cassandra] - - -The documentation for each implementation is kept in the documentation subprojects that start with `grails-documentation`. There are documentation projects for the core API, MongoDB, Neo4j, Redis, and Cassandra. - -Finally the Grails 3 plugins that are used to distribute the GORM implementations to end users can be found in the `grails-plugins` directory and the `grails2-plugins` directory for Grails 2.x. +In addition to this, there are separate projects of GORM implementations for various datastores: +* https://github.com/grails/gorm-hibernate4[GORM for Hibernate 4] +* https://github.com/grails/gorm-hibernate5[GORM for Hibernate 5] +* https://github.com/grails/gorm-mongodb[GORM for MongoDB] +* https://github.com/grails/gorm-neo4j[GORM for Neo4j] +* https://github.com/grails/gorm-redis[GORM for Redis] +* https://github.com/grails/gorm-cassandra[GORM for Cassandra] diff --git a/developer/src/docs/asciidoc/index.adoc b/developer/src/docs/asciidoc/index.adoc index e6577fa..52ebb09 100644 --- a/developer/src/docs/asciidoc/index.adoc +++ b/developer/src/docs/asciidoc/index.adoc @@ -1,9 +1,13 @@ -= GORM Developer Guide -Graeme Rocher :imagesdir: ./images :source-highlighter: coderay +:toc: left +:toc-title: GORM Developer Guide +:numbered: :last-update-label!: += GORM Developer Guide +Graeme Rocher + [[introduction]] == Introduction @@ -55,6 +59,6 @@ include::understandingApi/gormApis.adoc[] include::testing.adoc[] [[stepByStep]] -== Step by Step Guide to Creating an Implementation +== Step-by-Step Guide to Creating an Implementation include::stepByStep.adoc[] diff --git a/developer/src/docs/asciidoc/introduction.adoc b/developer/src/docs/asciidoc/introduction.adoc index 1ad826f..bee0574 100644 --- a/developer/src/docs/asciidoc/introduction.adoc +++ b/developer/src/docs/asciidoc/introduction.adoc @@ -1,8 +1,8 @@ -This documentation describes the GORM API mechanics and how a datastore implementation can be built to interface to any database providing a GORM API onto it. This documentation is mainly targeted at developers interested in creating implementations of GORM ontop of alternative datastores. +This documentation describes the GORM API mechanics and how a datastore implementation can be built to interface to any database providing a GORM API onto it. This documentation is mainly targeted at developers interested in creating implementations of GORM on top of alternative datastores. As of this writing the project has several implementations of GORM against a variety of different datastore implementations. Current implementations include: -* Hibernate 3,4 and 5 +* Hibernate 3, 4 and 5 * MongoDB * Redis * Neo4j diff --git a/developer/src/docs/asciidoc/stepByStep.adoc b/developer/src/docs/asciidoc/stepByStep.adoc index eb9929a..2e6085f 100644 --- a/developer/src/docs/asciidoc/stepByStep.adoc +++ b/developer/src/docs/asciidoc/stepByStep.adoc @@ -1,14 +1,13 @@ -To get started with your a new GORM implementation the following steps are required: +To get started with a new GORM implementation, the following steps are required: === Initial Directory Creation -[source,groovy] ---- -$ git clone git@github.com:grails/grails-data-mapping.git -$ cd grails-data-mapping -$ mkdir grails-datastore-gorm-xyz +git clone https://github.com/grails/grails-data-mapping.git +cd grails-data-mapping +mkdir grails-datastore-gorm-xyz ---- @@ -17,9 +16,8 @@ $ mkdir grails-datastore-gorm-xyz Create build.gradle: -[source,groovy] ---- -$ vi grails-datastore-gorm-xyz/build.gradle +vi grails-datastore-gorm-xyz/build.gradle ---- With contents: @@ -27,11 +25,11 @@ With contents: [source,groovy] ---- dependencies { - compile project(':grails-datastore-gorm'), + implementation project(':grails-datastore-gorm'), project(':grails-datastore-web'), project(':grails-datastore-gorm-support') - testCompile project(':grails-datastore-gorm-tck') + testImplementation project(':grails-datastore-gorm-tck') testRuntime "javax.servlet:javax.servlet-api:$servletApiVersion" } @@ -39,9 +37,8 @@ dependencies { Add new project to settings.gradle in root project: -[source,groovy] ---- -$ vi settings.gradle +vi settings.gradle ---- Changes shown below: @@ -51,33 +48,30 @@ Changes shown below: // GORM Implementations 'grails-datastore-gorm-neo4j', 'grails-datastore-gorm-xyz', -.... +... ---- === Create Project Source Directories -[source,groovy] ---- -$ mkdir grails-datastore-gorm-xyz/src/main/groovy -$ mkdir grails-datastore-gorm-xyz/src/test/groovy +mkdir grails-datastore-gorm-xyz/src/main/groovy +mkdir grails-datastore-gorm-xyz/src/test/groovy ---- -=== Generate IDE Project Files and Import into IDE +=== Generate IDE Project Files and Import into IDE (Optional) -[source,groovy] ---- -$ gradlew grails-datastore-gorm-xyz:idea +./gradlew grails-datastore-gorm-xyz:idea ---- Or -[source,groovy] ---- -$ gradlew grails-datastore-gorm-xyz:eclipse +./gradlew grails-datastore-gorm-xyz:eclipse ---- diff --git a/developer/src/docs/asciidoc/testing.adoc b/developer/src/docs/asciidoc/testing.adoc index c2b9468..7faabeb 100644 --- a/developer/src/docs/asciidoc/testing.adoc +++ b/developer/src/docs/asciidoc/testing.adoc @@ -1,4 +1,4 @@ -The `grails-datastore-gorm-tck` project provides a few hundred tests that ensure a particular GORM implementation is compliant. To use the TCK you need to define a dependency on the TCK in the subprojects `build.gradle` file: +The `grails-datastore-gorm-tck` project provides several hundred tests to guarantee that a particular GORM implementation is compliant. To use the TCK you need to define a dependency on the TCK in the subprojects `build.gradle` file: [source,groovy] ---- @@ -41,11 +41,11 @@ class Setup { } ---- -Some setup code has been omitted for clarity but basically the `Setup.groovy` class should initiase the `Datastore` and return a `Session` from the static `setup` method which gets passed a list of classes that need to be configured. +Some setup code has been omitted for clarity, but essentially, the `Setup.groovy` class should initialize the `Datastore` and return a `Session` from the static setup method, which is passed a list of classes to configure. -With this done all of the TCK tests will run against the subproject. If a particular test cannot be implemented because the underlying datastore doesn't support the feature then you can create a test that matches the name of the test that is failing and it will override said test. +With this setup, all the TCK tests will be run against the subproject. If a specific test cannot be implemented due to the underlying datastore lacking support for a particular feature, you can create a test with the same name as the failing test, and that will then override the corresponding test in the TCK. -For example SimpleDB doesn't support pagination so there is a `grails.gorm.tests.PagedResultSpec` class that overrides the one from the TCK. Each test is a Spock specification and Spock has an `Ignore` annotation that can be used to ignore a particular test: +For example: SimpleDB doesn't support pagination. Add a `grails.gorm.tests.PagedResultSpec` class that overrides the one from the TCK. Each test is a Spock specification and Spock has an `Ignore` annotation that can be used to ignore a particular test: [source,groovy] ---- diff --git a/developer/src/docs/asciidoc/understandingApi.adoc b/developer/src/docs/asciidoc/understandingApi.adoc index ec5a774..b5a711e 100644 --- a/developer/src/docs/asciidoc/understandingApi.adoc +++ b/developer/src/docs/asciidoc/understandingApi.adoc @@ -1,9 +1,7 @@ === Introduction +The GORM Developer API is divided into a low-level API that implementors must implement for each specific datastore, and a set of higher-level APIs that enhance domain classes with features visible to regular users, such as dynamic finders, criteria queries, and so on. -The GORM Developer API is split into a low-level API that implementors need to implement for each individual datastore and then set of higher level APIs that enhance domain classes with things regular users see such as dynamic finders, criteria queries and so on. - -The low-level API classes are found in the `grails-datastore-core` subproject, whilst the higher level APIs used to enhance domain classes are found in `grails-datastore-gorm`. In this section we will discuss the low-level API. - +The low-level API classes are located within the `grails-datastore-core` subproject, whereas the higher-level APIs used to enhance domain classes can be found in `grails-datastore-gorm`. In this section, we will discuss the low-level API. diff --git a/developer/src/docs/asciidoc/understandingApi/datastoreBasics.adoc b/developer/src/docs/asciidoc/understandingApi/datastoreBasics.adoc index a3976a7..0209133 100644 --- a/developer/src/docs/asciidoc/understandingApi/datastoreBasics.adoc +++ b/developer/src/docs/asciidoc/understandingApi/datastoreBasics.adoc @@ -1,5 +1,5 @@ -=== The MappingContext +==== The MappingContext The `org.grails.datastore.mapping.model.MappingContext` interface is used to obtain metadata about the classes that are configured for persistence. There are `org.grails.datastore.mapping.model.PersistentEntity` and `org.grails.datastore.mapping.model.PersistentProperty` interfaces that represent a class and its properties respectively. These can be obtained and introspected via the `MappingContext`. @@ -49,12 +49,12 @@ class Neo4jMappingContext extends AbstractMappingContext { Notice how Neo4j provides a custom `GraphGormMappingFactory` and `GraphPersistentEntity` to allow the domain class configuration to be changed for a given Neo4j `Node`. -=== The Datastore Interface +==== The Datastore Interface The `org.grails.datastore.mapping.core.Datastore` interface is the equivalent of a SQL `DataSource` where by it provides the necessary capability to create a connection. In most cases one can simply subclass the `AbstractDatastore` super class and implement the `createSession` method. The following implementation is from the `SimpleMapDatastore` which implements GORM ontop of a `ConcurrentHashMap`: -[source,groovy] +[,java] ---- @Override protected Session createSession(PropertyResolver connDetails) { @@ -64,7 +64,7 @@ protected Session createSession(PropertyResolver connDetails) { The implementation depends a lot on the underlying datastore. For example for MongoDB the following implementation is used: -[source,groovy] +[,java] ---- @Override protected Session createSession(PropertyResolver connDetails) { @@ -76,16 +76,16 @@ Notice that the `Datastore` also has a reference to the `MappingContext` discuss -=== The Session Interface +==== The Session Interface The `org.grails.datastore.mapping.core.Session` interface represents an active connection. It can be either stateful or stateless, depending on the implementation. For example of embedded databases where there is no network connection, a stateful session is not particularly useful, but a datastore that creates network connections you may want to cache returned instances to reduce load. The `AbstractSession` class provides some support for creating stateful sessions, if you prefer a stateless implementation then simply implement `Session` or subclass `AbstractAttributeStoringSession`. -In general if you subclass `AbstractSession` the minimum you need to do is implement the `createPersister` method: +In general, if you subclass `AbstractSession`, the minimum you need to do is implement the `createPersister` method: -[source,groovy] +[,java] ---- protected Persister createPersister(Class cls, MappingContext mappingContext) { PersistentEntity entity = mappingContext.getPersistentEntity(cls.getName()); @@ -97,4 +97,4 @@ protected Persister createPersister(Class cls, MappingContext mappingContext) { } ---- -The example above is from the `SimpleMapSession` implementation, which creates a `SimpleMapEntityPersister` instance and returns it. Returning null indicates that the class cannot be persisted and an exception will be thrown \ No newline at end of file +The example above is from the `SimpleMapSession` implementation, which creates a `SimpleMapEntityPersister` instance and returns it. Returning null indicates that the class cannot be persisted and an exception will be thrown. \ No newline at end of file diff --git a/developer/src/docs/asciidoc/understandingApi/gormApis.adoc b/developer/src/docs/asciidoc/understandingApi/gormApis.adoc index 774515d..1856a7e 100644 --- a/developer/src/docs/asciidoc/understandingApi/gormApis.adoc +++ b/developer/src/docs/asciidoc/understandingApi/gormApis.adoc @@ -1,4 +1,4 @@ -By default the GORM compiler will make all GORM entities implement the `GormEntity` trait. Which provide all of the default GORM methods. However if you want to extend GORM to provide more methods specific to a given data store you can do so by extending this trait. +By default, the GORM compiler ensures that all GORM entities implement the `GormEntity` trait, which provides them with all the default GORM methods. Nevertheless, if there's a need to extend GORM functionality to incorporate additional methods tailored to a specific datastore, you can achieve this by extending the `GormEntity` trait. For example Neo4j adds methods for Cypher querying: @@ -14,7 +14,7 @@ trait Neo4jEntity extends GormEntity { } ---- -With this addition then you then need to tell the GORM compiler to make entities implement this trait. To do that implement a `TraitProvider`: +With this addition, you need to instruct the GORM compiler to make entities implement this trait. To achieve this, implement a `TraitProvider`: [source,groovy] ---- @@ -32,14 +32,13 @@ class Neo4jEntityTraitProvider implements GormEntityTraitProvider { And then add a `src/main/resources/META-INF/services/org.grails.compiler.gorm.GormEntityTraitProvider` file specifying the name of your trait provider: -[source,groovy] ---- org.grails.datastore.gorm.neo4j.Neo4jEntityTraitProvider ---- -GORM will automatically inject to trait into any domain class found in `grails-app/domain` or annotated with the `Entity` annotation, unless Hibernate is on the classpath in which case you have to tell GORM to map the domain class with Neo4j: +GORM will automatically inject the trait into any domain class discovered in `grails-app/domain` or annotated with the `Entity` annotation. However, if Hibernate is present on the classpath, you must inform GORM to map the domain class with Neo4j: -[source,groovy] +[,groovy] ---- static mapWith = "neo4j" ---- diff --git a/developer/src/docs/asciidoc/understandingApi/gormEnhancer.adoc b/developer/src/docs/asciidoc/understandingApi/gormEnhancer.adoc index eaf4f15..316d24b 100644 --- a/developer/src/docs/asciidoc/understandingApi/gormEnhancer.adoc +++ b/developer/src/docs/asciidoc/understandingApi/gormEnhancer.adoc @@ -31,7 +31,7 @@ enhancer.enhance() def books = Book.list() ---- -They key part to enabling the usage of all the GORM methods (`list()`, dynamic finders etc.) is the usage of the `MongoGormEnhancer`. This class subclasses `org.grails.datastore.gorm.GormEnhancer` and provides some extensions to GORM specific to MongoDB. A subclass is not required however and if you don't require any datastore specific extensions you can just as easily use the regular `GormEnhancer`: +The key element for enabling the use of all GORM methods (`list()`, dynamic finders, etc.) is the utilization of the `MongoGormEnhancer`. This class is a subclass of `org.grails.datastore.gorm.GormEnhancer` and offers extensions to GORM specifically tailored for MongoDB. However, a subclass is not mandatory, and if you don't need any datastore-specific extensions, you can equally use the standard `GormEnhancer`: [source,groovy] ---- diff --git a/developer/src/docs/asciidoc/understandingApi/implementingCrud.adoc b/developer/src/docs/asciidoc/understandingApi/implementingCrud.adoc index b319971..227fc03 100644 --- a/developer/src/docs/asciidoc/understandingApi/implementingCrud.adoc +++ b/developer/src/docs/asciidoc/understandingApi/implementingCrud.adoc @@ -6,11 +6,11 @@ The `EntityPersister` interface is used to implement the basic Create, Read, Upd In many cases there is a representation of an entity in its "native" form as supplied by the datastore driver. For example in Cassandra this could be a `ColumnFamily`, or in MongoDB a `DBCollection`. -To support implementation such cases there is an abstract `NativeEntryEntityPersister` super class that provides the basis for an implementation that maps a native entry, such as a MongoDB `DBObject` or a Neo4j `Node` to a persist entity and back again. +To support implementing such cases, there is an abstract `NativeEntryEntityPersister` super class that provides the basis for an implementation that maps a native entry, such as a MongoDB `DBObject` or a Neo4j `Node`, to a persisted entity and back again. -The 2 generic types of this superclass indicate the native entry type (example `DBObject` in MongoDB) and the native key type (example `ObjectId` in MongoDB). The MongoDB implementation looks like this: +The two generic types of this superclass indicate the native entry type (example `DBObject` in MongoDB) and the native key type (example `ObjectId` in MongoDB). The MongoDB implementation looks like this: -[source,groovy] +[,java] ---- public class MongoEntityPersister extends NativeEntryEntityPersister ---- @@ -19,17 +19,17 @@ Note that `Object` is used for the key since MongoDB also supports Long and Stri They key methods that need implementing are defined below: -* `getEntityFamily()` - Defines the the name of the entity group or family. This could be a database table, a Cassandra Column Family or a MongoDB collection. +* `getEntityFamily()` - Defines the name of the entity group or family. This could be a database table, a Cassandra Column Family or a MongoDB collection * `T createNewEntry(String family)` - Creates a native entry ready to be inserted -* `Object getEntryValue(T nativeEntry, String property)` - Retrieves a value of entry and returns its Java object form. For example a "date" property stored as a String in the datastore would need to b returned as a java.util.Date at this point +* `Object getEntryValue(T nativeEntry, String property)` - Retrieves a value of entry and returns its Java object form. For example a "date" property stored as a String in the datastore would need to be returned as a java.util.Date at this point * `setEntryValue(T nativeEntry, String key, Object value)` - Sets a value of the native entry, converting any Java objects to the required native format * `deleteEntry(String family, K key, Object entry)` - Deletes an entry for the given family, native key and entry * `T retrieveEntry(PersistentEntity persistentEntity, String family, Serializable key)` - Retrieves a native entry for the given entity, family and key * `K storeEntry(PersistentEntity persistentEntity, EntityAccess entityAccess, K storeId, T nativeEntry)` - Stores a native entry for the given id * `updateEntry(PersistentEntity persistentEntity, EntityAccess entityAccess, K key, T entry)` - Updates an entry * `K generateIdentifier(PersistentEntity persistentEntity, T entry)` - Generate an identifier for the given native entry -* `PropertyValueIndexer getPropertyIndexer(PersistentProperty property)` - If the datastore requires manual indexing you'll need to implement a `PropertyIndexer` otherwise return null -* `AssociationIndexer getAssociationIndexer(T nativeEntry, Association association)` - If the datastore requires manual indexing you'll need to implement a `AssociationIndexer` otherwise return null +* `PropertyValueIndexer getPropertyIndexer(PersistentProperty property)` - If the datastore requires manual indexing, you'll need to implement a `PropertyIndexer`, otherwise return null +* `AssociationIndexer getAssociationIndexer(T nativeEntry, Association association)` - If the datastore requires manual indexing, you'll need to implement a `AssociationIndexer`, otherwise return null @@ -40,7 +40,7 @@ They key methods that need implementing are defined below: The `createNewEntry` method is used to create a native record that will be inserted into the datastore. In MongoDB this is a `DBObject` whilst in the implementation for `ConcurrentHashMap` it is another `Map`: -[source,groovy] +[,java] ---- @Override protected DBObject createNewEntry(String family) { @@ -54,7 +54,7 @@ protected DBObject createNewEntry(String family) { The `retrieveEntry` method is used to retrieve a native record for a given key: -[source,groovy] +[,java] ---- protected DBObject retrieveEntry(final PersistentEntity persistentEntity, String family, final Serializable key) { @@ -69,7 +69,7 @@ protected DBObject retrieveEntry(final PersistentEntity persistentEntity, Here you can see the `MongoDB` implementation that uses a Spring Data `MongoTemplate` to find a `DBObject` for the given key. There is a separate `storeEntry` method that is used to actually store the native object. In `MongoDB` this looks like: -[source,groovy] +[,java] ---- @Override protected Object storeEntry(final PersistentEntity persistentEntity, final EntityAccess entityAccess, @@ -83,14 +83,14 @@ protected Object storeEntry(final PersistentEntity persistentEntity, final Entit } ---- -Notice it doesn't actually do anything native insert into a MongoDB collection. This is because the Datastore API supports the notion of batch insert operations and flushing. In the case of `MongoDB` the `MongoSession` implementation overrides the `flushPendingInserts` method of `AbstractSession` and performs a batch insert of multiple MongoDB documents (ie `DBObject`s) at once: +Notice it doesn't actually do anything native insert into a MongoDB collection. This is because the Datastore API supports the notion of batch insert operations and flushing. In the case of `MongoDB` the `MongoSession` implementation overrides the `flushPendingInserts` method of `AbstractSession` and performs a batch insert of multiple MongoDB documents (ie ``DBObject``s) at once: -[source,groovy] +[,java] ---- -collection.insert(dbObjects.toArray(new DBObject[dbObjects.size()]), writeConcernToUse); +collection.insert(dbObjects.toArray(DBObject::new), writeConcernToUse); ---- -Other datastores that do not support batch inserts would instead to the insert in the `storeEntry` method itself. For example the implementation for `ConcurrentHashMap` looks like (note Groovy code): +Other datastores that do not support batch inserts would instead do the insert in the `storeEntry` method itself. For example the implementation for `ConcurrentHashMap` looks like __(with Groovy)__: [source,groovy] ---- @@ -98,7 +98,7 @@ protected storeEntry(PersistentEntity persistentEntity, EntityAccess entityAcces if (!persistentEntity.root) { nativeEntry.discriminator = persistentEntity.discriminator } - datastore<>.put(storeId, nativeEntry) + datastore.put(storeId, nativeEntry) return storeId } ---- @@ -109,7 +109,7 @@ protected storeEntry(PersistentEntity persistentEntity, EntityAccess entityAcces The `updateEntry` method is used to update an entry: -[source,groovy] +[,java] ---- public void updateEntry(final PersistentEntity persistentEntity, final EntityAccess ea, final Object key, final DBObject entry) { @@ -131,7 +131,7 @@ public void updateEntry(final PersistentEntity persistentEntity, final EntityAcc } ---- -As you can see again the underlying database specific `update` method is used, in this case the `DBCollection`'s `update` method. +As you can see again the underlying database specific `update` method is used, in this case the ``DBCollection``'s `update` method. ==== Delete @@ -142,13 +142,13 @@ The `deleteEntry` method is used to delete an entry. For example in the `Concurr [source,groovy] ---- protected void deleteEntry(String family, key, entry) { - datastore<>.remove(key) + datastore.remove(key) } ---- Whilst in `MongoDB` the `DBCollection` object's `remove` method is called: -[source,groovy] +[,java] ---- @Override protected void deleteEntry(String family, final Object key, final Object entry) { @@ -170,7 +170,7 @@ protected void deleteEntry(String family, final Object key, final Object entry) Note that if the underlying datastore supports batch delete operations you may want override and implement the `deleteEntries` method which allows for deleting multiple entries in a single operation. The implementation for MongoDB looks like: -[source,groovy] +[,java] ---- protected void deleteEntries(String family, final List keys) { mongoTemplate.execute(new DbCallback() { @@ -190,4 +190,4 @@ protected void deleteEntries(String family, final List keys) { } ---- -You'll notice this implementation uses a `MongoQuery` instance. Note that implementing an `EntityPersister` you have enabled basic CRUD operations, but not querying, which is a topic of the following sections. First, however secondary indices need to covered since they are required for querying. \ No newline at end of file +You'll notice that this implementation uses a `MongoQuery` instance. Also, it's important to note that when implementing an `EntityPersister`, you enable basic CRUD operations, but not querying. The latter is a subject we'll explore in the following sections. However, before delving into that, we need to cover secondary indices, as they are required for querying. \ No newline at end of file diff --git a/developer/src/docs/asciidoc/understandingApi/implementingQueries.adoc b/developer/src/docs/asciidoc/understandingApi/implementingQueries.adoc index 783343a..c24628f 100644 --- a/developer/src/docs/asciidoc/understandingApi/implementingQueries.adoc +++ b/developer/src/docs/asciidoc/understandingApi/implementingQueries.adoc @@ -1,8 +1,8 @@ -=== Introduction +==== Introduction -The `org.grails.datastore.mapping.query.Query` abstract class defines the query model and it is the job of the GORM implementor to translate this query model into an underlying database query. This is different depending on the implementation and may involve: +The `org.grails.datastore.mapping.query.Query` abstract class defines the query model, and it is the job of the GORM implementor to translate this query model into an underlying database query. This is different depending on the implementation and may involve: * Generating a String-based query such as SQL or JPA-QL * Creating a query object such as MongoDB's use of a `Document` to define queries @@ -11,19 +11,19 @@ The `org.grails.datastore.mapping.query.Query` abstract class defines the query The `Query` object defines the following: * One or many `Criterion` that define the criteria to query by. -* Zero or many `Projection` instances that define what the data you want back will look like. -* Pagination parameters such as `max`, `offset` +* Zero or more `Projection` instances that define what the data you want back will look like. +* Pagination parameters such as `max` and `offset` * Sorting parameters There are many types of `Criterion` for each specific type of query, examples include `Equals`, `Between`, `Like` etc. Depending on the capabilities of the underlying datastore you may implement only a few of these. There are also many types of `Projection` such as `SumProjection`, `MaxProjection` and `CountProjection`. Again you may implement only a few of these. -NOTE: If the underlying datastore doesn't for example support calculating a `sum` or `max` of a particular property, there is a `ManualProjections` class that you can use to perform these operations in memory on the client. +NOTE: If, for instance, the underlying datastore does not support the calculation of a `sum` or `max` for a specific property, you can utilize the `ManualProjections` class to carry out these operations in memory on the client. Writing a `Query` implementation is probably the most complex part of implementing a GORM provider, but starts by subclassing the `Query` class and implementing the `executeQuery` method: -[source,groovy] +[,java] ---- public class MongoQuery extends Query implements QueryArgumentsAware { ... @@ -32,10 +32,10 @@ public class MongoQuery extends Query implements QueryArgumentsAware { ---- -=== Using the Query Model +==== Using the Query Model -To implement querying you need to understand the Query model. As discussed a `Query` contains a list of `Criterion`, however the root `Criterion` could be a conjunction (an AND query) or a disjunction (an OR query). The `Query` may also contain a combination of regular criterion (=, !=, LIKE etc.) and junctions (AND, OR or NOT). Implementing a `Query` therefore requires writing a recursive method. The implementation for `ConcurrentHashMap` looks like +To implement querying you need to understand the Query model. As discussed, a `Query` contains a list of `Criterion`. However, the root `Criterion` could be a conjunction (an AND query) or a disjunction (an OR query). The `Query` may also contain a combination of regular criterion (=, !=, LIKE etc.) and junctions (AND, OR or NOT). Implementing a `Query` therefore requires writing a recursive method. The implementation for `ConcurrentHashMap` looks like: [source,groovy] ---- @@ -56,13 +56,13 @@ Collection executeSubQueryInternal(criteria, criteriaList) { } ---- -Notice that if a `Junction` is encountered (which represents an AND, OR or NOT) then the method recurses to handle the junctions, otherwise a handler for the `Criterion` class is obtained and executed. The `handlers` map is a map of `Criterion` class to query handlers. The implementation for `Equals` looks like: +Note that if a `Junction` is encountered (representing AND, OR, or NOT), the method recursively handles the junctions. Otherwise, it obtains and executes a handler for the `Criterion` class. The `handlers` map is a map of `Criterion` class to query handlers. The implementation for `Equals` appears as follows: [source,groovy] ---- def handlers = [ ... - (Query.Equals): { Query.Equals equals, PersistentProperty property-> + (Query.Equals): { Query.Equals equals, PersistentProperty property -> def indexer = entityPersister.getPropertyIndexer(property) final value = subqueryIfNecessary(equals) return indexer.query(value) @@ -71,9 +71,9 @@ def handlers = [ ] ---- -Which simply uses the property indexer to query for all identifiers. Of course here we are a describing a case of a datastore (in this case `ConcurrentHashMap`) which doesn't support secondary indices. It may be that instead of manually querying the secondary indices in this way that you simply build a String-based or native query. For example in MongoDB this looks like: +This approach simply employs the property indexer to query for all identifiers. However, it's worth noting that this is a scenario involving a datastore, such as `ConcurrentHashMap`, that lacks support for secondary indices. Instead of manually querying secondary indices in this manner, an alternative might be to construct a String-based or native query. For instance, in MongoDB, this process appears as follows: -[source,groovy] +[,java] ---- queryHandlers.put(Equals.class, new QueryHandler() { public void handle(PersistentEntity entity, Equals criterion, Document query) { @@ -85,9 +85,9 @@ queryHandlers.put(Equals.class, new QueryHandler() { }); ---- -Notice how the query in this case is a `DBObject`. For Gemfire again the implementation is different: +Observe that in this case, the query takes the form of a `DBObject`. In the context of Gemfire, the implementation differs as follows: -[source,groovy] +[,java] ---- queryHandlers.put(Equals.class, new QueryHandler() { public int handle(PersistentEntity entity, Criterion criterion, StringBuilder q, List params, int index) { diff --git a/developer/src/docs/asciidoc/understandingApi/secondaryIndexes.adoc b/developer/src/docs/asciidoc/understandingApi/secondaryIndexes.adoc index 7434806..688eccb 100644 --- a/developer/src/docs/asciidoc/understandingApi/secondaryIndexes.adoc +++ b/developer/src/docs/asciidoc/understandingApi/secondaryIndexes.adoc @@ -1,6 +1,6 @@ -Many datastores do not support secondary indexing or require you to build your own. In cases like this you will need to implement a `PropertyIndexer`. +Many datastores do not support secondary indexing or require you to build your own. In cases like this, you will need to implement a `PropertyIndexer`. -NOTE: If the underlying datastore supports secondary indexes then it is ok to just return a `null` PropertyIndexer and let the datastore handle the indexing +NOTE: If the underlying datastore supports secondary indexes then it is ok to just return a `null` `PropertyIndexer` and let the datastore handle the indexing. For example the `ConcurrentHashMap` implementation creates secondary indices by populating another `Map` containing the indices: @@ -9,10 +9,10 @@ For example the `ConcurrentHashMap` implementation creates secondary indices by void index(value, primaryKey) { def index = getIndexName(value) - def indexed = indices<> + def indexed = indices[index] if (indexed == null) { indexed = [] - indices<> = indexed + indices[index] = indexed } if (!indexed.contains(primaryKey)) { indexed << primaryKey @@ -22,7 +22,7 @@ void index(value, primaryKey) { The implementation for Redis is very similar and stores the primary key in a Redis set: -[source,groovy] +[,java] ---- public void index(final Object value, final Long primaryKey) { if (value == null) { @@ -40,7 +40,7 @@ An index name is typically built from the entity name, property name and propert List query(value, int offset, int max) { def index = getIndexName(value) - def indexed = indices<> + def indexed = indices[index] if (!indexed) { return Collections.emptyList() } @@ -56,7 +56,7 @@ Finally, when an object is deleted it will need to removed from the indices. Thi ---- void deindex(value, primaryKey) { def index = getIndexName(value) - def indexed = indices<> + def indexed = indices[index] if (indexed) { indexed.remove(primaryKey) } diff --git a/rx/src/docs/asciidoc/associations/index.adoc b/rx/src/docs/asciidoc/associations/index.adoc index 40d44f9..6af31ed 100644 --- a/rx/src/docs/asciidoc/associations/index.adoc +++ b/rx/src/docs/asciidoc/associations/index.adoc @@ -1,4 +1,4 @@ -Associations and how they are handled are the biggest difference between RxGORM and the regular blocking version of GORM. As a user it is import that you familiarize yourself with how associations are handled in RxGORM in order to effectively build reactive, non-blocking applications. The following sections will describe how to adapt your code to take into account associations and your data model. +Associations and how they are handled are the biggest difference between RxGORM and the regular blocking version of GORM. As a user it is important that you familiarize yourself with how associations are handled in RxGORM in order to effectively build reactive, non-blocking applications. The following sections will describe how to adapt your code to take into account associations and your data model. === Single Ended Associations diff --git a/rx/src/docs/asciidoc/associations/lazyLoadingManyEnded.adoc b/rx/src/docs/asciidoc/associations/lazyLoadingManyEnded.adoc index 96e2025..6cdfc85 100644 --- a/rx/src/docs/asciidoc/associations/lazyLoadingManyEnded.adoc +++ b/rx/src/docs/asciidoc/associations/lazyLoadingManyEnded.adoc @@ -1,14 +1,15 @@ -In the previous section we discussed the implications of association proxies for single-ended associations, however the same applies with many-ended associations. +In the previous section we discussed the implications of association proxies for single-ended associations. However, the same applies with many-ended associations. In the case of collection types, these implement the link:../api/grails/gorm/rx/collection/RxPersistentCollection.html[RxPersistentCollection] and again can be subscribed to: [source,groovy] ---- -Author author == ... // load author +Author author = Author.findByName("Steven King") author.books.subscribe { Collection books -> - for(Book book in books) + for (Book book in books) { println "Book: ${book.title}" + } } ---- diff --git a/rx/src/docs/asciidoc/associations/lazyLoadingSingleEnded.adoc b/rx/src/docs/asciidoc/associations/lazyLoadingSingleEnded.adoc index 06f6f42..7d11e6e 100644 --- a/rx/src/docs/asciidoc/associations/lazyLoadingSingleEnded.adoc +++ b/rx/src/docs/asciidoc/associations/lazyLoadingSingleEnded.adoc @@ -7,7 +7,6 @@ package example class Book implements RxMongoEntity { ObjectId id String title - Author author } ---- @@ -18,16 +17,16 @@ This is traditionally how most blocking object mapping implementations have work [source,groovy] ---- -for(book in books) { +for (Book book in books) { println "Author: ${book.author.name}" } ---- -In the above example RxGORM has no choice but to block in order to load the association lazy and access the `name`! Typically you do not want your reactive application to block at any point, so in order to get around this each proxy loaded by GORM implements the link:../api/grails/gorm/rx/proxy/ObservableProxy.html[ObservableProxy] interface. That means the above code can be written in a non-blocking manner: +In the above example RxGORM has no choice but to block in order to load the association lazy and access the `name`! Typically, you do not want your reactive application to block at any point, so in order to get around this, each proxy loaded by GORM implements the link:../api/grails/gorm/rx/proxy/ObservableProxy.html[ObservableProxy] interface. That means the above code can be written in a non-blocking manner: [source,groovy] ---- -for(book in books) { +for (Book book in books) { book.author.subscribe { Author author -> println "Author: ${author.name}" } @@ -39,10 +38,9 @@ Another alternative if you know you plan to access the `author` association, is [source,groovy] ---- // using a dynamic finder -Book.findByTitle("The Stand", [fetch:[author:"eager"]]) +Book.findByTitle("The Stand", [fetch: [author: "eager"]]) // using a where query -Book.where { - title == 'The Stand' -}.join('author') +Book.where { title == "The Stand" } + .join("author") ---- diff --git a/rx/src/docs/asciidoc/configuration/config.adoc b/rx/src/docs/asciidoc/configuration/config.adoc index 7595dc4..c859b2d 100644 --- a/rx/src/docs/asciidoc/configuration/config.adoc +++ b/rx/src/docs/asciidoc/configuration/config.adoc @@ -1,4 +1,4 @@ -If you are using Grails then configuration can be provided via the `grails-app/conf/application.yml` file. +If you are using Grails, configuration can be provided via the `grails-app/conf/application.yml` file. An example configuration can be seen below: @@ -16,10 +16,10 @@ grails: ---- -You can specify any setting of the https://api.mongodb.com/java/current/com/mongodb/async/client/MongoClientSettings.html[MongoClientSettings] within the `options` block. The example above is configuring the https://api.mongodb.com/java/current/com/mongodb/connection/ClusterSettings.html#getMaxWaitQueueSize--[maxWaitQueueSize] property. +You can specify any setting of the https://mongodb.github.io/mongo-java-driver/3.12/javadoc/com/mongodb/async/client/MongoClientSettings.html[MongoClientSettings] within the `options` block. The example above is configuring the https://mongodb.github.io/mongo-java-driver/3.12/javadoc/com/mongodb/connection/ClusterSettings.html#getMaxWaitQueueSize()[maxWaitQueueSize] property. -An alternative to specifying the host and port is to use a MongoDB https://api.mongodb.com/java/current/com/mongodb/ConnectionString.html[ConnectionString]: +An alternative to specifying the host and port is to use a MongoDB https://mongodb.github.io/mongo-java-driver/3.12/javadoc/com/mongodb/ConnectionString.html[ConnectionString]: [source,yaml] ---- @@ -28,7 +28,7 @@ grails: connectionString: mongodb://user:pass@mycompany:27017 ---- -If you are not using Grails you can either provide a `MongoClient` instance directly in the constructor or you can supply an instance of https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/env/PropertyResolver.html[PropertyResolver] with the necessary configuration. +If you are not using Grails you can either provide a `MongoClient` instance directly in the constructor or you can supply an instance of https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/PropertyResolver.html[PropertyResolver] with the necessary configuration. For example: @@ -39,11 +39,7 @@ import org.springframework.core.env.* def env = new StandardEnvironment() env.getPropertySources().addLast(new PropertiesPropertySource("myConfig", myConfig)) -new RxMongoDatastoreClient( - env, - "myDatabase", - MyClass -) +new RxMongoDatastoreClient(env, "myDatabase", MyClass) ---- If you are using Spring Boot then the `Environment` instance can be obtained from the `ApplicationContext`: @@ -51,8 +47,8 @@ If you are using Spring Boot then the `Environment` instance can be obtained fro [source,groovy] ---- new RxMongoDatastoreClient( - applicationContext.getEnvironment(), - "myDatabase", - MyClass + applicationContext.getEnvironment(), + "myDatabase", + MyClass ) ---- diff --git a/rx/src/docs/asciidoc/gettingStarted/CRUD.adoc b/rx/src/docs/asciidoc/gettingStarted/CRUD.adoc index 55aedf3..5990fdc 100644 --- a/rx/src/docs/asciidoc/gettingStarted/CRUD.adoc +++ b/rx/src/docs/asciidoc/gettingStarted/CRUD.adoc @@ -23,10 +23,10 @@ In a real world application you would typically subscribe to the observable in o [source,groovy] ---- new Book(title:"The Stand") - .save() - .subscribe { Book it -> - println "Title = ${it.title}" -} + .save() + .subscribe { Book book -> + println "Title = ${book.title}" + } ---- NOTE: If you don't call `subscribe(..)` or convert the `Observable` into a blocking operation, then no query will be executed! At least one call to `subscribe(..)` is required. @@ -38,65 +38,64 @@ To retrieve an instance you can use the static `get(..)` method: [source,groovy] ---- Book.get(id) - .subscribe { Book it -> - println "Title = ${it.title}" -} + .subscribe { Book book -> + println "Title = ${book.title}" + } ---- ==== Update - Updating instances -To update an instance after retreiving it you can use the https://reactivex.io/RxJava/javadoc/rx/Observable.html#switchMap(rx.functions.Func1)[Observable.switchMap] method: +To update an instance after retrieving it you can use the https://reactivex.io/RxJava/javadoc/rx/Observable.html#switchMap-rx.functions.Func1-[Observable.switchMap] method: [source,groovy] ---- Book.get(id) - .switchMap() { Book it -> - it.title = "The Shining" - it.save() -}.subscribe { Book updated -> - println "Book updated!" -} + .switchMap() { Book book -> + book.title = "The Shining" + book.save() + }.subscribe { Book updated -> + println "Book updated!" + } ---- -The `switchMap` transforms an `Observable` and converters the result into another `Observable`. However, this is not the most efficient way to perform updates as you can use https://docs.grails.org/latest/guide/GORM.html#whereQueries[where queries] to update an instance without retrieving it: +The `switchMap` transforms an `Observable` and converts the result into another `Observable`. However, this is not the most efficient way to perform updates as you can use https://gorm.grails.org/latest/hibernate/manual/index.html#whereQueries[where queries] to update an instance without retrieving it: [source,groovy] ---- -Book.where { - id == id -}.updateAll(title:"The Shining") - .subscribe { Number updateCount -> - println "${updateCount} books were updated!" -} +Book.where { id == id } + .updateAll(title: "The Shining") + .subscribe { Number updateCount -> + println "${updateCount} books were updated!" + } ---- ==== Delete - Deleting instances -To delete an instance after retreiving it you can use the https://reactivex.io/RxJava/javadoc/rx/Observable.html#switchMap(rx.functions.Func1)[Observable.switchMap] method: +To delete an instance after retreiving it you can use the https://reactivex.io/RxJava/javadoc/rx/Observable.html#switchMap-rx.functions.Func1-[Observable.switchMap] method: [source,groovy] ---- Book.get(id) - .switchMap() { Book it -> - it.delete() -}.subscribe { Boolean wasDeleted -> - if(wasDeleted) { - println "Book deleted!" + .switchMap() { Book book -> + book.delete() + } + .subscribe { Boolean wasDeleted -> + if(wasDeleted) { + println "Book deleted!" + } } -} ---- But, once again it is more efficient with where queries: [source,groovy] ---- -Book.where { - id == id -}.deleteAll() - .subscribe { Number deleteCount -> - println "${deleteCount} books were deleted!" -} +Book.where { id == id } + .deleteAll() + .subscribe { Number deleteCount -> + println "${deleteCount} books were deleted!" + } ---- diff --git a/rx/src/docs/asciidoc/gettingStarted/creatingDomainClasses.adoc b/rx/src/docs/asciidoc/gettingStarted/creatingDomainClasses.adoc index 41bfb55..b90df7f 100644 --- a/rx/src/docs/asciidoc/gettingStarted/creatingDomainClasses.adoc +++ b/rx/src/docs/asciidoc/gettingStarted/creatingDomainClasses.adoc @@ -1,6 +1,6 @@ When using RxGORM each entity that you persist to the database is known as a domain class. -If you are using RxGORM within Grails you can create a domain class with the `create-domain-class` command provided by the command line. Alternatively if you are not using Grails simply creating a Groovy class with `src/main/groovy` of your project will suffice. +If you are using RxGORM within Grails you can create a domain class with the `create-domain-class` command provided by the command line. Alternatively if you are not using Grails simply creating a Groovy class within `src/main/groovy` of your project will suffice. A domain class must as a minimum implement the link:../api/grails/gorm/rx/RxEntity.html[RxEntity] trait. @@ -23,4 +23,4 @@ NOTE: The type is provided as a generic argument to the `RxMongoEntity` trait. T In addition, for MongoDB an `id` of type `ObjectId` is required. -For more information on domain modelling in GORM, take a look at the https://docs.grails.org/latest/guide/GORM.html#domainClasses[GORM user guide documentation] on the subject. +For more information on domain modelling in GORM, take a look at the https://docs.grails.org/latest/guide/GORM.html#domainClasses[GORM user guide] on the subject. diff --git a/rx/src/docs/asciidoc/gettingStarted/firstSteps.adoc b/rx/src/docs/asciidoc/gettingStarted/firstSteps.adoc index 17686c2..4c4abec 100644 --- a/rx/src/docs/asciidoc/gettingStarted/firstSteps.adoc +++ b/rx/src/docs/asciidoc/gettingStarted/firstSteps.adoc @@ -5,7 +5,7 @@ To get started with RxGORM for MongoDB in a Grails application simply add the fo [source,groovy,subs="attributes"] .`build.gradle` ---- -compile "org.grails.plugins:rx-mongodb:{version}" +implementation "org.grails.plugins:rx-mongodb:{version}" ---- ==== Installation without Grails @@ -15,7 +15,7 @@ If you are not developing a Grails application then define the following depende [source,groovy,subs="attributes"] .`build.gradle` ---- -compile "org.grails:grails-datastore-gorm-rx-mongodb:{version}" +implementation "org.grails:grails-datastore-gorm-rx-mongodb:{version}" ---- With that done you will need to place the following logic in the bootstrap path of your application: @@ -28,9 +28,9 @@ import com.mongodb.rx.client.MongoClient MongoClient mongoClient = ... // create the MongoClient new RxMongoDatastoreClient( - mongoClient, <1> - "myDatabase", <2> - MyClass <3> + mongoClient, // <1> + "myDatabase", // <2> + MyClass // <3> ) ---- diff --git a/rx/src/docs/asciidoc/index.adoc b/rx/src/docs/asciidoc/index.adoc index d6e52f2..78c2b80 100644 --- a/rx/src/docs/asciidoc/index.adoc +++ b/rx/src/docs/asciidoc/index.adoc @@ -1,8 +1,11 @@ -= RxGORM - GORM for RxJava :author: Graeme Rocher :email: graeme.rocher@gmail.com :source-highlighter: coderay :numbered: +:toc: left +:toc-title: RxGORM Documentation + += RxGORM - GORM for RxJava == Introduction diff --git a/rx/src/docs/asciidoc/introduction.adoc b/rx/src/docs/asciidoc/introduction.adoc index ad3876f..42d3a66 100644 --- a/rx/src/docs/asciidoc/introduction.adoc +++ b/rx/src/docs/asciidoc/introduction.adoc @@ -2,9 +2,9 @@ https://gorm.grails.org[GORM], the object mapping technology built into the Grai Async features were added in GORM 4, however these are merely a way to isolate your blocking operations onto a different thread and not a truly non-blocking implementation. -RxGORM builds ontop of https://reactivex.io[RxJava] and provides a completely non-blocking, stateless implementation of GORM. +RxGORM builds on top of https://reactivex.io[RxJava] and provides a completely non-blocking, stateless implementation of GORM. -Currently only MongoDB is supported as a backing store, however implementations are planned for SQL, REST client and other NoSQL datastores in the future (subject to driver support) +Currently, only MongoDB is supported as a backing store. However, implementations are planned for SQL, REST client and other NoSQL datastores in the future (subject to driver support). == Getting Started diff --git a/rx/src/docs/asciidoc/querying/basicQuerying.adoc b/rx/src/docs/asciidoc/querying/basicQuerying.adoc index a75594e..c1f4828 100644 --- a/rx/src/docs/asciidoc/querying/basicQuerying.adoc +++ b/rx/src/docs/asciidoc/querying/basicQuerying.adoc @@ -3,9 +3,9 @@ To query for a single instance by identifier you can use the `get(id)` method: [source,groovy] ---- Book.get(id) - .subscribe { Book it -> - println "Title = ${it.title}" -} + .subscribe { Book book -> + println "Title = ${book.title}" + } ---- @@ -15,19 +15,20 @@ To query for a `List` of objects you can use the `list()` method: ---- Book.list() .subscribe { List books -> - for(book in books) - println "Title = ${book.title}" -} + for(Book book in books) { + println "Title = ${book.title}" + } + } ---- -However, for large data sets it is better to use the `findAll()` method which will return each result in a reactive manner rather than load the entire list into memory: +However, for large data sets, it is better to use the `findAll()` method which will return each result in a reactive manner rather than load the entire list into memory: [source,groovy] ---- Book.findAll() - .subscribe { Book it -> - println "Title = ${it.title}" -} + .subscribe { Book book -> + println "Title = ${book.title}" + } ---- To return the first result you can use the `first()` method: @@ -35,9 +36,9 @@ To return the first result you can use the `first()` method: [source,groovy] ---- Book.first() - .subscribe { Book it -> - println "Title = ${it.title}" -} + .subscribe { Book book -> + println "Title = ${book.title}" + } ---- And conversely to return the last result you can use the `last()` method: @@ -45,7 +46,7 @@ And conversely to return the last result you can use the `last()` method: [source,groovy] ---- Book.last() - .subscribe { Book it -> - println "Title = ${it.title}" -} + .subscribe { Book book -> + println "Title = ${book.title}" + } ---- diff --git a/rx/src/docs/asciidoc/querying/dynamicFinders.adoc b/rx/src/docs/asciidoc/querying/dynamicFinders.adoc index a3044a3..cf5dfeb 100644 --- a/rx/src/docs/asciidoc/querying/dynamicFinders.adoc +++ b/rx/src/docs/asciidoc/querying/dynamicFinders.adoc @@ -1,9 +1,9 @@ -Although Where queries are preferred, Dynamic finders are another option for simple queries and are also very expressive. The https://docs.grails.org/latest/guide/GORM.html#finders[syntax for Dynamic finders is described in the GORM user guide], the major difference in RxGORM is that all dynamic finders return an `rx.Observable`: +Although _Where queries_ are preferred, _Dynamic finders_ are another option for simple queries and are also very expressive. The https://gorm.grails.org/latest/hibernate/manual/index.html#finders[syntax for Dynamic finders is described in the GORM user guide]. The major difference in RxGORM is that all dynamic finders return an `rx.Observable`: [source,groovy] ---- Book.findByAuthor("Stephen King") - .subscribe { Book it -> - println "Title = ${it.title}" -} + .subscribe { Book book -> + println "Title = ${book.title}" + } ---- diff --git a/rx/src/docs/asciidoc/querying/index.adoc b/rx/src/docs/asciidoc/querying/index.adoc index d234cb9..c6e73ae 100644 --- a/rx/src/docs/asciidoc/querying/index.adoc +++ b/rx/src/docs/asciidoc/querying/index.adoc @@ -1,9 +1,9 @@ RxGORM supports all the typical ways of querying that you are used to with GORM including: -* https://docs.grails.org/latest/guide/GORM.html#finders[Dynamic finders] -* https://docs.grails.org/latest/guide/GORM.html#whereQueries[Where queries] -* https://docs.grails.org/latest/guide/GORM.html#detachedCriteria[Detached Criteria] -* https://docs.grails.org/latest/guide/GORM.html#criteria[Criteria queries] +* https://gorm.grails.org/latest/hibernate/manual/index.html#finders[Dynamic finders] +* https://gorm.grails.org/latest/hibernate/manual/index.html#whereQueries[Where queries] +* https://gorm.grails.org/latest/hibernate/manual/index.html#detachedCriteria[Detached Criteria] +* https://gorm.grails.org/latest/hibernate/manual/index.html#criteria[Criteria queries] The major difference is that all query operations return an `Observable`. In this section we will go through the various ways you can query for GORM objects. diff --git a/rx/src/docs/asciidoc/querying/whereQueries.adoc b/rx/src/docs/asciidoc/querying/whereQueries.adoc index 06d3475..04883ae 100644 --- a/rx/src/docs/asciidoc/querying/whereQueries.adoc +++ b/rx/src/docs/asciidoc/querying/whereQueries.adoc @@ -16,8 +16,9 @@ Like an `rx.Observable`, the `DetachedCriteria` class can be subscribed to: ---- Book.where { title == 'The Stand' -}.subscribe { Book it -> - println "Title = ${it.title}" +} +.subscribe { Book book -> + println "Title = ${book.title}" } ---- @@ -26,12 +27,13 @@ Alternatively you can execute one of the query methods to invoke the query and r [source,groovy] ---- -Book.where { - title == 'The Stand' -}.list().subscribe { List books -> - for(book in books) - println "Title = ${book.title}" -} +Book.where { title == 'The Stand' } + .list() + .subscribe { List books -> + for (Book book in books) { + println "Title = ${book.title}" + } + } ---- -For more information on the syntax of where queries see the https://docs.grails.org/latest/guide/GORM.html#whereQueries[relevant section in the GORM documentation]. +For more information on the syntax of where queries see the https://gorm.grails.org/latest/hibernate/manual/index.html#whereQueries[relevant section in the GORM documentation]. diff --git a/whatsNew/src/docs/asciidoc/index.adoc b/whatsNew/src/docs/asciidoc/index.adoc index a436c81..6c6953e 100644 --- a/whatsNew/src/docs/asciidoc/index.adoc +++ b/whatsNew/src/docs/asciidoc/index.adoc @@ -1,43 +1,43 @@ +:author: Puneet Behl + = Discover What's Exciting in GORM 8! -:author: Graeme Rocher -:source-highlighter: coderay -= GORM 8 Unleashed: Embracing Java 11 +== GORM 8 Unleashed: Embracing Java 11 -GORM 8 now supports Java 11, offering its latest features, improved performance, and better security. Learn more about Java 11 in the official Oracle documentation: [Java 11 API Docs](https://docs.oracle.com/en/java/javase/11/docs/api/index.html). +GORM 8 now supports Java 11, offering its latest features, improved performance, and better security. Learn more about Java 11 in the official Oracle documentation: https://docs.oracle.com/en/java/javase/11/docs/api/index.html[Java 11 API Docs]. == Powering Build Confidence: Gradle 7.6.2 Support -GORM 8 fully integrates with Gradle 7.6.2, providing faster builds and seamless compatibility with the latest Gradle features. Explore Gradle's capabilities in the user guide: [Gradle User Guide](https://docs.gradle.org/current/userguide/userguide.html). +GORM 8 fully integrates with Gradle 7.6.2, providing faster builds and seamless compatibility with the latest Gradle features. Explore Gradle's capabilities in the user guide: https://docs.gradle.org/current/userguide/userguide.html[Gradle User Guide]. -= Hibernate 5.6.15.Final: Mapping Mastery Amplified +== Hibernate 5.6.15.Final: Mapping Mastery Amplified -Discover the potential of GORM 8 with Hibernate 5.6.15.Final - the latest Java ORM framework release. Enjoy enhanced data mapping with new features and bug fixes. Learn more: [Hibernate 5.6 Documentation](https://hibernate.org/orm/documentation/5.6/). +Discover the potential of GORM 8 with Hibernate 5.6.15.Final - the latest Java ORM framework release. Enjoy enhanced data mapping with new features and bug fixes. Learn more: https://hibernate.org/orm/documentation/5.6/[Hibernate 5.6 Documentation]. == Seamlessly Compatible: MongoDB Java 4.5.0 -Connect effortlessly to MongoDB using the gorm-mongodb plugin with full compatibility for MongoDB Java driver version 4.5.0. Benefit from improved support and access to the latest MongoDB features. Find out more: [MongoDB Java Driver 4.5.0 Documentation](https://mongodb.github.io/mongo-java-driver/4.5/). +Connect effortlessly to MongoDB using the gorm-mongodb plugin with full compatibility for MongoDB Java driver version 4.5.0. Benefit from improved support and access to the latest MongoDB features. Find out more: https://mongodb.github.io/mongo-java-driver/4.5/[MongoDB Java Driver 4.5.0 Documentation]. -= Embrace the Graph: Neo4J Driver 4.4.10 Integration +== Embrace the Graph: Neo4J Driver 4.4.10 Integration -Optimize your graph database interactions with the gorm-neo4j plugin, now integrated with Neo4J Driver 4.4.10. Embrace the latest advancements in Neo4J. Learn how: [Neo4J Java Driver 4.4 Documentation](https://neo4j.com/docs/api/java-driver/4.4/). +Optimize your graph database interactions with the gorm-neo4j plugin, now integrated with Neo4J Driver 4.4.10. Embrace the latest advancements in Neo4J. Learn how: https://neo4j.com/docs/api/java-driver/4.4/[Neo4J Java Driver 4.4 Documentation]. == Empowering Spring Compatibility: Spring Boot 2.7.12 -Enjoy a seamless blend of Spring Boot features and GORM's powerful ORM capabilities with GORM 8's full compatibility with Spring Boot 2.7.12. Discover the world of Spring Boot: [Spring Boot 2.7.12 Reference Guide](https://docs.spring.io/spring-boot/docs/2.7.12/reference/htmlsingle/). +Enjoy a seamless blend of Spring Boot features and GORM's powerful ORM capabilities with GORM 8's full compatibility with Spring Boot 2.7.12. Discover the world of Spring Boot: https://docs.spring.io/spring-boot/docs/2.7.12/reference/htmlsingle/[Spring Boot 2.7.12 Reference Guide]. -= Embrace Spring Awesomeness: Spring 5.3.27 Integration +== Embrace Spring Awesomeness: Spring 5.3.27 Integration -GORM 8 integrates with Spring 5.3.27, offering cutting-edge features like dependency injection, AOP, and transaction management. Dive into the Spring Framework: [Spring Framework 5.3.27 Reference Guide](https://docs.spring.io/spring-framework/docs/5.3.27/reference/html/). +GORM 8 integrates with Spring 5.3.27, offering cutting-edge features like dependency injection, AOP, and transaction management. Dive into the Spring Framework: https://docs.spring.io/spring-framework/docs/5.3.27/reference/html/[Spring Framework 5.3.27 Reference Guide]. == Strengthened Foundation: Updated Dependencies GORM 8 has updated dependencies for enhanced stability and security. Refer to the GORM 8 release notes for details. -= Enhanced Reliability: Bug Fixes +== Enhanced Reliability: Bug Fixes GORM 8 addresses known issues from previous versions, providing a more reliable user experience. Check out the GORM 8 release notes for specifics. GORM 8 offers a powerful ORM solution for Java-based applications. Leverage the latest Java, Gradle, Hibernate, MongoDB, Neo4J, and Spring versions to build robust data-centric applications. GORM 8 provides a unified API for simplified data access and management, supporting traditional SQL databases, NoSQL data stores like MongoDB, and graph databases like Neo4J. -For comprehensive changes and in-depth information about GORM 8, explore the official GitHub repository and release notes. Get started with GORM 8 and explore its full capabilities in the [GORM 8 Documentation](https://gorm.grails.org/latest/documentation.html). \ No newline at end of file +For comprehensive changes and in-depth information about GORM 8, explore the official GitHub repository and release notes. Get started with GORM 8 and explore its full capabilities in the https://gorm.grails.org/latest/documentation.html[GORM 8 Documentation]. \ No newline at end of file