我们说我有一个实体列表:
0,593 0,250984 -20,523384 -25,406271
0,594 0,250984
0,595 0,250984
0,596 0,250984
0,597 0,250984 -15,793088 -21,286336
0,598 0,250984
0,599 0,908811
0,6 0,893612
0,601 0,784814 -12,130922 -11,825742
0,602 0,909238
0,603 0,25309
0,604 0,38435
0,605 0,602954 -8,316167 -3,43328
0,606 0,642628
0,607 0,39201
0,608 0,384289
0,609 0,251656 -11,825742 -5,874723
SomeEntity.java:
if (JSON.stringify(hits) === JSON.stringify(["a", "b", "c"])) {
MyEntityRepository.java:
List<SomeEntity> myEntities = new ArrayList<>();
所以当我跑:
@Entity
@Table(name = "entity_table")
public class SomeEntity{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private int score;
public SomeEntity() {}
public SomeEntity(long id, int score) {
this.id = id;
this.score = score;
}
然后Hibernate会将表中的所有记录加载到内存中。 有数百万条记录,所以我不想要那样。然后,为了交叉,我需要将结果集中的每条记录与我的List进行比较。 在原生MySQL中,我在这种情况下所做的是:
通过这种方式,我获得了很大的性能提升,避免了任何OutOfMemoryErrors,并让数据库的机器完成大部分工作。
有没有办法通过Spring Data JPA的查询方法(将hibernate作为JPA提供程序)实现这样的结果?我无法在文档或SO中找到任何此类用例。
答案 0 :(得分:1)
答案 1 :(得分:0)
我了解您有一组entity_table
标识符,并且您想查找其标识符在该子集和中其得分大于给定得分的每个entity_table
。
一个明显的问题是:您是如何到达entity_table
s的初始子集的,您是否不能仅将该查询的条件添加到您的查询中,该查询还检查“得分大于 x ”?
但是,如果我们忽略这一点,我认为有两种可能的解决方案。如果some_entity
标识符的列表很小(确切的“小”取决于您的数据库),则可以使用IN
子句并将您的方法定义为:
List<SomeEntity> findByScoreGreaterThanAndIdIn(int score, Set<Long) ids)
如果标识符的数量太大而无法容纳在IN
子句中(或者您担心使用IN
子句的性能),则需要使用临时表,食谱是:
创建一个映射到您的临时表的实体。为其创建一个Spring Data JPA存储库:
class TempEntity {
@Id
private Long entityId;
}
interface TempEntityRepository extends JpaRepository<TempEntity,Long> { }
使用其save
方法将所有实体标识符保存到临时表中。只要启用插入批处理,就应该可以正常工作-启用方法因数据库和JPA提供程序而异,但对于Hibernate,至少要将hibernate.jdbc.batch_size
Hibernate属性设置为足够大的值。此外,flush()
和clear()
会定期entityManager
或您的所有临时表实体在持久性上下文中累积,而您仍然会用尽内存。类似于:
int count = 0;
for (SomeEntity someEntity : myEntities) {
tempEntityRepository.save(new TempEntity(someEntity.getId());
if (++count == 1000) {
entityManager.flush();
entityManager.clear();
}
}
向您的find
添加一个SomeEntityRepository
方法,该方法运行一个本机查询,该查询在entity_table
上进行选择并加入临时表:
@Query("SELECT id, score FROM entity_table t INNER JOIN temp_table tt ON t.id = tt.id WHERE t.score > ?1", nativeQuery = true)
List<SomeEntity> findByScoreGreaterThan(int score);
@Service
类中创建一个方法,并用@Transactional(Propagation.REQUIRES_NEW)
进行注释,该方法将依次调用这两种存储库方法。否则,在运行SELECT
查询时,临时表的内容将消失,结果将为零。通过使临时表实体的@ManyToOne
到SomeEntity
,可以避免本机查询,因为您可以加入JPQL;我只是不确定您是否能够避免在这种情况下实际加载SomeEntity
来插入它们(或者是否可以仅使用ID创建新的SomeEntity
)。但是,既然您说您已经有了SomeEntity
的列表,那也许就不成问题了。
我自己也需要类似的东西,因此当我得到一个可行的版本时,将对其答案进行修改。