Thymeleaf 从 Spring Data JPA 迭代 Java 8 流
Thymeleaf iterating a Java 8 Stream from Spring Data JPA
我的 Google-Fu 让我失望了,所以我问你...有没有一种方法可以用 Thymeleaf 迭代 Java 8 Stream,类似于迭代 List 的方式,同时仍然保持 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 documentation 没有办法做你想做的事。
除了 Thymeleaf 不提供任何与流交互的方式,请考虑到 Stream
对象在您执行终端操作之前无法访问其包含的对象(例如 Collectors.toList()
)
虽然 Thymeleaf 根据 documentation 不支持流,但它确实支持 Iterable,因此您可以执行以下操作:
型号:
Stream<User> users = userRepository.findAll();
model.addAttribute("users", (Iterable<User>) users::iterator);
您的视图将按照您编写的方式运行:
<div th:each="u: ${users}">
<div th:text="${u.name}">
查看版本 3 documentation 它说它将支持任何实现 Iterator 的对象,因此也应该可以这样做:
型号:
Stream<User> users = userRepository.findAll();
model.addAttribute("users", users.iterator());
我没有使用那个版本,所以我无法让它工作。
这个post有点旧了,但是没看到更新的。可以用正确的秘方来完成。
在Java代码中,你必须做三件事:
- 使用@javax.transaction.Transactional注解
- 手动调用Thymeleaf处理模板
- 在模板处理中使用 try-with-resources 块以保证流关闭
在您的模板中,如果您传递 Stream 的迭代器,则无需执行任何不同的操作,因为 Thyemeleaf 已经理解迭代器。
从 Spring 数据返回流时需要 @Transactional 注释。关键是带注释的方法必须在它结束之前实际使用流 - 使用 Thyemleaf 不会发生这种情况 "normal" 方法只是 returns String 模板名称。
同时,流已关闭(使用流执行诸如将 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>
我的 Google-Fu 让我失望了,所以我问你...有没有一种方法可以用 Thymeleaf 迭代 Java 8 Stream,类似于迭代 List 的方式,同时仍然保持 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 documentation 没有办法做你想做的事。
除了 Thymeleaf 不提供任何与流交互的方式,请考虑到 Stream
对象在您执行终端操作之前无法访问其包含的对象(例如 Collectors.toList()
)
虽然 Thymeleaf 根据 documentation 不支持流,但它确实支持 Iterable,因此您可以执行以下操作:
型号:
Stream<User> users = userRepository.findAll();
model.addAttribute("users", (Iterable<User>) users::iterator);
您的视图将按照您编写的方式运行:
<div th:each="u: ${users}">
<div th:text="${u.name}">
查看版本 3 documentation 它说它将支持任何实现 Iterator 的对象,因此也应该可以这样做:
型号:
Stream<User> users = userRepository.findAll();
model.addAttribute("users", users.iterator());
我没有使用那个版本,所以我无法让它工作。
这个post有点旧了,但是没看到更新的。可以用正确的秘方来完成。
在Java代码中,你必须做三件事:
- 使用@javax.transaction.Transactional注解
- 手动调用Thymeleaf处理模板
- 在模板处理中使用 try-with-resources 块以保证流关闭
在您的模板中,如果您传递 Stream 的迭代器,则无需执行任何不同的操作,因为 Thyemeleaf 已经理解迭代器。
从 Spring 数据返回流时需要 @Transactional 注释。关键是带注释的方法必须在它结束之前实际使用流 - 使用 Thyemleaf 不会发生这种情况 "normal" 方法只是 returns String 模板名称。
同时,流已关闭(使用流执行诸如将 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>