Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ preprocessor/target
common/target
build
*.keystore
*.~*
projectBackupFiles
61 changes: 54 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ annotations and CRUD classes expose an expressive api for executing SQLite queri
preprocessors on Android.*
```groovy
dependencies {
apt 'com.memtrip.sqlking:preprocessor:1.1.5'
compile 'com.memtrip.sqlking:client:1.1.5'
apt 'com.memtrip.sqlking:preprocessor:1.1.6'
compile 'com.memtrip.sqlking:client:1.1.6'
}
```

Expand Down Expand Up @@ -230,12 +230,27 @@ User[] users = Select.getBuilder()
####Joins####
Joins can be performed using the `InnerJoin`, `LeftOutJoin`, `CrossInnerJoin`, `NaturalInnerJoin`, `NaturalLeftOuterJoin` classes.
The target table for the join must be defined as an @Column, the object will be populated with any join results.
Single Column Constraints and Foreignk Keys can also be defined.

```java
@Table
public class Comment {
@Column(index = true) int id;
@Column int userId;
@Column([@ForeignKey // Single column ForeignKey (not required for joins, but will be enforced by SQLite DBMS)
(
foreignTablename="User",
localColumnNames ={"user_id"},
foreignColumnNames = {"id"},
[update/deleteRule=RIRule.Cascade|...][,...]
)
int userId;]
@Constraints = {@Constraint // Single column Constraints, see SQLite documentation fot `table_constraint`
(
constraintName = "constraintName", // will be created as "table_name_constraint_name_constraint". the constraint name must be unique within the constraints for the table
expression = "some expression eg, PRIMARY|FOREIGN KEY, UNIQUE, CHECK"
[,@onConflict (ConflictAction.ROLLBACK|ABORT|FAIL|etc)] // optional ON CONFLICT clause, if required and appropriate
)[, ...] // optional additional @Constraint statements

@Column User user; // The target table for a potential join

public int getId() {
Expand Down Expand Up @@ -265,7 +280,7 @@ public class Comment {

@Table
public class User {
@Column(index = true) int id;
@Column(index = true) int id; // Single column index (short form syntax)

public int getId() {
return id;
Expand All @@ -282,8 +297,9 @@ Comment[] comments = Select.getBuilder()

User user = comments[0].getUser(); // The nested User object is populated by the join
```

####Primary Key####
An auto incrementing primary key can be defined using:
An auto incrementing primary key can be defined on an `int` or `long` column using:

```java
@Table
Expand All @@ -300,13 +316,44 @@ public class Data {
}
```

####Table Constraints, Composite Indexes and Foreign Keys####
Multiple Composite Indexes and Foreign Keys can be defined for the table, and will be created automatically when the table is created:

```java
@Table (
foreignkeys = {@ForeignKey ( // (not required for joins, but will be enforced by SQLite DBMS)
foreignTableName = "xxxx", // will be created as "fk_localTableName_foreignTableName_n", _n increments for multiple links to the same foreign table
localColumnNames = {"localcolumn"[, ...] // Multiple columns possible
},
foreignColumnNames = {"foreignColumn"[, ...] // Multiple columns possible
}
)[, ...] // optional additional @ForeignKey statements
},
indexes = {@Index (
indexName = "index_name", // will be created as "table_name_index_name_index". index_name must be unique within the indexes on the parent table
columns = {@IndexColumn (column = "column1"
[,sortOrder = SortOrder.ASC|SortOrder.DESC] // optional column sort order (default is SortOrder.ASC)
)[, ...] // optional additional @IndexColumn statements
}
)[, ...] // optional additional @Index statements
},
constraints = {@Constraint (
constraintName = "constraintName", // will be created as "table_name_constraint_name_constraint". the constraint name must be unique within the constraints for the table
expression = "some expression eg, PRIMARY|FOREIGN KEY, UNIQUE, CHECK" // see SQLite documentation fot `table_constraint`
[,@onConflict (ConflictAction.ROLLBACK|ABORT|FAIL|etc)] // as appropriate, if required
)[, ...] // optional additional @Constraint statements
}
)
```

####Tests####
The `tests/java/com/memtrip/sqlking` package contains a full set of unit and integration tests. The
tests can be used as a good reference on how to structure queries.

####TODO####
- Validate that object relationships defined by @Column are annotated with @Table
- Validate that auto_increment columns must be int or long
- @Table annotation should support foreign_key functionality
- @NotNull annotation and handle this validation in the software layer
- Composite Foreign Key Constraints
- Add table Alias support for Queries, Foreign keys and Joins
- Allow for join creation based on Foreign key Annotations (including aliases)
- Add support for database version upgrades scripts, so that exisiting data is retained
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ buildscript {
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.android.tools.build:gradle:1.5.0'
classpath 'com.android.tools.build:gradle:2.2.0'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
}
}
8 changes: 4 additions & 4 deletions client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ext {
siteUrl = 'https://github.com/memtrip/SQLKing'
gitUrl = 'https://github.com/memtrip/SQLKing.git'

libraryVersion = '1.1.5'
libraryVersion = '1.1.7'

developerId = 'samkirton'
developerName = 'Samuel Kirton'
Expand All @@ -44,7 +44,7 @@ android {
minSdkVersion 11
targetSdkVersion 24
versionCode 7
versionName "1.1.1"
versionName "1.1.7"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -137,11 +137,11 @@ install {
}

dependencies {
compile 'com.memtrip.sqlking:common:1.1.5'
compile 'com.memtrip.sqlking:common:1.1.7'
compile 'io.reactivex:rxjava:1.1.1'
compile 'io.reactivex:rxandroid:1.1.0'

androidTestApt 'com.memtrip.sqlking:preprocessor:1.1.5'
androidTestApt 'com.memtrip.sqlking:preprocessor:1.1.7'

androidTestCompile(
'com.android.support.test:runner:0.3',
Expand Down
17 changes: 15 additions & 2 deletions client/src/main/java/com/memtrip/sqlking/database/SQLInit.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,29 @@ public static SQLProvider createDatabase(String name,

String[] schemaArray = new String[modelCount];
String[] tableNameArray = new String[modelCount];

String[] createIndexArray = new String[modelCount];
List<String> indexNameArray = new ArrayList<>();

String[] createTriggerArray = new String[modelCount];;
List<String> triggerNameArray = new ArrayList<>();

for (int i = 0; i < modelClassDef.length; i++) {
SQLQuery sqlQuery = resolver.getSQLQuery(modelClassDef[i]);
schemaArray[i] = sqlQuery.getTableInsertQuery();

schemaArray[i] = sqlQuery.getTableCreateQuery();
tableNameArray[i] = sqlQuery.getTableName();
createIndexArray[i] = sqlQuery.getCreateIndexQuery();
createIndexArray[i] = sqlQuery.getCreateIndexesQuery();
createTriggerArray[i] = sqlQuery.getCreateTriggersQuery();

for (String indexName : sqlQuery.getIndexNames()) {
indexNameArray.add(indexName);
}

for (String triggerName : sqlQuery.getTriggerNames()) {
triggerNameArray.add(triggerName);
}

}

SQLOpen sqlOpen = new SQLOpen(
Expand All @@ -60,6 +71,8 @@ public static SQLProvider createDatabase(String name,
tableNameArray,
createIndexArray,
indexNameArray,
createTriggerArray,
triggerNameArray,
context
);

Expand Down
37 changes: 26 additions & 11 deletions client/src/main/java/com/memtrip/sqlking/database/SQLOpen.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,39 @@

/**
* @author Samuel Kirton [sam@memtrip.com]
* @author Adrian Velcich [adrian@higration.co.za]
*
*/
public class SQLOpen extends SQLiteOpenHelper {
private SQLiteDatabase mDatabase;
private String[] mSchemaArray;
private String[] mTableNameArray;
private String[] mCreateIndexQuery;
private List<String> mIndexNames;

private String[] mCreateIndexStatements;
private List<String> mIndexNames;
private String[] mCreateTriggerStatements;
private List<String> mTriggerNames;

protected SQLiteDatabase getDatabase() {
return mDatabase;
}

protected SQLOpen(String name, int version, String[] schemaArray,
String[] tableNameArray,
String[] indexQuery,
String[] createIndexStatements,
List<String> indexNames,
String[] createTriggerStatements,
List<String> triggerNames,
Context context) {

super(context, name, null, version);

mSchemaArray = schemaArray;
mTableNameArray = tableNameArray;
mCreateIndexQuery = indexQuery;
mIndexNames = indexNames;
mCreateIndexStatements = createIndexStatements;
mIndexNames = indexNames;
mCreateTriggerStatements = createTriggerStatements;
mTriggerNames = triggerNames;

mDatabase = getWritableDatabase();
}

Expand All @@ -57,11 +66,17 @@ public void onCreate(SQLiteDatabase db) {
db.execSQL(schema);
}

for (String createIndex : mCreateIndexQuery) {
if (createIndex != null) {
db.execSQL(createIndex);
}
}
for (String createIndex : mCreateIndexStatements) {
if (createIndex != null) {
db.execSQL(createIndex);
}
}

for (String createTrigger : mCreateTriggerStatements) {
if (createTrigger != null) {
db.execSQL(createTrigger);
}
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public void testMultipleInsert() {
};

// exercise
Insert.getBuilder().values(users).execute(getSQLProvider());
Insert.getBuilder().values((Object[])users).execute(getSQLProvider());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is the cast to Object[] required?


// verify
User angieUser = Select.getBuilder()
Expand Down Expand Up @@ -148,7 +148,7 @@ public void testMoreThan500RowInsert() {
);
}

Insert.getBuilder().values(users).execute(getSQLProvider());
Insert.getBuilder().values((Object[])users).execute(getSQLProvider());

User[] usersInserted = Select.getBuilder().execute(User.class, getSQLProvider());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright 2013-present memtrip LTD.
*
* 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.
*/
package com.memtrip.sqlking.integration;

import android.database.Cursor;

import com.memtrip.sqlking.operation.function.Raw;

import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
* @author Adrian Velcich [adrian@higration.co.za]
*/
public class ForeignKeyTest extends IntegrationTest
{
@Before
public void setUp()
{
super.setUp();

getSetupData().tearDownTestData(getSQLProvider());
getSetupData().setupTestData(getSQLProvider());

getSetupLog().tearDownTestLogs(getSQLProvider());
getSetupLog().setupTestLogs(getSQLProvider());
}

@Test
public void testUserForeignKeyIsCreated()
{
Cursor cursor = Raw.getBuilder()
.query("PRAGMA foreign_key_list('User');")
.execute(getSQLProvider());

List<String> foreignKeys = getForeignKeys(cursor);

assertEquals(1, foreignKeys.size());
}

private List<String> getForeignKeys(Cursor cursor)
{
List<String> foreignKeys = new ArrayList<>();

try {
while (cursor.moveToNext())
{
int foreignKey = cursor.getColumnIndex("name");

if (foreignKey != -1)
{
String foreignKeyName = cursor.getString(foreignKey);
foreignKeys.add(foreignKeyName);
}
}
}
finally
{
cursor.close();
}

return foreignKeys;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void setUp() {
@Test
public void testPostIndexesAreCreated() {
Cursor cursor = Raw.getBuilder()
.query("PRAGMA INDEX_LIST('Post');")
.query("PRAGMA index_list('Post');")
.execute(getSQLProvider());

List<String> indexes = getIndexes(cursor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
@Table(
foreignKeys = {
@ForeignKey(
targetTable = "User",
targetColumn = "id",
localColumn = "userId"
foreignTableName = "User",
localColumnNames = {"id"},
foreignColumnNames = {"userId"}
)
}
)
Expand Down
Loading