Skip to content

Commit

Permalink
Merge pull request #520 from pushtorefresh/user-with-tweets-example
Browse files Browse the repository at this point in the history
Example of UserWithTweets entity with custom Put/Get/Delete resolvers
  • Loading branch information
nikitin-da committed Sep 15, 2015
2 parents a5a773f + 9a355f0 commit fa709ab
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 10 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Expand Up @@ -101,8 +101,8 @@ ext.libraries = [
recyclerView : 'com.android.support:recyclerview-v7:' + supportLibsVersion,
rxAndroid : 'io.reactivex:rxandroid:1.0.1',
timber : 'com.jakewharton.timber:timber:3.0.1',
leakCanary : 'com.squareup.leakcanary:leakcanary-android:1.3',
leakCanaryNoOp : 'com.squareup.leakcanary:leakcanary-android-no-op:1.3',
leakCanary : 'com.squareup.leakcanary:leakcanary-android:1.3.1',
leakCanaryNoOp : 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1',

autoService : 'com.google.auto.service:auto-service:1.0-rc2',
javaPoet : 'com.squareup:javapoet:1.2.0',
Expand Down
5 changes: 5 additions & 0 deletions storio-sample-app/build.gradle
Expand Up @@ -68,4 +68,9 @@ dependencies {

debugCompile libraries.leakCanary
releaseCompile libraries.leakCanaryNoOp

testCompile libraries.junit
testCompile libraries.assertJ
testCompile libraries.robolectric
testCompile libraries.leakCanaryNoOp
}
Expand Up @@ -31,10 +31,6 @@ public class Tweet {
@StorIOSQLiteColumn(name = TweetsTable.COLUMN_CONTENT)
String content;

@NonNull
@StorIOSQLiteColumn(name = "some_bytes")
byte[] someBytes;

// leave default constructor for AutoGenerated code!
Tweet() {
}
Expand Down
Expand Up @@ -37,4 +37,14 @@ public static User newUser(@Nullable Long id, @NonNull String nick) {
user.nick = nick;
return user;
}

@Nullable
public Long id() {
return id;
}

@NonNull
public String nick() {
return nick;
}
}
@@ -0,0 +1,44 @@
package com.pushtorefresh.storio.sample.db.entities;

import android.support.annotation.NonNull;

import java.util.List;

import static java.util.Collections.unmodifiableList;

/**
* Example of entity with linked sub-entities!
*
* It's a User with his tweets.
*
* Main idea of this example is to show you that
* StorIO can solve ORM problems but still be a DAO.
*
* Moreover, we will write GetResolver in such manner
* that you will be able to write use any Query with it,
* but at the same time you could optimize any frequent case
* with JOIN and other SQL things directly in GetResolver.
*/
public final class UserWithTweets {

@NonNull
private final User user;

@NonNull
private final List<Tweet> tweets;

public UserWithTweets(@NonNull User user, @NonNull List<Tweet> tweets) {
this.user = user;
this.tweets = unmodifiableList(tweets); // We prefer immutable entities
}

@NonNull
public User user() {
return user;
}

@NonNull
public List<Tweet> tweets() {
return tweets;
}
}
@@ -0,0 +1,42 @@
package com.pushtorefresh.storio.sample.db.resolvers;

import android.support.annotation.NonNull;

import com.pushtorefresh.storio.sample.db.entities.UserWithTweets;
import com.pushtorefresh.storio.sample.db.tables.TweetsTable;
import com.pushtorefresh.storio.sample.db.tables.UsersTable;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResolver;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResult;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public final class UserWithTweetsDeleteResolver extends DeleteResolver<UserWithTweets> {

@NonNull
@Override
public DeleteResult performDelete(@NonNull StorIOSQLite storIOSQLite, @NonNull UserWithTweets userWithTweets) {
// 1 for user and other for his/her tweets
final List<Object> objectsToDelete = new ArrayList<Object>(1 + userWithTweets.tweets().size());

objectsToDelete.add(userWithTweets.user());
objectsToDelete.addAll(userWithTweets.tweets());

storIOSQLite
.delete()
.objects(objectsToDelete)
.prepare()
.executeAsBlocking();

// BTW, you can save it as static final
final Set<String> affectedTables = new HashSet<String>(2);

affectedTables.add(UsersTable.TABLE);
affectedTables.add(TweetsTable.TABLE);

return DeleteResult.newInstance(objectsToDelete.size(), affectedTables);
}
}
@@ -0,0 +1,82 @@
package com.pushtorefresh.storio.sample.db.resolvers;

import android.database.Cursor;
import android.support.annotation.NonNull;

import com.pushtorefresh.storio.sample.db.entities.Tweet;
import com.pushtorefresh.storio.sample.db.entities.User;
import com.pushtorefresh.storio.sample.db.entities.UserWithTweets;
import com.pushtorefresh.storio.sample.db.tables.TweetsTable;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.get.GetResolver;
import com.pushtorefresh.storio.sqlite.queries.Query;
import com.pushtorefresh.storio.sqlite.queries.RawQuery;

import java.util.List;

public final class UserWithTweetsGetResolver extends GetResolver<UserWithTweets> {

// We can even reuse existing get resolvers for our needs
// But, you can always write custom code, of course.
@NonNull
private final GetResolver<User> userGetResolver;

// Sorry for this hack :(
// We will pass you an instance of StorIO
// into the mapFromCursor() in v2.0.0.
//
// At the moment, you can save this instance in performGet() and then null it at the end
@NonNull
private final ThreadLocal<StorIOSQLite> storIOSQLiteFromPerformGet = new ThreadLocal<StorIOSQLite>();

public UserWithTweetsGetResolver(@NonNull GetResolver<User> userGetResolver) {
this.userGetResolver = userGetResolver;
}

@NonNull
@Override
public UserWithTweets mapFromCursor(@NonNull Cursor cursor) {
final StorIOSQLite storIOSQLite = storIOSQLiteFromPerformGet.get();

// BTW, you don't need a transaction here
// StorIO will wrap mapFromCursor() into the transaction if needed
try {
// Or you can manually parse cursor (it will be sliiightly faster)
final User user = userGetResolver.mapFromCursor(cursor);

// Yep, you can reuse StorIO here!
// Or, you can do manual low level requests here
// BTW, if you profiled your app and found that such queries are not very fast
// You can always add some optimized version for particular queries to improve the performance
final List<Tweet> tweetsOfTheUser = storIOSQLite
.get()
.listOfObjects(Tweet.class)
.withQuery(Query.builder()
.table(TweetsTable.TABLE)
.where(TweetsTable.COLUMN_AUTHOR + "=?")
.whereArgs(user.nick())
.build())
.prepare()
.executeAsBlocking();

return new UserWithTweets(user, tweetsOfTheUser);
} finally {
// Releasing StorIOSQLite reference
storIOSQLiteFromPerformGet.set(null);
}
}

@NonNull
@Override
public Cursor performGet(@NonNull StorIOSQLite storIOSQLite, @NonNull RawQuery rawQuery) {
storIOSQLiteFromPerformGet.set(storIOSQLite);
return storIOSQLite.internal().rawQuery(rawQuery);
}

@NonNull
@Override
public Cursor performGet(@NonNull StorIOSQLite storIOSQLite, @NonNull Query query) {
storIOSQLiteFromPerformGet.set(storIOSQLite);
return storIOSQLite.internal().query(query);
}
}
@@ -0,0 +1,43 @@
package com.pushtorefresh.storio.sample.db.resolvers;

import android.support.annotation.NonNull;

import com.pushtorefresh.storio.sample.db.entities.UserWithTweets;
import com.pushtorefresh.storio.sample.db.tables.TweetsTable;
import com.pushtorefresh.storio.sample.db.tables.UsersTable;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver;
import com.pushtorefresh.storio.sqlite.operations.put.PutResult;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public final class UserWithTweetsPutResolver extends PutResolver<UserWithTweets> {

@NonNull
@Override
public PutResult performPut(@NonNull StorIOSQLite storIOSQLite, @NonNull UserWithTweets userWithTweets) {
// 1 for user and other for his/her tweets
final List<Object> objectsToPut = new ArrayList<Object>(1 + userWithTweets.tweets().size());

objectsToPut.add(userWithTweets.user());
objectsToPut.addAll(userWithTweets.tweets());

storIOSQLite
.put()
.objects(objectsToPut)
.prepare()
.executeAsBlocking();

// BTW, you can save it as static final
final Set<String> affectedTables = new HashSet<String>(2);

affectedTables.add(UsersTable.TABLE);
affectedTables.add(TweetsTable.TABLE);

// Actually, we don't know what to return for such complex operation, so let's return update result
return PutResult.newUpdateResult(objectsToPut.size(), affectedTables);
}
}
Expand Up @@ -26,8 +26,6 @@ public class TweetsTable {
@NonNull
public static final String COLUMN_CONTENT = "content";

public static final String COLUMN_SOME_BYTES = "some_bytes";

// Yep, with StorIO you can safely store queries as objects and reuse them, they are immutable
@NonNull
public static final Query QUERY_ALL = Query.builder()
Expand All @@ -46,8 +44,7 @@ public static String getCreateTableQuery() {
return "CREATE TABLE " + TABLE + "("
+ COLUMN_ID + " INTEGER NOT NULL PRIMARY KEY, "
+ COLUMN_AUTHOR + " TEXT NOT NULL, "
+ COLUMN_CONTENT + " TEXT NOT NULL,"
+ COLUMN_SOME_BYTES + " BLOB "
+ COLUMN_CONTENT + " TEXT NOT NULL"
+ ");";
}
}
@@ -0,0 +1,26 @@
package com.pushtorefresh.storio.sample.db;

import android.database.sqlite.SQLiteDatabase;

import com.pushtorefresh.storio.contentresolver.BuildConfig;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public final class DbOpenHelperTest {

@Test
public void shouldCreateDb() {
SQLiteDatabase database = new DbOpenHelper(RuntimeEnvironment.application)
.getWritableDatabase();

assertThat(database).isNotNull();
}
}

0 comments on commit fa709ab

Please sign in to comment.