This is an implementation of the Nested Set model for storing trees in relational databases with Java and JPA 2.x.
It uses the variant with persistent depths attached to each node and supports multiple trees in a single table, including moving branches across trees.
The nested set model can be suitable for deeply nested tree structures which require frequent and diverse retrieval of arbitrary branches. It is not suited to trees that are comparatively frequently updated, since updates often require renumbering large parts of a tree. Also, it might not be the best option if your database supports efficient "recursive" queries or other dedicated features for working with hierarchical structures.
This repository is not currently actively maintained. Forks are welcome. If you would like to become an active collaborator on this repository, or you have an actively maintained fork that you want me to link to from here, please contact me (e.g. via opening an issue).
At the time of this writing, the most recently updated fork seems to reside at vitapublic.
Using this library generally involves the following steps:
Implement the NodeInfo
interface for the JPA entity that is to
represent a node in a tree, annotating the fields for
storing the left
, right
, level
and root
values with
the annotations @LeftColumn
, @RightColumn
, @LevelColumn
and @RootColumn
, respectively.
For an example see the
Category
entity from the test suite.
Create the matching database schema accordingly.
Whenever you want to operate on the tree structure of
the entities defined in step 1, create a JpaNestedSetManager
,
passing to it an EntityManager
in whose context all JPA
operations are performed. That is, a JpaNestedSetManager
always operates within the scope of a specific EntityManager
and hence must not outlive it.
Use the JpaNesteSetManager
to operate on the tree, e.g.
create new root nodes, lookup existing nodes, move
nodes around the tree(s) etc, as provided by the Node
and NestedSetManager
interfaces.
For an example, see the code in the
BasicTest
and
FunctionalTest
files from the test suite.
The current implementation does not in itself maintain integrity of tree structures (left / right / level / root attributes) in the presence of concurrent modifications. If there is a possibility for concurrent tree modifications, measures must be taken to prevent tree corruption, e.g. through SERIALIZABLE transaction isolation or additional optimistic or pessimistic locks.