How to show related objects in a jstl using a spring? - java

I have 3 objects: User, Comment and StatusUpdate(news). This is the User...
#Entity
#Table(name = "users")
#PasswordMatch(message = "{register.repeatpassword.mismatch}")
public class SiteUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "email", unique = true)
#Email(message = "{register.email.invalid}")
#NotBlank(message = "{register.email.invalid}")
private String email;
#Transient
#Size(min = 5, max = 15, message = "{register.password.size}")
private String plainPassword;
#Column(name = "password", length = 60)
private String password;
#Column(name = "enabled")
private Boolean enabled = false;
#NotNull
#Column(name = "firstname", length = 20)
#Size(min = 2, max = 20, message = "{register.firstname.size}")
private String firstname;
#NotNull
#Column(name = "surname", length = 25)
#Size(min = 2, max = 25, message = "{register.surname.size}")
private String surname;
#Transient
private String repeatPassword;
#Column(name = "role", length = 20)
private String role;
public SiteUser() {
}
Here comes the StatusUpdate(you can call it piece of news or article). That has a site user that is the one who has created that article.
#Entity
#Table(name = "status_update")
public class StatusUpdate {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(min=5, max=255, message="{addstatus.title.size}")
#Column(name = "title")
private String title;
#Size(min=5, max=5000, message="{addstatus.text.size}")
#Column(name = "text")
private String text;
#Column(name = "added")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern="yyyy/MM/dd hh:mm:ss")
private Date added;
#OneToOne(targetEntity = SiteUser.class)
#JoinColumn(name="user_id")
private SiteUser siteUser;
#PrePersist
protected void onCreate() {
if (added == null) {
added = new Date();
}
}
public StatusUpdate() {
}
And the Comment which can be done by any registered user, right? As you will notice the Comment has no User object to avoid circular references.
#Entity
#Table(name = "comments")
public class Comment {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "statusupdateid")
private StatusUpdate statusUpdate;
#Column(name = "commenttext")
private String commenttext;
#Column(name = "commentdate")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern = "yyyy/MM/dd hh:mm:ss")
private Date commentdate;
#Column(name = "userid")
private Long userid;
public Comment() {
}
Now I would like to show in my JSP an article, with all the related comments and each of them belong to a different user. Can I use a HashMap to relate the users and their comments? I do not see how.
#RequestMapping(value ="/viewonestatus/{id}")
public ModelAndView viewOneStatus(#PathVariable("id") Long id) {
StatusUpdate status = statusUpdateService.get(id);
int countComments = commentService.countStatusComments(status);
List<Comment> comments = commentService.readAllComments(status);
ModelAndView modelAndView = new ModelAndView();
for (Comment comment: comments){
SiteUser user = userService.get(comment.getUserid());
modelAndView.getModel().put("user", user);
}
modelAndView.getModel().put("commentscounter", countComments);
modelAndView.getModel().put("status", status);
modelAndView.getModel().put("comments", comments); //!!
modelAndView.setViewName("app.viewonestatus");
return modelAndView;
}
As you expect, when my JSP shows just one user (the last one) for all the comments, but I can not relate all the Comments with the corresponding Users
<table class="table table-hover">
<c:forEach var="comment" items="${comments}">
<tr>
<td>
<div class="col-sm-2 sm-margin-bottom-40">
<img class="img-responsive profile-img margin-bottom-20" id="profilePhotoImage" src="/profilephoto/${comment.userid}" />
</div>
<h4>
${user.firstname} ${user.surname}
<span>
<!-- <span>${counterUserMap[comment.key]}</span> -->
5 hours ago / Reply
</span>
</h4>
<p>
<fmt:formatDate pattern="EEEE d MMMM y 'at' H:mm:ss" value="${comment.commentdate}" />
</p>
<p>${comment.commenttext}</p>
</td>
</tr>
</c:forEach>
I do not want to use JSON. I'm thinking about an anonymous class with all the stuff inside. Well, I'm open to your thoughts. Thanks.

Shokulei answer was the solution:
Since you have the userid, you can link it using the #ManyToOne annotation. This would be the most ideal way. But if you really don't want to link them, then you can create a new #Transient SiteUser siteUser; attribute in Comment class. And then in your for loop, you can use comment.setSiteUser(user); instead of modelAndView.getModel().put("user", user);. Hope this will help.
Thanks Shokulei

Related

Data from entity are not showing on page from Thymeleaf

I have this table:
#Entity
#Table(name = "transaction")
public class Transaction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "transaction_id")
private Long id;
#Column(name = "user_id", nullable = false)
private Long userId;
#Column(name = "wallet_name", nullable = false)
private String walletName;
#NotNull(message = "Please, insert a amount")
#Min(value = 0, message = "Please, insert a positive amount")
private Double amount;
private String note;
#DateTimeFormat(pattern = "yyyy-MM-dd")
#Column(name = "date")
private LocalDate date;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "wallet_id", nullable = false)
private Wallet wallet;
#Enumerated(EnumType.STRING)
#Column(name = "transaction_type", columnDefinition = "ENUM('EXPENSE', 'INCOME')")
private TransactionType transactionType;
#Nullable
#Enumerated(EnumType.STRING)
#Column(name = "expense_categories", columnDefinition = "ENUM('FOOD_AND_DRINK', 'SHOPPING', 'TRANSPORT', 'HOME'," +
" 'BILLS_AND_FEES', 'ENTERTAINMENT', 'CAR', 'TRAVEL', 'FAMILY_AND_PERSONAL', 'HEALTHCARE'," +
" 'EDUCATION', 'GROCERIES', 'GIFTS', 'BEAUTY', 'WORK', 'SPORTS_AND_HOBBIES', 'OTHER')")
private ExpenseCategories expenseCategories;
#Nullable
#Enumerated(EnumType.STRING)
#Column(name = "income_categories", columnDefinition = "ENUM('SALARY', 'BUSINESS', 'GIFTS', 'EXTRA_INCOME', 'LOAN', 'PARENTAL_LEAVE', 'INSURANCE_PAYOUT', 'OTHER')")
private IncomeCategories incomeCategories;
Because, I want to display transactions by date on page I created a separated class like this:
public class TransactionGroup {
private LocalDate date;
private List<Transaction> transactions;
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public List<Transaction> getTransactions() {
return transactions;
}
public void setTransactions(List<Transaction> transactions) {
this.transactions = transactions;
}
public void setTransactions(String walletName, Double amount, String note, LocalDate date, TransactionType transactionType, ExpenseCategories expenseCategories, IncomeCategories incomeCategories) {
}
}
So, as you can see I have a list of transactions. In my controller where I'm saving transaction I have this:
#PostMapping("/saveIncome/{walletId}")
public String saveIncome(#PathVariable(value = "walletId") long walletId, #Valid Transaction transaction, BindingResult result, Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
long userId = user.getId();
Wallet wallet = walletService.getWalletById(walletId);
TransactionGroup transactionGroup = new TransactionGroup();
boolean thereAreErrors = result.hasErrors();
if (thereAreErrors) {
model.addAttribute("incomeCategories", IncomeCategories.values());
return "income_transaction";
}
transaction.setWallet(wallet);
transaction.setUserId(userId);
transaction.setWalletName(wallet.getWalletName());
transactionGroup.setTransactions(transaction.getWalletName(), transaction.getAmount(), transaction.getNote(), transaction.getDate(), transaction.getTransactionType(), transaction.getExpenseCategories(), transaction.getIncomeCategories());
transactionService.saveIncome(transaction, walletId, userId);
return "redirect:/api/wallet/userWallet/balance/" + userId;
}
This is the thing:
transactionGroup.setTransactions(transaction.getWalletName(), transaction.getAmount(), transaction.getNote(), transaction.getDate(), transaction.getTransactionType(), transaction.getExpenseCategories(), transaction.getIncomeCategories());
And that works fine, transaction is saved. Now when I want to show data from that entity like this:
<div th:each="group : ${transactionGroup}">
<h1 th:text="${group.date}"/>
<div th:each="transaction : ${group.transactions}">
<h2>Amount: <span th:text="${transactions.amount}"></span></h2><br>
<h2>Note: <span th:text="${transactions.note}"></span></h2><br>
<h2>Wallet name: <span th:text="${transactions.walletName}"></span></h2><br>
<h2>Expense Category: <span th:text="${transactions.expenseCategories}"></span></h2><br>
<h2>IncomeCategory: <span th:text="${transactions.incomeCategories}"></span></h2>
<div>
</div>
</div>
</div>
I'm getting a blank page without any transaction data.
I already asked a question similar to this, you can see it from my profile. Idk what to try more and why data is not displayed so far.

How to import a CSV file to MSSQL using Springboot

I am working on a project that has a function that allows users to import a file (Excel or CSV) to DB (MsSQL). I have read tutorials on the internet and followed them, but the problem is one of my entities contains an object.
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "question_bank")
public class QuestionBank implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column
private String content;
#Column
private String explanation;
#Column
private String option1;
#Column
private String option2;
#Column
private String option3;
#Column
private String option4;
#Column
private String answer;
#ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name = "status_id")
private Status status;
#ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name = "levelId")
private QuizLevel quizLevel;
The status_id and levelId are from joining other columns of other tables. And this is the code that I use to set the data from Excel file
questionBank.setAnswer(excelData.get(i));
questionBank.setContent(excelData.get(i + 1));
questionBank.setExplanation(excelData.get(i + 2));
questionBank.setOption1(excelData.get(i + 3));
questionBank.setOption2(excelData.get(i + 4));
questionBank.setOption3(excelData.get(i + 5));
questionBank.setOption4(excelData.get(i + 6));
questionBank.setStatus(Integer.valueOf(excelData.get(i + 8)));
questionBank.setCourse(Integer.valueOf(excelData.get(i + 9)));
questionBank.setQuizLevel(Integer.valueOf(excelData.get(i + 10)));
The IDE said the last 3 lines, setStatus, setCourse and setQuizLevel are errors because there are no functions like that in Entity QuestionBank.
How can I do this import, thank you if you are reading and have a solution for me?
For the last object I guess you will have to construct an instance of QuizLevel, fill it with values then pass it to your setter.
I do not know how your csv is structured, but if you isolate the values related to QuizLevel then pass it to your QuestionBank instance;
QuizLevel quizLevel= new QuizLevel();
quizLevel.setValue(myValueFromCsv)
quizLevel.setOtherValue(myOtherValueFromCSV)
questionBank.setQuizLevel(quizLevel);
Same goes for setCourse and setStatus.
You have to make the instance of your Status and Quizlevel object, and after that, you can access or set the values accordingly of these objects. You can't simplily set the value to a object. First create instance of object then the set or get the values associated with that object.
This example might help you properly:
Post post = new Post();
User user = userServiceImpl.getCurrentUser();
post.setTitle(title);
post.setContent(content);
post.setCreatedAt(time);
post.setAuthor(user.getName());
post.setPublishedAt(time);
post.setUpdatedAt(time);
post.setExcerpt(content);
post.setIsPublished(true);
post.setAuthorId(user.getId());
String[] tagsArray = tags.split(" ");
List<Tag> tagList = new ArrayList<Tag>();
for (String tag : tagsArray) {
Tag tagObject = new Tag();
if (tagService.checkTagWithName(tag)) {
tagList.add(tagService.getTagByName(tag));
} else {
tagObject.setName(tag);
tagObject.setCreatedAt(time);
tagObject.setUpdatedAt(time);
tagList.add(tagObject);
}
}
post.setTags(tagList);
postService.savePost(post);
This is my model class for post with a tag object in last:
#Table(name = "posts")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "title")
private String title;
#Column(name = "excerpt")
private String excerpt;
#Column(name = "content",columnDefinition="TEXT")
private String content;
#Column(name = "author")
private String author;
#Column(name = "published_at")
private Timestamp publishedAt;
#Column(name = "is_published")
private Boolean isPublished;
#Column(name = "created_at")
private Timestamp createdAt;
#Column(name = "updated_at")
private Timestamp updatedAt;
#Column(name = "authorId")
private Long authorId;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "post_tags",
joinColumns = {#JoinColumn(name = "post_id" , referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "tag_id", referencedColumnName = "id")})
private List<Tag> tags;

JPA one2many relation, how to keep the ids

I have 3 entity classes: User, Product and Fridge. The Fridge is something between User and Product. I mean that in the Fridge I store the ID of the Product, the id of the User and some quantity. And I need to see the IDs that are(stored) used for relation.
User Entity Class:
#Entity
#Table(name = "`user`")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#NotNull
#Column(name = "username")
private String username;
#NotNull
#Column(name = "password")
private String password;
#OneToMany(mappedBy="user")
#JsonManagedReference(value = "user-fridge")
private List<Fridge> fridge;
}
Product Entity Class :
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#Column(name = "name")
private String name;
#Column(name = "measurementUnit")
private Enum measurementUnit;
#Column(name = "calories")
private int calories;
#OneToMany(mappedBy="product")
#JsonManagedReference(value = "ingredients-products")
private List<Ingredients> ingredients;
#OneToMany(mappedBy="product")
#JsonManagedReference(value = "fridge-product")
private List<Fridge> fridge;
}
Fridge Entity Class :
#Entity(name = "Fridge")
#Table(name = "fridge")
public class Fridge{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#ManyToOne
#JoinColumn(name="user_id")
#JsonBackReference(value = "user-fridge")
private User user;
#ManyToOne
#JoinColumn(name="product_id")
#JsonBackReference(value = "fridge-product")
private Product product;
#Column(name = "quantity")
private int quantity;
#Transient
private DateFormat dform = new SimpleDateFormat("dd/MM/yy");
#Transient
private Date intermediar = new Date();
#Column(name = "added_at")
private String added_at = dform.format(intermediar);
}
What I get is something like this :
"fridge": [
{
"id": "79baae3e-8189-4ebb-8a40-2116a77693b8",
"quantity": 25,
"added_at": "25/08/21"
}
But I need the id's of the product and user as well.
How should I structure my model to get that?

how to add user to team to admin page

I can’t understand how to implement adding a user to Team on the admin page.
I wrote the add method in the controller, I can’t understand how to show it all in the interface.
Need two lists, one list of all Teams and a second list of all users and then save?
began to learn thymeleaf and a lot of strange things.
admin.html
</head>
<body>
<h1>Admin page </h1>
<!--
<form action="#" th:action="#{/admin}" th:object="${team}" method="post">
<p>Add Team: <input type="text" th:field="*{name}" /></p>
<p><input type="submit" value="addTeam" />
</form>
-->
<form th:action="#{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
</html>
Users
#Entity
#Table(name="users")
public class Users {
#Id
#Column(name="email",unique = true, nullable = false,length = 200)
String email;
#Column(name="name",nullable = false,length = 200)
String name;
#Column(name="password",nullable = false,length = 128)
#JsonIgnore
String password;
#Column(name = "avatar", nullable = true)
String avatar;
#ManyToOne
#JoinColumn(name="team_id", nullable=true)
Team team;
#ManyToOne
#JoinColumn(name="role", nullable=false)
#JsonIgnore
Role role;
public Users() {
}
get und set
}
Team
Entity
#Table(name="team")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#Column
String name;
#Column
String url;
#Lob
#Column(name = "avatar",nullable = true,columnDefinition="BLOB")
String avatar;
#OneToMany(mappedBy="team",cascade = CascadeType.ALL, orphanRemoval = true)
#JsonIgnore
Set<Users> users = new HashSet<>();
public Team() {
}
get und set
AdminController
#Controller//RestController
public class AdminController {
.....
#GetMapping("/admin/team")
List<Team> allTeams() {
return teamRepository.findAll();
}
#RequestMapping(value = "/admin/team/{id}/user/{email}", method = RequestMethod.POST,produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public Users addUserToTeam(
#PathVariable long id,#PathVariable String email) {
Team team = teamRepository.findById(id).orElseThrow(() -> new NoSuchTeamException("Team not found"));
Users user = userRpRepository.findById(email).orElseThrow(() -> new NoSuchUserException("User not found"));
user.setTeam(team);
user = userRpRepository.save(user);
return user;
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String adminPage(Model model) {
model.addAttribute("admin",new Team());
return "admin";
}
}
Ideologically from RMDB structure, the better way is creating the linkage table between User and Team.
User
#Entity
#Table(name = "user")
public class User {
#Id
#Column(name = "email", length = 200) //#Id controls nullable and unique
private String email;
#Column(name = "name", nullable = false, length = 200)
private String name;
#Column(name = "password", nullable = false, length = 128)
#JsonIgnore
private String password;
#Column(name = "avatar", nullable = true)
private String avatar;
#ManyToMany(cascade = CascadeType.ALL) //by default fetch - LAZY
#JoinTable(name = "user_team", joinColumn = #JoinColumn(name = "user_id",
foreignKey = #ForeignKey(name = "fk_user_team__user"), nullable = false),
inverseJoinColumns = #JoinColumn(name = "team_id",
foreignKey = #ForeignKey(name = "fk_user_team_team"), nullable = false))
private Set<Team> teams;
}
Team
#Entity
#Table(name = "team")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#Column
private String url;
#Lob
#Column(name = "avatar", nullable = true, columnDefinition = "BLOB")
private String avatar;
#ManyToMany(cascade = CascadeType.ALL) //by default fetch - LAZY
#JoinTable(name = "user_team", joinColumn = #JoinColumn(name = "team_id",
foreignKey = #ForeignKey(name = "fk_user_team__team"), nullable = false),
inverseJoinColumns = #JoinColumn(name = "user_id",
foreignKey = #ForeignKey(name = "fk_user_team_user"), nullable = false))
private Set<User> users;
}
UserTeam
#Entity
#Table(name = "user_team", uniqueConstraints =
#UniqueConstraints(columnNames = {"user_id", "team_id"}, name = "uniq_some")
public class UserTeam {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //it's easier to create new Long Id key then composite key with user_id and team_id
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", foreignKey = #ForeignKey(name = "fk_user_team__user"), nullable = false)
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "team_id", foreignKey = #ForeignKey(name = "fk_user_team__team"), nullable = false)
private Team team;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "role_id", nullable = false) //I don't see Role entity but think it has id field
#JsonIgnore
private Role role;
}
With this structure, you can get all users for the Team and all teams for the User. Collections are lazy so you need to use #Transactional, for example, for appropriate service methods.
And this structure is bi-directional: if you add new User into users collection in Team object, JPA will create new User. But ... linkage table contains one more required field role_id, so on such addition you will get an exception. So better first create User and Team objects, and after that create UserTeam linkage object with required Role (or set default Role and all new objects will be created with this Role).

Null pointer in persistentBag in backing bean

I would like to know why I am getting null pointer in my bean method. On my front end I'm interating on value adp.domain.elementsDomain, since the labels are being properly displayed on the screen.
This is my front:
<c:forEach items="#{atributosDaPesquisaBean.atributosDaPesquisaList}" var="adp" varStatus="loop">
<h:panelGrid columns="2" cellpadding="20">
<h:outputText value="#{adp.dominio.nome}"/>
<p:selectManyCheckbox id="gridQueryResult" layout="grid" columns="2">
<f:selectItems value="#{adp.dominio.elementosDominio}" var="elemento" itemLabel="#{elemento.label}" itemValue="#{elemento}" />
<f:ajax listener="#{atributosDaPesquisaBean.incluiNaPesquisa(elemento)}"/>
</p:selectManyCheckbox>
</h:panelGrid>
</c:forEach>
This is my bean:
#ManagedBean
#SessionScoped
public class AtributosDaPesquisaBean {
private List<AtributosDaPesquisa> atributosDaPesquisaList;
#PostConstruct
public void init() {
AtributosDaPesquisaDAO adpDao = new AtributosDaPesquisaDAO();
atributosDaPesquisaList = adpDao.buscaPorRelatorio(relatorio.getCodigo());
atributosDaPesquisaList.forEach(adp -> adp.setaClausulaDeData());
for(AtributosDaPesquisa adp: atributosDaPesquisaList){
if(adp.isEhDominio()){
List<ElementoDominio> elementosDominio = adp.getDominio().getElementosDominio();
elementosDominio.forEach(elemento -> elemento.setEstaNaPesquisa(false));
}
}
}
public void incluiNaPesquisa(ElementoDominio elemento){
//here elemento comes as null. Why?
elemento.setEstaNaPesquisa(true);
}
}
And these are my models:
#Entity
#Table(name = "tbl_atributosdapesquisa")
public class AtributosDaPesquisa {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "atr_codigo")
private Long codigo;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "tbl_dominio_dom_codigo", referencedColumnName = "dom_codigo")
private Dominio dominio;
//others columns
//getters and setters
}
Other model:
#Entity
#Table(name = "tbl_dominio")
public class Dominio {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "dom_codigo")
private Long codigo;
#Column(name="dom_nome", nullable = false)
private String nome;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "dominio")
private List<ElementoDominio> elementosDominio;
//getters and setters
}
And finally:
#Entity
#Table(name = "tbl_elemento_dominio")
public class ElementoDominio {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ele_dom_codigo")
private Long codigo;
#Column(name="ele_dom_label", nullable = false)
private String label;
#Column(name="ele_dom_esta_na_pesquisa", nullable = false)
private boolean estaNaPesquisa;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "dom_codigo", nullable = false)
private Dominio dominio;
//getters and setters
}
In my method 'incluiNaPesquisa' which is getting null as parameter. I do not know the reason since my screen is rendering the list and showing each of the labels. If I send AtributoDaPesquisa as parameter, I did not receive null pointer, but the object.

Categories

Resources