Poetry

Additional

Language
Java
Version
3.0.1 (Nov 6, 2015)
Created
Jul 29, 2014
Updated
Nov 11, 2015 (Retired)
Owner
Elastique (elastique)
Contributors
KenVanHoeylandt
Punksmurf
2
Activity
Badge
Generate
Download
Source code

Announcement

Poetry

Poetry is an Android persistence library that allows you to persist a JSON object tree directly into an SQLite database through OrmLite. Poetry enables you to write less code and persist data much faster.

Consider this JSON:

{
 "id" : 1,
 "name" : "John Doe"
}

And this Java model:

@DatabaseTable
class User
{
 @DatabaseField(id = true)
 public int id;

 @DatabaseField)
 public String name;
}

They can be stored into the database like this:

JSONObject json_object = ...; // processed JSON tree
DatabaseHelper helper = ...; // OrmLite databasehelper;
JsonPersister persister = new JsonPersister(helper.getWritableDatabase());
persister.persistObject(User.class, json_object);

Features

  • Annotation-based model configuration
  • Advanced DatabaseHelper with easy DatabaseConfiguration
  • Support for relationships:
    • One-to-one
    • Many-to-one
    • One-to-many
    • Many-to-many
  • Persist objects and arrays of objects
  • Persist objects with arrays of base types (e.g. JSON String array persisted to separate table)

Requirements

  • Android 2.3.3 (API level 10) or higher

Usage

build.gradle

repositories {
    mavenCentral()
    maven {
        url "https://dl.bintray.com/elastique/poetry"
    }
}
dependencies {
    compile (
        [group: 'nl.elastique.poetry', name: 'poetry', version: '3.0.1']
    )
}

Demo

A Demo application is available on GitHub

Behaviors

  • Models that are imported more than once within a single JSON tree are updated every time they are processed.
  • Attributes that are not specified in JSON are not updated
  • Attributes that are imported with null value will have a null value in the database. Make sure your model allows this.
  • When you use JsonPersister's persistArray() method, it will import the array and delete all objects from the database that do not correspond to any of the imported IDs.

Tutorial

Creating a DatabaseHelper

The Poetry DatabaseHelper allows you to easily configure your database. In the example below, you can see a custom DatabaseHelper with a DatabaseConfiguration that holds the model version and model classes.

MyDatabaseHelper.java

import android.database.sqlite.SQLiteDatabase;
import com.j256.ormlite.android.apptools.OpenHelperManager;
import com.j256.ormlite.support.ConnectionSource;
import nl.elastique.poetry.database.DatabaseConfiguration;

public class MyDatabaseHelper extends nl.elastique.poetry.database.DatabaseHelper
{
    public final static DatabaseConfiguration sConfiguration = new DatabaseConfiguration(1, new Class<?>[]
    {
        User.class,
        Group.class,
        UserTag.class,
        UserGroup.class
    });

    public DatabaseHelper(Context context)
    {
        super(context, sConfiguration);
    }

    public static DatabaseHelper getHelper(Context context)
    {
        return OpenHelperManager.getHelper(context, DatabaseHelper.class);
    }
}

Mapping custom JSON properties

By default, the name of the attribute is used to map from JSON. Your naming conventions might now allow this. You can specify the json key name with the @MapFrom annotation.

User.java

@DatabaseTable
class User
{
 @DatabaseField(id = true, columnName = "id")
 @MapFrom("id")
    private int mId;

 @DatabaseField(columnName = "name")
 @MapFrom("name")
    private String mName;
}

One-to-many relationships

In this example, a Game object holds a list of Player objects.

game.json

{
    "id": 1,
    "players" : [
        {
         "id": 1,
         "name": "John"
        },
        {
         "id": 2,
         "name": "Jane"
        }
    ]
}

Game.java

@DatabaseTable
public class Game
{
    @DatabaseField(id = true)
    public int id;
    
 @ForeignCollectionField(eager = true)
 public ForeignCollection<Player> players;
}

Player.java

@DatabaseTable
public class Player
{
    @DatabaseField(id = true)
    public int id;

    @DatabaseField
    public String name;
}

Many-to-many relationships

In this example, a User can have 0 or more Groups, and a Group can have 0 or more Users.

users.json

[
 {
     "id" : 1,
     "name" : "John",
     "groups" : [
      {
       "id" : 1,
       "name" : "Group 1"
      },
      {
       "id" : 2,
       "name" : "Group 2"
      }
     ]
 },
 {
     "id" : 2,
     "name" : "Jane",
     "groups" : [
      {
       "id" : 1,
       "name" : "Group 1"
      }
     ]
 }
]

User.java

@DatabaseTable
public class User
{
    @DatabaseField(id = true)
    public int id;

    @DatabaseField
    public String name;
    
    /**
     * Many-to-many relationships.
     *
     * OrmLite requires a ForeignCollectionField with the helper-type UserGroup to assist in the database relational mapping.
     * JSON-to-SQLite persistence also requires the additional annotation "ManyToManyField"
     */
    @ForeignCollectionField(eager = true)
    @ManyToManyField(targetType = Group.class)
 public ForeignCollection<UserGroup> groups;
}

UserGroup.java

@DatabaseTable
public class UserGroup
{
    @DatabaseField(generatedId = true)
    public int id;

    @DatabaseField(foreign = true)
 public User user;

    @DatabaseField(foreign = true)
    public Group group;
}

Group.java

@DatabaseTable
public class Group
{
    @DatabaseField(id = true)
    public int id;

    @DatabaseField
    public String name;
}

One-to-one relationships

In this example, a User can have 0 or 1 Friend user.

users.json

[
 {
     "id": 1,
     "name" : "John",
     "friend" : { "id" : 2 }
 },
 {
     "id": 2,
     "name" : "Jane",
     "friend" : { "id" : 1 }
 }
]

The following alternative JSON is also valid:

users.json

[
 {
     "id": 1,
     "name" : "John",
     "friend" : 2
 },
 {
     "id": 2,
     "name" : "Jane",
     "friend" : 1
 }
]

User.java

@DatabaseTable
public class User
{
    @DatabaseField(id = true)
    public int id;

    @DatabaseField
    public String name;
    
    @DatabaseField(foreign = true)
    public User friend;
}

Arrays of base types

Arrays of base types work the same as one-to-many relationships. The only difference is that you have to define a model that holds the base types and use the @ForeignCollectionFieldSingleTarget annotation to specify the database column name that holds the value.

user.json

{
    "id": 1,
    "tags" : [
        "tag1",
        "tag2"
    ]
}

User.java

@DatabaseTable
public class User
{
    @DatabaseField(id = true)
    public int id;
    
 // The targetField refers to the table's column name
 @ForeignCollectionField(eager = true)
 @ForeignCollectionFieldSingleTarget(targetField = "value")
 @MapFrom("tags")
 public ForeignCollection<UserTag> userTags;
}

UserTag.java

@DatabaseTable
public class UserTag
{
    @DatabaseField(generatedId = true)
    public int id;

    @DatabaseField(foreign = true)
    public User user;

    @DatabaseField
 public String value;
}