Creating like/dislike system on java/spring - java

Like/dislike system.
App has entity Post. Post has field List likes and it joins table, which has columns post_id, user_id.
When User presses button "like" app will add authenticated user in List in PostService. But I need to have the "isLiked" boolean field. This will define what the Like button will look like in frontend.
I can get value for field countLike just call method size() from field likes.
But I don't know now I can get value for field "isLiked".
Help me with it, please.
#Entity
public class Post {
//some fields...
//there I saved users, who has posed "like"
private List<User> likes;
#Transient
private int countLike;
//there I want to save status - liked/disliked;
#Transient
private boolean isLiked;
}
#Service
public class PostService {
//some fields and methods...
public void createLike(int postId, User authenticatedUser) {
Post post = postRepository.getOne(postId);
post.getLikes().add(authenticatedUser);
this.update(post);
}
}

While the approach hinted to by yourself and the comment by "Lino - Vote don't say Thanks" will work, I don't think they are a very good idea.
Working but wastful.
You can create methods like the following:
public int likeCount{}{
return getLikes().size();
}
public boolean isLiked(){
return getLikes().size() > 0;
}
The problem with that is, it will load a lot of data just for providing a single number of even just a single bit of information.
More efficient in most scenarios
Instead I recommend loading the information from the database.
Assuming you are using Hibernate as the JPA implementation you can do that with the #Formula annotation. With this the relevant code looks like this:
#Entity
public class Post {
#Formula("(select count(user_id) from Likes l where l.post_id = post_id)")
private int countLike;
public boolean isLiked(){
return getCountLike() > 0;
}
}

Related

How to get random record from one collection MongoDB

I have entity Article I'm trying to retrieve one random record from database collection.
This is entity Article:
#Data
#Document(value = "article")
public class Article {
#Id
private String articleId;
private String title;
private String description;
private String fullArticle;
This is service to save it:
#Override
public Article save(Article article) {
return articleRepository.save(article);
}
And repository:
#Repository
public interface ArticleRepository extends MongoRepository<Article, String> {
}
So, now I'm trying to create a method that will get me one random record from my collection Article also, I want to create a controller so when I go to some endpoint and submit some get method to retrieve one record from the collection and so I can check it in postman or with Swagger. I find some answers to similar question to mine but no one solved my problem, I want to have API for something like that.
You can use $sample in an aggregation query to get a random document:
db.collection.aggregate([
{
"$sample": {
"size": 1
}
}
])
Example here
I've tested the code and it works as expected.
You can create add this method into repository:
#Aggregation(pipeline={"{$sample:{size:1}}"})
AggregationResults<Article> random();
And call from service like this:
#Override
public Article random(){
return articleRepository.random().getMappedResults().stream().findFirst().orElse(null);
// also you can use .orElseThrow() or whatever you want
}

Change table on runtime - Spring API Rest

Now, I have the next entity. This one is the m1 table of my database.
#Entity(name = "m1")
#Data
public class Information {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String date;
private Double weight_1;
private Double weight_2;
private Double weight_3;
private Double weight_4;
private int working;
}
So, when I do some call to the APIRest it returns me the information corresponding to the m1 table. The controller that I have is the next (simple controller that returns all the information):
#Controller
#RequestMapping(path = "/information")
public class InformationController {
#Autowired
private InformationRepository repository;
#GetMapping(path="/all")
public #ResponseBody List<Information> getAllInformations() {
// This returns a JSON or XML with the users
return repository.findAll();
}
}
The question is: There is any way to change the name of the m1 on runtime. For example can I put the name of the table in the call path and in the API Rest take it?
Maybe this is impossible and I am doing it the bad way I do not know.
EDIT: I mean, can I change the table that the API Rest is taking the data by putting the table that I want in the url/path that I call. For example: in my case the default table/entity that the APIRest take the data is m1, so can I call http://localhost:8080/information/especifictable/all/ where especific table is the table that I want the recieve the data of the database and in the API Rest take that url parameter and change the default m1 with the especifictable.
I do not know if I have explained it well, I do not know how to explain it well.
Such a design would only make sense, if there are two tables in DB, which look the same. if that is the case there is something wrong with your DB design.
Basically it is not possible, to the best of my knowledge.

Pattern for persisting data in Realm?

My issue is how to organize the code. Let say I have a User class
public class User extends RealmObject {
#PrimaryKey
private String id;
#Required
private String name;
public User() { // per requirement of no args constructor
id = UUID.randomUUID().toString();
}
// Assume getter & setter below...
}
and a Util class is needed to handles the save in an asynchronous manner since RealmObjects cannot have methods other than getter/setter.
public class Util {
public static void save(User user, Realm realm) {
RealmAsyncTask transaction = realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.copyToRealm(user); // <====== Argument needs to be declared final in parent method's argument!
}
}, null);
}
}
The intention is to put save() in a Util class to prevent spreading similar save code all over the code-base so that every time I wanted to save I would just call it as such:
User u = new User();
u.setName("Uncle Sam");
Util.save(u, Realm.getDefaultInstance());
Not sure if this affects performance at all, but I was just going to save all fields overwriting what was there except for the unique id field every single time.
The problem is that I now need to set the "user" argument as final in the Util.save() method, which means I cannot pass in the object I need to save other than once.
Is there a different way of handling this? Maybe a different pattern? Or am I looking at this all wrong and should go back to SQLite?
Why is it a problem to set public static void save(final User user, Realm realm) ? It just means you cannot reassign the user variable to something else.
That said, the existence of a save() method can be a potential code smell as you then spread the update behaviour across the code base. I would suggest looking into something like the Repository pattern (http://martinfowler.com/eaaCatalog/repository.html) instead.
Realm is actually working on an example showing how you can combine the Model-View-Presenter architecture with a Repository to encapsulate updates which is a good pattern for what you are trying to do here. You can see the code for it here: https://github.com/realm/realm-java/pull/1960

Play framework 2 (Java) form data binding with nested allowed fields

Consider following model:
public class Contact {
#Required
public String name;
#Valid
public List<Information> informations;
}
public static class Information {
public String securedField;
#Required
public String email;
#Valid
public List<Phone> phones;
public static class Phone {
#Required
#Pattern(value = "[0-9.+]+", message = "A valid phone number is required")
public String number;
}
}
}
I don't want Information securedField to be affected by mass assignment vulnerability. So i decided to set array of allowedFields for Contact Form.
As i know, play forms are based on Spring DataBinder, so is it possible to handle collection fields? I don't want to write smth like:
name
informations[0].email
informations[0].phones*
informations[1].email
informations[1].phones*
etc
Following doesn't work:
name
informations.email
informations.phones*
Should i extend existing Spring DataBinder and Form classes and override bind method in this case?
Here's an arguably simpler solution. How about defining an extra constraint that will trigger a validation failure if the POST data contains any informations[%d].securedField values?
import javax.validation.constraints.Null;
public static class Information {
#Null
public String securedField;
...
}
I think that this way you can call the default bindFromRequest method instead of the one that accepts a whitelist of form field names, and still be protected against a mass assignment attack.
One shortcoming with this approach admittedly is that it would ultimately leak the names of your internal fields in the event of a concerted mass assignment attack. However if they had fairly bland, meaningless names such as securedField (no offence intended!), I'm not sure how this information could be exploited by an attacker.
Edit
If you want to allow assignment to the field based on the current user type, maybe bean validation groups could help:
import javax.validation.constraints.Null;
public class Contact {
public interface Administrator {}
public interface User {}
...
public class Information {
#Null(groups = User.class)
public String securedField;
...
}
}
Controller code
...
final Form<Contact> contactForm;
if (currentUser.isAdministrator()) {
contactForm = form(Contact.class, Administrator.class).bindFromRequest();
} else {
contactForm = form(Contact.class, User.class).bindFromRequest();
}
...
If I understand your question correctly, you can use the following patterns to whitelist nested collection fields:
informations[*].email
informations[*].phones[*].*
i.e.
form.bindFromRequest("name", "informations[*].email", "informations[*].phones[*].*");

Bi-directional one-to-many relationship does not work

I have made an application that displays a lot of questions from my database. For this I have made a question entity. I want to be able to "report" a question for being poor/good and so on, so for this I made a feedback entity.
The relationship between these would be: one question may have many feedbacks, and one feedback belongs to one question.
The problem is that when I save the question feedback instance it all maps perfectly in the database, but when I open a question and loops through all the feedbacks none of the feedbacks added is displayed. In order to have them displayed I need to re-deploy the web application.
Why does this happen?
For readability I only show the parts involved
QuestionFeedback entity
public class QuestionFeedback implements Serializable {
#ManyToOne
private Question question;
....
public void setQuestion(Question question) {
this.question = question;
if (!question.getFeedbacks().contains(this)) {
question.getFeedbacks().add(this);
}
}
....
}
Question entity
#Entity
public class Question implements Serializable {
#OneToMany(mappedBy = "question", fetch = FetchType.EAGER)
private List<QuestionFeedback> feedbacks;
public Question() {
feedbacks = new ArrayList<QuestionFeedback>();
}
public void addFeedback(QuestionFeedback questionFeedback) {
if (!getFeedbacks().contains(questionFeedback)) {
getFeedbacks().add(questionFeedback);
}
if (questionFeedback.getQuestion() != this) {
questionFeedback.setQuestion(this);
}
}
}
Backing bean for the report page
The question entity is already retrieved from the database.
public String flag() {
questionFeedback.setQuestion(question);
questionFeedbackService.persist(questionFeedback);
return "index";
}
DAO class
public void persist(QuestionFeedback questionFeedback) {
entityManager.persist(questionFeedback);
}
This is a simple instance of having a dirty session.
Although these can be caused by all sorts of issues, there are usually 2 simple things to keep in mind that will make it very easy to track this bug down .
First you must always remember that, when we persist our data in JPA/hibernate , we don't necessarily have any gaurantee that the transaction has completed in the database. The true meaning of the "persist" method is a common source of errors and questions, make sure you fully understand it and how it relates to your business logic. :
What's the advantage of persist() vs save() in Hibernate?
Second, after you have gauranteed that the transaction has been completed and data has been saved, you can use the EntityManager.refresh method to update the state of any objects from the database.
You can clear the JPA cache through the following code:
em.getEntityManagerFactory().getCache().evictAll();
For the record, I always flush after persisting data. Even though your database has the data, I would just try this.
public String flag() {
questionFeedback.setQuestion(question);
questionFeedbackService.persist(questionFeedback);
questionFeedbackService.flush();
return "index";
}

Categories