【问题标题】:Error accessing field by reflection for persistent property [model.Credentials#email] on @EmbeddedID通过 @EmbeddedID 上的持久属性 [model.Credentials#email] 的反射访问字段时出错
【发布时间】:2021-05-30 06:55:03
【问题描述】:

我在 Spring Boot 应用程序上实现 Authorities 时遇到了问题,经过一番挖掘后,我意识到我的 CredentialsAuthorities 表之间的关联可能是错误的。

事实上,我注意到 Spring 允许所有类型的用户(无论他们的权限如何)访问任何方法,即使是我认为已经安全的方法。那时,我实现了一个.findAll() 方法来实际查看关联是否存在某种问题,并且确实存在。

让我先展示一下 MySQL 表:

CREATE TABLE credentials (
  credential_id bigint UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  email varchar(50) NOT NULL UNIQUE,
  password varchar(255) NOT NULL,
  enabled BOOLEAN NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


CREATE TABLE authorities (
  email varchar(50) NOT NULL,
  authority varchar(50) NOT NULL,
  PRIMARY KEY (email, authority),
  CONSTRAINT fk_authorities_credentials FOREIGN KEY(email) REFERENCES credentials(email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在跳转到实体中的关联之前,值得注意的是,权限实体具有反映表设计的嵌入键。所以email 列同时是一个foreign key 和一个主:

@Embeddable
public class AuthoritiesKey implements Serializable {
  @OneToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "email", nullable = false
          ,referencedColumnName = "email")
  private Credentials credentials;

  @JoinColumn(name = "authority", nullable = false)
  private String authority;

//getters setters omitted
}

凭证类:

@Entity
@Table(name = "credentials")
public class Credentials implements Serializable {

  @OneToOne(cascade = CascadeType.ALL,
          fetch = FetchType.LAZY,
          mappedBy = "ak.credentials")
  private Authorities authorities;

//other fields and getters/setters omitted
}

删除双边关联(因此只保留在 AuthoritiesKey 类上),hibernate 成功返回所有凭据(当然没有他们的权限)和所有权限(这次是在凭据之前)。

我无法理解它。

当我离开双边关联时,我得到的完整有意义的堆栈跟踪如下:

Error accessing field [private java.lang.String com.server.model.Credentials.email] by reflection for persistent property [com.servermodel.Credentials#email] : 64; 
nested exception is org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.String com.server.model.Credentials.email] by reflection for persistent property [com.server.model.Credentials#email] : 64

其中 64 是 Credential 表中(最后一个,但被调用的第一个)行的 id。

=== 更新 ===

public class CredentialsService implements UserDetailsService {

  @Autowired
  private CredentialsRepository cr;

   @Override
  public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    Credentials user = cr.findByEmail(email);
    if (user == null){
      throw new UsernameNotFoundException(email);
    }
    return new UserPrincipal(user);
  }
}

【问题讨论】:

    标签: java spring-boot hibernate spring-security spring-data-jpa


    【解决方案1】:

    由于休眠错误,您似乎无法在此处使用@OneToOne。 (请参阅HHH-3824)即使使用 Hibernate 5.4.28.Final 也可以复制它

    作为解决方法,我建议您以这种方式更正您的映射:

    @Entity
    @Table(name = "authorities")
    public class Authorities {
       
       @EmbeddedId
       private AuthoritiesKey pk;
    
       // ...
    }
    
    @Embeddable
    public class AuthoritiesKey implements Serializable {
    
      @ManyToOne(fetch = FetchType.LAZY)
      @JoinColumn(name = "email", referencedColumnName = "email", nullable = false)
      private Credentials credentials;
    
      @Column(name = "authority", nullable = false)
      private String authority;
    
      public AuthoritiesKey(Credentials credentials, String authority) {
        this.credentials = credentials;
        this.authority = authority;
      }
    
      public AuthoritiesKey() {
      }
    
      // getters setters omitted
      
      @Override
      public boolean equals(Object o) {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
    
        AuthoritiesKey pk = (AuthoritiesKey) o;
        return Objects.equals( credentials, pk.credentials ) &&
                Objects.equals( authority, pk.authority );
      }
    
      @Override
      public int hashCode() {
        return Objects.hash( credentials, authority );
      }
    }
    
    @Entity
    @Table(name = "credentials")
    public class Credentials implements Serializable {
       
       @Id
       @Column(name = "credential_id")
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
    
       @OneToMany(cascade = CascadeType.ALL,
            fetch = FetchType.LAZY,
            mappedBy = "pk.credentials")
       private List<Authorities> authorities;
    
       @NaturalId
       @Column(name = "email")
       private String email;
    
       @Transient
       public Authorities getAuthority()
       {
          return this.authorities != null && this.authorities.size() > 0
              ? this.authorities.get(0) : null;
       }
     
       // ...
    }
    

    【讨论】:

    • 谢谢SternK,试过了,不行。我还修改了AuthoritiesRepository接口如下: public interface AuthoritiesRepository extends JpaRepository (替换ID)
    • 能否请您显示您获得的堆栈跟踪。
    • 我用完整的堆栈跟踪更新了这个问题,谢谢。
    • 能否请您出示CredentialsService.loadUserByUsername 方法。
    • 天哪,你救了我!我花了 6 个小时检查和仔细检查才发现这不是我的错。你把我从沮丧和绝望的夜晚救了出来。非常感谢!
    猜你喜欢
    • 2021-07-19
    • 2020-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    相关资源
    最近更新 更多