SQLite query with byte[] WHERE clause - java

From an Android SQLite database point of view - I have a table which has a field with BLOB type and then I want to query this table contents based on this BLOB field.
I can insert my BLOB field using ContentValues and retrieve it using:
cursor.getBlob(0)// index
I just can't figure out how to query this table contents based on this BLOB field and have found nothing about this problem.

You can't query the (textual? binary? other?) contents of a blob.
If you look, you'll see the contents is in hex:
EXAMPLE: X'53514C697465'.
Suggestions:
Create a new text column, e.g. "blob_index". You can search on the "index" column, then fetch the blob.
Alternatively, just store the data as "text".

I found that you can query on a blob. One needs to use the hex() function on the query.
For example I'm using UUIDs in my database rows as a unique key that I can generate locally and still be sure of uniqueness on the server.
CREATE TABLE example (_ID INTEGER PRIMARY KEY AUTOINCREMENT,
uuid BLOB NON NULL UNIQUE,
...)
When inserting data the following works:
final ContentValues values = new ContentValues(4);
values.put(Contract.Line.COL_UUID,
UuidFactory.toBlob(uuid));
Given a query URI in the form:
content://package.example.com/example/uuid/11112222-3333-0444-0555-666677778888
the query becomes:
final SQLiteDatabase db = mHelper.getReadableDatabase();
return db.query(table, projection,
"hex(uuid) = ?",
new String[] { UuidFactory.toHex(uri.getLastPathSegment()) },
null, null, null, null);
In UuidFactory (which also contains the code to generate new UUIDs) the follow static functions are defined thus:
#NonNull
public static String toHex(#NonNull final UUID uuid) {
return String.format("%016X%016X",
uuid.getMostSignificantBits(),
uuid.getLeastSignificantBits());
}
#NonNull
public static String toHex(#NonNull final String uuid) {
return toHex(UUID.fromString(uuid));
}
#NonNull
public static byte[] toBlob(#NonNull final UUID uuid) {
final ByteBuffer buf = ByteBuffer.allocate(16);
buf.putLong(uuid.getMostSignificantBits());
buf.putLong(uuid.getLeastSignificantBits());
return buf.array();
}
And for completeness:
#NonNull
public static UUID fromBlob(#NonNull final byte[] array) {
final ByteBuffer buf = ByteBuffer.allocate(16);
buf.mark();
buf.put(array);
buf.reset();
final long msb = buf.getLong();
final long lsb = buf.getLong();
return new UUID(msb, lsb);
}

Related

SQLite database cursor is returning the wrong resource IDs

I'm trying to use references to drawable and String resources from a SQLite database to display the appropriate resources in a fragment. I have a database helper file to populate the database, a database utilities file to create a cursor (or just get the cursor data in an array), and the fragment file.
DatabaseHelper.java
DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
private void updateMyDatabase(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 1) {
db.execSQL("CREATE TABLE FOOD (_id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "NAME TEXT, "
+ "IMAGE_RESOURCE_ID INTEGER, "
+ "QUOTE INTEGER);");
insertFood(db,"Alcohol", R.drawable.alcohol, R.string.symptom9);
}
private static void insertFood(SQLiteDatabase db, String name, int toxicity, int resourceId, int quote){
ContentValues foodValues = new ContentValues();
foodValues.put("NAME", name);
foodValues.put("TOXICITY", toxicity);
foodValues.put("IMAGE_RESOURCE_ID", resourceId);
foodValues.put("QUOTE", quote);
db.insert("FOOD", null, foodValues);
}
}
Here, R.drawable.alcohol = 2131099733
I'm using Android Studio and when I mouse over the values I'm adding to the database and display a drawable using one of those values, it's the correct drawable, but when I request a cursor (or array based on the cursor), the value that the cursor includes is completely different from the value stored in the database and produces a resource not found error.
I tried returning a data array from the helper method, but that also gave incorrect integer references for the drawables and strings so, I'm returning the cursor from the helper method instead:
DatabaseUtilities.java
//get all of the database data for a particular food given that food's name
public static Cursor getFoodById(Context context, long itemId){
try {
SQLiteOpenHelper DatabaseHelper = new DatabaseHelper(context);
SQLiteDatabase db = DatabaseHelper.getReadableDatabase();
return db.query(DatabaseHelper.FOOD,
new String[]{
DatabaseHelper.NAME, DatabaseHelper.IMAGE_RESOURCE_ID,
DatabaseHelper.QUOTE},
"_id = ?", new String[] {Long.toString(itemId)}, null, null, null
);
} catch (SQLiteException e) {
//TODO Add toast - food not available
return null;
}
}
Finally, I'm trying to display the values here:
DetailFragment.java
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Cursor items = DatabaseUtilities.getFoodById(getActivity(), itemId);
if(items.moveToFirst()) {
//set appropriate img
int img = items.getInt(1);
ImageView photo = (ImageView) view.getRootView().findViewById(R.id.detail_photo);
photo.setImageResource(img);
int quote = items.getInt(2);
TextView desc = (TextView) view.getRootView().findViewById(R.id.detail_text);
String descText = getString(quote);
desc.setText(descText);
}
}
This is log output of all of the cursor columns from the last file:
DetailFragment - cursor: Alcohol, 2131165269, 2131558463
The value after Alcohol should be R.drawable.alcohol, 2131099733, but the cursor returns 2131165269 instead. The same is the case with the string reference after the drawable reference. Also, the cursor returns different values for each inserted row, they're just the wrong values and I don't know where they're coming from or if there's a way to convert them to the correct values.
Everything in the program works except that the database doesn't return the correct resource references.
Don't store in the table the integer ids of resources.
Their values are not guaranteed to be the same every time you make changes in the resources and recompile your project.
Instead store the resource ids as string literals.
So change the table's definition to:
CREATE TABLE FOOD (_id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "NAME TEXT, "
+ "IMAGE_RESOURCE_ID TEXT, "
+ "QUOTE TEXT);
Now store in the columns IMAGE_RESOURCE_ID and QUOTE the string ids.
When you retrieve such a string id from the table by using a cursor, you can get its integer id this way:
int drawableId = getResources().getIdentifier(drawableString, "drawable", getPackageName());
int stringId = getResources().getIdentifier(stringString, "string", getPackageName());
You may need to provide a valid Context to getResources() and getPackageName(), like:
context.getResources().getIdentifier(drawableString, "drawable", context.getPackageName());
Replace drawableString and stringString with the values you retrieved from the table for the image and the string.
If you make the above proposed changes in the definition of the table, you must uninstall the app from the device/emulator and rerun so the database is deleted and recreated with the new definition of the table.

spring boot, jdbcTemplate, Java

I have a query that takes one column row from database and I want to set this data to my model. My model name is Pictures and my method is follow:
#Override
public Pictures getPictureList() throws Exception {
JdbcTemplate jdbc = new JdbcTemplate(datasource);
String sql= "select path from bakery.pictures where id=1";
Pictures pcList = jdbc.query(sql, new BeanPropertyRowMapper<Pictures>(Pictures.class));
return pcList;
}
This method returns "a query was Inferred to list".
How can I solve it?
Use JdbcTemplate.queryForObject() method to retrieve a single row by it's primary key.
Pictures p = jdbc.queryForObject(sql,
new BeanPropertyRowMapper<Pictures>(Pictures.class));
JdbcTemplate.query() will return multiple rows which makes little sense if you are querying by primary key.
List<Pictures> list = jdbc.query(sql,
new BeanPropertyRowMapper<Pictures>(Pictures.class));
Picture p = list.get(0);

Use Blob field as query parameter in SQLite

I'm working on Android and i'm studing use of SQLite database. I already known how to do operation like create query insert etc.. for the database.
Just for example, suppose to have the following table definition:
CREATE TABLE bean84_b (id INTEGER PRIMARY KEY AUTOINCREMENT, column_bean BLOB);
Then SQL query to execute is:
SELECT id, column_bean FROM bean84_b WHERE column_bean=?
The java code to execute above query is:
byte[] param1=...
String[] args={String.valueOf(param1)};
Cursor cursor = database(). rawQuery("SELECT id, column_bean FROM bean84_b WHERE column_bean=?", args);
Is it possible to use a BLOB column like SELECT parameter?
This is a design bug in the Android database API.
query and rawQuery accept only string parameters. execSQL accepts any Object parameters but does not return results. SQLiteStatement accepts parameters of any type but allows only queries that return a single value.
Another type you can bind blob with another fields
http://www.programcreek.com/java-api-examples/index.php?api=android.database.sqlite.SQLiteStatement
In the Android database API, execSQL() is the only function where the
parameters are not String[] but Object[]:
byte[] blob = ...;
db.execSQL("DELETE FROM t WHERE my_blob = ?", new Object[]{ blob });
Yes, it is possible. You have to simply extend CursorFactory, bind values to its SQLiteQuery that will be available in NewCursor method.
Xamarin code snippet:
internal sealed class SQLiteCursorFactory : Java.Lang.Object, SQLiteDatabase.ICursorFactory
{
private Dictionary<int, object> _selectionArgs;
internal SQLiteCursorFactory(Dictionary<int, object> selectionArgs)
{
_selectionArgs = selectionArgs;
}
ICursor SQLiteDatabase.ICursorFactory.NewCursor(SQLiteDatabase db, ISQLiteCursorDriver masterQuery, string editTable, SQLiteQuery query)
{
foreach(var key in _selectionArgs.Keys)
{
var val = _selectionArgs[key];
if(val == null)
{
query.BindNull(key);
}
else if(val is int)
{
query.BindLong(key, (int)val);
}
else if (val is long)
{
query.BindLong(key, (long)val);
}
else if (val is double)
{
query.BindDouble(key, (double)val);
}
else if (val is string)
{
query.BindString(key, (string)val);
}
else if (val is byte[])
{
query.BindBlob(key, (byte[]) val);
}
}
return new SQLiteCursor(masterQuery, editTable, query);
}
}
Usage:
dbInstance.RawQueryWithFactory(new SQLiteCursorFactory(selectionArgs), query, null, null);

Obtain Key SQLite Android

I have two tables in which the second table has a foreign key which is defined as table 1's primary key. Under the add operation how can I reference the newly added table 1 key to use as table 2's foreign key?
// Project Table columns names
private static final String KEY_ID = "id"; // Primary, integer
private static final String KEY_NAME = "name"; // Unique, text
// Images Table column names
private static final String KEY_IM_ID = "id"; // Primary, integer
private static final String IM_URI = "uri"; // text
private static final String PROJ_IMAGE_ID = "proImgId"; // foreign for Projects Table
public void addProject(Project project) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values_PT = new ContentValues(); // PROJECTS TABLE
ContentValues values_IT = new ContentValues(); // IMAGES TABLE
values_PT.put(KEY_NAME, project.getName()); // project name
// does the KEY ID get added on its own?
values_IT.put(IM_URI, project.getURI()); // image uri
values_IT.put(PROJ_IMAGE_ID, ????????);
}
PROJ_IMAGE_ID needs to have KEY_ID's value, however I do not know how retrieve it. Is there a way to do this?
If your ID columns are declared as INTEGER PRIMARY KEY, then they are autoincrementing and will get their value automatically if you don't set them when inserting.
To get the ID of a new record, you have to actually insert the record first; the new ID is the return value of the insert() function.

Making a better android sqlite query

Currently I have a query that looks at the table and returns the newest row, reads that row and sets the string to the value in the chosen column index.
At the moment I have 9 columns so I have end up making 9 methods for returning each column just to return a string and set it to each individual textview.
Do I make a cursor to return the row and then set the column index when I am setting the textviews. Something like
cursor.getString(1)
This is my current query
public String getName() {
// TODO Auto-generated method stub
String[] columns = new String[] { KEY_ROWID, KEY_NAME, KEY_COLUMN2,
KEY_COLUMN3, KEY_COLUMN4, KEY_COLUMN5, KEY_COLUMN6, KEY_COLUMN7, KEY_COLUMN8 };
Cursor c = mDb.query(true, DATABASE_TABLE, columns, null, null, null,
null, "_id DESC", "1");
if (c != null) {
c.moveToFirst();
String name = c.getString(1);
return name;
}
return null;
}
You could easily change getName() to something field-agnostic, such as:
public String getField(String fieldName) {
...
String s = cursor.getString(cursor.getColumnIndexOrThrow(fieldName));
}
And use it like:
String s = mDb.getField(mDb.KEY_NAME);
A problem with this approach is that you can't use getString() for a numerical column, so you wind up with multiple methods for each datatype supported in your table. A way to tackle this is to look into the cursor's getType() method or just return a complete data structure from your database access methods. Say you have:
DB Column Field Type
FOO varchar
BAR integer
BAZ varchar
If you define a class, say, MyRowMapper you can do something like this:
public MyRowMapper getRow(String... queryParameters) {
//query table based on queryParameters
MyRowMapper mapper = null;
if (cursor.moveToFirst()) {
String foo = cursor.getString(cursor.getColumnIndexOrThrow("FOO"));
Integer bar = cursor.getInteger(cursor.getColumnIndexOrThrow("BAR"));
String baz = cursor.getString(cursor.getColumnIndexOrThrow("BAZ"));
mapper = new MyRowMapper(foo, bar, baz);
}
cursor.close(); // NEVER FORGET TO CLOSE YOUR CURSOR!
return mapper;
}
This is just abstract mailercode, so take the syntaxt with a grain of salt. But hopefully you get the idea.

Categories

Resources