Coder Social home page Coder Social logo

slingerorm's Introduction

SlingerORM

SlingerORM is a simple Object Relation Mapper (ORM) focusing on speed and simplicity. It uses code generation to generate code against the database that you don't want to write over and over again.

Basic usage

The bare minimum that is needed for SlingerORM to work is an entity and an interface written by you:

@DatabaseEntity
public class ExampleEntity {
    @PrimaryKey
    private String id;
    private String name;

    public void setId(String id) {
      this.id = id;
    }

    public String getId() {
      return id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

@DatabaseStorage
public interface ExampleStorage {
    @CreateTable(ExampleEntity.class)
    void createTable();

    @Insert
    void insert(ExampleEntity entity);

    @Delete
    void delete(ExampleEntity entity);

    @Select
    List<ExampleEntity> getAllExamples();
}

Get an instance of your interface by accessing the generated builder by prefixing your interface name with "Slinger":

SQLiteDatabase db = ...
ExampleStorage storage = SlingerExampleStorage
    .builder()
    .database(db)
    .build();

storage.createTable();

ExampleEntity entity = new ExampleEntity()
entity.setId(42);
entity.setName("David");
storage.insert(entity);

List<ExampleEntity> examples = storage.getAllExamples();
...

What does it really solve?

When writing your code for your database layer in Android you will have a lot of code like this:

Insert:

SQLiteDatabase db = ...

ExampleEntity item = ...
ContentValues values = new ContentValues();
values.put("id", item.getId());
values.put("name", item.getName());
db.insertOrThrow("ExampleEntity", null, values);

Query:

Cursor cursor = null;
try {
    cursor = db.query(false, "ExampleEntity", new String[] {
        "id",
        "name"
    }, "_id = ?", new String[] {
        String.valueOf(id)
    }, null, null, null, "1");

    if(!cursor.moveToFirst()) return null;

    ExampleEntity readItem = new ExampleEntity();
    readItem.setId(cursor.getLong(0));
    readItem.setName(cursor.getString(1));
    return readItem;
} finally {
    if(cursor != null) cursor.close();
}

Writing that becomes tedious when having over hundred fields in your database table that you want to have mapped. You will write the wrong field name or having to add even more code to put those into constants to be sure every field is mapped right. SlingerORM solves this problem by looking at annotations for your class that represents your database table and generate the code above for you!

SlingerORM generates code using two annotation processors. The first generates a mapper class that you can use to map the tedious part in the example above:

Insert:

Mapper<ExampleEntity> mapper = new ExampleEntityMapper()
db.insertOrThrow(mapper.getTableName(), null, mapper.mapValues(item));

Query:

Mapper<ExampleEntity> mapper = new ExampleEntityMapper()
Cursor cursor = null;
try {
    cursor = db.query(false, mapper.getTableName(), mapper.getFieldNames(), "_id = ?",
            new String[] {
        String.valueOf(id)
    }, null, null, null, "1");
    if(!cursor.moveToFirst()) return null;

    return mapper.mapItem(cursor);
} finally {
    if(cursor != null) cursor.close();
}

The second annotation processor generates code for inserting, updating, deleting, querying etc depending on what methods you provide in your custom storage interface (as seen in the basic usage above)

Configuring the DatabaseEntity

SlingerORM will first look after getters and setters that match the fields (i.e setId,getId etc). If it can't find any it getters or setters, it will try to access the fields directly. If that's not possible the compiler will show an error that explains this.

If you have fields that doesn't match your corresponding set and get methods. You can annotate your set and get methods with an annotation telling SlingerORM which field the method will set or get:

@DatabaseEntity
public class ExampleEntity {
  @PrimaryKey
  private String mId;
  private String mName;
  
  @SetField("mId")
  public void setId(String id) {
    mId = id;
  }
  
  @GetField("mId")
  public String getId() {
    return mId;
  }
  
  @SetField("mName")
  public void setName(String name) {
    mName = name;
  }
  
  @GetField("mName")
  public void getName() {
    return mName;
  }
}

If you don't want to have the names of the field in your class as the columns in your table, you can annotate these fields with @FieldName:

@DatabaseEntity
public class ExampleEntity {
  @FieldName("Id") @PrimaryKey
  private String id;
  @FieldName("Name")
  private String name;

  ...
}

Using the mapper standalone

It's possible to use the database entity mapper and then do the quering etc yourself. You get an implementation of the Mapper interface by suffixing the database entity name with "Mapper":

Mapper<ExampleEntity> exampleEntityMapper = ExampleEntityMapper.create();
...

Configuring the storage class

The storage class uses one or more mappers that can be configured by calling methods in the builder. The number of mappers used for a storage depends on how many different database entities are used in the storage class:

ExampleEntityStorage storage = SlingerExampleEntityStorage.builder()
    .exampleEntityMapper(ExampleEntityMapper.create())
    .otherExampleMapper(OtherExampleMapper.create())
    .database(db)
    .build();
...

Here are some of the annotations that can be used on the methods in your storage interface:

@DatabaseStorage
public interface ExampleEntityStorage {
    //annotate method with this to create a table based on the given entity class
    @CreateTable(ExampleEntity.class)
    void createTable();

    // slingerORM will run an INSERT INTO statement
    @Insert
    void insert(ExampleEntity exampleEntity);

    // slingerORM will run an UPDATE statement using primary keys in the where clause
    @Update
    void update(ExampleEntity exampleEntity);

    // using an empty delete together with an "@DatabaseEntity" annotated class as parameter 
    // deletes the entity.
    @Delete
    void delete(ExampleEntity exampleEntity);

    // you can also specify a "where" annotation to delete everything that matches the given query
    @Delete @Where("someVar = ?")
    void deleteItemsWithSomeVar(String someVar);

    // slingerorm will match the "?" in order with your method parameters
    @Select @Where("_id = ?")
    ExampleEntity getEntity(long id);

    // not annotating with "@Where" will cause it to use default and query everything
    @Select
    List<ExampleEntity> getAll();

    // @OrderBy and @Limit can be set on the method to add additional data to the sql query
    @Select @OrderBy("created DESC") @Limit("5")
    List<ExampleEntity> getLatest();
}

Using a custom serializer

Sometimes some type of fields in the database entity will not be a native data type that SlingerORM supports. You will then need to implement your custom serializer. Create a class that implements the Serializer interface and add "@SerializeTo" to the field that needs to be serialized.

@DatabaseEntity
public class ExampleEntity {
    @SerializeTo(SerializeType.LONG)
    private Date created;
    
    // ...
}

public class ExampleDateSerilizer implements Serializer<Date,Long> {
    @Override
    public Long serialize(Date date) {
        return date != null ? date.getTime() : 0;
    }
    
    @Override
    public Date deserialize(long time) {
        return new Date(time);
    }
}

To tell SlingerORM which serializer to use, set the serializer in the builder:

Mapper<ExampleEntity> mapper = ExampleEntityMapper.builder()
    .dateToLongSerializer(new MyDateSerializer())
    .build();

The storage class will then require you to pass in the mapper because it now doesn't know about the mappers dependencies:

ExampleEntityStorage storage = SlingerExampleEntityStorage.builder()
    .exampleEntityMapper(mapper)
    .build();

Download

The project is currently in an alpha stage. Will upload it to Maven Central eventually. In the meantime you have to call the following code to create a local repository:

./gradlew build uploadArchives

You could change the build script so the build is placed in your local maven repository, right now it's placed in the pkg/ folder in the root of the project.

You refer to it this way in gradle:

compile 'net.daverix.slingerorm:slingerorm-android:0.4'
annotationProcessor 'net.daverix.slingerorm:compiler:0.4'

License

Copyright 2017 David Laurell

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

slingerorm's People

Contributors

daverix avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

Forkers

gustavjon

slingerorm's Issues

Convention based mappings

Is there a possibility that convention based mapping can be added, so insteda of having an attribute on those who should be mapped, add an attribute if it should be ignored or have an different column name.

Mapping fields

Is it not better to map fields than methods?
What happens if I want a readonly on my entity, then I will not have an setMethod, or if I do not want to have all my private fields visible.

Or I just simply want to bypass validations or other logic on get/sets

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.