JPA的findBy方法总是进入orElseThrow。

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

JPA findBy method always goes to orElseThrow

问题

这是我们的代码

private IdentificationMaster validateIdentificationType(String idType) {
    if (!StringUtils.isNotBlank(idType))
        throw new IllegalArgumentException("Invalid idType");
        
    Optional<IdentificationMaster> op1 = specRepo.findById(idType); // 测试用途
        
    Optional<IdentificationMaster> op2 = specRepo.findByIdentificationType(idType); // 测试用途
        
    return specRepo.findById(idType)
            .orElse(specRepo.findByIdentificationType(idType)
                    .orElseThrow(() -> new ResourceNotFoundException("Id Type Not Found " + idType)));
}

对于 idType,我们期望它可以是两个值之一,要么是主键id,要么是相应的identificationType。表格只有两列,ididentificationType。问题是,即使 op1op2 不为空,它仍然会抛出 ResourceNotFoundException 异常。现在,如果我像这样更改返回值

return specRepo.findByIdentificationType(idType)
            .orElse(specRepo.findById(idType)
                .orElseThrow(() -> new ResourceNotFoundException("Id Type Not Found " + idType)));

它仍然会抛出相同的异常!

Repository

@Repository
public interface IdentificationSpecRepository extends CrudRepository<IdentificationMaster, String>{

    Optional<IdentificationMaster> findByIdentificationType(String identificationType);
}

Entity

@Entity
@Table(name = "IDENTIFICATION_MASTER")
public class IdentificationMaster {
    
    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "IDENTIFICATION_TYPE", unique = true)
    private String identificationType;

    // 获取器和设置器

}

问题可能是什么?

英文:

This is our code

private IdentificationMaster validateIdentificationType(String idType) {
	if(!StringUtils.isNotBlank(idType))
		throw new IllegalArgumentException(&quot;Invalid idType&quot;);
		
	Optional&lt;IdentificationMaster&gt; op1 = specRepo.findById(idType); //testing purpose
		
	Optional&lt;IdentificationMaster&gt; op2 = specRepo.findByIdentificationType(idType); //testing purpose
		
	return specRepo.findById(idType)
			.orElse(specRepo.findByIdentificationType(idType)
					.orElseThrow(() -&gt; new ResourceNotFoundException(&quot;Id Type Not Found &quot; + idType)));
}

For idType we're expecting two values it can either be primary key id or its corresponding identificationType. Table only has two columns id and identificationType. The problem is that it throws ResourceNotFoundException even if op1 or op2 is not empty. Now if I change my return like this

return specRepo.findByIdentificationType(idType)
			.orElse(specRepo.findById(idType)
				.orElseThrow(() -&gt; new ResourceNotFoundException(&quot;Id Type Not Found &quot; + idType)));

Its again throwing the same exception!

Repository

@Repository
public interface IdentificationSpecRepository extends CrudRepository&lt;IdentificationMaster, String&gt;{

	Optional&lt;IdentificationMaster&gt; findByIdentificationType(String identificationType);
}

Entity

@Entity
@Table(name = &quot;IDENTIFICATION_MASTER&quot;)
public class IdentificationMaster {
	
	@Id
	@Column(name = &quot;ID&quot;)
	private String id;

	
	@Column(name = &quot;IDENTIFICATION_TYPE&quot;, unique = true)
	private String identificationType;

    // getters and setters

}

What could be the problem?

答案1

得分: 6

return specRepo.findByIdentificationType(idType)
                .orElse(specRepo.findById(idType)
                    .orElseThrow(() -> new ResourceNotFoundException("...")));

Is the reason.

Java is quite eager in execution and always calls the orElse method to prepare just in case it would need it. 

The order of your execution is somehow:

1. `specRepo.findByIdentificationType(idType)`
2. `orElse` cannot be executed as its argument is not evaluated yet
3. `specRepo.findById(idType)`
4. `.orElseThrow(() -> new ResourceNotFoundException("..."))`
5. The result of 3 and 4 becomes an object `o`
6. `orElse(o)`

Instead of using `orElse`, one should prefer `orElseGet`.

return specRepo.findByIdentificationType(idType)
                .orElseGet(() -> specRepo.findById(idType)
                    .orElseThrow(() -> new ResourceNotFoundException("...")));

It will only be called when needed.

We have two scenarios here:

1. `specRepo` returns a non-empty Optional.
2. `specRepo` returns an empty object.

In scenario 1, `idType` is a valid `identificationType` and thus is not an `id`, so the `findById` will throw an exception.
In scenario 2, `idType` is not a valid `identificationType`, and if it is a legal `id`, the method should result in an exception being thrown.

### Edit:

While this answer diagnoses the problem and describes what is the reason for such behavior, the answer by `@Abinash Ghosh` provides the simplest and in my opinion the best solution to the problem.

In general, avoid using `orElse`. In this case, add the `findByIdentificationTypeOrId(String it, String id)` to your repository.
英文:
return specRepo.findByIdentificationType(idType)
.orElse(specRepo.findById(idType)
.orElseThrow(() -&gt; new ResourceNotFoundException(&quot;...&quot;)));

Is the reason.

Java is quite eager in execution and always calls the orElse method to prepare just in case it would need it.

The order of your execution is somehow:

  1. specRepo.findByIdentificationType(idType)
  2. orElse cannot be executed as it's argument is not evaluated yet
  3. specRepo.findById(idType)
  4. .orElseThrow(() -&gt; new ResourceNotFoundException(&quot;...&quot;))
  5. The result of 3 and 4 becomes an object o
  6. orElse(o)

Instead of using orElse one should prefer orElseGet.

return specRepo.findByIdentificationType(idType)
.orElseGet(() -&gt; specRepo.findById(idType)
.orElseThrow(() -&gt; new ResourceNotFoundException(&quot;...&quot;)));

It will only be called when needed.

We have two scenarios here:

  1. specRepo returns an non-empty Optional.
  2. specRepo returns empty object.

In scenario 1, idType is a valid identificationType thus is not an id, so the findById will throw an exception.
In scenario 2, idType is not a valid identificationType and if it is a legal id the method should result in exception being thrown.

Edit:

While this answers diagnoses the problem and describes what is the reason of such behavior, @Abinash Ghosh answer provides the simplest and imo best solution of the problem.

In general, avoid using orElse. In this case, add the findByIdentificationTypeOrId(String it, String id) to your repository.

答案2

得分: 3

@xenteros 是对的,那就是问题所在。
你可以使用 findByIdentificationTypeOrId 在一次查询中获取数据。

return specRepo.findByIdentificationTypeOrId(idType, idType)
                .orElseThrow(() -> new ResourceNotFoundException("...")));

以及类似这样的 repository:

@Repository
public interface IdentificationSpecRepository extends CrudRepository<IdentificationMaster, String>{

    Optional<IdentificationMaster> findByIdentificationTypeOrId(String identificationType, String id);
}
英文:

@xenteros is right that is the problem.
You can use findByIdentificationTypeOrId to get data in one query

return specRepo.findByIdentifcationTypeOrId(idType, idType)
.orElseThrow(() -&gt; new ResourceNotFoundException(&quot;...&quot;)));

and repository like

@Repository
public interface IdentificationSpecRepository extends CrudRepository&lt;IdentificationMaster, String&gt;{
Optional&lt;IdentificationMaster&gt; findByIdentificationTypeOrId(String identificationType, String id);
}
</details>

huangapple
  • 本文由 发表于 2020年4月6日 16:34:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/61055835.html
匿名

发表评论

匿名网友

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

确定