Spring repository does not find a `findBy*` method and marks the transaction for rollback, causing persistence failures

huangapple go评论77阅读模式
英文:

Spring repository does not find a `findBy*` method and marks the transaction for rollback, causing persistence failures

问题

Sure, here's the translation of the code and the related content you provided:

## 相关代码

### 实体

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

依赖项

以下为您提供的依赖项内容。

测试

以下为您提供的测试内容。

错误消息

以下为您提供的错误消息内容。


请注意,代码和内容均已经过翻译,代码部分以行内代码块方式呈现,其余内容直接呈现为文本。
<details>
<summary>英文:</summary>
## Relavent code
### Entity
@Data
@Entity
@Table(name = &quot;SOME_FEATURE&quot;)
@SequenceGenerator(name = &quot;FEATURE_SEQ&quot;, sequenceName = &quot;SOME_FEATURE_SEQ&quot;, allocationSize = 1)
@EqualsAndHashCode(exclude = {&quot;featuredItems&quot;})
@ToString(exclude = {&quot;featuredItems&quot;})
public class SomeFeatureEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = &quot;SOME_FEATURE_SEQ&quot;)
private Long id;
@Temporal(TemporalType.TIMESTAMP)
@Generated(GenerationTime.INSERT)
private Date created;
private String name;
private String featureType = &quot;Text&quot;;
@Column(name = &quot;feature_description&quot;)
private String description;
@OneToMany(mappedBy = &quot;feature&quot;)
@Setter(AccessLevel.NONE)
private Set&lt;SubFeatureEntity&gt; featuredItems;
public Set&lt;SubFeatureEntity&gt; getFeaturedItems() {
return Collections.unmodifiableSet(featuredItems);
}
@Temporal(TemporalType.TIMESTAMP)
@Generated(GenerationTime.ALWAYS)
private Date modified;
}
### Repository
@Repository
public interface SomeFeatureEntityRepository extends JpaRepository&lt;SomeFeatureEntity, Long&gt;{
SomeFeatureEntity findByName(String name);
}
### Dependencies 
I do not control the versions, can&#39;t upgrade
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.data&lt;/groupId&gt;
&lt;artifactId&gt;spring-data-releasetrain&lt;/artifactId&gt;
&lt;version&gt;Ingalls-SR20&lt;/version&gt;
&lt;type&gt;pom&lt;/type&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework&lt;/groupId&gt;
&lt;artifactId&gt;spring-framework-bom&lt;/artifactId&gt;
&lt;version&gt;4.3.23.RELEASE&lt;/version&gt;
&lt;type&gt;pom&lt;/type&gt;
&lt;scope&gt;import&lt;/scope&gt;
&lt;/dependency&gt;
### The test 
This has been sanitized to I do not get yelled at by bean counters for exposing IP - my hands are tied here.
@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&lt;FeatureManyRelationEntity&gt; 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());
}
}
### Error message
&gt; 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
&gt; 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
&gt;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&#39;t need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByName]: This method isn&#39;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 -&gt; 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]
&gt; All good so far since an actual transaciton is not yet needed.
&gt;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 &#39;transactionManager&#39;
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]
&gt; Error below (TransactionRequiredException) is misleading. There is/was a transaction bound to the thread, it&#39;s just marked for rollback, os it&#39;s useless for  persist.
&gt;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
This causes an issue only when persisting. The look up itself works fine.
The issue here is not that there is no named query. I know there isn&#39;t. I would have expected Spring to create one based on the name of the repository method and move on. Instead it marks the transaction for rollback, and does not attempt the eventual persist on a different entity.
Please help if you can.
</details>
# 答案1
**得分**: 1
根据其他用户的建议,我理解你尝试使用`CRUDRepository`而不是`JPA`。
由于出现了多个错误,我能够看到。对于`findByName`,如果出现问题,在这种情况下,你的应用程序本身将无法启动,并且应用程序启动时会抛出找不到名称属性的错误。
对于`TransactionRequiredException`,你需要使用`@Transactional`注解管理,使用`[propagation]`属性。
另外,尝试为`@Transactional`注解提供一些属性,如`propagation`和`rollbackFor`。
还有一点要确保的是,你已经从这个包中导入了`@org.springframework.transaction.annotation.Transactional`。
```java
@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());
}
}

或者在最坏的情况下,尝试通过从此方法中删除事务注解来解决。

英文:

What i understood as other user suggested try with CRUDRepository rather than JPA.

As there is multiple error i am able to see. For findByName if issue is there, in that case your application itself will not get up and application starting it's throw an error that no property found with name and all.
For TransactionRequiredException, you need to manage with @Transactional annotation with propagation attribute.

Also, just try to provide some attributes like propagation and rollbackFor for @Transactional annotation.

And one more point make sure , you had imported @Transactional from this package @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&lt;FeatureManyRelationEntity&gt; 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());
}
}

Or in worst case you just try by removing transaction annotation from this method.

答案2

得分: 0

使用CrudRepository将在使用findByName(String 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会为您提供查询,请试试吧。

英文:

Using CrudRepository will automaticly query bij Name when using findByName(String name).
It will query based on Name, which is a property of your entity.

I'm not sure if JPA Repo does the same, but I'm using Crud and this works for me. Maybe JPA requires a manual query and Crud does not.

From what I understand from your error, JPA does not have a query for 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 will provide the query for you, give it a try.

答案3

得分: 0

谢谢大家的帮助。事实证明,我的问题是错误的布线引起的。我并不知道我们正在向事务管理器提供一个非托管的实体管理器。一旦我修复了这个问题,代码就开始正常工作了。这个测试确实揭示了一个真正的问题。事实证明,这与findBy方法无关。

英文:

Thanks all for your help. It turns out my issue was wrong wiring. Unbeknownst to me, we were giving the transaction manager a non-managed entity manager. Once I fixed that, the code started working properly. This test did uncover a real issue. As it turned out, this had nothing to do with the findBy method.

huangapple
  • 本文由 发表于 2020年8月27日 03:13:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/63604286.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定