【问题标题】:Saving entity in repository does not work SPRING在存储库中保存实体不起作用春天
【发布时间】:2015-05-08 11:34:20
【问题描述】:

我正在尝试将实体保存在存储库中,但它根本不起作用。存储库是自动装配的,在运行时我使用 saveAndFlush 来保存实体。我正在使用 PostgreSQL。在上面的测试方法中,我添加了 cmets 并解释了发生了什么。我希望方法 saveAndFlush 应该可以工作,但它没有。我找不到原因。

@Transactional
public class TestClass{

    @Autowired private MyRepository repository;
    @Autowired private EntityManager entityManager;

    // Working version
    public void writingToRepositoryWorking() {
        entityManager.getTransaction().begin();
        entityManager.persist(new MyData(99));
        entityManager.getTransaction().commit();

    }

    // not working and throws exception : 
    // TransactionRequiredException: no transaction is in progress
    public void writingToRepositoryNotWorking() {
        repository.saveAndFlush(new MyData(99));
    }

    // not working, no exception, no data in repository, 
    // but auto generated ID is incremented
    public void writingToRepositoryNotWorkingToo() {
        repository.save(new MyData(99));
    }
}

存储库接口文件

@Repository
@Transactional
public interface MyRepository extends JpaRepository<MyData, Long> {}

我的数据文件

@Entity(name = "myData")
public class MyData {
    @Id @GeneratedValue(strategy = GenerationType.AUTO) long id;

    private int testValue;

    public MyData() { }

    public BugData(int testValue) {
        this.testValue = testValue;
    }

    public long getId() {
        return id;
    }

    public int getTestValue() {
        return testValue;
    }
}

应用程序配置文件

@Configuration
@EnableJpaRepositories("com.mypackage.app")
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
@EnableWebMvc
class ApplicationConfiguration extends WebMvcConfigurationSupport {

    @Value("${jdbc.url}") private String KEY_JDBC_URL;

    @Value("${jdbc.username}") private String KEY_JDBC_USERNAME;

    @Value("${jdbc.password}") private String KEY_JDBC_PASSWORD;

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    @Autowired
    public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
        LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setPackagesToScan("com.mypackage.app");
        factory.setHibernateProperties(hibernateProperties());
        return factory;
    }

    public Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        properties.setProperty("hibernate.show_sql", "true");
        properties.setProperty("hibernate.hbm2ddl.auto", "update");
        return properties;
    }

    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
        return new HibernateTransactionManager(sessionFactory);
    }

    @Bean
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl(KEY_JDBC_URL);
        dataSource.setUsername(KEY_JDBC_USERNAME);
        dataSource.setPassword(KEY_JDBC_PASSWORD);
        return dataSource;
    }

    @Bean
    public EntityManagerFactory  entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();

        em.setDataSource(dataSource());
        em.setPackagesToScan("com.mypackage.app");
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        em.setJpaProperties(hibernateProperties());
        em.afterPropertiesSet();

        return em.getObject();
    }

    @Bean
    public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
        return entityManagerFactory.createEntityManager();
    }

    ...
}

【问题讨论】:

  • 您使用了错误的事务管理器。对于 JPA,您需要 JpaTransactionManager 而不是 HibernateTransactionManager。还有为什么要混合 2.
  • 不是创建 2 个实例,而是创建一个 LocalContainerEntityManagerFactory 并使用 HibernateJpaSessionFactoryBean 公开一个 SessionFactory。你现在只需要一个事务管理器,如果你真的需要的话,可以同时使用 JPA 和普通的 Hibernate SessionFactory。
  • 它没有帮助,因为我仍然需要 beginTransaction 和 commit 而不是在存储库上使用 saveAndFlush。你认为这可能是hibernate+postgresql的具体情况吗?不调用begin和commit就可以完成吗?
  • Transactional 在接口上的作用不大(Spring Data 的处理方式也发生了变化)。使用 @Transactional 注释您的测试类。
  • Testclass 是 Transactional 我在复制时错过了。

标签: java spring hibernate repository spring-data


【解决方案1】:

首先,您实际上是在非工作测试用例中处理 2 个不同的 EntityManager:

  1. EntityManager Spring 自动连接到您的测试中(这个是单例的,无论如何都应该避免),其他的是
  2. EntityManager 由 ApplicationConfiguration 中配置的 EntityManagerFactory 创建。

同时,由于您配置了 Hibernate SessionFactory,您还有另一个 Session 与上述 2 EntityManagers 一起运行。另外,由于配置了HibernateTransactionManager,@Transactional创建的所有事务都绑定到SessionFactory创建的Hibernate的Session上,而你的Repository使用的EntityManager肯定无从知晓。这就是当您的存储库尝试持久化数据时抛出TransactionRequiredException 的原因。

要修复它,您可以考虑删除 Hibernate 的 SessionFactory,并将事务管理器切换到 JpaTransactionManager。然后,您的 Repository 上的 @Transactional 将产生创建新事务并将其绑定到 Spring 已知的现有 EntityManager 的效果。

附带说明的是,您的 TestClass 上的 @Transactional 根本没有帮助,因为此类的实例不是由 Spring 实例化和管理的。要完成这项工作,需要提供事务测试类的正确配置,如下所述:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html。

希望这会有所帮助。

【讨论】:

  • 我已经从应用程序配置中删除了所有不必要的东西,它现在可以工作了!感谢回复
猜你喜欢
  • 2020-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-30
  • 1970-01-01
  • 2016-08-10
  • 1970-01-01
相关资源
最近更新 更多