Skip to content

Instantly share code, notes, and snippets.

@leandrofavarin
Forked from philipphager/A.java
Created May 2, 2017 21:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leandrofavarin/e0b2fbf1471fa6ab0e3c720322e2d60f to your computer and use it in GitHub Desktop.
Save leandrofavarin/e0b2fbf1471fa6ab0e3c720322e2d60f to your computer and use it in GitHub Desktop.
Delighting Leandro

A short remark:

The file strucutre of your project is crutial for this to work. Your SQLDelight files must be in a folder matching the file structure of the java project. Otherwise SQLDelight won't be able to make the generated interfaces accessible in Java.

An examplary file structure would be:

app/src/main/
- java/
    - com/example/project/models/
      - A.java
        ...
- sqldelight/
    - com/example/project/models/
      - A.sq
        ...
@AutoValue public abstract class A implements AModel {
/**
* SQLDelight Factories contain all SQL statement that were defined in the
* corresponding A.sq file in form of a SQLDelightStatement.
+ They are a sort of prepared statement,
* that contain the actual SQL string and provide type safe mappings.
* A usage of the SQLDelightStatement is demonstrated in Repository.java.
* The Factory also provides mapper, that map a row inside a cursor result
* to a Java model.
* Therefore the Factory's constructor takes a creator, that knows how to build
* an instance of the main entity (See A::create). This will be used as a default
* mapper for all SQL queries, that return exactly this entity (Select A.* ...).
* For statements, that return a list of custom attribute, the Factory defines
* custom models and mappers. This is demonstrated in C.java.
*/
public static final AModel.Factory<A> FACTORY = new Factory<>(A::create);
public static A create(String id, Integer numberOfWheels, String bId) {
return new AutoValue_A(id, numberOfWheels, bId);
}
/**
* This would be usually the place to define our AutoValue methods:
* public abstract String id();
* ...
*
* This can be skipped, since SQLDelight defines them for us in AModel.
**/
}
CREATE TABLE A (
id TEXT PRIMARY KEY,
numberOfWheels INTEGER AS Integer,
bId TEXT NOT NULL,
FOREIGN KEY(bId) REFERENCES B(id)
);
/**
* Make sure to prevent clashing attribute names in joins,
* this can often be a problem when using the * operator.
* In this example A.id and B.id would have had the same name.
* Either use alias (A.id AS aId) in select statement or
* exclude one attribute. In this example B.id was excluded
* from the SQL projection.
**/
selectWithB:
SELECT A.id, A.numberOfWheels, B.officeName
FROM A JOIN B
ON A.bId = B.id
WHERE A.id = ?;
@AutoValue public abstract class B implements BModel {
public static final BModel.Factory<B> FACTORY = new Factory<>(B::create);
private static B create(String id, String officeName) {
return new AutoValue_B(id, officeName);
}
}
CREATE TABLE B (
id TEXT PRIMARY KEY,
officeName TEXT NOT NULL
);
/**
* This class represents the result of the join statement.
* It can be defined either as an static inner class inside A,
* or inside it's own file. I tend to create separate files
* to gain a better overview of my view models and it keeps the
* actual model class A a bit tidier.
* The examples on the SQLDelight page use the first approach
* and define nested classes.
**/
@AutoValue public abstract class C implements A.SelectWithBModel {
public static final RowMapper<C> MAPPER = new A.SelectWithBMapper<>(C::create);
public static C create(String aId, Integer numberOfWheels, String appCount) {
return new AutoValue_C(aId, numberOfWheels, appCount);
}
}
/**
* As parsing cursors is a pain,
* I developed this little helper class to
* parse the cursor result to a list of my model class
* using the SQLDelight RowMapper.
**/
public class CursorToListMapper<T> implements Func1<Cursor, List<T>> {
private final RowMapper<T> rowMapper;
public CursorToListMapper(RowMapper<T> rowMapper) {
this.rowMapper = rowMapper;
}
@Override public List<T> call(Cursor cursor) {
boolean cursorPresent = cursor != null;
int resultSize = cursorPresent ? cursor.getCount() : 0;
List<T> items = new ArrayList<>(resultSize);
if (cursorPresent) {
while (cursor.moveToNext()) {
items.add(rowMapper.map(cursor));
}
cursor.close();
}
return items;
}
}
/**
* This class serves as a small example to demonstrate how to execute the select stament
* using a bit of SQLBrite magic, which is often combined with SQLDelight.
*/
public class Repository {
public Observable<C> selectWithB(BriteDatabase db, String id) {
SqlDelightStatement selectWithBStatement = A.FACTORY.selectWithB(id);
// Custom mapper to parse android.database.Cursor to a list of models.
CursorToListMapper<C> toListMapper = new CursorToListMapper<>(C.MAPPER);
return db.createQuery(selectWithBStatement.tables, selectWithBStatement.statement,
selectWithBStatement.args)
.map(SqlBrite.Query::run)
.map(toListMapper)
.flatMap(cList -> Observable.from(cList).first());;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment