Nagrand

a lightweight groovy orm

View project on GitHub

Nagrand

Nagrand is a lightweight groovy orm


How To Use

Maven dependency

<dependency>
    <groupId>io.github.noahshen</groupId>
    <artifactId>nagrand</artifactId>
    <version>0.0.2</version>
</dependency>

You need to register classes through Nagrand on app start.

Nagrand nagrand = ...
nagrand.register(Person)

or init as a Spring Framework bean

<bean id="entityInitializer" class="io.github.noahshen.nagrand.spring.EntityInitializer"
        init-method="init">
    <property name="entityPackage" value="io.github.noahshen.nagrand.spring.entity"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="createTable" value="true"/>
    <property name="sqlLog" value="true"/>
</bean>

Basic Samples

Entity defination

@Entity
class Person {
    def name
    int age
}

Create

def person = new Person(name: 'Spiderman', age: 30)
person.save()

Update

def person = new Person(name: 'Spiderman', age: 30)
person.save()

person.name = 'Batman'
person.save()

Delete

def person = new Person(name: 'Spiderman', age: 30)

//...

person.delete()

Query

Get entity by ID

Integer id = 1
Person p = Person.get(id)
if (!p) {
   //... not found
}

Dynamic Finders

Dynamic finder looks like a static method invocation. The method is auto-generated using code synthesis at runtime, based on the properties of a entity class.

new Person(name: 'Spiderman', age: 30).save()
new Person(name: 'Batman', age: 31).save()
new Person(name: 'Superman', age: 32).save()
new Person(name: 'Ironman', age: 32).save()

def persons = Person.findByAge(32)
assert persons.size() == 2
assert persons*.name == ["Superman", "Ironman"]

def batman = Person.findFirstByName("Batman")
assert batman.name == "Batman"

Find by multi properties

List<Person> persons = Person.findByNameAndAge('Superman', 32)
assert persons.size() == 1
assert persons*.name == ["Superman"]

Find by more options

List<Person> persons = Person.findByAge(32) {
    order("name", "desc")
}
assert persons.size() == 2
assert persons[0].name == "Superman"

Where Query

The where method is more flexible than dynamic finders

by using the following methods:

  • .find {}
  • .findWhere {}
  • .where {}
  • .findFirst {}
  • .findFirstWhere {}
new Person(name: 'Spiderman', age: 30).save()
new Person(name: 'Batman', age: 31).save()
new Person(name: 'Superman', age: 32).save()
new Person(name: 'Ironman', age: 32).save()

List<Person> persons = Person.find {
    eq "name", "Superman"
    eq "age", 32
}
assert persons.size() == 1
assert persons*.name == ["Superman"]

More condition:

def results = Account.find {
    between("balance", 500, 1000)
    eq("bankName", "boc")
    or {
        like("firstName", "Noah%")
        like("firstName", "Sara%")
    }
    maxResults(10)
    order("balance", "desc")
}

Groovy-style query

Nagrand supports groovy-style query by providing an enhanced, compile-time checked query DSL for common queries

def person = Person.where {
   firstName == "Ironman"
}
def person = Person.where {
    (lastName != "Shen" && firstName != "Noah") || (firstName == "Sara" && age > 20)
}

Groovy operator maps onto a where method. The following table provides a map of Groovy operators to methods:

Operator where method description
== eq Equal to
!= nq Not equal to
> gt Greater than
< lt Less than
>= ge Greater than or equal to
<= le Less than or equal to
in inList Contained within the given list

Events

.beforeInsert

.afterInsert

Called before/after first save

class Item {
  void beforeInsert() {
    ...
  }
  void afterInsert() {
    ...
  }
}

.beforeUpdate

.afterUpdate

Called before/after object update

class Item {
  void beforeUpdate() {
    ...
  }
  void afterUpdate() {
    ...
  }
}

.beforeDelete

.afterDelete

Called before/after object delete

class Item {
  void beforeDelete() {
    ...
  }
  void afterDelete() {
    ...
  }
}

Optimistic Locking and Version

Nagrand uses optimistic locking by a version property which is in turn mapped to a version column at the database.

class ClassWithVersion {
    Integer id
    String name
    Integer version
}
def entity = new ClassWithVersion(name: 'Spiderman').save()
assert entity.version == 1

entity.name = "Superman"
entity.save()
assert entity.version == 2

Automatic timestamping

If you define a dateCreated property, it will be set to the current date for you when you create new instances. Likewise, if you define a lastUpdated property it will be automatically be updated for you when you change the instances.

class ClassAutoTimestamp {
    Integer id
    String name
    Date dateCreated
    Date lastUpdated
}

License

Project is licensed under Apache License 2.