Description
- Version: 3.1.2
- Environment: Spring Boot 3.2.4, Java 22
I have a HTML Thymeleaf view that renders a form. In this form there is a select
element in which the user may select a Preset
object. I have registered with Spring a Converter
that can convert a preset's slug
string into a proper Preset
. When I render the view with a bound form object which has the Article
field set to null
, the view will not call the conversion service.
When the bound form DOES have an object set (for example, if I preselect a value) and then render the view, the template will call the spring conversion service for every single object in the list, only to convert it back to a string. Why? This is causing hibernate to produce SQL output like this:
Hibernate: select ge1_0.id,ge1_0.enabled,p1_0.generator_id,p1_1.id,p1_1.enabled,p1_1.ordinal,p1_1.recommended,p1_1.settings_preset,p1_1.slug,ge1_0.version from generators ge1_0 join generator_presets p1_0 on ge1_0.id=p1_0.generator_id join presets p1_1 on p1_1.id=p1_0.preset_id where ge1_0.enabled=true
Hibernate: select pe1_0.id,pe1_0.enabled,pe1_0.ordinal,pe1_0.recommended,pe1_0.settings_preset,pe1_0.slug from presets pe1_0 where pe1_0.enabled=true order by pe1_0.ordinal desc
Hibernate: select ge1_0.id,ge1_0.enabled,p1_0.generator_id,p1_1.id,p1_1.enabled,p1_1.ordinal,p1_1.recommended,p1_1.settings_preset,p1_1.slug,ge1_0.version from generators ge1_0 join generator_presets p1_0 on ge1_0.id=p1_0.generator_id join presets p1_1 on p1_1.id=p1_0.preset_id where ge1_0.enabled=true and ge1_0.version=?
Hibernate: select ge1_0.id,ge1_0.enabled,p1_0.generator_id,p1_1.id,p1_1.enabled,p1_1.ordinal,p1_1.recommended,p1_1.settings_preset,p1_1.slug,ge1_0.version from generators ge1_0 join generator_presets p1_0 on ge1_0.id=p1_0.generator_id join presets p1_1 on p1_1.id=p1_0.preset_id where ge1_0.enabled=true and ge1_0.version=?
# These are all called from the conversion service:
Hibernate: select pe1_0.id,pe1_0.enabled,pe1_0.ordinal,pe1_0.recommended,pe1_0.settings_preset,pe1_0.slug from presets pe1_0 where pe1_0.enabled=true and pe1_0.slug=?
Hibernate: select pe1_0.id,pe1_0.enabled,pe1_0.ordinal,pe1_0.recommended,pe1_0.settings_preset,pe1_0.slug from presets pe1_0 where pe1_0.enabled=true and pe1_0.slug=?
Hibernate: select pe1_0.id,pe1_0.enabled,pe1_0.ordinal,pe1_0.recommended,pe1_0.settings_preset,pe1_0.slug from presets pe1_0 where pe1_0.enabled=true and pe1_0.slug=?
Hibernate: select pe1_0.id,pe1_0.enabled,pe1_0.ordinal,pe1_0.recommended,pe1_0.settings_preset,pe1_0.slug from presets pe1_0 where pe1_0.enabled=true and pe1_0.slug=?
Here is the Thymeleaf view:
<div class="form-field">
<label for="generator-preset" th:text="#{seeds.create.form.preset.label}">Version</label>
<select id="generator-preset" th:field="*{preset}">
<option th:each="p : ${presets}" th:value="${p.slug}"
th:text="#{|seeds.presets.${p.slug}|}"></option>
</select>
<ul class="form-field__errors" th:if="${#fields.hasErrors('preset')}">
<li th:each="error : ${#fields.errors('preset')}" th:text="${error}"></li>
</ul>
</div>
And here is the form:
@Getter
@Setter
public class CreateSeedForm {
@NotNull
private Generator generator;
@NotNull
private Preset preset;
}
Why does this happen? Why is Thymeleaf trying to convert every single value in an option
? I understand the conversion service will be run for for every th:field
but that scarcely explains this behavior, considering the array in ${presets}
is already of the correct object type. Why are we going from Object.toString()
-> Converter
to turn it back into an Object
, and then back to a string AGAIN? This has to be a bug, right?