我的 Google-Fu 失败了,所以我问你...是否有一种方法可以使用 Thymeleaf 迭代 Java 8 Stream,类似于迭代列表的方式,同时仍保持 Stream 的性能目的?
存储库
Stream<User> findAll()
型号
Stream<User> users = userRepository.findAll();
model.addAttribute("users", users);
查看
<div th:each="u: ${users}">
<div th:text="${u.name}">
如果我尝试这个,我会得到:
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'name' cannot be found on object of type 'java.util.stream.ReferencePipeline$Head' - maybe not public?
如果我使用列表,它会按预期工作。
是否有正确的方法来处理我找不到的流?
虽然 Thymeleaf 不支持流,但它支持 Iterable,因此您可以执行以下操作: 型号:
Stream<User> users = userRepository.findAll();
model.addAttribute("users", (Iterable<User>) users::iterator);
您的视图将像您已经编写的那样工作:
<div th:each="u: ${users}">
<div th:text="${u.name}">
查看版本 3文档
,它说它将支持任何实现 Iterator 的对象,因此也应该可以执行此操作: 型号:
Stream<User> users = userRepository.findAll();
model.addAttribute("users", users.iterator());
我没有使用该版本,所以我无法让它工作。
在Java代码中,你必须做三件事:
使用@javax.transaction.Transactional注释
从 Spring Data 返回流时需要 @Transactional 注解。关键是带注释的方法必须在结束之前实际消耗流 - 使用 Thyemleaf 的“正常”方式不会发生这种情况,其中该方法仅返回字符串模板名称。
同时,流已关闭(当使用流执行将 List 转换为 Map 之类的操作时,您不必执行此操作)。通过自己控制模板生成过程,您可以确保流在 @Transactional 方法中关闭和消耗。
Java 代码如下所示(我使用的是 Spring 5 MVC):
@Controller
public class CustomerController {
@Autowired
SpringTemplateEngine templateEngine;
@Autowired
private CustomerRepository customerRepository;
@RequestMapping("/customers")
@Transactional
public void customers(
final String firstName,
final HttpServletRequest request,
final HttpServletResponse response
) throws IOException {
final WebContext ctx = new WebContext(
request,
response,
request.getServletContext(),
request.getLocale()
);
try (
final Stream<CustomerModelEntity> models =
(firstName == null) || firstName.isEmpty() ?
customerRepository.findAll() :
customerRepository.findByFirstNameIgnoringCaseOrderByLastNameAscFirstNameAsc(firstName)
) {
ctx.setVariable(
"customers",
models.iterator()
);
response.setContentType("text/html");
templateEngine.process(
"customer-search",
ctx,
response.getWriter()
);
}
}
}
Thymeleaf 模板如下所示(我使用解耦逻辑):
<?xml version="1.0"?>
<thlogic>
<attr sel=".results" th:remove="all-but-first">
<attr sel="/.row[0]" th:each="customer : ${customers}">
<attr sel=".first-name" th:text="${customer.firstName}" />
<attr sel=".middle-name" th:text="${customer.middleName}" />
<attr sel=".last-name" th:text="${customer.lastName}" />
</attr>
</attr>
</thlogic>
<div th:each="u: ${users.toIterable}">
<div th:text="${u.name}">
我可能在另一个 Stack Overflow 答案中找到了这个,但我现在不记得是哪个答案了。