在JPA中坚持使用可嵌入Id的OneToMany [英] Persisting OneToMany with Embeddable Id in JPA

查看:76
本文介绍了在JPA中坚持使用可嵌入Id的OneToMany的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

授权服务基于 http:// www。 svlada.com/jwt-token-authentication-with-spring-boot/ (可惜它没有提供注册示例)



我有以下实体和服务:

User.java

  package com.test.entity; 

import javax.persistence。*;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name =user)
public class User implements Serializable {
private static final long serialVersionUID = 1322120000551624359L;

@Id
@Column(name =id)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name =username)
private String username;

@Column(name =password)
private String password;

@Column(name =first_name)
private String firstName;

@Column(name =last_name)
private String lastName;

@Column(name =activated)
private布尔激活;

@Column(name =activation_token)
private String activationToken;

@Column(name =activation_token_exp)
私人时间戳activationTokenExpirationDate;

@Column(name =reset_token)
private String resetToken;

@Column(name =reset_token_exp)
私人时间戳resetTokenExpirationDate;

@Column(name =created)
private LocalDateTime created;

@Column(name =updated)
私人LocalDateTime更新;

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name =user_id,referencedColumnName =id)
private List< UserRole> roles = new ArrayList<>(0);

public User(){}

// getters and setters
}


$ b

UserRole.java

  @Entity 
@Table(name =user_role)
public class UserRole实现Serializable {
@Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = 1322120000551624359L;

@Column(name =user_id)
保护长userId;

@Enumerated(EnumType.STRING)
@Column(name =role)
保护角色角色;

public Id(){}

public Id(Long userId,Role role){
this.userId = userId;
this.role =角色;

$ b @Override
public boolean equals(Object o){
if(this == o)
return true;
if(o == null || getClass()!= o.getClass())
return false;

Id id =(Id)o;

if(!userId.equals(id.userId))
return false;
返回角色== id.role;
}

@Override
public int hashCode(){
int result = userId.hashCode();
result = 31 * result + role.hashCode();
返回结果;
}
}

@EmbeddedId
Id id = new Id();

@Enumerated(EnumType.STRING)
@Column(name =role,insertable = false,updatable = false)
保护角色角色;
$ b $ public UserRole(){
}

public UserRole(角色角色){
this.role = role;
}

public Role getRole(){
return role;
}

public void setRole(角色角色){
this.role = role;
}
}

UserService.java

  @Override 
public User userUser(UserDTO userDto){
可选< User> existingUser = this.getByUsername(userDto.getUsername());
if(existingUser.isPresent()){
throw new RegistrationException(User is already taken);
}

User newUser = new User();
newUser.setUsername(userDto.getUsername());
newUser.setPassword(encoder.encode(userDto.getPassword()));
newUser.setFirstName(userDto.getFirstName());
newUser.setLastName(userDto.getLastName());
newUser.setActivated(Boolean.FALSE);
newUser.setActivationToken(RandomUtil.generateActivationKey());
newUser.setActivationTokenExpirationDate(Timestamp.valueOf(LocalDateTime.now()。plusSeconds(ACTIVATION_TOKEN_TTL)));
newUser.setCreated(LocalDateTime.now());

newUser.addRole(new UserRole(Role.MEMBER));

返回userRepository.save(newUser);

这行返回userRepository.save(newUser); 然而会抛出一个异常,因为无法保持关系。我无法手动设置UserRole ID(userId +角色),因为我还没有它们(用户将被持久保存)

  com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:列'role'不能为空
在sun.reflect.NativeConstructorAccessorImpl.newInstance0(本地方法)
在sun.reflect.NativeConstructorAccessorImpl。 newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
在com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
在com.mysql.jdbc.Util.getInstance(Util.java:387)
在com.mysql.jdbc .SQLError.createSQLException(SQLError.java:934)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3966)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java :3902)
在com.mysql.jdbc.MysqlIO.s endCommand(MysqlIO.java:2526)
...
...

这是持续这种关系的正确方法,同时将组合主键作为Embeddable吗?



如果我避免设置与UserRole的关系,用户会正确保持(没有角色)

数据库

  CREATE TABLE`user`(
`id` bigint(20)unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50)NOT NULL,
`password` varchar(64)NOT NULL,
`first_name` varchar(20)DEFAULT NULL,
`last_name` varchar(20)DEFAULT NULL,$ b $``activated` tinyint(1)NOT NULL DEFAULT'0',
`activation_token` varchar(50)DEFAULT NULL,
`activation_token_exp` timestamp NULL DEFAULT NULL,
`reset_token` varchar(50)DEFAULT NULL,
`reset_token_exp` timestamp NULL DEFAULT NULL,
`created`日期时间NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated`时间戳NULL DEFAULT NULL,
PRIMARY KEY(`id`)
)ENGINE = InnoDB AUTO_INCREMENT = 27 DEFAULT CHARSET = utf8;

CREATE TABLE`user_role`(
`user_id` bigint(20)unsigned NOT NULL,
`role` varchar(50)NOT NULL DEFAULT'',
PRIMARY KEY(`user_id`,`role`)
)ENGINE = InnoDB DEFAULT CHARSET = utf8;


解决方案

UserRole ,该角色被映射两次:一次作为简单属性

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ b @Column(name =role,insertable = false,updatable = false)
保护角色角色;

并再次嵌入到嵌入ID中:

  @Enumerated(EnumType.STRING)
@Column(name =role)
保护角色角色;

当您调用 userRepository.save(newUser),您只设置简单的属性 UserRole.role 指向一个非空角色。但是,由于简单属性标记为 insertable = false ,因此它在 INSERT 语句中被忽略。 UserRole.id.role 反过来被设置为 null ,这是正在考虑的值 INSERT 语句。由于您已为角色列创建了非空约束,因此 INSERT 语句失败。

(请注意, DEFAULT''只有当 INSERT 子句的字段列表,这里不是这种情况)



解决方法是简单地更新 UserRole.id.role ,只要 User.role 正在设置。

An authorisation service is based upon http://www.svlada.com/jwt-token-authentication-with-spring-boot/ (sadly it doesn't provide a registration example)

I have the following Entities and Service:

User.java

package com.test.entity;

import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "user")
public class User implements Serializable {
  private static final long serialVersionUID = 1322120000551624359L;

  @Id
  @Column(name = "id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(name = "username")
  private String username;

  @Column(name = "password")
  private String password;

  @Column(name = "first_name")
  private String firstName;

  @Column(name = "last_name")
  private String lastName;

  @Column(name = "activated")
  private Boolean activated;

  @Column(name = "activation_token")
  private String activationToken;

  @Column(name = "activation_token_exp")
  private Timestamp activationTokenExpirationDate;

  @Column(name = "reset_token")
  private String resetToken;

  @Column(name = "reset_token_exp")
  private Timestamp resetTokenExpirationDate;

  @Column(name = "created")
  private LocalDateTime created;

  @Column(name = "updated")
  private LocalDateTime updated;

  @OneToMany(cascade = CascadeType.ALL)
  @JoinColumn(name = "user_id", referencedColumnName = "id")
  private List<UserRole> roles = new ArrayList<>(0);

  public User() { }

  // getters and setters
}

UserRole.java

@Entity
@Table(name = "user_role")
public class UserRole implements Serializable {
  @Embeddable
  public static class Id implements Serializable {
    private static final long serialVersionUID = 1322120000551624359L;

    @Column(name = "user_id")
    protected Long userId;

    @Enumerated(EnumType.STRING)
    @Column(name = "role")
    protected Role role;

    public Id() { }

    public Id(Long userId, Role role) {
      this.userId = userId;
      this.role = role;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o)
        return true;
      if (o == null || getClass() != o.getClass())
        return false;

      Id id = (Id) o;

      if (! userId.equals(id.userId))
        return false;
      return role == id.role;
    }

    @Override
    public int hashCode() {
      int result = userId.hashCode();
      result = 31 * result + role.hashCode();
      return result;
    }
  }

  @EmbeddedId
  Id id = new Id();

  @Enumerated(EnumType.STRING)
  @Column(name = "role", insertable = false, updatable = false)
  protected Role role;

  public UserRole() {
  }

  public UserRole(Role role) {
    this.role = role;
  }

  public Role getRole() {
    return role;
  }

  public void setRole(Role role) {
    this.role = role;
  }
}

UserService.java

@Override
  public User registerUser(UserDTO userDto) {
    Optional<User> existingUser = this.getByUsername(userDto.getUsername());
    if (existingUser.isPresent()) {
      throw new RegistrationException("User is already taken");
    }

    User newUser = new User();
    newUser.setUsername(userDto.getUsername());
    newUser.setPassword(encoder.encode(userDto.getPassword()));
    newUser.setFirstName(userDto.getFirstName());
    newUser.setLastName(userDto.getLastName());
    newUser.setActivated(Boolean.FALSE);
    newUser.setActivationToken(RandomUtil.generateActivationKey());
    newUser.setActivationTokenExpirationDate(Timestamp.valueOf(LocalDateTime.now().plusSeconds(ACTIVATION_TOKEN_TTL)));
    newUser.setCreated(LocalDateTime.now());

    newUser.addRole(new UserRole(Role.MEMBER));

    return userRepository.save(newUser);
  }

This line return userRepository.save(newUser); however throws an exception as is not able to persist the relation. I can't set the UserRole ID (userId + role) manually as I don't yet have them (the user is to be persisted)

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'role' cannot be null
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
    at com.mysql.jdbc.Util.getInstance(Util.java:387)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:934)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3966)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3902)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2526)
    ...
    ...

Is this the correct way to persist this kind of relation while having an composite primary key as Embeddable?

If I avoid setting the realation with UserRole, the user gets persisted correctly (with no roles)

DB

CREATE TABLE `user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(64) NOT NULL,
  `first_name` varchar(20) DEFAULT NULL,
  `last_name` varchar(20) DEFAULT NULL,
  `activated` tinyint(1) NOT NULL DEFAULT '0',
  `activation_token` varchar(50) DEFAULT NULL,
  `activation_token_exp` timestamp NULL DEFAULT NULL,
  `reset_token` varchar(50) DEFAULT NULL,
  `reset_token_exp` timestamp NULL DEFAULT NULL,
  `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;

CREATE TABLE `user_role` (
  `user_id` bigint(20) unsigned NOT NULL,
  `role` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`user_id`,`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

解决方案

In UserRole, the role is mapped twice: once as a simple property

@Enumerated(EnumType.STRING)
@Column(name = "role", insertable = false, updatable = false)
protected Role role;

and once more inside the embedded id:

@Enumerated(EnumType.STRING)
@Column(name = "role")
protected Role role;

At the time you call userRepository.save(newUser), you've only set the simple property UserRole.role to point to a non-null role. However, since the simple property is marked as insertable=false, it is being ignored in the INSERT statement. UserRole.id.role, in turn, is set to null and that is the value that is being considered for the INSERT statement. Since you've created a non-null constraint for the role column, the INSERT statement fails.

(note that DEFAULT '' is only honored when the column is not present in the INSERT clause's field list, which is not the case here)

The solution is, simply, to update the value of UserRole.id.role whenever User.role is being set.

这篇关于在JPA中坚持使用可嵌入Id的OneToMany的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆