Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thymeleaf calls conversion service for every value of a select element. #323

Open
jessebrands opened this issue Mar 24, 2024 · 0 comments
Open

Comments

@jessebrands
Copy link

  • 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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant