#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
методу.