Thymeleaf 从 Spring Data JPA 迭代 Java 8 流

问题描述 投票:0回答:4

我的 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?

如果我使用列表,它会按预期工作。

是否有正确的方法来处理我找不到的流?

java spring spring-data-jpa thymeleaf spring-el
4个回答
3
投票

虽然 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());

我没有使用该版本,所以我无法让它工作。


2
投票
文档

,没有办法做你想做的事。 除了 Thymeleaf 不提供任何与流交互的方式,请考虑到

Stream

对象无法访问其包含的对象,直到您执行终端操作(例如

Collectors.toList()
    


1
投票

在Java代码中,你必须做三件事:

使用@javax.transaction.Transactional注释
  1. 手动调用Thymeleaf处理模板
  2. 在模板处理中使用 try-with-resources 块来保证流关闭
  3. 在模板中,如果传递 Stream 的迭代器,则无需执行任何不同的操作,因为 Thyemeleaf 已经理解迭代器。

从 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>



0
投票

<div th:each="u: ${users.toIterable}"> <div th:text="${u.name}">

我可能在另一个 Stack Overflow 答案中找到了这个,但我现在不记得是哪个答案了。

© www.soinside.com 2019 - 2024. All rights reserved.