Guide / build route

Inventory management program in Java

Java is a fine choice for an inventory program: typed models, real concurrency primitives, and a database ecosystem on tap. The design that makes it work — movements, not totals — and the honest assessment of what a homemade program costs once it works.

The key facts

  • The core classes: Product (with variants), Location, StockMovement (append-only, signed quantity), Inventory (deriving on-hand), Order and OrderLine — mapped to tables via JDBC or JPA.
  • The one rule: on-hand is computed from movements, never a mutable field. A setQuantityOnHand() method is a design bug, not a convenience.
  • The Java specifics: transactions via JDBC or Spring, SELECT FOR UPDATE or atomic updates for the last-unit race, BigDecimal for quantities and money — never double.
  • The honest exit: the program teaches the domain; the maintained record is what a business runs — the build-vs-buy arithmetic prices both.
  • The wider system: inventory management end to end maps the whole record this page sits in.
Diagram — index of this pageThe ground this page covers
  1. 01The key facts
  2. 02The classes that matter
  3. 03The Java specifics that bite
  4. 04The scope, honestly staged
  5. 05The exit that keeps the learning

The classes that matter

Product / ProductVariant — identity, unit, cost; variants for size and pack. Location — where stock sits. StockMovement — the heart: variant, location, signed quantity, timestamp, actor, reason, reference. Append-only by convention: no setter ever mutates a movement; corrections are new movements. Inventory — not a stored balance but a query: SELECT SUM(signed_quantity) ... GROUP BY variant, location, or a rebuildable projection. Order / OrderLine — stateful records with a copied-at-order-time price, moving through a state machine (received → allocated → picked → invoiced) enforced in one service class.

The Java-ness is in the discipline the type system enables: make StockMovement immutable, expose no mutators for derived values, and let the compiler help you keep the invariants you wrote down. The database design pairs with this class model table for table.

Genuine BSimple screenThe BSimple stocktakes index at the count and review step.
The BSimple stocktakes index at the count and review step.

The Java specifics that bite

Money and quantities are BigDecimal, with explicit scale and rounding — double turns cents into folklore. The last-unit race is solved in the database, not the JVM: SELECT ... FOR UPDATE on the affected rows, or an atomic conditional insert for the reservation, so two threads racing resolve to one sale and one clean rejection. Transactions wrap the invariant — allocate-plus-reserve, cancel-plus-release — committed or rolled back as a unit. Persistence starts with plain JDBC or SQLite for learning and moves to Postgres with a connection pool for anything shared; JPA works, but map movements as immutable entities and resist the convenience of a cached quantityOnHand field.

Console first, then a thin web layer if the program earns one: the domain logic should never know about the interface, because the interface will change and the invariants will not.

DiagramDiagram: spreadsheet data imported into live stock records.
Diagram: spreadsheet data imported into live stock records.

The scope, honestly staged

A console program over one table file — a weekend. A JDBC program with the movement core, reorder reporting and a stocktake-correction flow — a few weekends, and genuinely useful for learning the domain. A multi-user program with an order state machine, purchasing and an invoice handoff — months, plus the permanent tail: hosting, security patches, backups, and every future requirement. That tail is the real cost of the Java route, and it is paid in the hours of whoever owns the program. The worked example shows the domain behaviour the program should reproduce.

DiagramOrderPick and packInvoiceXero
Diagram: Order → Pick and pack → Invoice → Xero — how this work moves through BSimple.

The exit that keeps the learning

We build BSimple, so weigh that: it runs this exact domain model — movements, reservations, deliberate invoice approval, per-customer pricing — as maintained cloud software, from $180/month AUD. (Its own implementation stack is not published, and should not influence your Java choice anyway.) The exit is not a loss: the classes you designed are the specification, and the public REST API with scoped keys and read projections means your Java program can become a client of a maintained record instead of its lonely owner — the trial shows the record it would talk to.

In practiceBusiness processes built into the system, not remembered by staff.
Business processes built into the system, not remembered by staff.

Frequently Asked Questions

Is Java a good language for an inventory program?

Yes — strong typing, mature database access, and real concurrency primitives suit the domain. The language matters far less than the design: append-only movements and derived quantities, whatever the stack.

Should I use JPA/Hibernate or plain JDBC?

Either works; plain JDBC teaches more and hides less for a learning project. With JPA, map movements as immutable and avoid cached balance fields — the ORM should not tempt you into storing what must be derived.

How do I handle two users selling the last item?

In the database: row-level locking (SELECT ... FOR UPDATE) or an atomic conditional reservation insert, inside a transaction. Application-level check-then-write logic loses races under load, however tidy the Java.

What type should I use for quantities and money?

BigDecimal with explicit scale for both — never double or float. Inventory and billing errors from binary floating point are silent, small, and exactly the kind that destroy trust in a record.

Can my Java program connect to a maintained system instead?

Yes — BSimple's public REST API (scoped keys, read projections for reorder levels, stocktakes, purchase orders and suppliers) lets a Java client render or extend a maintained record; the trial shows the data it would read.

BSimple

Get started with BSimple