当前位置: 首页 > 知识库问答 >
问题:

使用Spring数据JpaRepository的异常

夏华藏
2023-03-14

我有以下实体:

用户:

@Entity
public class User {

    @Id
    @Column(nullable = false)
    private String email = "";

    @Column(nullable = false)
    private String nickname = "";

    @Column(nullable = false)
    private String password = "";

    @ManyToMany(cascade = CascadeType.ALL)
    private List<NewsSource> newsSources;

    // getters and setters
}

新闻来源:

@Entity
public class NewsSource {

    @Id
    @Column(nullable = false)
    private URL url;

    private LocalDateTime updateTime;

    @OneToMany(cascade = CascadeType.ALL)
    private List<News> newses;

    @ManyToMany(cascade = CascadeType.ALL)
    private List<User> users;
}

UsersRepository和NewsSourcesRepository是来自Spring数据JPA的简单JPA存储。其配置如下:

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:database_config.properties")
@EnableJpaRepositories(basePackages = {"news.repositories" })
public class RepositoriesConfiguration {

    @Bean(destroyMethod = "close")
    DataSource dataSource(Environment env) {
        HikariConfig dataSourceConfig = new HikariConfig();
        dataSourceConfig.setDriverClassName(env.getRequiredProperty("db.driver"));
        dataSourceConfig.setJdbcUrl(env.getRequiredProperty("db.url"));
        dataSourceConfig.setUsername(env.getRequiredProperty("db.username"));
        dataSourceConfig.setPassword(env.getRequiredProperty("db.password"));

        return new HikariDataSource(dataSourceConfig);
    }

    @Bean
    LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.setPackagesToScan("pl.mielecmichal.news.entities");

        Properties jpaProperties = new Properties();
        jpaProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
        jpaProperties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty("hibernate.hbm2ddl.auto"));
        jpaProperties.put("hibernate.ejb.naming_strategy", env.getRequiredProperty("hibernate.ejb.naming_strategy"));
        jpaProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
        jpaProperties.put("hibernate.format_sql", env.getRequiredProperty("hibernate.format_sql"));
        entityManagerFactoryBean.setJpaProperties(jpaProperties);

        return entityManagerFactoryBean;
    }

    @Bean
    JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

}

我的测试在第15行抛出一个LazyInitializationException异常。信息是:

未能延迟初始化角色:新闻的集合。实体。用户。使用者新闻源,无法初始化代理-无会话

    @Test
    public void cascadeRelationsShouldBeRetrieved() throws MalformedURLException {
        NewsSource source = new NewsSource();
        source.setUrl(new URL(SOME_URL));
        newsSourcesRepository.save(source);
        newsSourcesRepository.flush();

        User user = new User();
        user.setEmail(EMAIL);
        List<NewsSource> sources = new ArrayList<>();
        sources.add(source);
        user.setNewsSources(sources);
        usersRepository.save(user);
        usersRepository.flush();

        User savedUser = usersRepository.findOne(EMAIL);
        NewsSource newsSource = savedUser.getNewsSources().get(0);
        assertThat("News source should be saved", newsSource.getUrl(), is(SOME_URL));

        NewsSource savedSource = newsSourcesRepository.findOne(newsSource.getUrl());
        assertThat("New user should be saved in M2M relation", savedSource.getUsers(), Matchers.contains(user));
    }

如果我将我的测试注释为@Transactional exception不会被抛出,但我不确定这是解决这个问题的正确方法。

共有1个答案

燕钟展
2023-03-14

默认情况下,ManyToMany注释的获取类型是惰性的

FetchType fetch() default LAZY;

在您的情况下,用户类中的新闻源将被懒惰地获取。

为了简单起见,假设您没有直接使用事务性注释。对于任何数据库操作,都需要事务。在使用SpringDataJPA存储库时,事务性注释应用于所有jpa存储库方法。这些方法调用的事务在调用方法时开始,在方法执行完成时结束。除非存在同一数据库的外部事务,否则最后一条语句将保持有效。

考虑以下行,

User savedUser = usersRepository.findOne(EMAIL);
NewsSource newsSource = savedUser.getNewsSources().get(0);

事务在usersRepository.findOne(EMAIL)本身中开始和结束。现在“用户保存用户”对象有新闻来源,它将被懒惰地加载。因此,当您调用SavedUser.getNewsSSource()时,它会尝试使用持久性会话懒惰地加载。由于事务上下文已经关闭,因此没有活动的关联会话。

现在,如果将事务性注释添加到使用测试注释进行注释的方法中,则事务从这里开始,现在从savedUser开始。调用getNewsSources()时,将使用相同的事务。现在当你做savedUser的时候。getNewsSources(),存在与其关联的会话,因此将正常工作。

在测试方法上添加事务性注释并没有错。由于映射是惰性的,所以必须在某个地方放置事务性注释。在这里,当您在测试方法中直接调用jpa repository方法并对惰性引用对象执行操作时,您肯定必须对带注释的测试方法使用事务性注释。

类似的问题:Lazy初始化异常:未能懒惰初始化角色集合,无法初始化代理-无会话

 类似资料:
  • 我使用的是Spring数据JpaRepository,我发现它非常容易使用。我实际上需要所有这些功能——分页、排序、过滤。不幸的是,有一件令人讨厌的事情似乎迫使我退回到普通JPA的使用上来。 我需要按关联集合的大小订购。例如,我有: 我必须按bes排序。size() 是否有办法以某种方式自定义排序,仍然利用分页、过滤和其他Spring Data的强大功能?

  • 我有这个实体(为简洁起见省略注释): 我必须得到一个不同的值的列表为一个特定的 在我的JpaRepository中我有以下内容: 如果没有注释,则返回ID,而不是值。生成的SQL如下所示: 但我需要的是不同的类别,而不是产品实体。除此之外,我在上面添加了注释。但现在我得到了这个错误: 但是我不能将添加到结果列表中,因为这会使无效,因为是主键。 所以我要么需要一个合适的方法名来自动生成查询,要么需要

  • 问题内容: 我正在使用Spring Data JpaRepository,发现它非常易于使用。我实际上需要所有这些功能- 分页,排序,过滤。不幸的是,有一件令人讨厌的事情似乎迫使我退回到使用普通的JPA。 我需要按相关集合的大小排序。例如,我有: 我必须排序 有没有办法通过分页,过滤和其他Spring Data强大功能来自定义排序? 问题答案: 关于spring-data,我尚未意识到的第一件事也

  • 这是我的代码: user.java UserController.java 结构: 提前谢谢

  • 我有一个关于Spring Boot和使用JpaRepositories的多个数据源的设置的问题。 [4编辑如下] 项目的结构看起来像这样: 所以我有两个域(A和B),DataSource设置是单独处理的。 抽象JPA配置类用于减少冗余,并使用自定义数据源管理器来处理数据源: 位于域A包中的配置实现如下所示: 然后,实际存储库被定义为JPA存储库: 这似乎工作,根据应用程序日志: 之后,以及成功的F

  • Job1在org.eko.businessjobs.Job1.doperform(Job1.java:23)在org.eko.jobs.businessjoba.executeinternal(businessjoba.java:34)在org.springframework.scheduling.quartz.quartzjobbean.execute(quartzjobbean.java:75