1

大量のドメイン エンティティに対して休止状態のバッチ保存処理を使用しています。まず、DB に挿入するエンティティが 1 つしかないときは、パフォーマンスに問題はありませんでした。しかし、別のエンティティに 1 対 1 の関係を追加した後、パフォーマンスとメモリ リークの問題が発生しました。Hibernate は一度に 2 つの挿入を行うようになりました。

ここに私のドメインモデル:

@Entity
@Table(name = "PRODUCT", uniqueConstraints = { @UniqueConstraint(columnNames = { "UPC" }) })
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {

    @Id
    @Column(name = "PRODUCT_ID", columnDefinition = "bigint")
    @Index(name = "PRODUCT_ID_IDX")
    private Long id;

    @Column(name = "UPC")
    private String upc;

    @Column(name = "CURRENCY")
    private String currency;

    @Column(name = "RETAIL_PRICE")
    private Double retailPrice;

    @Column(name = "SALE_PRICE")
    @Index(name = "PRODUCT_PRICE_IDX")
    private Double salePrice;

    @Type(type = "true_false")
    @Column(name = "INSTOCK")
    private boolean instock;

    @Column(name = "SHIPPING")
    private Double shipping;

    @Column(name = "RATING")
    private Integer rating;

    @Column(name = "VIEWS")
    @Index(name = "PRODUCT_VIEWS_IDX")
    private Long views;

    @OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
    @JoinColumn(name = "PRODUCT_ID", nullable = false)
    @Index(name = "PRODUCT_ID_REVIEW_IDX")
    private List<ProductReview> reviews = new ArrayList<ProductReview>();

    //Here is new relation
    @OneToOne(fetch = FetchType.LAZY, mappedBy = "product", cascade = CascadeType.ALL)
    private ProductInformation information;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnore
    @JoinColumn(name = "CATEGORY_ID", nullable = true)
    @Index(name = "PRODUCT_CAT_IDX")
    private ProductCategory category;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnore
    @JoinColumn(name = "BRAND_ID", nullable = true)
    @Index(name = "PRODUCT_BRAND_IDX")
    private ProductBrand brand;

ProductInformation エンティティ:

@Indexed
@Entity
@Table(name = "PRODUCT_INFO")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class ProductInformation {

    @Id
    @DocumentId
    @Column(name = "PRODUCT_ID", columnDefinition = "integer", unique = true, nullable = false)
    private Long id;

    @Column(name = "BUY_URL")
    private String buyUrl;

    @Column(name = "IMAGE_URL")
    private String imageUrl;

    @Column(name = "NAME", length = 10000)
    @Fields({
        @Field(index = org.hibernate.search.annotations.Index.TOKENIZED, analyzer = @Analyzer(impl = LowercaseKeywordAnalyzer.class)),
        @Field(name = "name", index = org.hibernate.search.annotations.Index.TOKENIZED, analyzer = @Analyzer(impl = LowercaseKeywordAnalyzer.class)) })
    @Index(name = "product_info_name_idx")
    private String name;

    @Column(name = "LONG_DESCRIPTION", length = 10000)
    private String longDescription;

    @Column(name = "SHORT_DESCRIPTION", length = 10000)
    private String shortDescription;

    @Column(name = "KEYWORDS", length = 10000)
    private String keywords;

    @OneToOne(fetch = FetchType.LAZY)
    @PrimaryKeyJoinColumn
    private Product product;

製品のセットアップ方法:

result.setInformation(info);
    info.setProduct(result);

私のDAOメソッド:

@Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int batchSave(List<Product> products, int batchSize) {

        int insertedCount = 0;
        Session batchSession = null;
        Transaction batchTransaction = null;
        try {
            batchSession = getSessionFactory().openSession();
            batchTransaction = batchStateLessSession.beginTransaction();
            for (Product product : products) {
                //First I used save() but this method calls select before insert and this                    //not a good idea.
                //batchSession.save(product);
                batchSession.persist(product);
                if (++insertedCount % batchSize == 0) {
                    batchSession.flush();
                    batchSession.clear();
                }
            }
            batchSession.flush();
            batchSession.clear();
            batchTransaction.commit();
            batchSession.close();
        } catch (ConstraintViolationException ex) {
            if (batchSession != null && batchTransaction != null) {
                batchSession.clear();
                batchTransaction.commit();
                batchSession.close();
            }
        }

        return insertedCount;
    }

私のバッチサイズは50行です。すべての休止状態のプロパティが正しく設定されています。エンティティに全文検索インデックスを作成する必要があるため、StatelessSession は使用しません。30K のエンティティを保存した後、メモリ リークが発生しました。しかし、私の XML ファイルには 10M のエントリがあります。ProductInformation エンティティがなく、すべてのフィールドが Product エンティティに格納されていた場合、すべてが漏れることなく完全に機能しました。

私の問題について何か考えがありますか?StatelessSession を使用し、挿入後にすべての行が 10M 行のテーブルにフルテキスト インデックスを作成する必要がありますか?

4

0 に答える 0