web-dev-qa-db-ja.com

エンティティをリポジトリに保存してもSPRINGが機能しない

エンティティをリポジトリに保存しようとしていますが、まったく機能しません。リポジトリは自動接続されており、実行時にsaveAndFlushを使用してエンティティを保存します。 PostgreSQLを使用しています。テスト方法の上に、何が起こっているのかを説明するコメントを追加しました。 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> {}

MyDataファイル

@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;
    }
}

ApplicationConfigurationファイル

@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();
    }

    ...
}
10
Marcin Lagowski

手始めに、実際には、非稼働テストケースで2つの異なるEntityManagerに取り組んでいます。

  1. EntityManager Springによってテストに自動配線されます(これはシングルトンであり、とにかく回避する必要があります)、その他は
  2. EntityManagerは、ApplicationConfigurationで構成されたEntityManagerFactoryによって作成されます。

同時に、Hibernate EntityManagersの構成により、前述の2 SessionFactoryと並行して別のセッションも実行されます。さらに、HibernateTransactionManagerが構成されているため、@Transactionalによって作成されたすべてのトランザクションは、SessionFactoryによって作成されたHibernateのセッションにバインドされ、リポジトリで使用されるEntityManagerには確実にそれについて知る方法はありません。これが、リポジトリがデータを永続化しようとしたときにTransactionRequiredExceptionがスローされた理由です。

これを修正するには、HibernateのSessionFactoryを削除して、トランザクションマネージャをJpaTransactionManagerに切り替えることを検討してください。次に、リポジトリの@Transactionalは、新しいトランザクションを作成し、それをSpringが認識している既存のEntityManagerにバインドする効果があります。

補足として、このクラスのインスタンスはSpringによってインスタンス化および管理されないため、TestClassの@Transactionalはまったく役に立ちません。これを機能させるには、トランザクションテストクラスの適切な構成をここで説明するように提供する必要があります。 http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing。 html

お役に立てれば。

11
Phoenix VN