Android: update single Room column with Dao call from repository? - android-asynctask

I have a Room database and RecyclerView that shows a list of CardViews. One of the database columns holds Sort Index values so the CardViews can easily be sorted and moved within the RecyclerView list. I have a Dao call to update the Sort Index values whenever a CardView is moved, deleted or added.
Room requires CRUD on a background thread and the syntax I have in the AsyncTask() in the respository that calls the Dao method is wrong. I am trying to pass the individual "int" sortOrders and the "int" sortIds. What am I missing here? Or would " Executor mExecutor = Executors.newSingleThreadExecutor();" be a better solution here?
MainActivity
int sortId = -1;
int sortOrder = -1;
...
mViewModel.updateSortOrder(sortOrder, sortId);
ViewModel
...
public void updateSortOrder(int sortOrder, int sortId) {
repository.updateSortOrder(sortOrder, sortId);
}
Repository
...
public void updateSortOrder(int sortOrder, int sortId) {
new UpdateSortOrderColAsyncTask(quickcardDao).execute(sortOrder, sortId);
}
**I think this is where the syntax is not correct**
private static class UpdateSortOrderColAsyncTask extends AsyncTask<Integer, Void, Void> {
private QuickcardDao asyncTaskDao;
UpdateSortOrderColAsyncTask(QuickcardDao dao) {
asyncTaskDao = dao;
}
#Override
protected Void doInBackground(final Integer... params) {
asyncTaskDao.updateSortorder(params[0]);
return null;
}
}
Dao
#Query("UPDATE cards SET cardSortorder = :sortOrder WHERE cardId = :sortId")
void updateSortorder(int sortOrder, int sortId);

Related

Why might Room database IDs all show as 0 in a log/Toast message?

I'm trying to get the id of the last inserted object into a database using Room with Android. I can fetch the last object using an SQL query and can call other methods to get the various properties of that object which the user has set when saving the object. But getId() always returns 0. When I examine the table contents in Android Studio's app inspector, I can clearly see that Room is generating a unique primary key for each row, but I just can't get at it. Can anyone suggest what the problem might be?
Here's the Dao query:
#Query("SELECT * FROM gamebooks_table WHERE gamebookId=gamebookId ORDER BY gamebookId DESC LIMIT 1")
LiveData<Gamebook> getSingleGamebookByID();
And here's the annotated entity class:
#Entity(tableName = "gamebooks_table")
public class Gamebook {
#PrimaryKey(autoGenerate = true)
private long gamebookId;
private String gamebookName;
private String gamebookComment;
private String gamebookPublisher;
private float gamebookStarRating;
public Gamebook(String gamebookName, String gamebookComment, String gamebookPublisher, float gamebookStarRating) {
this.gamebookName = gamebookName;
this.gamebookComment = gamebookComment;
this.gamebookPublisher = gamebookPublisher;
this.gamebookStarRating = gamebookStarRating;
}
public long getGamebookId() {
return gamebookId;
}
public String getGamebookName() {
return gamebookName;
}
public String getGamebookComment() {
return gamebookComment;
}
public String getGamebookPublisher() {
return gamebookPublisher;
}
public float getGamebookStarRating(){
return gamebookStarRating;
}
public void setGamebookId(long gamebookId) {
this.gamebookId = gamebookId;
}
}
SOLVED
Finally sorted this by adding an Observer to my DAO method which returns a single gamebook. Within the Observer's onChanged() method, I can loop through all Gamebooks in the LiveData List (even though there's only one because I'm limiting it to one in the SQL query) and call getId() to get their respective IDs.
mainViewModel.getSingleGamebook().observe(this, new Observer<List<Gamebook>>() {
#Override
public void onChanged(List<Gamebook> gamebooks) {
int i=0;
for(Gamebook gamebook : gamebooks){
gamebookId= gamebook.getGamebookId();
Log.d(TAG, "Gamebook Name: "+gamebook.getGamebookName()+ " Database ID: " +gamebookId);
i++;
}
}
});
I believe that your issue is due to the only constructor being available not setting the id so the LiveData uses the default value of 0 for a long.
I'd suggest having a default constructor and thus all setters/getters and (optionally) using #Ignore annotation for one of the constructors..
without #Ignore you get warnings Gamebook.java:8: warning: There are multiple good constructors and Room will pick the no-arg constructor. You can use the #Ignore annotation to eliminate unwanted constructors. public class Gamebook {
e.g. :-
#Entity(tableName = "gamebooks_table")
public class Gamebook {
#PrimaryKey(autoGenerate = true)
private long gamebookId;
private String gamebookName;
private String gamebookComment;
private String gamebookPublisher;
private float gamebookStarRating;
public Gamebook(){} /*<<<<< ADDED */
#Ignore /*<<<<< ADDED - is not required - could be on the default constructor but not both*/
public Gamebook(String gamebookName, String gamebookComment, String gamebookPublisher, float gamebookStarRating) {
this.gamebookName = gamebookName;
this.gamebookComment = gamebookComment;
this.gamebookPublisher = gamebookPublisher;
this.gamebookStarRating = gamebookStarRating;
}
public long getGamebookId() {
return gamebookId;
}
public String getGamebookName() {
return gamebookName;
}
public String getGamebookComment() {
return gamebookComment;
}
public String getGamebookPublisher() {
return gamebookPublisher;
}
public float getGamebookStarRating(){
return gamebookStarRating;
}
public void setGamebookId(long gamebookId) {
this.gamebookId = gamebookId;
}
/* ADDED setters */
public void setGamebookName(String gamebookName) {
this.gamebookName = gamebookName;
}
public void setGamebookComment(String gamebookComment) {
this.gamebookComment = gamebookComment;
}
public void setGamebookPublisher(String gamebookPublisher) {
this.gamebookPublisher = gamebookPublisher;
}
public void setGamebookStarRating(float gamebookStarRating) {
this.gamebookStarRating = gamebookStarRating;
}
}
You also probably want to be able to pass the respective id to the getSingleGamebookByID, so you may wish to change this to:-
#Query("SELECT * FROM gamebooks_table WHERE gamebookId=:gamebookId /*<<<<< ADDED to use id passed */ ORDER BY gamebookId DESC LIMIT 1")
LiveData<Gamebook> getSingleGamebookByID(long gamebookId /*<<<<< ADDED to use id passed */);
you would probably want to remove the comments.
Note the LiveData aspect has not been tested and is conjecture.
Example
This example shows that room is fine with your original code but that the issues is on the LiveData/Viewmodel side :-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
GamebookDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* Note The Database has .allowMainThreadQueries */
db = TheDatabase.getInstance(this);
dao = db.getGamebookDao();
long gb1id = dao.insert(new Gamebook("Gamebook1","blah","Gamebook1 Publisher", 10.1F));
long gb2id = dao.insert(new Gamebook("Gamebook2","blah","Gamebook2 Publisher", 6.1F));
long gb3id = dao.insert(new Gamebook("Gamebook3","blah","Gamebook3 Publisher", 10.1F));
logGameBook(dao.getSingleGamebookByID());
logGameBook(dao.getSingleGamebookByID());
logGameBook(dao.getSingleGamebookByID());
/* Alternative that allows the ID to be specified */
logGameBook(dao.getSingleGamebookByIDAlternative(gb1id));
logGameBook(dao.getSingleGamebookByIDAlternative(gb2id));
logGameBook(dao.getSingleGamebookByIDAlternative(gb3id));
}
void logGameBook(Gamebook gb) {
Log.d("GAMEBOOKINFO","Gamebook is " + gb.getGamebookName() + " id is " + gb.getGamebookId());
}
}
The above uses your original code, the TheDatabase is a basic #Database annotated class BUT with .allowMainThreadQueries so it is run on the main thread.
The log, after running, includes:-
2022-03-12 08:16:12.556 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
2022-03-12 08:16:12.558 I/chatty: uid=10132(a.a.so71429144javaroomidreturnedaszero) identical 1 line
2022-03-12 08:16:12.561 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
2022-03-12 08:16:12.568 D/GAMEBOOKINFO: Gamebook is Gamebook1 id is 1
2022-03-12 08:16:12.572 D/GAMEBOOKINFO: Gamebook is Gamebook2 id is 2
2022-03-12 08:16:12.574 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
Note how the first just returns the same object and thus id.

Automapper to Map two List Classes of different structure and also Memberwise explicit Mapping

I have 4 classes namely ClassA, ClassADto, ClassAA(inner class to ClassA) and the final Result class.
ClassAA
{
public int HouseNumber{get;set;}
public string StreetName{get;set;}
public string State{get;set;}
}
ClassA
{
public int Age{get;set;}
public string Name{get;set;}
public ClassAA AObj[get;set;}
}
ClassADto
{
public int Age{get;set;}
public string Name{get;set;}
}
class Result
{
public string StreetName{get;set;}
public int TotalCount{get;set;}
public int TodaysDate{get;set;}
public List<ClassADto> AObjectsList{get;set;}
}
Now my aim is map the 'Result' class with the List of ClassA object to fill it the property 'AObjectsList' as below:
Result data= map mapper.map>(obj);
Also at the same time in automapper i want to use custom function either using 'Resolve' or 'AfterMap' to set properties like 'TodaysDate' to current datetime of system and property 'TotalCount' by counting the number of data.
I tried in many ways using 'CreateMap' and also used 'ForMembers' as from 'classAA' we only need the 'StreetName' but it didn't work. Need some help please.
One time typing approach ;)
public static Result ToResult(this List<ClassA> users)
{
return new Result
{
TotalCount = users.Count,
TodaysDate = DateTime.Today,
AObjectsList = users
.Select(user => new ClassADto
{
Name = user.Name,
Age = user.Age
})
.ToList()
};
}
// Usage
var users = new List<ClassA> { new ClassA(), new ClassA() };
var result = users.ToResult();

Error when trying to fetch changes for class extending HashMap

I'm trying a simple test where I try to commit an object of a Class that extends HashMap. I'm left with a MANAGED_CLASS_MAPPING_ERROR: given javaClass 'class com.vehco.Configuration' is mapped to MapType, expected ManagedType. Do Javers not support is-a but only has-a?
Been reading the documentation forwards and backwards but unable to find anything. Google was neither my friend this time around.
Please find the test code below:
Tester.java:
public class Tester {
Javers javers = JaversBuilder.javers().build();
public static void main(String[] args) {
Tester t = new Tester();
t.start();
}
private void start() {
Configuration data = new Configuration("global");
for (Integer i = 0; i < 10; i++) {
data.getProp().put(i.toString(), UUID.randomUUID().toString());
}
javers.commit("svenie",data);
for (Integer i = 0; i < 10; i++) {
if (i % 2 == 0)
data.getProp().put(i.toString(), UUID.randomUUID().toString());
}
javers.commit("svenie",data);
List<Shadow<Configuration>> changes = javers.findShadows(QueryBuilder.byClass(Configuration.class).build());
for (Shadow<Configuration> change : changes) {
System.out.println(change.getCommitMetadata());
}
}
}
Configuration.java:
public class Configuration extends HashMap<String,String> {
#Id
private String name;
private Properties prop = new Properties();
public Configuration(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Properties getProp() {
return prop;
}
}
Looks like the Javers type inferring is ambiguous here. On the one hand your class extends Map so JaVers can infer it as MapType, on the other hand it has #Id property, so JaVers can infer it as Entity. I think you have to decide which Javers type is better for your class and register this class explicitly using JaversBuilder.register*()?
A class can't be mapped to more than one Javers type.

managedbean live cycle listener

I use a managed bean with a view scoped because I need it ( for a p:datatable with lazy and selection modes ), but i'd like to do some things into this managed bean at the render response phase, is there a convenient way to do that ?
I need a view scoped to save a lazyDataModel, but I noticed that the rowCount method is called several times, each time a request ( Select count ) is executed.
So I deciced to save the result of the request. But if I add a data and refresh the datatable with ajax, the row count still contain the same result because of the scope. I use a boolean to know if the rowcount method has been already executed and I can set this boolean to false each time I add or remove a data but if I could do that a the render response phase it would be more convenient for me.
#ManagedBean
#ViewScoped
public class ListBean {
protected MyLazyDataModel myLazyDataModel = new MyLazyDataModel();
public MyLazyDataModel getMyLazyDataModel() {
return myLazyDataModel;
}
public void reloadList() {
this.reloadList = true;
}
protected class MyLazyDataModel extends LazyDataModel{
private int rowCount ;
#Override
public List load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, Object> filters) {
...
}
#Override
public Object getRowKey(Type object) {
...
}
#Override
public Type getRowData(String rowKey) {
...
}
#Override
public int getRowCount() {
if( reloadList ) {
this.rowCount = getDao().getRowCount().intValue();
reloadList = false;
}
return rowCount;
}
}
}
Thanks.

Neo4j eager/lazy loading with Spring data

I'm investigating Neo4j and have a question with regards to object eager/lazy loading. Lets say I have class Trolley with has Set<Item> (with getters/setters). If I do the following:
Trolley t = new Trolley(...); // create empty trolley
t.addItem(f); // add one item to the trolley
t.persist(); // persist the object
I then later find the trolley based on the nodeId:
repo.findOne(xxx); // returns the trolley successfully
When I do something like:
trolley.getItems().size()
this is empty. I guess this is the intended behaviour. Is there any mechanism similar to JPA where is the session/tx is open to load the collection dynamically.
Code:
#NodeEntity
public class Trolley
{
#Indexed
private String name;
#RelatedTo
private Set<Item> items;
public Trolley(){}
public Trolley(String name)
{
this.name = name;
}
public void addItem(Item item)
{
this.items.add(item);
}
public Set<Item> getItems()
{
return items;
}
}
#NodeEntity
public class Item
{
private String name;
public Item(){}
public Item(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
}
#Test
public void trolleyWithItemPersist()
{
Trolley trolley = new Trolley("trolley1").persist();
// Persisting - however I would've expected a cascade to
// occur when adding to the collection.
Item item = new Item("item1").persist();
// now add to the trolley
trolley.addItem(item);
// persist
trolley.persist();
// Now use repo to get trolley
Trolley loadedTrolley = trolleyRepository.findOne(trolley.getNodeId());
// should have one item
assertEquals(1, loadedTrolley.getItems().size());
}
Afaik, in Spring Data Jpa, to populate an lazy loaded field you need to annotate the method which call the findOne(xxx) with
#Transactional
from (org.springframework.transaction.annotation.Transactional)
Maybe it works also with neo4j...
I'm not really an skilled developper on Spring Data but this is the only way I know to retrieve lazy loaded fields. If someone has a better solution, feel free to write it!

Resources