jpa:向多对多添加新条目需要很长时间

llmtgqce  于 2021-06-17  发布在  Mysql
关注(0)|答案(2)|浏览(320)

在多对多关系中添加新条目时,我遇到了一个问题,因为列表太大了。前任:

Item item = new Item(1);
Category cat = dao.find(1, Category.class);
List<Category> list = new ArrayList<>();
list.add(cat);
item.setCategoryList(list);
cat.getItemList().add(item);

问题是category itens列表非常庞大,有很多itens,因此执行cat.getitemlist()需要很长时间。无论在哪里,我都在寻找正确的方法来添加多对多条目,这说明有必要这样做。有人能帮忙吗?
编辑:一个小上下文:我用标签组织我的iten,所以一个项目可以有多个标签,一个标签可以有多个iten,时间过去了,现在我有很多iten(>5.000)的标签,现在当我用这些标签中的一个保存一个新项目时,需要很长时间,我已经调试了我的代码,发现大部分延迟都在cat.getitenslist()行中,with是有意义的,因为它有大量的iten列表。我已经搜索了很多关于如何做到这一点的方法,每个人都说在多对多的情况下保存条目的正确方法是在关系的两边添加到列表中,但是如果其中一方很大,那么调用getitenslist()会在上下文中加载它们,这将花费很多时间。我正在寻找一种方法来保存我的项目参考标签没有加载该标签的所有iten。
编辑2:我的类:项:

@Entity
@Table(name = "transacao")
@XmlRootElement
 public class Transacao implements Serializable {
    @ManyToMany(mappedBy = "transacaoList")
    private List<Tagtransacao> tagtransacaoList;
    ...(other stuff)
}

标签:

@Entity
@Table(name = "tagtransacao")
@XmlRootElement
public class Tagtransacao implements Serializable {
  @JoinTable(name = "transacao_has_tagtransacao", joinColumns = {
        @JoinColumn(name = "tagtransacao_idTagTransacao", referencedColumnName = "idTagTransacao")}, inverseJoinColumns = {
        @JoinColumn(name = "transacao_idTransacao", referencedColumnName = "idTransacao")})
    @ManyToMany
    private List<Transacao> transacaoList;
...(other stuff)
}

编辑3:我做了什么来解决:正如ariel kohan所回答的,我试图做一个nativequery来插入关系:

Query query = queryDAO.criarNativeQuery("INSERT INTO " + config.getNomeBanco() + ".`transacao_has_tagtransacao` "
            + "(`transacao_idTransacao`, `tagtransacao_idTagTransacao`) VALUES (:idTransacao, :idTag);");
    query.setParameter("idTransacao", transacao.getIdTransacao());
    query.setParameter("idTag", tag.getIdTagTransacao());

我能够将查询时间从10秒减少到300英里,这是令人印象深刻的。最后,对于我的项目来说,最好是它已经开始运行了,而不是创建一个表示多对多关系的新类。感谢所有试图帮助的人\o/

kr98yfug

kr98yfug1#

这基本上是从一个类似的答案,我之前给出了,但类似的问题,以及复制。下面的代码在我第一次写的时候运行,但是我修改了名字来匹配这个问题,所以可能会有一些拼写错误。spring数据jpa是jpa之上的一层。每个实体都有自己的存储库,您必须处理它。为了处理springdatajpa中的多对多关系,如果您认为这是个好主意,可以为link表创建一个单独的存储库。

@Entity
public class Item {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "item", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<ItemCategory> categories;

@Entity
public class Category {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<ItemCategory> items;

@Entity
public class ItemCategory {
    @EmbeddedId
    private ItemcategoryId id = new ItemcategoryId();

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("itemId")
    private Item Item;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("categoryId")
    private Category category;

    public ItemCategory() {}
    public ItemCategory(Item Item, Category category) {
        this.item = item;
        this.category = category;
    }

@SuppressWarnings("serial")
@Embeddable
public class ItemCategoryId implements Serializable {
    private Long itemId;
    private Long categoryId;
    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        ItemCategoryId that = (ItemCategoryId) o;
        return Objects.equals(itemId, that.itemId) && Objects.equals(categoryId, that.categoryId);
    }
    @Override
    public int hashCode() {
        return Objects.hash(itemId, categoryId);
    }

并使用它。步骤3显示了您当前的执行方式,并在执行更新之前创建对现有联接的读取。步骤4只是直接在联接表中插入一个关系,不会导致对现有联接的预读取。

@Transactional
private void update() {
    System.out.println("Step 1");
    Category category1 = new Category();
    Item item1 = new Item();
    ItemCategory i1c1 = new ItemCategory(Item1, Category1);
    categoryRepo.save(Category1);
    ItemRepo.save(Item1);
    ItemCategoryRepo.save(p1t1);

    System.out.println("Step 2");
    Category category2 = new Category();
    Item item2 = new Item();
    ItemCategory p2t2 = new ItemCategory(item2, category2);
    ItemRepo.save(item2);
    categoryRepo.save(category2);
    ItemCategoryRepo.save(p2t2);

    System.out.println("Step 3");
    category2 = CategoryRepo.getOneWithitems(2L);
    category2.getitems().add(new ItemCategory(item1, category2));
    categoryRepo.save(Category2);

    System.out.println("Step 4 -- better");
    ItemCategory i2c1 = new ItemCategory(item2, category1);
    itemCategoryRepo.save(i2c1);
}

我没有明确设置 ItemCategoryId 身份证。这些由持久层(在本例中为hibernate)处理。
另外请注意,您可以更新 ItemCategory 条目要么用自己的回购协议明确说明,要么从列表中添加和删除它们 CascadeType.ALL 已设置,如图所示。使用 CascadeType.ALL 对于spring数据jpa来说,即使您预取了连接表实体,spring数据jpa仍然会再次执行。试图通过 CascadeType.ALL 对于新实体来说是有问题的。
没有 CascadeType 也不是 items 或者 categories 列表(应该是集合)是关系的所有者,因此添加到列表中并不能实现持久性,只能用于查询结果。
在阅读 ItemCategory 因为你没有关系,所以你需要特别的联系他们 FetchType.EAGER . 问题在于 FetchType.EAGER 如果你不想要连接,并且你把它放在两个上面,那么开销是多少 Category 以及 Item 然后您将创建一个递归的获取 categories 以及 items 任何查询。

@Query("select c from Category c left outer join fetch c.items is left outer join fetch is.Item where t.id = :id")
Category getOneWithItems(@Param("id") Long id);
js5cn81o

js5cn81o2#

在这种情况下,我将阻止您的代码在内存中加载项列表。
为此,我可以考虑两种选择:

使用@modyfing查询直接在数据库中插入项。

[建议用于希望避免更改模型的情况]
您可以尝试使用普通jpql创建查询,但是根据您的模型,您可能需要使用本机查询。使用本机查询类似于:

@Query(value = "insert into ...", nativeQuery = true)
void addItemToCategory(@Param("param1") Long param1, ...);

创建此查询后,需要更新代码,删除内存中加载对象的部分,并添加调用insert语句的部分。
[更新]正如您在评论中提到的,这样做可以将性能从10英里提高到300英里。

修改您的实体,以便将@manytomany替换为@onetomanys关系

该解决方案的思想是替换实体之间的多个关系 A 以及 B 中间实体 RelationAB . 我认为你可以通过两种方式来做到这一点:
仅将relationab中a和b的id保存为复合键(当然,您可以添加其他字段,如日期或任何您想要的内容)。
向relationab添加自动生成的id,并将a和b作为relationab实体中的其他字段添加。
我使用第一个选项做了一个示例(您将看到类不是公共的,这只是因为为了简单起见,我决定在单个文件中执行它。当然,如果需要,可以在多个文件和公共类中执行此操作: Entities A and B :

@Entity
class EntityA  {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    public EntityA() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

}

@Entity
class EntityB {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    public EntityB() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

}
``` `RelationABEntity` 以及 `RelationABId` :

@Embeddable
class RelationABId implements Serializable {
private Long entityAId;
private Long entityBId;

public RelationABId() {
}

public RelationABId(Long entityAId, Long entityBId) {
    this.entityAId = entityAId;
    this.entityBId = entityBId;
}

public Long getEntityAId() {
    return entityAId;
}

public void setEntityAId(Long entityAId) {
    this.entityAId = entityAId;
}

public Long getEntityBId() {
    return entityBId;
}

public void setEntityBId(Long entityBId) {
    this.entityBId = entityBId;
}

}

@Entity
class RelationABEntity {

@EmbeddedId
private RelationABId id;

public RelationABEntity() {
}

public RelationABEntity(Long entityAId, Long entityBId) {
    this.id = new RelationABId(entityAId, entityBId);
}

public RelationABId getId() {
    return id;
}

public void setId(RelationABId id) {
    this.id = id;
}

}

我的存储库:

@Repository
interface RelationABEntityRepository extends JpaRepository<RelationABEntity, RelationABId> {

}

@Repository
interface ARepository extends JpaRepository<EntityA, Long> {

}

@Repository
interface BRepository extends JpaRepository<EntityB, Long> {

}

测试:

@RunWith(SpringRunner.class)
@DataJpaTest
public class DemoApplicationTest {

@Autowired RelationABEntityRepository relationABEntityRepository;
@Autowired ARepository aRepository;
@Autowired BRepository bRepository;

@Test
public void test(){
    EntityA a = new EntityA();
    a = aRepository.save(a);
    EntityB b = new EntityB();
    b = bRepository.save(b);

    //Entities A and B in the DB at this point

    RelationABId relationABID = new RelationABId(a.getId(), b.getId());

    final boolean relationshipExist = relationABEntityRepository.existsById(relationABID);
    assertFalse(relationshipExist);

    if(! relationshipExist){
        RelationABEntity relation = new RelationABEntity(a.getId(), b.getId());
        relationABEntityRepository.save(relation);
    }

    final boolean relationshipExitNow = relationABEntityRepository.existsById(relationABID);
    assertTrue(relationshipExitNow);
    /**
     * As you can see, modifying your model you can create relationships without loading big list and without complex queries. 
     */
}    

}

上面的代码解释了处理这类事情的另一种方法。当然,你可以根据你的具体需要进行修改。
希望这有帮助:)

相关问题