@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>
此应用程序没有 /error 的显式映射,因此您将其视为后备。
1 月 15 日星期三 13:53:26 AZT 2025
出现意外错误(类型=内部服务器错误,状态=500)。
模板解析时发生错误(模板:“类路径资源[templates/products/index.html]”)
org.thymeleaf.exceptions.TemplateInputException:模板解析时发生错误(模板:“类路径资源[templates/products/index.html]”)
引起:org.attoparser.ParseException:评估SpringEL表达式的异常:“product.id”(模板:“products/index” - 第31行,第17列)在org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393)在org.attoparser.MarkupParser.parse(MarkupParser.java:257) 在 org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ... 48 更多
由 org.thymeleaf.exceptions.TemplateProcessingException 引起:评估 SpringEL 表达式时出现异常:“product.id”(模板:“products/index” - 第 31 行,第 17 栏)
引起原因:org.springframework.expression.spel.SpelEvaluationException:EL1008E:在“com.example.beststore.Model.Product”类型的对象上找不到属性或字段“id” - 可能不是公开的或无效?
你已经把它写在堆栈跟踪中了——
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "product.id"
在您的对象产品上,id 可能无法作为属性访问,这是正确的,正确的方法是使用 getter/setter。因此,将属性的所有访问器替换为 getter 就可以了。