#Gaelyk Easy Datastore Service Plugin (EasyDS)
Gaelyk EasyDS Plugin is inspired by GORM methods on Grails domain classes. It provides basic CRUD and validation support for basic app engine entities. Because the app engine datastore entites are distinguished by their kinds and keys the most of the methods are added to the String and Map classes.
This plugin is expected to use for basic datastore operations. For more sophisticated use e.g. Obgaektify or other ORM framework.
This plugin provides DSLD file for better STS (Eclipse) support.
Caching could be simply enabled in easyDBPlugin
script.
The easiest way how to install the plugin using gradle gaelyk plugin 0.2+ which is able to download and install the plugin by following command
gradle gaelykInstallPlugin -Pplugin=easyds
Otherwise you can just download the archive and unpack it into your project's directory. The plugin expects
your source codes lives in src
directory. If you are using the new layout, copy easyDS.dsdl
from src
folder
into src\main\groovy
one to get the code completition in STS.
To create new entity just call the create
method on the string representing the entity kind and supply
map of new entity parameters as the arguments.
Entity book = 'book'.create(author: 'Stephen King', title: 'It', description: 'Oh, my! This is scary!')
There are several approaches how to retrive entites from the datastore. The most simple is to retrive them by their identifier.
To fetch entity from datastore just call the method fetch
on single map with single entry.
The key specifies the kind and the value the id. The id could be any object which can be coereced to long eg. string.
Entity book = [book: 15].fetch()
You can also fetch more then one entity by providing a list of identifiers as a value of the single map entry
and calling the fetchAll
method.
List<Entity> books = [book: [14,15,16]].fetchAll()
To fetch all entities of given kind just call the list
method on the string representing the entity kind.
List<Entity> books = 'book'.list()
You can cusomize result list by calling the list
method with map of configurations. Available customizations
are
max
- the maximum size of result list as intoffset
- the offset of the result list as intsort
- the property to be used for sorting the listorder
- the sort direction - usedesc
orSortDirection.DESCENDING
to swich default ascending order to descending
List<Entity> tenBooks = 'book'.list(max:10)
List<Entity> tenBooksFrom20 = 'book'.list(max:10, offset: 20)
List<Entity> booksOrderByAuthor = 'book'.list(sort: 'author')
List<Entity> booksOrderByAuthorDesc = 'book'.list(sort: 'author', order: 'desc')
You can retrieve entities using the simple queries against their properties. There is two similar method
fetchBy
and fetchAllBy
on string which only differs by their number of entities returned. The fetchBy
returns
only first entity found and the fetchAllBy
method returns all the results. The properties supplied in the
map are searched for equality. You can also use customization map as for list
method as the second argument.
Entity firstKingsBook = 'book'.fetchBy(autor: 'Stephen King')
List<Entity> kingsBooksByTitle = 'book'.fetchAllBy(author: 'Stephen King', [sort: 'title'])
You can simply obtain count of entites in the database by using the countAll
method on the
string representing the entity kind. You can also get count of the simple query result using
the countBy
method the same way.
int booksCount = 'book'.countAll()
int kingsBooksCount = 'book'.countBy(author: 'Stephen King')
You can check easily if there is corresponding entity in the datastore for given id or ids using the
exists
or existAll
method. The method is called on the map having the same form as in the fetch
and fetchAll
example.
boolean itExists = [book: 15].exists()
boolean theyExist = [book: [15, 20, 30]].existAll()
You can update entity properties by calling the update
method on the map. The map must have the same
form as in the fetch
example. The update
method works on entities themselves too.
Entity updated = [book: 15].update(author: 'Paul King')
updated.update(author: 'Me Myself I.')
Sometimes you want set properties only if they weren't set before. You can use the updateIfNotSet
method
on maps and entities in the same manner as the update
method. The entity.hasPropery(propertyName)
method
is used to determine whether the property is set.
Entity book = 'book'.create(author: 'Stephen King')
book.updateIfNotSet(title: 'It') // the title is 'It' now
book.updateIfNotSet(title: 'Cujo') // the title is still the same, because it was already set
To delete entity or entities just use the delete
or deleteAll
method on the map. The map must have the same
form as in the fetch
and fetchAll
example.
[book: 15].delete()
[book: [11, 12, 13]].deleteAll()
You can validate your entity or map using the validate
method. You call the method with map as an argument.
The map contains validatiors closures. The key in the map of validators must be the same as the name of property
or entry you want to validate. The validator closure must accept one or two parameters. If the closure
accepts one parameter the value of the validated property is supplied. If the closure accepts two parameters
the property value is sent as the first parameter and the map of all properties is supplied as the second parameter.
The method returns map of errors. Any non-null return value from the closure is supposed to be an error.
You usually want to return the error message as string.
def trueKing = { value, entity ->
if(value == 'It' && entity.author != 'Stephen King'){
return 'The only true author of It is Stephen King!'
}
}
def authorShort = {
if(it.size() < 4){
return 'Author too short'
}
}
def errors = [title: 'The only true author of It is Stephen King!', author: 'Author too short']
[author: 'Poe', title: 'It'].validate(title: trueKing, author: authorShort) == errors
[author: 'Stephen King', title: 'It'].validate(title: trueKing, author: authorShort) == [:]
Since version 0.2 you can choose whether you want to enable automatic caching of entities. The caching
is disabled by default for backward compatibility reason but you can easily enable it by editing the
easyDSPlugin
script.
Every method mentioned before are aware of caching. For example the exists
method on the map.
// this is the first call of the method
// db operaion will be performed and the result will be cached
[book: 15].exists()
// any other calls will return cached result
[book: 15].exists()
[book: 15].exists()
[book: 15].exists()
// if you know the entities was changed, you can invalidate their cache
'book'.invalidateCache()
// next call will ask the datastore again
[book: 15].exists()
You could also cache entity-related query results and other resources
'book'.cache('rss') {
getBooksAtom()
}
The result is kept in cache as long as there are no entity added, updated or deleted.
- 0.1 - Basic CRUD operations and validation
- 0.2 - Ability to cache entities
- 0.3 - Reusable and combinable validations