Overview

Fresh ORM

Fresh ORM is my attempt at creating a fluent ORM for a local SQLite database for Android.

  • Quickly create schemas that automatically turn into database tables
  • Relate your Models to each other
  • Fluently create, retreive, update, and delete records in the database

Version

0.1


Tech

Fresh ORM uses a number of open source projects to work properly:


Getting started


If you prefer to jump straight to some actual usage check out the only activity of the sample app. Or check out an example Model to see how to define relationships...or you can clone it, update the pom.xml with the location of your Android SDK, update the project, and run the tests to get a working app up and running(assuming you have Maven installed).

git clone https://dadamssg@bitbucket.org/dadamssg/freshapp.git

//open pom.xml in an editor and change the Android SDK path to your valid SDK path

cd freshapp
android update project -p .

//only run 'ant maven-setup' once
ant maven-setup

mvn clean test

Open the pom.xml file with IntelliJ or Eclipse to get the project into an editor


What it looks like

Saving a Model works like you would expect:

Customer customer = app.get(Customer.class);
customer.setValue("name", "John Doe");
customer.setValue("address","123 Main St, Euless, TX 76039");
customer.save();

The Model now has an id Integer id = customer.getId();


Retrieve a Model

Customer customer = dataSource.getModelById(1, Customer.class);

SQL looks like: SELECT * FROM customers WHERE _id = 1


Retrieve a Model's only child (One-to-One)

Profile profile = user.hasOne(Profile.class).get();

SQL looks like: SELECT * FROM profiles WHERE _id = 12

where 12 is user.getValue("profile_id")


Retrieve a Model's only parent (One-to-One)

User user = profile.belongsTo(User.class).get();

SQL looks like: SELECT * FROM users WHERE profile_id = 3

where 3 is profile.getId()


Retrieve a Model's child Models (One-to-Many)

List<Document> documents = customer.hasMany(Document.class).get();

SQL looks like: SELECT * FROM documents WHERE customer_id = 1


List<Group> userGroups = user.belongsToMany(Group.class).get();

SQL looks like: SELECT * FROM groups WHERE _id IN(SELECT group_id FROM groups_users WHERE user_id = 2)

where 2 is user.getId()

Note: Many-to-many relationships use a pivot table to hold the relationships. Fresh ORM assumes the name of this table is the two table names of the Models arranged alphabetically. For example, if you have Models Customer and Document and their table names are customers and documents, then Fresh will assume the pivot table's name is "customers_documents".


You can attach a Model to another with the approprate relationship like so

boolean wasSuccessful = customer.hasMany(Document.class).attach(aDocument);

Similarly, detach a related Model like so

boolean wasSuccessful = customer.hasMany(Document.class).detach(aDocument);

Flexibility

Need a little more fine tuning? When retrieving many models you can tack on extra criteria

For example:

List<Document> documents = customerA.hasMany(Document.class)
                                    .select()
                                    .only(app.get(Criteria.class)
                                        .where("date").is("2013-07-08-")
                                        .and("title").not("blah blah blah")
                                        .or("author").is("John Doe", "Jane Doe"))
                                    .models();

Note: You can retrieve a Cursor object instead of a List of Model, simply end the statement with .cursor() instead of .models()

Selecting specific columns is also supported

Cursor cursor = dataSource.find("aTable")
                            .select("name","address")
                            .only(app.get(Criteria.class)
                                .where("name").startsWith("Da")
                                .and("age").greaterThan(21))
                            .cursor();

Inserting rows looks like

int insertId = dataSource.insertInto("aTable")
                            .setValue("key1","value1")
                            .setValue("key2","value3")
                        .x();

Updating rows looks like

int rowsAffected = dataSource.update("aTable")
                                .setValue("key1","value1")
                                .setValue("key2","value3")
                                .only(app.get(Criteria.class)
                                    .where("key4").is("someValue","orAnotherValue"))
                                .x();

Deleting rows looks like

int rowsAffected = dataSource.deleteFrom("aTable")
                                .only(app.get(Criteria.class)
                                    .where("key4").not("someValue","orAnotherValue"))
                                .x();

Note: All Query objects have a .x() method which executes the Query.

Note: The app object is an App Singleton which extends the EntityManager class. The .get() method wraps RoboGuice's .getInstance() method to resolve an instance of the passed in class.


Defining Model Schemas

Model schemas are easily defined by extending the Schema class

@Singleton
public class DocumentSchema extends Schema {

    @Override
    public String table() {
        return "documents";
    }

    @Override
    public void build() {

        column("_id", INTEGER).primaryKey().autoIncrement();
        column("created_at", LONG);
        column("updated_at", LONG);
        column("name", TEXT);
        column("content", TEXT).defaultTo("this is some content");
        column("customer_id", INTEGER);

        uniqueIndex("name","customer_id").onConflictReplace();
    }
}

Attach the Schema to the Model in the Model's class by overriding the .getSchema() method

public class Document extends Model {

    @Inject
    private DocumentSchema schema;

    @Override
    public Schema getSchema() {
        return schema;
    }
}

Note: By default, Fresh ORM will manage the timestamps of when your Models are created and updated via the created_at and updated_at columns. To disable this you can set the hasTimestamps instance variable to FALSE


Note: If a default value is provided in a Model's Schema, that value will be set when the Model is instantiated. This value will also be defaulted for the actual table column.

Document document = app.get(Document.class);

String content = document.getValue("content");

content.equals("this is some content"); //would be true if the above Schema was used

Once you have defined all your Models and Schemas, you must register them in the App class

@Singleton
public class App extends EntityManager {

    @Override
    public void setUp() {

        registerModel(Customer.class);
        registerModel(Document.class);

        registerSchema(GroupsSchema.class);
    }    
}

Once registered, Fresh will automatically create the tables when the app is launched.

License

Apache License

Please credit me if you decide to use this in your own projects. Happy coding!