[英]Spring Jpa Bulk Insert whole data or Update some fields of entity if already available
[英]Spring Data JPA update makes some fields null
我有一個簡單的場景,其中有實體:
@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;
}
一個角色實體很簡單:Role.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 安全配置如下:
安全配置.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;
}
}
一切正常,但是我用以下形式更新了用戶實體的特定字段:
<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
。 幾天前它工作正常,沒有任何奇怪的行為。 我搜索了很多,發現@DynamicUpdate 和 Spring Data JPA ,但它從來沒有幫助過我。 任何人,請在這種情況下幫助我。
當您保存User user
時,當前user
object 的所有數據都會更新。 如果任何數據不是更新形式,這意味着數據是 null 和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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.