Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Batch Inserts and the Art of the Flush

Posted on August 4, 2011 By Luis Fernandez

Hibernate batch inserts look simple on a slide, then your app crawls the moment you try to push a few hundred thousand rows. I spent a late night nudging a job while watching chatter about new features on Google Plus, and the culprit was not the database. It was the art of the flush.

Why do batch inserts feel slow?

Because the Session is a pack rat. Every new entity goes into the first level cache. No flush means more stuff in memory, more dirty checking, and more time spent thinking rather than writing. The driver can also send one statement at a time if you do not nudge it.

What actually happens when Hibernate flushes?

On flush, Hibernate figures out insert order, builds JDBC batches if allowed, and pushes them to the driver. With a huge Session, that planning step is heavy. The trick is to flush early and clear often. Keep the Session light, or it will fight back.

Which knobs should I turn first?

Turn on JDBC batching and sorted inserts. Then check stats. Small changes here bring big wins. Here is a minimal set that pays rent.

# hibernate.properties or hibernate.cfg.xml equivalent
hibernate.jdbc.batch_size=50
hibernate.order_inserts=true
hibernate.order_updates=true
hibernate.generate_statistics=true

# Optional for optimistic lock updates
hibernate.jdbc.batch_versioned_data=true

How do I insert a big list without melting memory?

Use a loop, call flush and clear every N rows. This keeps the first level cache small and lets the driver batch work. You get steady memory and steady throughput.

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

int batchSize = 50;
int i = 0;

for (MyEntity e : entities) {
  session.save(e);
  if (++i % batchSize == 0) {
    session.flush();
    session.clear();
  }
}

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

Do identity columns ruin batching?

They can. With IDENTITY generators the driver needs the key for each insert right away, so Hibernate often falls back to one by one inserts. SEQUENCE and TABLE strategies batch much better. If you control the schema, prefer sequences. If you must keep identity, consider a StatelessSession.

Should I use StatelessSession for bulk work?

For pure inserts with no cascade or lazy fields, yes. StatelessSession does not track the first level cache or dirty checking. It is boring and fast for bulk writes. Just remember it will not trigger lifecycle events the way a normal Session does.

StatelessSession s = sessionFactory.openStatelessSession();
Transaction tx = s.beginTransaction();

for (MyEntity e : entities) {
  s.insert(e);
}

tx.commit();
s.close();

Is my driver batching for real?

Some drivers accept addBatch calls then still send work row by row. For MySQL you want rewriteBatchedStatements=true in the JDBC url or it will not group values. PostgreSQL batches well but still sends multiple statements in a single round trip. Oracle and SQL Server do fine with JDBC batching in most common cases.

jdbc:mysql://localhost:3306/app?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true

What batch size should I pick?

Start with 30 to 50. Move up to 100 if the driver and database smile. Past that point many systems show returns that fade or even slow down due to network buffers and server parsing.

How do I keep transactions sane?

Wrap chunks in a transaction. Commit after each chunk. If one batch fails you do not lose the entire job. On a single huge commit the redo logs grow and lock time grows. With chunked commits you keep the pipeline moving.

int chunk = 1000;
int i = 0;

Transaction tx = session.beginTransaction();
for (MyEntity e : entities) {
  session.save(e);
  if (++i % chunk == 0) {
    session.flush();
    session.clear();
    tx.commit();
    tx = session.beginTransaction();
  }
}
session.flush();
session.clear();
tx.commit();

How do I see what is really going on?

Turn on SQL and bind logging. Check Hibernate statistics. You want to see batch sizes near your setting and low entity count in the Session between flushes.

# log4j.properties
log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.type.descriptor.sql=TRACE
log4j.logger.org.hibernate.engine.jdbc.batch.internal.BatchingBatch=DEBUG

What about insert order and foreign keys?

Hibernate can order inserts so parents go first. That saves round trips from constraint failures. Keep cascades simple in bulk jobs. If you need to import children, stage the data or insert parents first then children in a second pass.

How do I avoid surprises with large payloads?

Watch for triggers and default values. They can punch holes in plans. Keep the entity lean for bulk paths, avoid eager relationships, and prefer plain inserts with simple types. If you must fill a blob, stream it and keep the batch size small for that step.

Can I mix reads and writes in the same run?

You can, but mix with care. Reads fill the first level cache. Do bulk reads with a separate Session or scroll results. Then write with a fresh Session so your flush is clean and quick.

Wrap up

Batch inserts fly when you keep the Session lean, pick a friendly id strategy, nudge the driver, and flush with intent. Start with a small batch size, flush and clear in a loop, and watch the logs. If the job still drags, move to StatelessSession for the bulk step. Simple moves, real speed.

General Software Software Engineering

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes