
com.j256.ormlite.android.AndroidDatabaseConnection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ormlite-android Show documentation
Show all versions of ormlite-android Show documentation
Lightweight Object Relational Model (ORM) Android classes
The newest version!
package com.j256.ormlite.android;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;
import com.j256.ormlite.dao.ObjectCache;
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.field.SqlType;
import com.j256.ormlite.logger.Logger;
import com.j256.ormlite.logger.LoggerFactory;
import com.j256.ormlite.misc.IOUtils;
import com.j256.ormlite.stmt.GenericRowMapper;
import com.j256.ormlite.stmt.StatementBuilder.StatementType;
import com.j256.ormlite.support.CompiledStatement;
import com.j256.ormlite.support.DatabaseConnection;
import com.j256.ormlite.support.GeneratedKeyHolder;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.os.Build;
/**
* Database connection for Android.
*
* @author kevingalligan, graywatson
*/
public class AndroidDatabaseConnection implements DatabaseConnection {
private static Logger logger = LoggerFactory.getLogger(AndroidDatabaseConnection.class);
private static final String[] NO_STRING_ARGS = new String[0];
private final SQLiteDatabase db;
private final boolean readWrite;
private final boolean cancelQueriesEnabled;
public AndroidDatabaseConnection(SQLiteDatabase db, boolean readWrite) {
this(db, readWrite, false);
}
public AndroidDatabaseConnection(SQLiteDatabase db, boolean readWrite, boolean cancelQueriesEnabled) {
this.db = db;
this.readWrite = readWrite;
this.cancelQueriesEnabled = cancelQueriesEnabled;
logger.trace("{}: db {} opened, read-write = {}", this, db, readWrite);
}
@Override
public boolean isAutoCommitSupported() {
return true;
}
@Override
public boolean isAutoCommit() throws SQLException {
try {
boolean inTransaction = db.inTransaction();
logger.trace("{}: in transaction is {}", this, inTransaction);
// You have to explicitly commit your transactions, so this is sort of correct
return !inTransaction;
} catch (android.database.SQLException e) {
throw new SQLException("problems getting auto-commit from database", e);
}
}
@Override
public void setAutoCommit(boolean autoCommit) {
/*
* Sqlite does not support auto-commit. The various JDBC drivers seem to implement it with the use of a
* transaction. That's what we are doing here.
*/
if (autoCommit) {
if (db.inTransaction()) {
db.setTransactionSuccessful();
db.endTransaction();
}
} else {
if (!db.inTransaction()) {
db.beginTransaction();
}
}
}
@Override
public Savepoint setSavePoint(String name) throws SQLException {
try {
db.beginTransaction();
logger.trace("{}: save-point set with name {}", this, name);
return new OurSavePoint(name);
} catch (android.database.SQLException e) {
throw new SQLException("problems beginning transaction " + name, e);
}
}
/**
* Return whether this connection is read-write or not (real-only).
*/
public boolean isReadWrite() {
return readWrite;
}
@Override
public void commit(Savepoint savepoint) throws SQLException {
try {
db.setTransactionSuccessful();
db.endTransaction();
if (savepoint == null) {
logger.trace("{}: transaction is successfully ended", this);
} else {
logger.trace("{}: transaction {} is successfully ended", this, savepoint.getSavepointName());
}
} catch (android.database.SQLException e) {
if (savepoint == null) {
throw new SQLException("problems committing transaction", e);
} else {
throw new SQLException("problems committing transaction " + savepoint.getSavepointName(), e);
}
}
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
try {
// no setTransactionSuccessful() means it is a rollback
db.endTransaction();
if (savepoint == null) {
logger.trace("{}: transaction is ended, unsuccessfully", this);
} else {
logger.trace("{}: transaction {} is ended, unsuccessfully", this, savepoint.getSavepointName());
}
} catch (android.database.SQLException e) {
if (savepoint == null) {
throw new SQLException("problems rolling back transaction", e);
} else {
throw new SQLException("problems rolling back transaction " + savepoint.getSavepointName(), e);
}
}
}
@Override
public void releaseSavePoint(Savepoint savePoint) {
// noop
}
@Override
public int executeStatement(String statementStr, int resultFlags) throws SQLException {
return AndroidCompiledStatement.execSql(db, statementStr, statementStr, NO_STRING_ARGS);
}
@Override
public CompiledStatement compileStatement(String statement, StatementType type, FieldType[] argFieldTypes,
int resultFlags, boolean cacheStore) {
// resultFlags argument is not used in Android-land since the {@link Cursor} is bi-directional.
CompiledStatement stmt = new AndroidCompiledStatement(statement, db, type, cancelQueriesEnabled, cacheStore);
logger.trace("{}: compiled statement got {}: {}", this, stmt, statement);
return stmt;
}
@Override
public int insert(String statement, Object[] args, FieldType[] argFieldTypes, GeneratedKeyHolder keyHolder)
throws SQLException {
SQLiteStatement stmt = null;
try {
stmt = db.compileStatement(statement);
bindArgs(stmt, args, argFieldTypes);
long rowId = stmt.executeInsert();
if (keyHolder != null) {
keyHolder.addKey(rowId);
}
/*
* I've decided to not do the CHANGES() statement here like we do down below in UPDATE because we know that
* it worked (since it didn't throw) so we know that 1 is right.
*/
int result = 1;
logger.trace("{}: insert statement is compiled and executed, changed {}: {}", this, result, statement);
return result;
} catch (android.database.SQLException e) {
throw new SQLException("inserting to database failed: " + statement, e);
} finally {
closeQuietly(stmt);
}
}
@Override
public int update(String statement, Object[] args, FieldType[] argFieldTypes) throws SQLException {
return execute(statement, args, argFieldTypes, "updated");
}
@Override
public int delete(String statement, Object[] args, FieldType[] argFieldTypes) throws SQLException {
return execute(statement, args, argFieldTypes, "deleted");
}
@Override
public Object queryForOne(String statement, Object[] args, FieldType[] argFieldTypes,
GenericRowMapper rowMapper, ObjectCache objectCache) throws SQLException {
Cursor cursor = null;
AndroidDatabaseResults results = null;
try {
cursor = db.rawQuery(statement, toStrings(args));
results = new AndroidDatabaseResults(cursor, objectCache, true);
logger.trace("{}: queried for one result: {}", this, statement);
if (!results.first()) {
return null;
} else {
T first = rowMapper.mapRow(results);
if (results.next()) {
return MORE_THAN_ONE;
} else {
return first;
}
}
} catch (android.database.SQLException e) {
throw new SQLException("queryForOne from database failed: " + statement, e);
} finally {
IOUtils.closeQuietly(results);
closeQuietly(cursor);
}
}
@Override
public long queryForLong(String statement) throws SQLException {
SQLiteStatement stmt = null;
try {
stmt = db.compileStatement(statement);
long result = stmt.simpleQueryForLong();
logger.trace("{}: query for long simple query returned {}: {}", this, result, statement);
return result;
} catch (android.database.SQLException e) {
throw new SQLException("queryForLong from database failed: " + statement, e);
} finally {
closeQuietly(stmt);
}
}
@Override
public long queryForLong(String statement, Object[] args, FieldType[] argFieldTypes) throws SQLException {
Cursor cursor = null;
AndroidDatabaseResults results = null;
try {
cursor = db.rawQuery(statement, toStrings(args));
results = new AndroidDatabaseResults(cursor, null, false);
long result;
if (results.first()) {
result = results.getLong(0);
} else {
result = 0L;
}
logger.trace("{}: query for long raw query returned {}: {}", this, result, statement);
return result;
} catch (android.database.SQLException e) {
throw new SQLException("queryForLong from database failed: " + statement, e);
} finally {
closeQuietly(cursor);
IOUtils.closeQuietly(results);
}
}
@Override
public void close() throws Exception {
try {
db.close();
logger.trace("{}: db {} closed", this, db);
} catch (android.database.SQLException e) {
throw new SQLException("problems closing the database connection", e);
}
}
@Override
public void closeQuietly() {
IOUtils.closeQuietly(this);
}
@Override
public boolean isClosed() throws SQLException {
try {
boolean isOpen = db.isOpen();
logger.trace("{}: db {} isOpen returned {}", this, db, isOpen);
return !isOpen;
} catch (android.database.SQLException e) {
throw new SQLException("problems detecting if the database is closed", e);
}
}
@Override
public boolean isTableExists(String tableName) {
Cursor cursor = db.rawQuery("SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name = ?",
new String[] { tableName });
try {
boolean result;
if (cursor.getCount() > 0) {
result = true;
} else {
result = false;
}
logger.trace("{}: isTableExists '{}' returned {}", this, tableName, result);
return result;
} finally {
cursor.close();
}
}
@Override
public Connection getUnderlyingConnection() {
// not relevant to android-land
return null;
}
@Override
public String toString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode());
}
private int execute(String statement, Object[] args, FieldType[] argFieldTypes, String label) throws SQLException {
int result = -1;
SQLiteStatement stmt = null;
try {
stmt = db.compileStatement(statement);
bindArgs(stmt, args, argFieldTypes);
if (Build.VERSION.SDK_INT >= 11) { // Build.VERSION_CODES.HONEYCOMB
result = stmt.executeUpdateDelete();
} else {
stmt.execute();
}
} catch (android.database.SQLException e) {
throw new SQLException("updating database failed: " + statement, e);
} finally {
closeQuietly(stmt);
}
if (result < 0) {
result = selectNumChanges(label);
}
logger.trace("{} statement is compiled and executed, changed {}: {}", label, result, statement);
return result;
}
/**
* Return the number of changes from the previous statement. This is needed because the executeUpdateDelete() method
* was not introduced until HONEYCOMB. This needs to be in the same connection as the previous statement. Thanks
* to @WonShaw.
*/
private int selectNumChanges(String label) {
SQLiteStatement stmt = null;
try {
stmt = db.compileStatement("SELECT CHANGES()");
return (int) stmt.simpleQueryForLong();
} catch (android.database.SQLException e) {
logger.warn(e, "{} unable to run statement 'SELECT CHANGES()' to get the changed lines", label);
// ignore the exception and just return 1
return 1;
} finally {
closeQuietly(stmt);
}
}
private void bindArgs(SQLiteStatement stmt, Object[] args, FieldType[] argFieldTypes) throws SQLException {
if (args == null) {
return;
}
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg == null) {
stmt.bindNull(i + 1);
} else {
SqlType sqlType = argFieldTypes[i].getSqlType();
switch (sqlType) {
case STRING:
case LONG_STRING:
case CHAR:
stmt.bindString(i + 1, arg.toString());
break;
case BOOLEAN:
case BYTE:
case SHORT:
case INTEGER:
case LONG:
stmt.bindLong(i + 1, ((Number) arg).longValue());
break;
case FLOAT:
case DOUBLE:
stmt.bindDouble(i + 1, ((Number) arg).doubleValue());
break;
case BYTE_ARRAY:
case SERIALIZABLE:
stmt.bindBlob(i + 1, (byte[]) arg);
break;
case DATE:
// this is mapped to a STRING under Android
case BLOB:
// this is only for derby serializable
case BIG_DECIMAL:
// this should be handled as a STRING
throw new SQLException("Invalid Android type: " + sqlType);
case UNKNOWN:
default:
throw new SQLException("Unknown sql argument type: " + sqlType);
}
}
}
}
private String[] toStrings(Object[] args) {
if (args == null || args.length == 0) {
return null;
}
String[] strings = new String[args.length];
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg == null) {
strings[i] = null;
} else {
strings[i] = arg.toString();
}
}
return strings;
}
/**
* We can't use IOUtils here because older versions didn't implement Closeable.
*/
private void closeQuietly(Cursor cursor) {
if (cursor != null) {
cursor.close();
}
}
/**
* We can't use IOUtils here because older versions didn't implement Closeable.
*/
private void closeQuietly(SQLiteStatement statement) {
if (statement != null) {
statement.close();
}
}
private static class OurSavePoint implements Savepoint {
private String name;
public OurSavePoint(String name) {
this.name = name;
}
@Override
public int getSavepointId() {
return 0;
}
@Override
public String getSavepointName() {
return name;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy