Обновление Spring Data JPA делает некоторые поля пустыми - PullRequest
0 голосов
/ 28 апреля 2020

У меня есть простой сценарий, в котором есть сущности:

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Data
public abstract class AuditModel implements Serializable {

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_at", nullable = false, updatable = false)
    @CreatedDate
    private Date createdAt;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated_at", nullable = false)
    @LastModifiedDate
    private Date updatedAt;

    @Column(name = "created_by")
    @CreatedBy
    private String createdBy;

    @Column(name = "modified_by")
    @LastModifiedBy
    private String modifiedBy;

}

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

@Data
@Entity(name = "users")
@NoArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class User extends AuditModel{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="id", unique = true, nullable = false)
    private Integer userId;

    @NonNull
    private String userFirstName;

    @NonNull
    private String userLastName;

    @Email
    @NonNull
    private String userEmail;

    @NonNull
    private String userPassword;

    @Column(name = "enabled")
    private boolean userEnabled;

    @NonNull
    private String userFbLink;

    @NonNull
    private String userTwLink;

    @NonNull
    private String userLiLink;

    @NonNull
    @ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.MERGE)
    @JoinTable(
            name="user_role",
            joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")},
            inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
    private List<Role> roles;

}

Роль сущности так же просто, как: Роль. java

@Data
@Entity(name = "roles")
@NoArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false, exclude = {"userList"})
public class Role  extends AuditModel{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="id", unique = true, nullable = false)
    private Integer roleId;

    @NonNull
    @Column(nullable = false, unique = true)
    @NotEmpty
    private String roleName;

    @ManyToMany(mappedBy = "roles")
    private List<User> userList;

}

Конфигурация Spring Security имеет вид:

SecurityConfiguration. java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final UserDetailsServiceImpl userDetailsService;

    @Autowired
    public SecurityConfiguration(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/assets/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        String [] permissibleResources = {"/register**", "/login**","/post_register", "/confirm**", "/reset", "/api/**"};
        http.csrf().disable()
                .authorizeRequests()
                    .antMatchers(permissibleResources).permitAll()
                        // START Add missing configs
                        .anyRequest()
                        .authenticated()
                        // END Add missing configs
                .and()
                    .formLogin()
                    .loginPage("/login")
                        // username password
                        .usernameParameter("username")
                        .passwordParameter("password")
                        // success and failure handlers
                    .successHandler(appAuthenticationSuccessHandler())
                    .failureHandler(appAuthenticationFailureHandler())
                    .permitAll()
                .and()
                    .logout()
                        .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                        .logoutSuccessUrl("/login?logout")
                        .invalidateHttpSession(true)
                        .clearAuthentication(true)
                .permitAll()
                .and()
                    .exceptionHandling().accessDeniedHandler(accessDeniedHandler())
                .and()
                    .headers()
                    .defaultsDisabled()
                    .cacheControl()
        ;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .authenticationProvider(daoAuthenticationProvider());
    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler(){
        return new AppAccessDeniedHandler();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(bCryptPasswordEncoder());

        return provider;
    }

    @Bean
    public PasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder(5);
    }

    // Auth success handler
    @Bean
    public AuthenticationSuccessHandler appAuthenticationSuccessHandler(){
        return new AppAuthenticationSuccessHandler();
    }

    // Auth failure handler
    @Bean
    public AuthenticationFailureHandler appAuthenticationFailureHandler() {

        ExceptionMappingAuthenticationFailureHandler failureHandler = new ExceptionMappingAuthenticationFailureHandler();
        Map<String, String> failureUrlMap = new HashMap<>();
        failureUrlMap.put(BadCredentialsException.class.getName(), AppAuthenticationFailureHandler.BAD_CREDENTIALS_URL);
        failureUrlMap.put(AccountExpiredException.class.getName(), AppAuthenticationFailureHandler.EXPIRED_URL);
        failureUrlMap.put(LockedException.class.getName(), AppAuthenticationFailureHandler.LOCKED_URL);
        failureUrlMap.put(DisabledException.class.getName(), AppAuthenticationFailureHandler.DISABLED_URL);
        failureHandler.setExceptionMappings(failureUrlMap);

        return failureHandler;

    }

}

все работало нормально, но пока я обновляю указанные c поля поля пользовательский объект со следующей формой:

<form th:action="@{/user/__${user.userId}__/update}" th:object="${user}" method="post">
    <h5>Personal</h5>
    <hr>          
    <input type="hidden" th:value="${user.userId}" th:field="*{userId}" /> <br>       
    <input type="text" th:field="*{userFirstName}" th:value="${user.userFirstName}" required /> <br>
    <input type="text" th:field="*{userLastName}" th:value="${user.userLastName}" required /><br>
    <h5>Social</h5>
    <hr>
    <input type="url" th:field="*{userFbLink}" th:value="${user.userFbLink}" /><br>
    <input type="url" th:field="*{userTwLink}" th:value="${user.userTwLink}" /><br>        
    <input type="url" th:field="*{userLiLink}" th:value="${user.userLiLink}" /><br>
    <button name="save-user" type="submit">Save</button>
    </div>
</form>

через UserController. java задан ниже:

@Controller
@RequestMapping(value = "/user")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(
            UserService userService
    ) {
        this.userService = userService;
    }

    @PreAuthorize(value = "hasAuthority('USER')")
    @GetMapping(value = "/{id}/edit")
    public String editBio(
            @PathVariable("id") Integer id,
            Model model
    ){
        User user = userService.findById(id);
        String pageTitle = "Edit User " + user.getUserFirstName() ;
        model.addAttribute("pageTitle", pageTitle);
        model.addAttribute("user", user);
        return "edit_user";
    }

    @PreAuthorize(value = "hasAuthority('USER')")
    @PostMapping(value = "/{id}/update")
    public String updateBio(
            @PathVariable("id") Integer id,
            @ModelAttribute("user") @Valid User user,
            BindingResult result
    ){
        if (result.hasErrors()) {
            return "edit_user";
        }
        userService.save(user);
        return "redirect:/user/" + id;
    }
}

выполняет запрос как:

Hibernate: update users set created_by=?, modified_by=?, user_first_name=?, user_last_name=?, updated_at=?, enabled=?, user_fb_link=?, user_li_link=?, user_password=?, user_tw_link=?, where id=?

и

Hibernate: delete from user_role where user_id=?

Вставляет новые данные только в поля, указанные в форме обновления, все остальные поля становятся null, даже пароль пользователя становится null, а включенное поле становится false. Роль пользователя также удаляется. Когда я выхожу и пытаюсь войти снова, я получаю ошибку disabled. Пару дней go все работало нормально, без каких-либо странных действий. Я много искал и нашел @ DynamicUpdate с Spring Data JPA , но это мне никогда не помогало. Кто-нибудь, пожалуйста, помогите мне в этом случае.

1 Ответ

1 голос
/ 28 апреля 2020

При сохранении User user все данные текущего объекта user будут обновлены. Если какие-либо данные не находятся в форме обновления, это означает, что данные являются нулевыми и для boolean это false. Так что извлеките данные из базы данных, используя id, затем установите новое значение в fetch user, затем сохраните.

@PostMapping(value = "/{id}/update")
public String updateBio(
        @PathVariable("id") Integer id,
        @ModelAttribute("user") @Valid User updatedUser,
        BindingResult result
){
    if (result.hasErrors()) {
        return "edit_user";
    }
    User user = userService.findById(updatedUser.getUserId());
    // Set the updated data from updatedUser to user 
    user.setUserFirstName(updatedUser.getUserFirstName()); // set other updated field like this
    // Now save
    userService.save(user);
    return "redirect:/user/" + id;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...