Spring Boot启动问题与Spring Boot JPA Starter相关

3

当我尝试使用spring-boot-starter-data-jpa启动我的spring boot项目时,出现了一个奇特的错误。奇怪的是,在添加一些存储库和服务之前,我的应用程序可以正常启动,但我似乎无法缩小范围,找到为什么Spring无法初始化先前有效的存储库。

以下是相关错误:

11:38:42.313 INFO  org.hibernate.Version.logVersion() @37 [localhost-startStop-1] - HHH000412: Hibernate Core {5.0.11.Final}
11:38:42.316 INFO  org.hibernate.cfg.Environment.<clinit>() @213 [localhost-startStop-1] - HHH000206: hibernate.properties not found 
11:38:42.319 INFO  org.hibernate.cfg.Environment.buildBytecodeProvider() @317 [localhost-startStop-1] - HHH000021: Bytecode provider name : javassist
11:38:42.430 INFO  org.hibernate.annotations.common.Version.<clinit>() @66 [localhost-startStop-1] - HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
11:38:43.165 INFO  org.hibernate.dialect.Dialect.<init>() @156 [localhost-startStop-1] - HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
11:38:43.583 ERROR org.springframework.boot.context.embedded.tomcat.TomcatStarter.onStartup() @63 [localhost-startStop-1] - Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'emailAuthenticationFilter' defined in class path resource [gg/leet/security/WebSecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [gg.leet.security.EmailAuthenticationFilter]: Factory method 'emailAuthenticationFilter' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationManager' defined in class path resource [gg/leet/security/WebSecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.authentication.AuthenticationManager]: Factory method 'authenticationManager' threw exception; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'emailAuthenticationProvider': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userServiceImpl' defined in file [/Users/andrew/Programs/leet-tournaments/backend/build/classes/main/gg/leet/service/UserServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Cannot create inner bean '(inner bean)#76212c93' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#76212c93': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
11:38:43.617 WARN  org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext.refresh() @550 [restartedMain] - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat
11:38:43.631 ERROR org.springframework.boot.SpringApplication.reportFailure() @839 [restartedMain] - Application startup failed

最有趣的是以下这行:
11:38:43.583 ERROR org.springframework.boot.context.embedded.tomcat.TomcatStarter.onStartup() @63 [localhost-startStop-1] - Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException。Message: Error creating bean with name 'emailAuthenticationFilter' defined in class path resource [gg/leet/security/WebSecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [gg.leet.security.EmailAuthenticationFilter]: Factory method 'emailAuthenticationFilter' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationManager' defined in class path resource [gg/leet/security/WebSecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.authentication.AuthenticationManager]: Factory method 'authenticationManager' threw exception; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'emailAuthenticationProvider': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userServiceImpl' defined in file [/Users/andrew/Programs/leet-tournaments/backend/build/classes/main/gg/leet/service/UserServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Cannot create inner bean '(inner bean)#76212c93' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#76212c93': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory。
因此,出问题的类是userServiceImpl的参数0中的userRepository
这是我的UserRepository的样子:
package gg.leet.repository;

import gg.leet.model.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
    @Query("select u from User u where u.id = ?1")
    Optional<User> findOneById(Long id);
    User save(User user);
    Optional<User> findOneByUsername(String username);
    List<User> findByEmailContaining(String search);
    List<User> findByFirstNameContaining(String search);
    List<User> findByLastNameContaining(String search);
    List<User> findByUsernameContaining(String search);
    @Query("select u from User u where u.username like ?1 or u.firstName like ?1 or u.lastName like ?1")
    Page<User> findByUsernameOrLastNameOrFirstNameContaining(String search, Pageable pageable);
}

UserService 实现:

package gg.leet.service;

import gg.leet.model.User;
import gg.leet.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * Implementation of the UserService over the mysql repository
 */
@SuppressWarnings("unused")
@Service
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public Optional<User> getByUsername(String username) {
        return this.userRepository.findOneByUsername(username);
    }

    @Override
    public Optional<User> getByEmail(String email) { return this.userRepository.findByEmail(email); }

    @Override
    public Optional<User> getById(Long id) {
        return this.userRepository.findOneById(id);
    }

    @Override
    public User save(User user) {
        return userRepository.save(user);
    }

    @Override
    public Page<User> findByContaining(String search, Pageable pageable) {
        return this.userRepository.findByUsernameOrLastNameOrFirstNameContaining(search, pageable);
    }
}

虽然不是太复杂,但我不理解为什么我们不能实例化身份验证过滤器的bean。错误信息比较普通,所以我不知道应该去哪里寻找答案。我尝试在配置类中手动初始化entityManagerFactory bean,并在@EnableJpaRepositories注解中引用它,但无济于事。你有什么建议吗?

----- 编辑 1 -----

这是我的数据源配置,非常简单但过去一直有效:

spring:
  profiles: default
  datasource:
    url: jdbc:mysql://localhost:3306/leettournaments?autoReconnect=true&useSSL=false&useTimezone=true&serverTimezone=UTC
    username: root
    password:
  jpa:
    hibernate:
      ddl-auto: create-drop


spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    max-active: 25
    max-idle: 10
  jpa:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect

抱歉,您需要提供具体的待翻译内容。
@SuppressWarnings("unused")
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=Integer.MAX_VALUE, redisFlushMode=RedisFlushMode.IMMEDIATE)
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    private static final Logger LOGGER = LogManager.getLogger(WebSecurityConfig.class);

    @Value("${gg.leet.debug}")
    private boolean debug;

    /**
     * Establishes role hierarchy for user roles.
     * @return the RoleHierarchy
     */
    @Bean
    public RoleHierarchyImpl roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy(
                "ROLE_SUPER_ADMIN > ROLE_LOCATION_ADMIN " +
                        "ROLE_LOCATION_ADMIN > ROLE_REGULAR_USER ");
        return roleHierarchy;
    }

    @Bean
    public RoleHierarchyVoter roleVoter() {
        return new RoleHierarchyVoter(roleHierarchy());
    }


    @Bean
    HeaderHttpSessionStrategy sessionStrategy() {
        return new HeaderHttpSessionStrategy();
    }


    @Bean
    public AuthenticationProvider emailAuthenticationProvider() {
        return new EmailAuthenticationProvider();
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(Arrays.asList(
                emailAuthenticationProvider()
        ));
    }

    @Bean
    public AuthenticationSuccess authenticationSuccess() {
        return new AuthenticationSuccess();
    }

    @Bean
    public AuthenticationFailure authenticationFailure() {
        return new AuthenticationFailure();
    }

    @Bean
    public EmailAuthenticationFilter emailAuthenticationFilter() {
        EmailAuthenticationFilter filter = new EmailAuthenticationFilter(new AntPathRequestMatcher("/login-process", "POST"));
        applyFilterAuthRules(filter);
        return filter;
    }

    private void applyFilterAuthRules(AbstractAuthenticationProcessingFilter filter) {
        filter.setAuthenticationManager(authenticationManager());
        filter.setAuthenticationSuccessHandler(authenticationSuccess());
        filter.setAuthenticationFailureHandler(authenticationFailure());
        filter.setAllowSessionCreation(true);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
                .ignoring()
                .antMatchers("/", "/**/*.css", "/**/*.js", "/index.html", "/ws/**", "/assets/**/*");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        if(debug) {
            LOGGER.warn("Allowing preflight options requests to all");
            // For the pre-flight request for options
            http
                    .authorizeRequests()
                    .antMatchers(HttpMethod.OPTIONS, "/**")
                    .permitAll();
            // Disable csrf on debug dev
            LOGGER.warn("Allowing no CSRF protection");
            http
                    .csrf()
                    .disable();
        } else {
            http
                    .csrf()
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
        }

        http
                .authorizeRequests()
                .antMatchers( "/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)))
                .invalidateHttpSession(true)
                .permitAll()
                .and()
                .addFilterBefore(emailAuthenticationFilter(), FilterSecurityInterceptor.class);



        http.requestCache().requestCache(new NullRequestCache());
        http.headers().cacheControl();
        http.headers().httpStrictTransportSecurity().disable();
        // .headers().contentSecurityPolicy(
        // .and().antMatcher("/bingo/card").;
    }

    @Bean
    protected BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

------ 第三次编辑 ------

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.4.3.RELEASE'
        // classpath 'org.springframework:springloaded:1.2.6.RELEASE'
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

repositories {
    mavenCentral()
}

idea {
    module {
        inheritOutputDirs = false
        outputDir = file("$buildDir/classes/main/")
    }
}

jar {
    baseName = 'leet-tournaments'
    version = '0.1.0'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

configurations {
    dev
    all*.exclude module: 'spring-boot-starter-logging'
}

dependencies {

    // Spring Boot Starter Framework
    compile(
            [group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '1.4.3.RELEASE'],
            [group: 'org.springframework.boot', name: 'spring-boot-starter-log4j2', version: '1.2.0.RELEASE'],
            [group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '1.4.3.RELEASE'],
            [group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.3.2.RELEASE'],
            [group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '1.0.0.RELEASE'],
            [group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '1.4.3.RELEASE'],
            [group: 'org.springframework.boot', name: 'spring-boot-devtools', version: '1.4.3.RELEASE'],
            [group: 'org.springframework.session', name: 'spring-session', version: '1.3.0.RELEASE'],
            [group: 'org.springframework', name: 'spring-messaging', version: '4.3.6.RELEASE'],
    )

    compile(
            [group: 'javax.mail', name: 'mail', version: '1.4.7'],
            [group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.8.5'],
            [group: 'mysql', name: 'mysql-connector-java', version: '5.1.35'],
            [group: 'org.projectlombok', name: 'lombok', version: '1.16.12'],
            [group: 'com.goebl', name: 'david-webb', version: '1.3.0'],
            [group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.77'],
            [group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5', version: '2.8.8'],
            [group: 'com.google.guava', name: 'guava', version: '22.0-rc1'],
    )

    compile(
            [group: 'io.netty',                 name: 'netty-all',              version: '4.1.7.Final'],
            [group: 'io.projectreactor',        name: 'reactor-core',           version: '2.0.7.RELEASE'],
            [group: 'io.projectreactor',        name: 'reactor-net',            version: '2.0.7.RELEASE'],
            [group: 'io.projectreactor.spring', name: 'reactor-spring-core',    version: '2.0.7.RELEASE'],
            [group: 'io.projectreactor.spring', name: 'reactor-spring-context', version: '2.0.7.RELEASE'],
    )

    // testing
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile('junit:junit')
}

// gradle wrapper
task wrapper(type: Wrapper) {
    gradleVersion = '3.0'
}

// run spring boot app
bootRun {
    addResources = true
    classpath = sourceSets.main.runtimeClasspath + configurations.dev
}

主类:

@SpringBootApplication
@EnableConfigurationProperties
@EnableAutoConfiguration
@EnableScheduling
@ComponentScan
public class LeetTournaments {
    private static final Logger LOGGER = LogManager.getLogger(LeetTournaments.class);

    public static void main(String[] args) {
        SpringApplication.run(LeetTournaments.class, args);
        // LeetTournaments.initDB();
    }

    static void initDB() {
        Webb webb = Webb.create();
        webb.get("http://127.0.0.1:8080/init").asString();
    }
}

3
你能否发布你的数据源配置? - Apollo
2
Spring 无法启动 Hibernate,很可能是您的 Hibernate 配置或 Spring JPA 配置存在问题。 - jwenting
1
我已经添加了数据源配置,这个配置过去是有效的。我应该在哪里查找进一步配置Spring JPA? - AndrewD
1
@AndrewD,你能否请发布gg.leet.security.WebSecurityConfig类的实现? - Sergey Vyacheslavovich Brunov
1
@SergeyBrunov 我添加了相关的类 - 感谢您为我查看此事。 - AndrewD
显示剩余4条评论
1个回答

0
请注意加粗字体标记的文本:

Spring Data 仓库通常扩展自 RepositoryCrudRepository 接口。如果您使用自动配置,仓库将从包含主配置类的包中搜索 (该类带有 @EnableAutoConfiguration@SpringBootApplication 注解)。

Spring Boot 参考指南, 29.3.2 Spring Data JPA Repositories

实际上,我认为这是问题的解决方案(加粗字体标记的文本):

默认情况下,Spring Boot 将启用 JPA 存储库支持,并查找 @SpringBootApplication 所在的包(及其子包)。如果您的配置中的 JPA 存储库接口定义位于不可见的包中,您可以使用 @EnableJpaRepositories 及其类型安全的 basePackageClasses=MyRepository.class 参数指出替代包。Getting Started · Accessing Data with JPA
总之,考虑按以下方式更新 LeetTournaments 类的实现:
@EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class LeetTournaments {
    ...
}

希望这有所帮助。

1
经过仔细检查,我发现我的一个模型存在冲突键,这就是我收到的错误。感谢所有时间 - 我花了几天时间来追踪这个问题。希望即使使用调试标志,ORM也能提供更多的洞察力。 - AndrewD
@AndrewD,这是个好消息。我认为你应该将解决方案作为单独的答案分享,并将其标记为被接受的答案。 - Sergey Vyacheslavovich Brunov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接