Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of UserWithTweets entity with custom Put/Get/Delete resolvers #520

Merged
merged 2 commits into from Sep 15, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any plans when to release v2?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As soon as we will complete all issues with milestone v2.0.0 :) No concrete dates, but probably near to the end of this year, I hope :)


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();
}
}