Репозиторий Spring не находит метод `findBy *` и помечает транзакцию для отката, вызывая сбои сохранения

#java #spring #spring-data-jpa

#java #spring #spring-data-jpa

Вопрос:

Повторный код

Сущность

 @Data
@Entity
@Table(name = "SOME_FEATURE")
@SequenceGenerator(name = "FEATURE_SEQ", sequenceName = "SOME_FEATURE_SEQ", allocationSize = 1)
@EqualsAndHashCode(exclude = {"featuredItems"})
@ToString(exclude = {"featuredItems"})
public class SomeFeatureEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SOME_FEATURE_SEQ")
private Long id;

@Temporal(TemporalType.TIMESTAMP)
@Generated(GenerationTime.INSERT)
private Date created;

private String name;

private String featureType = "Text";

@Column(name = "feature_description")
private String description;

@OneToMany(mappedBy = "feature")
@Setter(AccessLevel.NONE)
private Set<SubFeatureEntity> featuredItems;

public Set<SubFeatureEntity> getFeaturedItems() {
    return Collections.unmodifiableSet(featuredItems);
}

@Temporal(TemporalType.TIMESTAMP)
@Generated(GenerationTime.ALWAYS)
private Date modified;
}
  

Репозиторий

     @Repository
public interface SomeFeatureEntityRepository extends JpaRepository<SomeFeatureEntity, Long>{
    SomeFeatureEntity findByName(String name);
}
  

Зависимости

Я не контролирую версии, не могу обновить

         <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-releasetrain</artifactId>
            <version>Ingalls-SR20</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>4.3.23.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
  

Тест

Это было исправлено, чтобы счетчики компонентов не кричали на меня за раскрытие IP-адреса — здесь у меня связаны руки.

 @Transactional
@Test
public void testFindByFeaturesName() {
    assertTrue(TestTransaction.isActive());
    FeatureEntity featureEntity;
    featureEntity = featureEntityRepository.findByName(TEST_FEATURE);
    FeatureManyRelationEntity FeatureManyRelationEntity = new FeatureManyRelationEntity();
    FeatureManyRelationEntity.setCreated(now);
    FeatureManyRelationEntity.setFeatures(featureManyRelationEntity);
    FeatureManyRelationEntity.setFeature(featureEntity);
    // This assertion passes - must be a different transaction
    assertTrue(TestTransaction.isActive());

    FeatureManyRelationEntityRepository.saveAndFlush(FeatureManyRelationEntity);
    // This assertion is never executed the flush above does not bother to execute.
    assertTrue(TestTransaction.isActive());
    Set<FeatureManyRelationEntity> featuresFeatures = FeatureManyRelationEntityRepository.findByFeatures(featureManyRelationEntity);
    assertNotNull(featuresFeatures);
    assertEquals(1, featuresFeatures.size());
    for (FeatureManyRelationEntity e : featuresFeatures) {
        assertEquals(TEST_FEATURE, e.getFeature().getName());
        assertEquals(TEST_Features_ITEM, e.getFeatures().getName());
    }
}
  

Сообщение об ошибке

 > 2020-08-25 13:51:04,471 [main] DEBUG o.s.d.j.r.query.JpaQueryFactory - Looking up query for method findByName
2020-08-25 13:51:04,472 [main] DEBUG o.s.d.j.repository.query.NamedQuery - Looking up named query SomeFeatureEntity.findByName
2020-08-25 13:51:04,472 [main] DEBUG o.h.j.spi.AbstractEntityManagerImpl - Mark transaction for rollback
2020-08-25 13:51:04,472 [main] DEBUG o.s.d.j.repository.query.NamedQuery - Did not find named query SomeFeatureEntity.findByName

> Note that the transaction is doomed to fail above even before the test starts
The folloing logs show that there is no problem actually `running`  the findByName method

>2020-08-25 13:51:06,681 [main] TRACE o.s.t.s.TransactionSynchronizationManager - Bound value [org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$DefaultCrudMethodMetadata@55f26df5] for key [public abstract com.myCompany.myDepartment.product.storage.impl.repository.entity.SomeFeatureEntity com.myCompany.myDepartment.product.storage.impl.repository.SomeFeatureEntityRepository.findByName(java.lang.String)] to thread [main]
2020-08-25 13:51:06,681 [main] TRACE o.s.t.i.TransactionInterceptor - Don't need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByName]: This method isn't transactional.
2020-08-25 13:51:06,681 [main] TRACE o.s.t.s.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@641312f] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@90181ca] bound to thread [main]
2020-08-25 13:51:06,681 [main] DEBUG o.h.jpa.criteria.CriteriaQueryImpl - Rendered criteria query -> select generatedAlias0 from SomeFeatureEntity as generatedAlias0 where generatedAlias0.name=:name
2020-08-25 13:51:06,684 [main] TRACE o.s.t.s.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@641312f] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@90181ca] bound to thread [main]
Hibernate: select Somefeatureent0_.id as id1_3_, Somefeatureent0_.created as created2_3_, Somefeatureent0_.Somefeature_description as Somefeature_3_3_, Somefeatureent0_.Somefeature_type as Somefeature_4_3_, Somefeatureent0_.modified as modified5_3_, Somefeatureent0_.name as name6_3_ from ec_Somefeature Somefeatureent0_ where Somefeatureent0_.name=?
2020-08-25 13:51:06,685 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0
2020-08-25 13:51:06,685 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[com.myCompany.myDepartment.product.storage.impl.repository.entity.SomeFeatureEntity#7]
2020-08-25 13:51:06,686 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [com.myCompany.myDepartment.product.storage.impl.repository.entity.SomeFeatureEntity#7]
2020-08-25 13:51:06,686 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Done materializing entity [com.myCompany.myDepartment.product.storage.impl.repository.entity.SomeFeatureEntity#7]
2020-08-25 13:51:06,686 [main] TRACE o.s.t.s.TransactionSynchronizationManager - Removed value [org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$DefaultCrudMethodMetadata@55f26df5] for key [public abstract com.myCompany.myDepartment.product.storage.impl.repository.entity.SomeFeatureEntity com.myCompany.myDepartment.product.storage.impl.repository.SomeFeatureEntityRepository.findByName(java.lang.String)] from thread [main]
  

Пока все хорошо, поскольку фактическая транзакция еще не требуется.

 >2020-08-25 13:51:06,686 [main] TRACE o.s.t.s.TransactionSynchronizationManager - Bound value [org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$DefaultCrudMethodMetadata@44653789] for key [public abstract java.lang.Object org.springframework.data.jpa.repository.JpaRepository.saveAndFlush(java.lang.Object)] to thread [main]
2020-08-25 13:51:06,690 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
2020-08-25 13:51:06,691 [main] TRACE o.s.t.s.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@6731203d] for key [org.hibernate.jpa.internal.EntityManagerFactoryImpl@67b95f82] bound to thread [main]
2020-08-25 13:51:06,691 [main] TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]
2020-08-25 13:51:06,693 [main] TRACE o.s.t.s.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@641312f] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@90181ca] bound to thread [main]
Hibernate: call next value for SOME_FEATURE_SEQ
2020-08-25 13:51:06,700 [main] DEBUG org.hibernate.id.SequenceGenerator - Sequence identifier generated: BasicHolder[java.lang.Long[16]]
2020-08-25 13:51:06,701 [main] DEBUG o.h.e.i.AbstractSaveEventListener - Generated identifier: 16, using strategy: org.hibernate.id.SequenceHiLoGenerator
2020-08-25 13:51:06,708 [main] TRACE o.s.t.s.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@641312f] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@90181ca] bound to thread [main]
  

Ошибка ниже (TransactionRequiredException) вводит в заблуждение. Есть / была транзакция, привязанная к потоку, она просто помечена для отката, в операционной системе она бесполезна для сохранения.

 >2020-08-25 13:51:06,709 [main] TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush] after exception: javax.persistence.TransactionRequiredException: no transaction is in progress
  

Это вызывает проблему только при сохранении. Сам поиск работает нормально.
Проблема здесь не в том, что нет именованного запроса. Я знаю, что это не так. Я бы ожидал, что Spring создаст один на основе имени метода репозитория и пойдет дальше. Вместо этого он помечает транзакцию для отката и не пытается в конечном итоге сохранить в другом объекте.
Пожалуйста, помогите, если можете.

Комментарии:

1. не могли бы вы также указать свой класс entity?

2. @SagarGangwal У меня нет Skype, но спасибо за предложение. Я добавлю свой класс entity.

3. Вы говорите, что ошибка возникает только при сохранении, но не предоставили код, который это делает. Hibernate вызывает последовательность в persist, по-видимому, было бы полезно получить дополнительную информацию об этом, классе сущности и любом разделении транзакции relavent.

4. @SagarGangwal — Да, это так, смотрите Код выше.

5. @Prashant Я думаю, вы сами найдете решение с этим упрощенным кодом.

Ответ №1:

Насколько я понял, другой пользователь предложил попробовать с CrudRepository, а не с JPA.

Поскольку я могу видеть множественные ошибки. Для findByName, если проблема существует, в этом случае само ваше приложение не запустится, и запуск приложения выдает ошибку, что не найдено свойство с именем и все. Для TransactionRequiredException вам нужно управлять с помощью @Transactional аннотации с propagation атрибутом.

Кроме того, просто попробуйте предоставить некоторые атрибуты, такие как propagation и rollbackFor для @Transactional аннотации.

И еще один момент, убедитесь, что вы импортировали @Transactional из этого пакета @org.springframework.transaction.annotation.Transactional .

     @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    @Test
    public void testFindByFeaturesName() {
        assertTrue(TestTransaction.isActive());
        FeatureEntity featureEntity;
        featureEntity = featureEntityRepository.findByName(TEST_FEATURE);
        FeatureManyRelationEntity FeatureManyRelationEntity = new FeatureManyRelationEntity();
        FeatureManyRelationEntity.setCreated(now);
        FeatureManyRelationEntity.setFeatures(featureManyRelationEntity);
        FeatureManyRelationEntity.setFeature(featureEntity);
        // This assertion passes - must be a different transaction
        assertTrue(TestTransaction.isActive());
    
        FeatureManyRelationEntityRepository.saveAndFlush(FeatureManyRelationEntity);
        // This assertion is never executed the flush above does not bother to execute.
        assertTrue(TestTransaction.isActive());
        Set<FeatureManyRelationEntity> featuresFeatures = FeatureManyRelationEntityRepository.findByFeatures(featureManyRelationEntity);
        assertNotNull(featuresFeatures);
        assertEquals(1, featuresFeatures.size());
        for (FeatureManyRelationEntity e : featuresFeatures) {
            assertEquals(TEST_FEATURE, e.getFeature().getName());
            assertEquals(TEST_Features_ITEM, e.getFeatures().getName());
        }
    }
  

Или, в худшем случае, вы просто пытаетесь удалить аннотацию транзакции из этого метода.

Комментарии:

1. Спасибо за ответ. Если я удалю @Transactional из метода, это не настоящий тест.

Ответ №2:

Использование CrudRepository автоматически запрашивает имя bij при использовании findByName(String name) . Он будет запрашивать на основе Name, которое является свойством вашей сущности.

Я не уверен, делает ли JPA Repo то же самое, но я использую Crud, и это работает для меня. Возможно, JPA требует запроса вручную, а Crud — нет.

Насколько я понимаю из вашей ошибки, у JPA нет запроса для findByName:

 2020-08-25 13:51:04,472 [main] DEBUG o.s.d.j.repository.query.NamedQuery - Did not find named query SomeFeatureEntity.findByName
  

Crud предоставит вам запрос, попробуйте.

Комментарии:

1. Я попробую это. Спасибо!

2. Я компилирую по мере ввода, но резервируются 1- Репозиторий JPA наследует CrudRepo 2 — я должен отказаться от подкачки и сортировки для этого.

3. Хотя я ценю предложение, мне действительно нужны подкачка и сортировка, поэтому в этой ситуации я не могу использовать этот подход.

Ответ №3:

Спасибо всем за вашу помощь. Оказывается, моя проблема заключалась в неправильном подключении. Без моего ведома мы предоставляли менеджеру транзакций неуправляемый менеджер сущностей. Как только я это исправил, код начал работать должным образом. Этот тест действительно выявил реальную проблему. Как оказалось, это не имело никакого отношения к findBy методу.