子表行的休眠批处理

Posted

技术标签:

【中文标题】子表行的休眠批处理【英文标题】:Hibernate Batch processing for child table rows 【发布时间】:2018-09-29 13:52:08 【问题描述】:

我在此链接Hibernate Batch insert 中尝试了批量插入的解决方案,但仍然无法正常工作。以下是相关代码供您参考。 我只上传了一个包含 100 行试算表的试算表文件。所以一个试算表是一个包含 100 个试算表行的列表,我需要批量插入这 100 行数据。 这是一个 REST API,因为它是双向关系,所以我使用 JSON 托管引用。

下面代码的结果是在试算表中插入了 6 行,而在试算表行表中插入了 100 行,仅针对最后一个创建的试算表 id,其余 id 没有数据插入到试算表行表中.

你能指出我哪里错了吗?

***TRIAL_BALANCE Entity***

@Entity
@Table(name="TRIAL_BALANCE")
public class TrialBalance 

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="ID")
private int id;

@OneToMany(mappedBy="trialBalance", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@JsonManagedReference
private List<TrialBalanceRow> trialBalanceRows;

public void addTrialBalanceRows(List<TrialBalanceRow> tbRows) 

if ( trialBalanceRows == null ) 
trialBalanceRows = new ArrayList<TrialBalanceRow>();

for ( TrialBalanceRow tb : tbRows ) 
trialBalanceRows.add(tb);
tb.setTrialBalance(this);



***TRIAL_BALANCE_ROW Entity***

@Entity
@Table(name="TRIAL_BALANCE_ROW")
public class TrialBalanceRow 

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="ID")
private int id;

@JsonBackReference
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="TRIAL_BALANCE_ID")
private TrialBalance trialBalance;

***TrialBalanceDOAImpl.java file***

public void addTrialBalance(TrialBalance trialBalance) 
    try 

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        for ( int i=0; i<1200; i++ ) 

        session.save(trialBalance);
        if( i % 50 == 0 )  // Same as the JDBC batch size
              //flush a batch of inserts and release memory:
              session.flush();
              session.clear();
           
        
        tx.commit();
        session.close();

     catch (HibernateException e) 
        e.printStackTrace();
        throw new DataNotSavedException();
    

休眠属性

hibernate.dialect = org.hibernate.dialect.mysqlDialect
hibernate.show_sql = true
hibernate.format_sql = false
spring.jpa.properties.hibernate.jdbc.time_zone = UTC
spring.jpa.properties.hibernate.jdbc.batch_size = 50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
spring.jpa.properties.hibernate.id.new_generator_mappings = false

休眠配置

private Properties hibernateProperties() 
    Properties properties = new Properties();
    properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
    properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
    properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
    properties.put("hibernate.jdbc.time_zone", 
            environment.getRequiredProperty("spring.jpa.properties.hibernate.jdbc.time_zone"));
    properties.put("hibernate.jdbc.batch_size", 
            environment.getRequiredProperty("spring.jpa.properties.hibernate.jdbc.batch_size"));
    properties.put("hibernate.order_inserts", 
            environment.getRequiredProperty("spring.jpa.properties.hibernate.order_inserts"));
    properties.put("hibernate.order_updates", 
            environment.getRequiredProperty("spring.jpa.properties.hibernate.order_updates"));
    properties.put("hibernate.jdbc.batch_versioned_data", 
            environment.getRequiredProperty("spring.jpa.properties.hibernate.jdbc.batch_versioned_data"));
    properties.put("hibernate.id.new_generator_mappings", 
            environment.getRequiredProperty("spring.jpa.properties.hibernate.id.new_generator_mappings"));

    return properties;        

日志

2018-07-12 16:28:25 TRACE AbstractEntityPersister:3142 - 更新实体:[in.greenstack.ikon.entity.TrialBalanceRow#107399] 2018-07-12 16:28:25 DEBUG AbstractBatchImpl:129 - 重用批处理语句 2018-07-12 16:28:25 调试 SQL:92 - 更新 TRIAL_BALANCE_ROW 设置 ACCOUNT_CODE=?, ACCOUNT_NAME=?, CLOSING_BALANCE=?, GL_AMOUNT_CURR=?, GL_AMOUNT_PROP=?, GL_AMOUNT_REV=?, OPENING_BALANCE=?, REMARKS=?, TRANSACTION_CR=?, TRANSACTION_DR=?, TRIAL_BALANCE_ID=? ID =? 休眠:更新 TRIAL_BALANCE_ROW 设置 ACCOUNT_CODE=?, ACCOUNT_NAME=?, CLOSING_BALANCE=?, GL_AMOUNT_CURR=?, GL_AMOUNT_PROP=?, GL_AMOUNT_REV=?, OPENING_BALANCE=?, REMARKS=?, TRANSACTION_CR=?, TRANSACTION_DR=?, TRIAL_BALANCE_ID=? ID =? 2018-07-12 16:28:25 TRACE AbstractEntityPersister:2723 - 脱水实体:[in.greenstack.ikon.entity.TrialBalanceRow#107399] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数1 作为 [INTEGER] - [0] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [2] 作为 [VARCHAR] - [POOJA ENTERPRISE] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [3] 作为 [DOUBLE] - [0.0] 2018-07-12 16:28:25 TRACE BasicBinder:53 - 绑定参数 [4] 作为 [DOUBLE] - [null] 2018-07-12 16:28:25 TRACE BasicBinder:53 - 绑定参数 [5] 作为 [DOUBLE] - [null] 2018-07-12 16:28:25 TRACE BasicBinder:53 - 绑定参数 [6] 作为 [DOUBLE] - [null] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [7] 作为 [DOUBLE] - [0.0] 2018-07-12 16:28:25 TRACE BasicBinder:53 - 绑定参数 [8] 作为 [VARCHAR] - [null] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [9] 作为 [DOUBLE] - [14000.0] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [10] 作为 [DOUBLE] - [14000.0] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [11] 作为 [INTEGER] - [361] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [12] 作为 [INTEGER] - [107399] 2018-07-12 16:28:25 TRACE AbstractEntityPersister:3142 - 更新实体:[in.greenstack.ikon.entity.TrialBalanceRow#107400] 2018-07-12 16:28:25 DEBUG AbstractBatchImpl:129 - 重用批处理语句 2018-07-12 16:28:25 调试 SQL:92 - 更新 TRIAL_BALANCE_ROW 设置 ACCOUNT_CODE=?, ACCOUNT_NAME=?, CLOSING_BALANCE=?, GL_AMOUNT_CURR=?, GL_AMOUNT_PROP=?, GL_AMOUNT_REV=?, OPENING_BALANCE=?, REMARKS=?, TRANSACTION_CR=?, TRANSACTION_DR=?, TRIAL_BALANCE_ID=? ID =? 休眠:更新 TRIAL_BALANCE_ROW 设置 ACCOUNT_CODE=?, ACCOUNT_NAME=?, CLOSING_BALANCE=?, GL_AMOUNT_CURR=?, GL_AMOUNT_PROP=?, GL_AMOUNT_REV=?, OPENING_BALANCE=?, REMARKS=?, TRANSACTION_CR=?, TRANSACTION_DR=?, TRIAL_BALANCE_ID=? ID =? 2018-07-12 16:28:25 TRACE AbstractEntityPersister:2723 - 脱水实体:[in.greenstack.ikon.entity.TrialBalanceRow#107400] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数1 作为 [INTEGER] - [0] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [2] 作为 [VARCHAR] - [Poonam Pride Bunglows] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [3] 作为 [DOUBLE] - [0.0] 2018-07-12 16:28:25 TRACE BasicBinder:53 - 绑定参数 [4] 作为 [DOUBLE] - [null] 2018-07-12 16:28:25 TRACE BasicBinder:53 - 绑定参数 [5] 作为 [DOUBLE] - [null] 2018-07-12 16:28:25 TRACE BasicBinder:53 - 绑定参数 [6] 作为 [DOUBLE] - [null] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [7] 作为 [DOUBLE] - [3842.0] 2018-07-12 16:28:25 TRACE BasicBinder:53 - 绑定参数 [8] 作为 [VARCHAR] - [null] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [9] 作为 [DOUBLE] - [15467.0] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [10] 作为 [DOUBLE] - [11625.0] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [11] 作为 [INTEGER] - [361] 2018-07-12 16:28:25 TRACE BasicBinder:65 - 绑定参数 [12] 作为 [INTEGER] - [107400] 2018-07-12 16:28:25 DEBUG BatchingBatch:384 - 执行批量大小:50

我正在使用带有 spring security 和 spring v5+ 的 Hibernate v5.2.17。

谢谢, 修行

【问题讨论】:

【参考方案1】:

弄清楚发生了什么的最好方法是enable JDBC logging。

就您而言,尚不清楚您是否正在提交交易。您是否在服务中使用@Transactional

此外,您应该使用适当的日志记录框架,而不是 e.printStackTrace

【讨论】:

是的,我在服务上使用@Transactional 然后,使用日志,看看执行了哪些语句。 我现在看到批量插入正在完成,但是对于大约 1200 行数据来说它的速度很慢。还有什么我想念的吗。用一些有效的日志和配置更改更新了帖子。 我看到另一个问题,即父表有多个同一行的插入。因此,在上面的示例中,如果 DAO 中的 addTrialBalance() 方法具有用于 15 行数据且批量大小为 5 的 for() 循环,我看到在 TrialBalance(父)表中添加了 4 行,并且 TrialBalanceRows(子)表已输入数据仅适用于生成的最后一个父 ID。为什么会在父表中生成 4 个插入? 在我给你的那个资源中都有解释。只需阅读它,您就会了解 INSERT 批量排序的工作原理。【参考方案2】:

对于以后需要参考的其他人来说,对我有用的解决方案是为批量插入编写自己的 SQL 查询,如下所示。我还删除了父实体中的 CASCADETYPE.ALL 功能。

@Override
    public void addTrialBalance(TrialBalance trialBalance) 
        try 

            //Batch insertion code
            Session session = sessionFactory.openSession();
            Transaction tx = session.beginTransaction();
            final int batchSize = 50;


            session.save(trialBalance);
            session.doWork(new Work()
            
                public void execute(Connection con) throws SQLException
                
                    PreparedStatement st = con.prepareStatement(""
                            + "insert into TRIAL_BALANCE_ROW (ACCOUNT_CODE, ACCOUNT_NAME, ....,TRIAL_BALANCE_ID) "
                            + "values (?, ?,...,?)");
                    List<TrialBalanceRow> tbRows = trialBalance.getTrialBalanceRows();
                    List<TrialBalanceRow> trialBalanceRows = new ArrayList<TrialBalanceRow>();
                    int count = 0;

                    for (TrialBalanceRow tb : tbRows)
                    
                        trialBalanceRows.add(tb);
                        tb.setTrialBalance(trialBalance);

                        count++;
                        st.setString(1, tb.getAccountCode());
                        st.setString(2, tb.getAccountName());
                        st.setDouble(3, tb.getClosingBalance());
                        .
                        .
                        .
                        st.setInt(11, tb.getTrialBalance().getId());
                        System.out.println("Adding to batch .......");
                        st.addBatch();

                        if(count % batchSize == 0) 
                            System.out.println("Executing Batch Size of : " + batchSize);
                            st.executeBatch();
                        
                    
                    st.executeBatch();
                    st.close();
                

            );

            session.refresh(trialBalance);
            tx.commit();
            session.close();


         catch (HibernateException e) 
            e.printStackTrace();
            throw new DataNotSavedException();
        
    

感谢 Vlad 提供的参考资料(尤其是 GENERATIONTYPE.AUTO 上的参考资料)和所有帮助,但即使使用 order_inserts 我也无法获得预期的结果。通过上述解决方案,我能够达到所需的性能。

【讨论】:

以上是关于子表行的休眠批处理的主要内容,如果未能解决你的问题,请参考以下文章

如何将嵌套的子表值与父表行关联并插入子表值对应于php中的父表行

Postgres db删除父表行不删除子表行数据?

应用子表时如何将开窗查询事件做成多选?

如何在 PHP 中向表行添加多个类? [关闭]

休眠仅保存具有复合主键中的父外键的子表条目

使用 jquery 在引导程序中获取选定表行的值