Thymeleaf: ошибка поля при применении роли к пользователю (отношение «один ко многим»)

Я пытаюсь создать приложение Thymeleaf, которое может зарегистрировать учетную запись с прикрепленной ролью, выбранной во время регистрации. Вот визуализация. Я постараюсь добавить как можно больше релевантного кода, чтобы лучше понять, что происходит, а затем объяснить проблему.

Пользователь.java:

package com.example.demo.model;

<imports>

@Entity
@Table(name = "user", uniqueConstraints = @UniqueConstraint(columnNames = "account"))
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "first_name")
    private String firstName;
    
    @Column(name = "last_name")
    private String lastName;
    
    private String account;
    
    private String password;
    
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name = "role_id", nullable=false)
    private Role role;
    
    @OneToMany(mappedBy = "user")
    private Collection<Comment> comments;
    
    public User() {
        
    }

    public User(String firstName, String lastName, String account, String password, Role role,
            Collection<Comment> comments) {
        super();
        this.firstName = firstName;
        this.lastName = lastName;
        this.account = account;
        this.password = password;
        this.role = role;
        this.comments = comments;
    }

    <getters and setters>
    
}

Роль.java:

package com.example.demo.model;

<imports>

@Entity
@Table(name = "role")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    
    @OneToMany(fetch=FetchType.LAZY, mappedBy = "role")
    private Set<User> users = new HashSet<User>(0);
    
    public Role() {
        
    }

    public Role(String name, Set<User> users) {
        super();
        this.name = name;
        this.users = users;
    }

    <getters and setters>
}

Комментарий.java:

<not important>

Пользовательский репозиторий.java:

package com.example.demo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.demo.model.User;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    User findByAccount(String account);
    
}

Ролевепозиторий.java:

package com.example.demo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.demo.model.Role;

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
    
}

UserService.java:

package com.example.demo.service;

<imports>

@Service("userService")
public class UserService {

    private UserRepository userRepository;
    
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;
    
    public UserService(UserRepository userRepository) {
        super();
        this.userRepository = userRepository;
    }
    
    public User findByAccount(String account) {
        return userRepository.findByAccount(account);
    }
    
    public void saveUser(User user) {
        userRepository.save(user);
    }
}

РольСервис.java:

package com.example.demo.service;

<imports>

@Service
public class RoleService {
    
    private RoleRepository roleRepository;
    
    public RoleService(RoleRepository roleRepository) {
        super();
        this.roleRepository = roleRepository;
    }
}

UserRegistrationController.java:

package com.example.demo.web;

<imports>

@Controller
@RequestMapping("/registration")
public class UserRegistrationController {

    private UserService userService;
    
    @ModelAttribute("user")
    public User user() {
        return new User();
    }
    
    @Autowired
    RoleRepository roleRepository;
    
    @GetMapping
    public String showRegistrationForm(Model model) {
        model.addAttribute("roles", roleRepository.findAll());
        return "registration";
    }
    
    @PostMapping
    public String registerUserAccount(User user) {
        System.out.println();
        userService.saveUser(user);
        return "redirect:/registration?success";
    }
}

SecurityConfiguration.java (на всякий случай):

package com.example.demo.config;

<imports>

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;
    
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers(
            "/registration**",
            "/js/**",
            "/css/**",
            "/img/**").permitAll().anyRequest().authenticated().
            and().formLogin().loginPage("/login").permitAll().
            and().logout().invalidateHttpSession(true).clearAuthentication(true)
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/login?logout")
            .permitAll();
    }
    
}

Соответствующий фрагмент кода из Registration.html:

<form th:action = "@{/registration}" method = "post" th:object = "${user}">
    <div class = "form-group">
        <label class = "control-label" for = "firstName">First name</label> <input
            id = "firstName" class = "form-control" th:field = "*{firstName}"
            required autofocus = "autofocus" />
    </div>

    <div class = "form-group">
        <label class = "control-label" for = "lastName">Last name</label> <input
            id = "lastName" class = "form-control" th:field = "*{lastName}"
            required autofocus = "autofocus" />
    </div>

    <div class = "form-group">
        <label class = "control-label" for = "account">Account</label> <input
            id = "account" class = "form-control" th:field = "*{account}" required
            autofocus = "autofocus" />
    </div>

    <div class = "form-group">
        <label class = "control-label" for = "password">Password</label> <input
            id = "password" class = "form-control" type = "password"
            th:field = "*{password}" required autofocus = "autofocus" />
    </div>
    
    <div class = "form-group">
        <label class = "control-label" for = "role">Role</label> 
        <select class = "form-control" th:field = "*{role}" id = "role">
            <option th:each = "role: ${roles}" th:value = "${role}" th:text = "${role.name}"></option>
        </select>
    </div>

    <div class = "form-group">
        <button type = "submit" class = "btn btn-success">Register</button>
        <span>Already registered? <a href = "/" th:href = "@{/login}">Login
                here</a></span>
    </div>
</form>

Проблема: Всякий раз, когда я пытаюсь создать пользователя с выбранной ролью, я получаю следующую ошибку:

Ошибка поля в объекте «пользователь» в поле «роль»: отклоненное значение [com.example.demo.model.Role@6e32df30]; коды [typeMismatch.user.role, typeMismatch.role, typeMismatch.com.example.demo.model.Role, typeMismatch]; аргументы [org.springframework.context.support.DefaultMessageSourceResolvable: коды [user.role,role]; аргументы []; сообщение по умолчанию [роль]]; сообщение по умолчанию [Не удалось преобразовать значение свойства типа 'java.lang.String' в требуемый тип 'com.example.demo.model.Role' для свойства 'role'; вложенным исключением является org.springframework.core.convert.ConversionFailedException: не удалось преобразовать тип [java.lang.String] в тип [java.lang.Long] для значения 'com.example.demo.model.Role@6e32df30'; вложенным исключением является java.lang.NumberFormatException: для входной строки: "com.example.demo.model.Role@6e32df30"]]

Итак, я предполагаю, что мой код должен использовать значение роли, которое мы получаем из формы (${role}), и применять его к переменной роли в User.java, соединяя их в отношениях. Вместо этого, по-видимому, происходит то, что вместо того, чтобы принимать значение роли, оно принимает строковое значение «com.example.demo.model.Role@[random id]» из формы.

Я все еще новичок в Spring Boot и Thymeleaf. Кто-нибудь знает, в чем проблема? Я пытался найти решения именно этой проблемы в течение многих часов, но до сих пор не могу найти ничего, что помогло бы мне. Заранее спасибо.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
254
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хорошо, я еще немного осмотрелся и, наконец, сам нашел решение. Оказывается, мне пришлось передать th:value="${role.id}" вместо "${role}". Вот изменения, которые я сделал:

UserRegistrationController.java:

package com.example.demo.web;

<imports>

@Controller
@RequestMapping("/registration")
public class UserRegistrationController {

    private UserService userService;

    public UserRegistrationController(UserService userService) {
        super();
        this.userService = userService;
    }
    
    @Autowired
    RoleRepository roleRepository;
    
    @GetMapping
    public String showRegistrationForm(Model model, User user) {
        model.addAttribute("roles", roleRepository.findAll());
        return "registration";
    }
    
    @PostMapping
    public String registerUserAccount(@Valid @ModelAttribute("user") User user, BindingResult result) {
        userService.saveUser(user);
        return "redirect:/registration?success";
    }
}

Соответствующий фрагмент кода из Registration.html:

<form th:action = "@{/registration}" method = "post" th:object = "${user}">
    <div class = "form-group">
        <label class = "control-label" for = "firstName">First name</label> <input
            id = "firstName" class = "form-control" th:field = "*{firstName}"
            required autofocus = "autofocus" />
    </div>

    <div class = "form-group">
        <label class = "control-label" for = "lastName">Last name</label> <input
            id = "lastName" class = "form-control" th:field = "*{lastName}"
            required autofocus = "autofocus" />
    </div>

    <div class = "form-group">
        <label class = "control-label" for = "account">Account</label> <input
            id = "account" class = "form-control" th:field = "*{account}" required
            autofocus = "autofocus" />
    </div>

    <div class = "form-group">
        <label class = "control-label" for = "password">Password</label> <input
            id = "password" class = "form-control" type = "password"
            th:field = "*{password}" required autofocus = "autofocus" />
    </div>
    
    <div class = "form-group">
        <label class = "control-label" for = "role">Role</label> 
        <select class = "form-control" th:field = "*{role}" id = "role">
            <option th:each = "role: ${roles}" th:value = "${role.id}" th:text = "${role.name}"></option>
        </select>
    </div>

    <div class = "form-group">
        <button type = "submit" class = "btn btn-success">Register</button>
        <span>Already registered? <a href = "/" th:href = "@{/login}">Login
                here</a></span>
    </div>
</form>

Надеюсь, это поможет любому, кто был так же сбит с толку.

Другие вопросы по теме