Trouble using ThymeLeaf - spring

I'm having trouble figuring out how to add text and image to a table using ThymeLeaf. All model attributes are empty. If I change the size of the repo, there remains only one row. But if I do print statement in run(), everything works. I have the following code in movies.html:
<div class = "container">
<div class = "row">
<h1> List Students </h1>
</div>
<table class = "table table-striped table-bordered">
<thead class = "table-dark">
<tr>
<th th:text="${hello}"></th>
<th th:text="${world}"></th>
</tr>
</thead>
<tbody>
<tr th:each = "movie : ${movies}">
<td th:text="${movie.title}"></td>
</tr>
</tbody>
</table>
</div>
I have the following Controller class. I've tried changing return type to ModelAndView but that makes no difference.
#Controller
public class MovieController {
// https://www.baeldung.com/spring-boot-logging
private final Logger logger = LoggerFactory.getLogger(MovieController.class);
#Autowired
private MovieService movieService;
/**
* This method is being called because if I change
* the size of the movies repo, the number of cells
* in the webpage table also changes. However, all
* of the cells are blank! "Hello" and "World!" are
* also blank!
*/
#GetMapping("/movies")
public String listMovies(Model model) {
model.addAttribute("movies", movieService.getAllMovies());
model.addAttribute("hello", "Hello");
model.addAttribute("world", "World!");
// not printing to console
System.out.println("TEST TEST TEST!");
// also not printing to console
logger.info("TEST TEST TEST");
return "movies";
}
}
Current Movie class. I've tried typing out my own getters, setters, and constructor in case for some reason Lombok wasn't working, but it makes no difference.
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "movies")
/**
* Lombok generates getters and setters.
* If I write out getters and setters myself,
* the problem still persists. Also, I'm able
* to call the getters and setters of this class
* with Lombok, which means Lombok is working.
*/
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "movie_id")
private Long id;
#Column(name = "title")
private String title;
#Column(name = "director")
private String director;
}
And I have the following in Application class:
#Override
public void run(String... args) {
movieRepository.deleteAll();
Movie empireStrikesBack = new Movie();
empireStrikesBack.setTitle("The Empire Strikes Back");
empireStrikesBack.setDirector("Irvin Kershner");
movieRepository.save(empireStrikesBack);
// add more movies here...
// This works just fine, prints all info to console
for (Movie movie : movieService.getAllMovies()) {
System.out.println(movie.getTitle());
System.out.println(movie.getDirector());
}
}
The movie entry is visible in the MySQL table after running the app, but on the webpage the table contents is completely empty. "Hello" does not appear and the movie title doesn't appear either. Why are all my model attributes empty?

Related

Thymeleaf iterator th:each does not view information

So, i write spring boot Controller, write Entity -> Course, write html views-> courses, but my information did not view in my template.
#Controller
#RequestMapping ("/courses")
public class CourseController {
private CourseService courseService;
public CourseController(CourseService courseService) {
this.courseService = courseService;
}
#GetMapping ("/index")
public String getCourses (Model model, #RequestParam (name="keyword", defaultValue = "") String keyword ) {
List <Course> courses = courseService.findCourseBycourseName(keyword);
model.addAttribute("listCourses", courses);
model.addAttribute("keyword", keyword);
return "views/courses";
}
HTML + Thymeleaf courses.html -> when I start app loaded only empty table.
<tbody>
<tr class ="text-center" th:each="course : ${listCourses}" >
<td th:text = "${course.getCourseId()}"> </td>
<td th:text = "${course.getCourseName()}"> </td>
<td th:text = "${course.getCourseDuration()}"> </td>
<td th:text = "${course.getCourseDescription()}"> </td>
<td th:text = "${course.getInstructor().getInstructorFirstName()} + '' + course.getInstructor().getInstructorLastName"> </td>
<td th:text = "${course.getListStudent().size()}"> </td>
</tbody>
</table>
There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class. There is my Course Class.
#Entity
#Table (name = "courses")
public class Course {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column (name = "course_id", nullable=false)
private Long courseId;
#Basic
#Column (name = "name", nullable=false, length = 45)
private String courseName;
#Basic
#Column (name = "duration", nullable=false, length = 45)
private String courseDuration;
#Basic
#Column (name = "description", nullable=false, length = 45)
private String courseDescription;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn (name = "instructor_id", referencedColumnName = "instructor_id", nullable = false)
private Instructor instructor;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable (name = "enrolled_in",
joinColumns = {#JoinColumn (name = "course_id")},
inverseJoinColumns = {#JoinColumn (name = "student_id")})
private Set<Student> listStudent = new HashSet<>();
#Override
public int hashCode() {
return Objects.hash(courseId, courseName, courseDuration, courseDescription);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Course course = (Course) obj;
return courseId.equals(course.courseId) && Objects.equals(courseName, course.courseName) && Objects.equals(courseDuration, course.courseDuration) && Objects.equals(courseDescription, course.courseDescription);
}
// add object student to the list students;
// add course to the List of Students;
public void assignStudentToCourse (Student student) {
this.listStudent.add(student);
student.getCourses().add(this);
}
// remove student from course
public void removeStudents (Student students) {
this.listStudent.remove(students);
students.getCourses().remove(this);
}
public Course () {}
public Course(String courseName, String courseDuration, String courseDescription, Instructor instructor) {
this.courseName = courseName;
this.courseDuration = courseDuration;
this.courseDescription = courseDescription;
this.instructor = instructor;
}
public Long getCourseId() {
return courseId;
}
public void setCourseId(Long courseId) {
this.courseId = courseId;
}
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public String getCourseDuration() {
return courseDuration;
}
public void setCourseDuration(String courseDuration) {
this.courseDuration = courseDuration;
}
public String getCourseDescription() {
return courseDescription;
}
public void setCourseDescription(String courseDescription) {
this.courseDescription = courseDescription;
}
public Instructor getInstructor() {
return instructor;
}
public void setInstructor(Instructor instructor) {
this.instructor = instructor;
}
public Set<Student> getListStudent() {
return listStudent;
}
public void setListStudent(Set<Student> listStudent) {
this.listStudent = listStudent;
}
#Override
public String toString() {
return "Course [courseId=" + courseId + ", courseName=" + courseName + ", courseDuration=" + courseDuration
+ ", courseDescription=" + courseDescription + "]";
}
}
Who knows where problem
You need to close your tag in your HTML snippet.
<tbody>
<tr class ="text-center" th:each="course : ${listCourses}" >
<td th:text = "${course.getCourseId()}"> </td>
<td th:text = "${course.getCourseName()}"> </td>
<td th:text = "${course.getCourseDuration()}"> </td>
<td th:text = "${course.getCourseDescription()}"> </td>
<td th:text = "${course.getInstructor().getInstructorFirstName()} + '' + course.getInstructor().getInstructorLastName"> </td>
<td th:text = "${course.getListStudent().size()}"> </td>
</tr><!-- bingo -->
</tbody>
</table>
Also see How to print Array size in thymeleaf? because it might be your next question.
In your service class, make sure that you have #Repository as an annotation. Each method name needs to reflect the property, in a case-sensitive camelCase format. You should also rename it to CourseRepository to better describe what it is doing. Note the difference:
#Repository
public interface CourseRepository extends CrudRepository <Course, Long> {
Iterable<Course> findByCourseName(String courseName);
//other ones
//CrudRepository will give you these two so you don't need to add them:
//Iterable<Course> findAll();
//Optional<Course> findById(Long courseId);
}
You can look at CrudRepository or JPARepository to extend depending on your needs. Info here and here.
More modern design shows these queries returning with Java's Optional but this should not be the cause of your error.
(Also, in the future, you can just update the question with the code instead of commenting it.)
https://github.com/vphilipnyc/forvladislav

EL1008E: Property or field 'applicationName' cannot be found on object of type 'java.lang.String' - maybe not public or not valid?

Been stuck with this error for a while now.
EL1008E: Property or field 'applicationName' cannot be found on object of type 'java.lang.String' - maybe not public or not valid?
I'm trying to get the records from database through thymeleaf, but always gets this error. I think I've declared everything that is needed this.
Here's my code
Model
#Entity
#Table(name = "sms_alert", schema = "public")
public class SmsAlert {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sms_id")
private Long smsId;
#Column(name = "application_name")
private String applicationName;
#Column(name = "sms_flag")
private String smsFlag;
public SmsAlert() {
}
public SmsAlert(String applicationName, String smsFlag) {
super();
this.applicationName = applicationName;
this.smsFlag = smsFlag;
}
public Long getSmsId() {
return smsId;
}
public void setSmsId(Long smsId) {
this.smsId = smsId;
}
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public String getSmsFlag() {
return smsFlag;
}
public void setSmsFlag(String smsFlag) {
this.smsFlag = smsFlag;
}
}
Controller
#GetMapping(value = "/smsAlert/{applicationName}")
public String viewApplicationSmsAlert(#PathVariable("applicationName") String applicationName, Model model) {
System.out.println(applicationName);
model.addAttribute("sms", smsService.findSmsFlagByAppName(applicationName));
return "test";
}
Repository
#Repository
public interface SmsRepository extends JpaRepository<SmsAlert, Long> {
#Query(value = "SELECT s.application_name, s.sms_flag FROM public.sms_alert s WHERE s.application_name= :applicationName ", nativeQuery = true)
public String findSmsFlagByAppName(#Param("applicationName") String applicationName);
Service
#Service
public class SmsService {
#Autowired
private SmsRepository smsRepository;
public List<SmsAlert> findAll() {
return smsRepository.findAll();
}
public String findSmsFlagByAppName(String applicationName) {
return smsRepository.findSmsFlagByAppName(applicationName);
}
public void updateSmsFlag(String applicationName, String smsFlag) {
smsRepository.updateSmsFlag(applicationName, smsFlag);
}
}
HTML File
<body>
<div id="content">
<div>
<table class="table table-hover table-striped" id="invTable">
<thead>
<tr class="table-primary">
<th id="terminalId">Terminal ID</th>
</tr>
</thead>
<tbody>
<tr th:each="sms : ${sms}">
<td th:text="${sms.applicationName}"></td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
The findSmsFlagByAppName method in SmsRepository returns a String. Your controller is putting that String in the model with the variable name sms. Then in your view you are referencing ${sms.applicationName} which is trying to evaluate the applicationName property on that String, which of course does not exist.
You could make the error go away a number of ways. One is, populate the model withe applicationName as a String and then in the view simply refer to that String.
Another option is to rework the findSmsFlagByAppName method to return a SmsAlert.

Spring Boot JPARepository is not displaying the id when using the findAll() method

I'm creating a Spring Boot application but my findAll (implemented with JPARepository) returns every attribute except the id, and I need the id for the view I'm trying to create. Is there any way to change this? I currently have
/model/rol.java
#Entity
#Table(name = "roles")
public class rol {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "rol", nullable = false)
private String rol;
#OneToMany(mappedBy = "rol", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Administrador> administradores;
public rol() {
}
public rol(String rol) {
this.rol = rol;
}
/* Getters and Setters */
/repository/rolrepository.java
#Repository
public interface rolrepository extends JpaRepository<rol, Long>{
}
/controller/rolcontroller.java
#Controller
public class rolcontroller {
#Autowired
private rolrepository rolRepository;
#GetMapping("/roles")
public String showAll(Model model) {
model.addAttribute("roles", rolRepository.findAll());
return "roles";
}
/templates/roles.html
<table class="table table-striped">
<thead class="thead-dark" >
<tr>
<th scope="col"> ID </th>
<th scope="col"> Rol </th>
</tr>
</thead>
<tbody>
<tr th:if="${roles.empty}">
<td colspan="2"> No hay roles registrados </td>
</tr>
<tr th:each="rol : ${roles}">
<td><span th:text="${rol.id}"> Rol </span></td>
<td><span th:text="${rol.rol}"> Rol </span></td>
</tr>
</tbody>
</table>
However, I get the error Exception evaluating SpringEL expression: "rol.id"
After some research I found out that apparently JPARepository doesn't include the id of the model in the findAll() method.
Is there any way to modify findAll() or any other file to be able to use the id in my HTML table?
Thanks in advance
The expected output is the rol.id value in my table, however, the actual result is Exception evaluating SpringEL expression: "rol.id"
#Repository
public interface rolrepository extends JpaRepository<rol, Long>{
}
The JpaRepository arg
JpaRepository<rol, Long>
Indicates the ID is of type Long but you are using int id in your rol java code
Try using Long id in rol.java
First of all rol is not proper Class name.You should start practicing naming conventions.
Second thing, when you are using JPARespository/CrudRepository,make sure that the data type of id column that you take in your POJO class and the one in repository are same.
Refer this.
#Repository
public interface rolrepository extends JpaRepository<Rol, Long>{
}
maybe you just forgot to add getter for id in your model class along with the long type instead of int:
public long getId() {
return id;
}
By default, springJpa does not expose ids of entities so you have to make further configurations to get into it, here is one of the solutions: create a java class, write the new config as displayed below and override the default rest repository config to expose it, the full java code will be:
// package com.aminecode.springboot.myapp.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import javax.persistence.EntityManager;
import javax.persistence.metamodel.EntityType;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
#Configuration
public class MyDataRestConfig implements RepositoryRestConfigurer {
private EntityManager entityManager;
#Autowired
public MyDataRestConfig(EntityManager theEntityManager) {
entityManager = theEntityManager;
}
private void exposeIds(RepositoryRestConfiguration config) {
// expose entity ids
// - get a list of all entity classes from the entity manager
Set<EntityType<?>> entities = entityManager.getMetamodel().getEntities();
// - create an array of the entity type
List<Class> entityClasses = new ArrayList<>();
// - get the entity types for the entities
for (EntityType tempEntityType : entities) {
entityClasses.add(tempEntityType.getJavaType());
}
// - expose the entity ida for the array of entuity/domain types
Class[] domainTypes = entityClasses.toArray(new Class[0]);
config.exposeIdsFor(domainTypes);
}
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration
config, CorsRegistry corsRegistry) {
exposeIds(config);
}
}

Spring receive data from the client

Good evening!
public class Order {
private int idOrder;
private Basket basket;
// getter and setter
}
public class AnonymousOrder {
private String name;
private String telephone;
// getter and setter
}
public class UserOrder {
private User user;
// getter and setter
}
public class OrdersForm {
private List< ? extends Order> orders;
// getter and setter
}
#RequestMapping(value="/showOrders")
public String showOrders(Model model){
List<? extends Order> orders= adminManager.searchAllOrders();
OrdersShowForm ordersForm = new OrdersShowForm();
ordersForm.setOrders(orders);
model.addAttribute("ordersForm", ordersForm);
return "showOrders";
}
#RequestMapping(value="/showOrders", method = RequestMethod.POST)
public String showOrdersPOST(#ModelAttribute("ordersForm") OrdersShowForm ordersForm){
System.out.print(ordersForm);
return "showOrders";
}
<form:form modelAttribute="ordersForm">
<table class="features-table" border="1">
<c:forEach items="${ordersForm.orders}" var="order" varStatus="status">
<tr>
<c:if test="${order['class'].simpleName != 'UserOrder'}">
<td>
<input name="orders[${status.index}].name" value="${order.name}"/>
</td>
</c:if>
</c:forEach>
</table>
Problem: I am passing on page two types of data: UserOrder and AnonymousOrder, but when I try to get them on the server then come data type Order.
Question: How to transfer data to the server without changing their actual type?
P.S. sorry for my English)

Use a single freemarker template to display tables of arbitrary pojos

Attention advanced Freemarker gurus:
I want to use a single freemarker template to be able to output tables of arbitrary pojos, with the columns to display defined separately than the data. The problem is that I can't figure out how to get a handle to a function on a pojo at runtime, and then have freemarker invoke that function (lambda style). From skimming the docs it seems that Freemarker supports functional programming, but I can't seem to forumulate the proper incantation.
I whipped up a simplistic concrete example. Let's say I have two lists: a list of people with a firstName and lastName, and a list of cars with a make and model. would like to output these two tables:
<table>
<tr>
<th>firstName</th>
<th>lastName</th>
</tr>
<tr>
<td>Joe</td>
<td>Blow</d>
</tr>
<tr>
<td>Mary</td>
<td>Jane</d>
</tr>
</table>
and
<table>
<tr>
<th>make</th>
<th>model</th>
</tr>
<tr>
<td>Toyota</td>
<td>Tundra</d>
</tr>
<tr>
<td>Honda</td>
<td>Odyssey</d>
</tr>
</table>
But I want to use the same template, since this is part of a framework that has to deal with dozens of different pojo types.
Given the following code:
public class FreemarkerTest {
public static class Table {
private final List<Column> columns = new ArrayList<Column>();
public Table(Column[] columns) {
this.columns.addAll(Arrays.asList(columns));
}
public List<Column> getColumns() {
return columns;
}
}
public static class Column {
private final String name;
public Column(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class Person {
private final String firstName;
private final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
public static class Car {
String make;
String model;
public Car(String make, String model) {
this.make = make;
this.model = model;
}
public String getMake() {
return make;
}
public String getModel() {
return model;
}
}
public static void main(String[] args) throws Exception {
final Table personTableDefinition = new Table(new Column[] { new Column("firstName"), new Column("lastName") });
final List<Person> people = Arrays.asList(new Person[] { new Person("Joe", "Blow"), new Person("Mary", "Jane") });
final Table carTable = new Table(new Column[] { new Column("make"), new Column("model") });
final List<Car> cars = Arrays.asList(new Car[] { new Car("Toyota", "Tundra"), new Car("Honda", "Odyssey") });
final Configuration cfg = new Configuration();
cfg.setClassForTemplateLoading(FreemarkerTest.class, "");
cfg.setObjectWrapper(new DefaultObjectWrapper());
final Template template = cfg.getTemplate("test.ftl");
process(template, personTableDefinition, people);
process(template, carTable, cars);
}
private static void process(Template template, Table tableDefinition, List<? extends Object> data) throws Exception {
final Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("tableDefinition", tableDefinition);
dataMap.put("data", data);
final Writer out = new OutputStreamWriter(System.out);
template.process(dataMap, out);
out.flush();
}
}
All the above is a given for this problem. So here is the template I have been hacking on. Note the comment where I am having trouble.
<table>
<tr>
<#list tableDefinition.columns as col>
<th>${col.name}</th>
</#list>
</tr>
<#list data as pojo>
<tr>
<#list tableDefinition.columns as col>
<td><#-- what goes here? --></td>
</#list>
</tr>
</#list>
</table>
So col.name has the name of the property I want to access from the pojo. I have tried a few things, such as
pojo.col.name
and
<#assign property = col.name/>
${pojo.property}
but of course these don't work, I just included them to help convey my intent. I am looking for a way to get a handle to a function and have freemarker invoke it, or perhaps some kind of "evaluate" feature that can take an arbitrary expression as a string and evaluate it at runtime.
?eval is (almost?) always a bad idea, because it often comes with performance drawbacks (e.g. a lot of parsing) and security problems (e.g. "FTL injection").
A better approach is using the square bracket syntax:
There is an alternative syntax if we want to specify the subvariable name with an expression: book["title"]. In the square brackets you can give any expression as long as it evaluates to a string.
(From the FreeMarker documentation about retrieving data from a hash)
In your case I'd recommend something like ${pojo[col.name]}.
Found the answer.
${("pojo." + col.name)?eval}

Resources