Hibernate Simulated Persistence: What is possible between persistence and commit?

Viewed 204

I'm currently writing a simulation context for objects that are of mixed origin. Some of these objects are persistent hibernate entities that are loaded from the database. Others are new 'simulated' objects that are created transient and might be stored in the database later.

The present application processes directed graphs, that are loaded from the database with Hibernate Entities. Currently it simply checks for cyclic sub-structures. The mapping is arranged such, that it lazily loads Nodes and Edges from the database. This allows my cycle checker to walk the entire graph by lazy fetching adjacent Nodes according to the information stored in the directly related Edges.

Node {
    Integer id; // mapped id
    List<Edge> edges; // mapped and fetched lazy
}

Edge {
    Integer id; // mapped id
    Integer predecessorId; // mapped
    Integer successorId; // mapped
    Node predecessor; // mapped and fetched lazy
    Node successor; // mapped and fetched lazy
}

Now I want to add an Edge to the graph, without storing it in the database. Anyhow, the CycleChecker should run first, but take the edge into account. I wonder if I can reuse my existing CycleChecker and solve that problem purely with Hibernate entities, without having to write additional code for "simulated" entities.

Two questions:

  1. If I create a new Edge and persist it in the Hibernate Session (say scoped by a transaction context), will I be able to lazy load its Node successor from database before commit()?

  2. If I create a new Node and persist it in the session (no commit or flush happens), will Hibernate find it via get(Id) or lazy loading from another fresh Edge?

And if all that is not possible, is there a Hibernate way to hook into the fetch in order to return objects from my simulation context?

1 Answers

As we use algorithms that work with Hibernate entities (and rely on Hibernate lazy loading relations), I worked out this solution a while ago. Simulation starts a transaction and persists all objects to simulate. All simulated objects have to be evicted from cache first, otherwise lazy relation loading would not work. After calculation is finished, the simulation can be stopped, i.e. the transaction is rolled back. See my code for details:

public class SimulationContext {

    private Session session = null;
    private Transaction transaction = null;

    private List<Simulateable> simulatedObjects;

    public SimulationContext() {
        this.simulatedObjects = new ArrayList<Simulateable>();
    }

    /** ... add, remove, clear, ... */

    /**
     * Start simulation: Start transaction and persist simulation entities.
     * @param session
     */
    public void start(Session session) {
        if (this.session != null) {
            this.stop();
        }
        this.session = session;
        this.transaction = session.beginTransaction();

        // Initializing simlation context, persisting simulated objects
        for (Simulateable object : simulatedObjects) {
            // persist object ...
            session.persist(object);
            session.flush();
            // ... and remove it from session cache (otherwise lazy loaded objects referenced by foreign keys might not get loaded)
            session.evict(object);
        }
    }

    /**
     * Stop simulation: Roll-back the transaction and close the session.
     */
    public void stop() {
        transaction.rollback();
        transaction = null;
        session = null;
    }
}
Related