@Controller
@RequestMapping("/products")
public class ProductsController {
@Autowired
private ProductsRepository repo;
@GetMapping({"", "/"})
public String showProductList(Model model) {
List<Product> products = repo.findAll(Sort.by(Sort.Direction.DESC, "id"));
model.addAttribute("products", products);
return "products/index";
}
}
//properties
spring.application.name=beststore
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/beststore
spring.datasource.username=root
spring.datasource.password=9874156R
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
resources/static/index.html
<body>
<div class="container">
<h1 class="text-center my-4">Welcome to our website</h1>
<a class="btn btn-primary" href="/products">Products</a>
</div>
resources/templates/products/index.html
<body>
<div class="container">
<h1 class="text-center my-4">Products</h1>
<a class="btn btn-primary" href="/products/create">Create Product</a>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Brand</th>
<th>Category</th>
<th>Price</th>
<th>Image</th>
<th>Create At</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr th:each="product : ${products}">
<td th:text="${product.id}"></td>
<td th:text="${product.name}"></td>
<td th:text="${product.brand}"></td>
<td th:text="${product.category}"></td>
<td th:text="${product.price} + '$'"></td>
<td>
<img th:src="@{'/images/' + ${product.imageFilename}}" alt="..." width="100">
</td>
<td th:text="${#dates.format(product.createdAt, 'yyyy-MM-dd')}"></td>
<td style="white-space: nowrap">
<a class="btn btn-primary btn-sm" th:href="@{/products/edit(id=${product.id})}">Edit</a>
<a class="btn btn-danger btn-sm" th:href="@{/products/delete(id=${product.id})}"
onclick="return confirm('Are you sure?')">Delete</a>
</td>
</tr>
</tbody>
</table>
</div>
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Jan 15 13:53:26 AZT 2025
There was an unexpected error (type=Internal Server Error, status=500).
An error happened during template parsing (template: "class path resource [templates/products/index.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/products/index.html]")
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "product.id" (template: "products/index" - line 31, col 17) at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393) at org.attoparser.MarkupParser.parse(MarkupParser.java:257) at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ... 48 more
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "product.id" (template: "products/index" - line 31, col 17)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'id' cannot be found on object of type 'com.example.beststore.Model.Product' - maybe not public or not valid?
You have it written in stacktrace --
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "product.id"
On your object product is probably id not accessible as property, and that is correct, correct way is to use getter/setters. So replace all accessors to properties with getters and it should be ok.