Android Room Persistence Library - Many to Many Relation - database

I am trying to achieve many-to-many relationship in Room Persistence Library. I am trying to create a Notes App with Tags.
The idea is:
A note will have multiple tags.
A tag will have multiple notes.
Show all notes in RecyclerView along with Tags.
To achieve this, I have created two models Note.java, Tag.java and TagJoin model to store the relationship b/w notes and tags. Achieving one-to-one is very easy using #Relation annotation.
Here are my models
#Entity(tableName = "notes")
public class Note {
#PrimaryKey
#NonNull
public final String id;
#ColumnInfo(name = "note")
public String note;
#Ignore
public List<Tag> tags;
#Ignore
public Note(String note) {
this(UUID.randomUUID().toString(), note);
}
public Note(String id, String note) {
this.id = id;
this.note = note;
}
public String getId() {
return id;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public List<Tag> getTags() {
return tags;
}
public void setTags(List<Tag> tags) {
this.tags = tags;
}
#Entity(tableName = "note_tag_join",
primaryKeys = {"noteId", "tagId"},
foreignKeys = {
#ForeignKey(
entity = Note.class,
parentColumns = "id",
childColumns = "noteId",
onDelete = CASCADE),
#ForeignKey(
entity = Tag.class,
parentColumns = "id",
childColumns = "tagId",
onDelete = CASCADE)},
indices = {
#Index(value = "noteId"),
#Index(value = "tagId")
}
)
public static class TagJoin {
#NonNull
public final String noteId;
#NonNull
public final String tagId;
public TagJoin(String noteId, String tagId) {
this.noteId = noteId;
this.tagId = tagId;
}
}
}
Tags Model:
#Entity(tableName = "tags", indices = {#Index(value = "name", unique = true)})
public class Tag {
#PrimaryKey
#NonNull
public String id;
#ColumnInfo(name = "name")
public String name;
#Ignore
public Tag(String name) {
this(UUID.randomUUID().toString(), name);
}
public Tag(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Notes Dao:
#Dao
public interface NoteDao {
#Query("SELECT * FROM notes ORDER BY id DESC")
LiveData<List<Note>> getAllNotes();
#Insert
long insert(Note note);
#Update
void update(Note note);
#Delete
void delete(Note note);
#Query("DELETE FROM notes")
void deleteAll();
#Query("SELECT COUNT(*) FROM notes")
int getNotesCount();
#Query("SELECT notes.* FROM notes\n" +
"INNER JOIN note_tag_join ON notes.id=note_tag_join.noteId\n" +
"WHERE note_tag_join.tagId=:tagId")
List<Note> getAllNotesOfTag(String tagId);
#Insert
void insert(Note.TagJoin... joins);
#Delete
void delete(Note.TagJoin... joins);
}
So far everything is good. Now I want to show the Notes in RecyclerView but I can't find a way to fetch all Notes along with Tags at once. One way is, getting the tags of each note in onBindViewHolder method which I think is wrong as we have to query the db each time row is displayed.
Please provide me suggestions.
PS: I have followed the code provided in this article
https://commonsware.com/AndroidArch/previews/mn-relations-in-room

Related

Can't properly display data in tableView in JavaFX

I was trying to add data to my tableView in my JavaFX app. I am using hibernate to do operations on my Database. I used a query to get all the orders and store each order in an object and added the object to the observable list of the tableView. I created the orders class and mapped it to my database. This is the class of the orders:
#Entity
#Table(name = "orders")
public class orders implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private int order_id;
#JoinColumn(name = "item_id")
#ManyToOne
#NotNull
private items item_id;
#Column(name = "quantity")
#NotNull
private int quantity;
#Column(name = "price_per_unit")
#NotNull
private double price_per_unit;
#Column(name = "total_price")
#NotNull
private double total_price;
#Column(name = "order_date")
#NotNull
private Date order_date;
#JoinColumn(name = "user_id")
#ManyToOne
#NotNull
private users user_id;
public orders() {
}
public orders(int order_id, items item_id, int quantity, double price_per_unit, double total_price, Date order_date, users user_id) {
this.order_id = order_id;
this.item_id = item_id;
this.quantity = quantity;
this.price_per_unit = price_per_unit;
this.total_price = total_price;
this.order_date = order_date;
this.user_id = user_id;
}
public int getOrder_id() {
return order_id;
}
public void setOrder_id(int order_id) {
this.order_id = order_id;
}
public items getItem_id() {
return item_id;
}
public void setItem_id(items item_id) {
this.item_id = item_id;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getPrice_per_unit() {
return price_per_unit;
}
public void setPrice_per_unit(double price_per_unit) {
this.price_per_unit = price_per_unit;
}
public double getTotal_price() {
return total_price;
}
public void setTotal_price(double total_price) {
this.total_price = total_price;
}
public Date getOrder_date() {
return order_date;
}
public void setOrder_date(Date order_date) {
this.order_date = order_date;
}
public users getUser_id() {
return user_id;
}
public void setUser_id(users user_id) {
this.user_id = user_id;
}
}
And the below code is the code of the view in which I have the tableView that loads the orders and displays the orders from the database:
public class OrdersPageController implements Initializable {
private Main app;
private Session session;
private Transaction transaction = null;
#FXML
private TableView<orders> table;
public void setApp(Main app) {
this.app = app;
}
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
session = HibernateUtil.getSessionFactory().openSession();
transaction = session.beginTransaction();
//Fill the table view
getOrders();
}
public void goBack(ActionEvent event){
session.close();
transaction = null;
app.goToHomePage();
}
public void processLogout(ActionEvent event){
session.close();
transaction = null;
app.userLogout();
}
public void addOrder(ActionEvent event){
session.close();
transaction = null;
app.addOrdersPage();
}
public void deleteOrder(ActionEvent event){
session.close();
transaction = null;
app.closeOrdersPage();
}
public void getOrders(){
try{
String hql = "FROM orders";
Query query = session.createQuery(hql);
List<orders> list = query.getResultList();
for (orders o : list) {
//Create an order object
orders order = new orders();
order.setOrder_id(o.getOrder_id());
order.setItem_id(o.getItem_id());
order.setPrice_per_unit(o.getPrice_per_unit());
order.setQuantity(o.getQuantity());
order.setOrder_date(o.getOrder_date());
order.setTotal_price(o.getTotal_price());
order.setUser_id(o.getUser_id());
//Create an observable list for the table
ObservableList<orders> tableList = table.getItems();
//Add the order object to the list
tableList.add(order);
//Set the created list to the table to show data
table.setItems(tableList);
}
}catch(Exception e){
System.out.println(e.getMessage());
}
finally{
session.close();
}
}
}
Note that the getOrders method is the method that gets the orders from the database and sets the observable list of the tableView.
I am having problem displaying the item_id and the user_id of the order. I think the problem is that they both are objects of type items and users respectively and the table displays the address of the objects. Instead I want to display the numbers of the ids of the item ordered and the user that made the order. If you know what I can do to fix my problem please share it with me.
Add cellFactorys to the relevant columns. You haven't shown the FXML in the question, so I don't know the names you assigned to the appropriate TableColumn instances, but you can do something like this:
public class OrdersPageController implements Initializable {
// ...
#FXML
private TableView<orders> table;
#FXML
private TableColumn<orders, users> userColumn ;
#Override
public void initialize(URL url, ResourceBundle rb) {
userColumn.setCellFactory(tc -> new TableCell<>() {
#Override
protected void updateItem(users user, boolean empty) {
super.updateItem(user, empty);
if (empty || user == null) {
setText("");
} else {
String text = /* anything you need based on user */
setText(text);
}
}
});
session = HibernateUtil.getSessionFactory().openSession();
transaction = session.beginTransaction();
//Fill the table view
getOrders();
}
}
Just override toString method in users and items Classes:
Example: in your users Class ->
#Override
public String toString() {
return user_id.toString();
}
As James_D stated, have a look on java conventions. Java Classes should be always be with Capital Letter.

Spring MVC + AngularJs: JSON/Model values set to null

I am trying submit json data to a Spring MVC controller mapped with a model. Instead of getting the json values, the values of the fields of the model are all NULL.
IDE debugger:
Chrome:
Exception:
org.springframework.dao.InvalidDataAccessApiUsageException: The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!
Controller:
#RequestMapping(value = "/update", method = RequestMethod.POST)
#ResponseBody
public PostResponse update(Setting setting, BindingResult bindingResult) {
return settingService.processUpdate(setting, bindingResult, messageSource);
}
JSON data:
{
"updatedAt":1460600207000,
"id":1,
"createdBy":null,
"description":"This is a setting",
"code":"MY_SETTING",
"value":"{\"id\":\"1018\",\"title\":\"Another setting\",\"code\":\"220-203-10-101\"}"
}
Model:
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Setting {
#Id
#GeneratedValue
#Column
private Integer id;
#Column(unique = true)
private String code;
#Column
private String description;
#Column
private String value;
#Temporal(TemporalType.TIMESTAMP)
#Column(nullable = false)
private Date createdAt;
#Temporal(TemporalType.TIMESTAMP)
#Column(nullable = false)
private Date updatedAt;
#NotFound(action = NotFoundAction.IGNORE)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="FK_createdByUserId")
private User createdBy;
public Setting() {}
public Setting(String code, String description, String value, Date createdAt, Date updatedAt, User createdBy) {
this.code = code;
this.description = description;
this.value = value;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.createdBy = createdBy;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public User getCreatedBy() {
return createdBy;
}
public void setCreatedBy(User createdBy) {
this.createdBy = createdBy;
}
I guess that the setting bean is not mapped at all.
You need to tell spring how to map the http request to the method arguments. If you're posting data, the best way is to add #RequestBody annotation to the relevant method argument (setting in your case)
Modify your controller method like this:
#RequestMapping(value = "/update", method = RequestMethod.POST)
#ResponseBody
public PostResponse update(#RequestBody Setting setting, BindingResult bindingResult) {
return settingService.processUpdate(setting, bindingResult, messageSource);
}

JsonMappingException: Infinite recursion in Cloud Endpoints

I created an app server using Google Endpoints, it is a backend for an instant messaging app. Every user has a list of friends.
When I create a new friend, I am adding users to each other's friend list using the method below. However, it is giving me following error when I add a friend, because of circular dependency.
I looked at other questions and solutions posted. Most of them are structured differently and they didn't solve my problem.
One answer in this website recommends adding #JSONIgnore, but I don't have any field to add that. I tried to put #JsonManagedReference but I couldn't figure out where to put #JSONBackReference. Other examples on this website, usually have another field that refers to parent, but I don't have it.
Thanks for your help in advance.
Error 500 com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException:
Infinite recursion (StackOverflowError) (through reference chain: com.google.common.collect.TransformingRandomAccessList[0]-
>com.net.myapplication.backend.model.User[\"friends\"]->com.google.common.collect.TransformingRandomAccessList[0]-
>com.net.myapplication.backend.model.User[\"friends\"]-
addFriend method
#ApiMethod(name = "addFriend", httpMethod = "post")
public User addFriend(#Named("regId") String regId, #Named("email") String email) {
User user = findRecord(regId);
User friend = findRecordByEmail(email);
if (user == null){
log.info("User " + regId + " is not registered.");
} else{
if (friend == null){
log.info("User " + email + " is not registered.");
} else{
if (hasFriend(user, friend)){
log.info("User " + email + " is already a friend.");
} else {
user.getFriendsRef().add(Ref.create(friend));
friend.getFriendsRef().add(Ref.create(user));
ofy().save().entity(friend).now();
ofy().save().entity(user).now();
return friend;
}
}
}
return null;
}
User model
#Entity
public class User {
#Id
Long id;
#Index
private String regId;
private String firstName;
private String lastName;
#Index
private String email;
private Language language;
#Load
private List<Ref<User>> friends = new ArrayList<>();
public User() {}
public User(Long id, String regId, String firstName, String lastName, String email, Language language, List<Ref<User>> friends) {
this.id = id;
this.regId = regId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.language = language;
this.friends = friends;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRegId() {
return regId;
}
public void setRegId(String regId) {
this.regId = regId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Language getLanguage() {
return language;
}
public void setLanguage(Language language) {
this.language = language;
}
public List<User> getFriends() {
return Deref.deref(friends);
}
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
public List<Ref<User>> getFriendsRef(){
return friends;
}
public void setFriends(ArrayList<Ref<User>> friends) {
this.friends = friends;
}
}
It sounds like this question is: "How do I return data structures with cycles through Cloud Endpoints?"
If it serializes data with vanilla Jackson (Jackson 1.x, from the look of that stacktrace), you can't. Jackson 2 has support for JSOG, but that would mean abandoning Cloud Endpoints:
https://github.com/jsog/jsog
https://github.com/jsog/jsog-jackson

How to delete entity by Id from data-store in AppEngine?

I created an API to delete an Entity by its key however I'm gettin Http 204 and Entity does not delete from data-store.
This is my API,
#ApiMethod(name = "deleteContact", path = "contact", httpMethod = ApiMethod.HttpMethod.DELETE)
public void deleteContact(final #Named("id") long contactId)
{
ofy().delete().type(Contact.class).id(contactId).now();
}
and my Contact class is like this:
#Entity
#Cache
public class Contact
{
#Id
private long id;
#Index
private String cName;
private Email cEmail;
private PhoneNumber cPhoneNumber;
// private key, to connect this Entity to Profile Entity
#Parent
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
private Key<Profile> profileKey;
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
private String profileId;
// default constructor is private
private Contact()
{
}
public Contact(final long id, final String profileId, final ContactForm contactForm)
{
Preconditions.checkNotNull(contactForm.getUserName(), "The name is required");
this.id = id;
this.profileKey = Key.create(Profile.class, profileId);
this.profileId = profileId;
updateWithContactForm(contactForm);
}
/**
* Updates the Contact with ContactForm.
* This method is used upon object creation as well as updating existing Contact.
*
* #param contactForm contains form data sent from the client.
*/
public void updateWithContactForm(final ContactForm contactForm)
{
this.cName = contactForm.getUserName();
this.cEmail = contactForm.getUserEmailAddress();
this.cPhoneNumber = contactForm.getUserPhoneNumber();
}
public long getId() {
return id;
}
public String getcName() {
return cName;
}
public Email getcEmail() {
return cEmail;
}
public PhoneNumber getcPhoneNumber() {
return cPhoneNumber;
}
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
public Key<Profile> getProfileKey() {
return profileKey;
}
// Get a String version of the key
public String getWebSafeKey()
{
return Key.create(profileKey, Contact.class, id).getString();
}
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
public String getProfileId() {
return profileId;
}
#Override
public String toString() {
return "Contact{" +
"id=" + id +
", cName='" + cName + '\'' +
", cEmail=" + cEmail +
", profileId='" + profileId + '\'' +
", cPhoneNumber=" + cPhoneNumber +
'}';
}
}
Any idea would be appreciated.
You've got a parent associated with your class Contact.
// private key, to connect this Entity to Profile Entity
#Parent
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
private Key<Profile> profileKey;
In datastore, Contact entities are stored as:
/User1Profile/SomeContact1
/User1Profile/SomeContact2
Datastore can't search any entity with just the ID of contact (i.e. "SomeContact1") but it can search if you provide parent as well. The right way to delete would be:
ofy().delete().type(Contact.class).parent(profileKey).ids(contactId).now();
Read this for more details: https://code.google.com/p/objectify-appengine/wiki/BasicOperations#Deleting

JAVA Google App Engine + Facebook API + GSON = Trouble with Javabean

I am trying to get the user's friends list from Facebook.
The problem seems to be the Javabean...
FBUser fbuser = new Gson().fromJson(jsonStr, FBUser.class);
public class FBUser implements Serializable {
private static final long serialVersionUID = -3154429420153433117L;
private String id;
private String name;
private String email;
private Friends friendsList = new Friends();
private FBUser() { }
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Data> getFriendsList() {
return friendsList.getData();
}
public static class Friends implements Serializable {
private static final long serialVersionUID = 6991758772193514527L;
private List<Data> data;
private Friends() { }
public List<Data> getData() {
return data;
}
public void setData(List<Data> data) {
this.data = data;
}
public class Paging implements Serializable {
private static final long serialVersionUID = 1689816298710621080L;
private String next;
private Paging() { }
public String getNext() {
return next;
}
public void setNext(String next) {
this.next = next;
}
}
}
public class Data implements Serializable {
private static final long serialVersionUID = -5008541658519841090L;
private String id;
private String name;
private Data() { }
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
Json:
json: {"id":"10861234","name":"Whatever","email":"whatever\u0040gmail.com","friends":{"data":[{"name":"Someone","id":"10861234"},{"name" ...43"}],"paging":{"next":"https:\/\/graph.facebook.com\/10861234\/friends..."}}}
The fields ID, Name and Email I can retrieve succesfully... but the friendsList is null... =(
Maybe it is the way I am trying to get it from the nested class, any suggestions on that?
There is no friendsList in your JSON (or, there's no friends in your Java class - whichever way you'd like to look at it). Gson silently ignores anything in the JSON that is not present in your classes.
You have a field friends whose value is an object. That object has a field data which is an array of objects and a field paging which is another object.
You need to write Java classes that match that structure. You're ... close.
In your FBUser class change:
private Friends friendsList = new Friends();
to:
private Friends friends = new Friends();
or:
#SerializedName("friends")
private Friends friendsList = new Friends();
Then in your Friends class you need to add:
private Paging paging = new Paging();
Also note that you don't have to initialize these values unless you specifically don't want them to be non-null when using these classes elsewhere.

Resources