Android Room Database - java

I can't wrap my head around how should i get my data without much boilerplate.
The problem:
I have a database that i cannot alter. Which has multiple field of same type almost same for example i have text_en and text_fr (both are the same word in different language English and French) and i got + 71 same field but different languages.
What I need is something like
#Entitiy(tableName = "blabla")
class X {
private String textTarget;
private String textMain;
...
}
How should I do my Dao interface to get desired language and map into x class
what should work is to update entity ColumnInfo(name ="text_en") for example.
#Query("select :main , :target from phrases where :id ")
List<X> getPhrase(String main,String target);
usage : getPhrase("text_en","text_esp");
// for example returning object X with field main = "hello" and target " holla")
The above example return the following error:
error: Not sure how to convert a Cursor to this method's return type

What you put in #Query is an SQL-statement, you can actually test them in sqlite command line utility or any desktop software to verify their correctness. So if you want a translation to the desired language, it should look like this:
#Query("SELECT :main AS text_main, :target AS text_target FROM `phrases` WHERE id = :id)
List<Translation> getTranslationById(String firstLang, String secondLang, long id);
Where Translation should be something like this:
class Translation {
#ColumnInfo("text_main")
String main;
#ColumnInfo("text_target")
String target;
//setters, getters, etc
}
This class is used only as a return value from the method.

Related

Generating a query at runtime using Room Persistance

I want to run queries on my SQLite database that have been generated at runtime (instead of the standard compiletime queries in the #Dao). For example I might want to search a TEXT column in the SQLite db, to see if it contains all words in a list of N length. In raw SQLITE, a query where N is 3 would look like this :
SELECT * FROM table
WHERE textValue LIKE %queryTerm1%
AND textValue LIKE %queryTerm2%"
AND textValue LIKE %queryTerm3%"
I have tried generating, and passing the end of the query, instead of just passing variables. For example :
String generatedQuery = "textValue LIKE %queryTerm1% AND textValue LIKE %queryTerm2% AND textValue LIKE %queryTerm3%";
tableDao.find(generatedQuery);
and in the #Dao:
#Query("SELECT * FROM tableName WHERE :endQuery")
List<POJO> find(String endQuery);
This doesn't seem to work for me. Do you have any idea how to get runtime generated queries working with Room?
PS:
I have debugged the Dao implementation and looked at the statement it is running. This confirms that the generated query information, and the query are being passed correctly. I assume this is an issue with SQL injection prevention (aka more of an SQLITE problem, than a Room problem)
Update: latest release 1.1.1 of Room now uses SupportSQLiteQuery instead of String.
A query with typed bindings. It is better to use this API instead of
rawQuery(String, String[]) because it allows binding type safe
parameters.
New Answer:
#Dao
interface RawDao {
#RawQuery(observedEntities = User.class)
LiveData<List<User>> getUsers(SupportSQLiteQuery query);
}
Usage:
LiveData<List<User>> liveUsers = rawDao.getUsers( new
SimpleSQLiteQuery("SELECT * FROM User ORDER BY name DESC"));
Update your gradle to 1.1.1 (or whatever the current version is)
implementation 'android.arch.persistence.room:runtime:1.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1'
annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
The problem is you want to pass a part of SQL statement, but Room treats it like a query parameter.
If you want you can try to use Kripton Persistence Library, an open source library written (by me :) ) that drastically simplify SQLite's management code for Android platform and support situations like this.
Kripton works with DAO pattern too, so concept are quite similar. Just to write an example that fit your needs:
Given a model class:
#BindType
public class User {
public long id;
public String name;
public String username;
public String email;
public Address address;
public String phone;
public String website;
public Company company;
}
a DAO definition:
#BindDao(User.class)
public interface UserDao {
#BindSqlInsert
void insert(User bean);
#BindSqlSelect
List<User> selectDynamic(#BindSqlDynamicWhere String where, #BindSqlDynamicWhereParams String[] args);
}
and a data source definition:
#BindDataSource(daoSet={UserDao.class}, fileName = "kripton.quickstart.db", generateAsyncTask = true)
public interface QuickStartDataSource {
}
Kripton will generate at compile time all code is need to work with database. So to accomplish your task with Kripton you have to write a code similar to:
BindQuickStartDataSource ds = BindQuickStartDataSource.instance();
// execute operation in a transaction
ds.execute(new BindQuickStartDataSource.SimpleTransaction() {
#Override
public boolean onExecute(BindQuickStartDaoFactory daoFactory) throws Throwable
{
UserDaoImpl dao = daoFactory.getUserDao();
String[] p={"hello"};
dao.selectDynamic("name=?",p);
return true;
}
});
In logcat when code above is executed you will see the generated log:
database OPEN READ_AND_WRITE_OPENED (connections: 1)
UserDaoImpl, selectDynamic (line 352): SELECT id, name, username, email, address, phone, website, company FROM user WHERE name=?
selectDynamic (line 357): ==> param0: 'hello'
Rows found: 0
database CLOSED (READ_AND_WRITE_OPENED) (connections: 0)
Kripton obviously supports static where conditions too and many other features (i start to develop it in 2015).
For more information about Kripton Persistence Library:
https://github.com/xcesco/kripton
http://abubusoft.com/
https://github.com/xcesco/kripton/wiki

Use SELECT NEW CONSTRUCTOR with parameter not from tables

I'm using something like this now in my HQL:
"SELECT NEW com.somepackage.dto.SomeClass(myObj) "
But now I want to add a boolean parameter to constructor.
I've added it to my DAO method with HQL and to constructor of my dto object:
"SELECT NEW com.somepackage.dto.SomeClass(myObj, :param) "
...
.setParameter("param", param)
After adding parameter I got an exception:
Unable to locate appropriate constructor on class
Is there a way to add param to constructor? Or I was made something wrong?
Thx for your replies and sorry for my English.
Update
(Simple copy of my SomeClass):
public class SomeClass extends SomeClassParent {
private final String someParam;
private final List<MyObject> myObjects;
public SomeClass(MyObject myObject) {
super(myObject.getFirstField,
myObject.getSecondField, ...);
this.someParam = myObject.getSomeParamValue();
StringBuilder bodyBuilder = new StringBuilder();
...
I want it to be
public SomeClass(MyObject myObject, boolean myBoolean) {
I don't know exactly what was the problem with boolean, but now I'm using String parameter against boolean and use it like this:
"SELECT NEW com.somepackage.dto.SomeClass(myObj, '" + param + "') "...
setParameter doesn't want to work with String, because it requires quotes to be result string like this:
"SELECT NEW com.somepackage.dto.SomeClass(myObj, 'Some string') "...
against
"SELECT NEW com.somepackage.dto.SomeClass(myObj, Some string) "...
Ok, what it wasn't clear to me was if you had the SomeClass definition, you can define as many constructors as you want as long as they have different types, or quantities of parameters passed by.
It can be see as a kind of override (although it is not!)
It's up to you to define it, and do whatever you want with that Boolean, in fact you can copy and paste the original constructor (just leave that one there, don't erase it) with the Boolean's addition and it would be as valid as the previous one.
I'm pretty sure that your problem is with the boolean field. I worked with Hibernate HQL and I had a similar problem with a field. At the end I realized that I have to use nullable files for every field.
So, I think this problem could be solved turning boolean primitive field into Boolean Object class.
I'm having a similar problem with constructing a result entity using a query parameter as a constructor argument - like "select new com.example.ResultType(t.id, ?1) from Table t where ...". It seems such bare query params just get ignored when looking for constructors - in that example it would look for a constructor that takes a single argument matching t.id.
The only work-around I've found so far is to wrap the parameter with a cast (or some other non-trivial expression), e.g. "select new com.example.ResultType(t.id, cast(?1 as string)) ...".

using search module in playframework,proper

I have a model Item that has a name and description.I need to allow the user to search for a part of string in name or description.Instead of doing this using an sql query,I thought of using the search module that can be installed for playframework.
Looking at the documentation for search module ,I put these annotations to the model
#Entity
#Indexed
class Item{
#Field
public String name;
#Field
public String description;
public Date creationDate;
...
...
}
In application.conf ,I set
play.search.reindex=enabled
If I use an sql query like this
public static List<Item> getSearchResults(String kw){
List<Item> items = null;
if(kw!=null && kw.length()>0) {
String trimkw = kw.trim().toLowerCase();
String pattern = "%"+trimkw+"%";
String query="select distinct b from Item b where (lower(name) like :pattern or lower(description) like :pattern)";
items = Item.find(query).bind("pattern", pattern).fetch();
System.out.println("getSearchResults():: items="+items.size());
}
return items;
}
This works properly,and handles the cases where input string is uppercase or lowercase etc.Also it will get results for partial strings ..
For example ,
I have items JavaRing ,Android
when the kw="JAvA"
the search returns a list containing JavaRing
I tried using Search module like this
import play.modules.search.Search;
import play.modules.search.Query;
...
String qstr = "name:"+trimkw+" OR description:"+trimkw;
System.out.println("query string="+qstr);
Query q = Search.search(qstr, Item.class);
items = q.fetch();
System.out.println("items="+items.size());
But this returns an empty list for the same keyword as I used in the previous case.
keyword = "JAvA"
query string=name:java OR description:java
items=0
Is there something wrong with the way I have coded the search string?
Search module is based on Lucene. By default, Lucene searches for whole words. You didn't find anything because there isn't whole word 'java' in your fields.
Using wildcards, for instance name:java* OR description:java* you'll fit your needs. You can find more examples there
Updated link is http://lucene.apache.org/java/3_0_2/queryparsersyntax.html
In this case, if the keyword is to be found anywhere, I assume string pattern needs to be modified from % to *.
ie. String pattern = trimkw+"*";
The rest of the code could remain the same.

Java Play Framework UnexpectedException applying PropertiesEnhancer

I get the following error on compile:
An unexpected error occured caused by exception UnexpectedException:
While applying class play.classloading.enhancers.PropertiesEnhancer on controllers.xxxxx
I've narrowed it down to the following line in my controller:
String sender_alert_string = "Your message to " + app_user.fullname + " has failed";
With the line, I get the error, without the line, I don't.
I even tried splitting it up into separate strings and then combining them. Also tried .concat()
Why would an attempt to join strings (as I do in many other places) cause this?
EDIT: I'm beginning to think it has something to do with the #Entity tag just before declarations in my model files. Alternatives?
EDIT: app_user is of a basic model type User. constructor below
public User(String app_user_uuid, String fullname, String auth_token)
{
//timestamp
Calendar now_time = Application.get_now();
this.time_modified = now_time;
this.time_created = now_time;
//other
this.app_user_uuid = app_user_uuid;
this.fullname = fullname;
this.auth_token = auth_token;
this.timezone_offset = Application.default_timezone_offset;
}
Play enhances all Entity, models with a few things :
Add an ID column,
Adds getter/setters for public attributes,
It is probably one of those that is failing your application. When calling fullname are you sure it exists, that is is public and has no getter/setter method?

Java annotations

I've created simple annotation in Java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Column {
String columnName();
}
and class
public class Table {
#Column(columnName = "id")
private int colId;
#Column(columnName = "name")
private String colName;
private int noAnnotationHere;
public Table(int colId, String colName, int noAnnotationHere) {
this.colId = colId;
this.colName = colName;
this.noAnnotationHere = noAnnotationHere;
}
}
I need to iterate over all fields, that are annotated with Column and get name and value of field and annotation. But I've got problem with getting value of each field, since all of them are of different data type.
Is there anything that would return collection of fields that have certain annotation?
I managed to do it with this code, but I don't think that reflection is good way to solve it.
Table table = new Table(1, "test", 2);
for (Field field : table.getClass().getDeclaredFields()) {
Column col;
// check if field has annotation
if ((col = field.getAnnotation(Column.class)) != null) {
String log = "colname: " + col.columnName() + "\n";
log += "field name: " + field.getName() + "\n\n";
// here i don't know how to get value of field, since all get methods
// are type specific
System.out.println(log);
}
}
Do I have to wrap every field in object, which would implement method like getValue(), or is there some better way around this? Basicly all I need is string representation of each field that is annotated.
edit: yep field.get(table) works, but only for public fields, is there any way how to do this even for private fields? Or do I have to make getter and somehow invoke it?
Every object should has toString() defined. (And you can override this for each class to get a more meaningful representation).
So you where your "// here I don't know" comment is, you could have:
Object value = field.get(table);
// gets the value of this field for the instance 'table'
log += "value: " + value + "\n";
// implicitly uses toString for you
// or will put 'null' if the object is null
Reflection is exactly the way to solve it. Finding out things about types and their members at execution time is pretty much the definition of reflection! The way you've done it looks fine to me.
To find the value of the field, use field.get(table)
Reflection is exactly the way to look at annotations. They are a form of "metadata" attached to the class or method, and Java annotations were designed to be examined that way.
Reflection is one way to process the object (probably the only way if the fields are private and don't have any kind of accessor method). You'll need to look at Field.setAccessible and perhaps Field.getType.
Another approach is to generate another class for enumerating the annotated fields using a compile-time annotation processor. This requires a com.sun API in Java 5, but support is better in the Java 6 JDK (IDEs like Eclipse may require special project configuration).

Categories

Resources