我应该如何在SpringBoot中使用JpaRepository.findOne()?

时间:2019-01-05 08:50:03

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

我刚刚通过阅读《 Spring Boot in Action》 一书开始学习Spring Boot,我正在学习本书的示例,尝试自己运行它们,但是我有一个使用#include<stdio.h> main(){ int n; printf("set:"); scanf("%d",&n); float arr[n+1],resarr[n+1]; float sum=0; for(int i=1; i<=n; i++){ scanf("%f",&arr[i]); sum=arr[i]+sum; float res= sum/(float)i; resarr[i]=res; } int i=1; while(i<=n) { printf("%0.10f\n",resarr[i]); i++; } return 0; } 时出现问题。

我遍历了本章以查找可能的不匹配之处。但是,它根本不起作用。

该项目应该是一个简单的阅读清单。

这是代码:

读者@Entity:

JpaRepository.findOne()

Jpa界面:

package com.lixin.readinglist;

import org.springframework.data.annotation.Id;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.Entity;
import java.util.Collection;
import java.util.Collections;

/**
 * @author lixin
 */
@Entity
public class Reader implements UserDetails {

    private static final long serialVersionUID = 1L;

    @Id
    private String username;
    private String fullname;
    private String password;

    @Override
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getFullname() {
        return fullname;
    }

    public void setFullname(String fullname) {
        this.fullname = fullname;
    }

    @Override
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.singletonList(new SimpleGrantedAuthority("READER"));
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

SecurityConfig:

package com.lixin.readinglist;

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

/**
 * @author lixin
 */
public interface ReaderRepository extends JpaRepository<Reader, String> {
}

我一直收到此错误:

package com.lixin.readinglist;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

/**
 * @author lixin
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final ReaderRepository readerRepository;

    @Autowired
    public SecurityConfig(ReaderRepository readerRepository) {
        this.readerRepository = readerRepository;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/").access("hasRole('READER')")
                .antMatchers("/**").permitAll()
                .and()
                .formLogin()
                .loginPage("/login")
                .failureUrl("/login?error=true");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService((UserDetailsService) username -> readerRepository.findOne(username));
    }
}

6 个答案:

答案 0 :(得分:4)

findOne()被定义为<S extends T> Optional<S> findOne(Example<S> example);
这意味着在您的情况下,它接受Example<Reader>并返回Optional<Reader>
您将String传递给它,这是错误的,并且将其用作AuthenticationManagerBuilder.userDetailsService()中的lambda返回值,这也是错误的 因为UserDetailsService是定义为

的接口功能
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

因此,您需要返回一个UserDetails实例而不是它的Optional;如果与用户名to be compliant with the javadoc不匹配,则抛出UsernameNotFoundException

  

返回:

     

完全填充的用户记录(绝不为空)

     

投掷:

     

UsernameNotFoundException-如果找不到该用户或该用户   没有GrantedAuthority

此外,您无需使用作为示例查询的findOne()。通过ID查询就足够了。

因此您可以编写类似的内容:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   auth.userDetailsService(username -> readerRepository.findById(username)
                                                       .orElseThrow( () -> new UsernameNotFoundException("user with username " + username + " not found"));
}

请注意,getOne()非常棘手,因为它依赖于延迟加载,在某些情况下可能会带来令人惊讶的意外。
JB Nizet的话很有趣。 因此,我现在进行了测试。当Spring Security类访问实体(即isAccountNonLocked())时,JPA会话仍然没有打开。
因此,在任何情况下(用户名正确或否)都将引发LazyInitializationException

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:155)
        at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:268)
        at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73)
        at davidhxxx.example.angularsboot.model.db.User_$$_jvstd90_5.isAccountNonLocked(User_$$_jvstd90_5.java)
        at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider$DefaultPreAuthenticationChecks.check(AbstractUserDetailsAuthenticationProvider.java:352)
        at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:165)

This question可能会让您感兴趣。

答案 1 :(得分:1)

阅读@davidxxx的answer和@JB Nizet的评论

我发现自己犯了一个严重的错误,用getOne()代替findOne()的想法肯定会违反合同,并时不时地产生一些意外的惊喜。

我意识到Nizet的评论非常出色。我只是一个天真的学生,很荣幸能纠正我的错误并引导我采用正确的方法。我将编辑我的答案以更正我天真的错误。谢谢(@JB Nizet && @davidxxx) &&,谢谢!

解决方案:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.
            userDetailsService(username -> readerRepository.findById(username)
                    .orElseThrow(() -> new UsernameNotFoundException("user with username " + username + " not found")));
}

您可以找到原因here#实际上,这是@davidxxx的答案。

答案 2 :(得分:1)

正如其他人所说,在最新版本的Spring Data 2.x中,您应该使用findById,而不是findOne,而应使用最新版本的Spring Data(如果是Spring-Boot 2.x的一部分,则是findOne)。使用那个)想要一个示例对象。我的猜测是,您所使用的书是在Spring 5 / Spring Boot 2 / Spring Data 2.x的最新发行版之前编写的。

希望将迁移指南作为参考,并与[稍微过时]的书一起使用会有所帮助:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide

答案 3 :(得分:1)

您可以使用getOne()代替findOne()。作者可能犯了一个错误。

答案 4 :(得分:0)

您可以使用findById代替findOne,findOne需要一个示例对象,您可以在here中查找更多

答案 5 :(得分:0)

findOne方法来自名为QueryByExampleExecutor的接口,JpaRepository接口对其进行了扩展。而QueryByExampleExecutor用于QBE(一种使用Example的查询)。在您的代码中,不应使用它,可以使用getOne方法或findById方法,该findById继承自CrudRepository接口。