Skip to content
Martin Ledvinka edited this page Dec 8, 2025 · 17 revisions

We wrote a paper comparing JOPA with other object-triple mapping libraries (such as AliBaba, Empire, KOMMA, RDFBeans). It presents:

  • A feature comparison of the libraries
  • A benchmark showcasing typical application operations - create, retrieve, retrieve all (instances of a specific type), update, delete

More information about the benchmark, the methodology, raw data and the original results can be found at https://kbss.felk.cvut.cz/web/otm-benchmark.

However, the benchmark is quite old. The following table thus provides a perspective on how the performance of JOPA in the benchmark has evolved since version 0.10.7 (the version used in the original benchmark). The benchmark was run with the following setup:

  • Linux Mint 21.3 (kernel 6.8.0-87-generic) - disabled network, no other applications running
  • AMD Ryzen 7 PRO 4750U
  • 32GB RAM
  • GraphDB 10.8.4
JOPA version Create   Retrieve   Retrieve all   Update   Delete  
  Mean/ms σ / ms Mean / ms σ / ms Mean / ms σ / ms Mean / ms σ / ms Mean / ms σ / ms
0.10.7 16694.17 181.4378 11930.94 871.2306 12077.37 921.4616 15142.52 455.5812 25580.1 959.1004
2.6.0 12338.22 637.955 12660.44 420.3243 12991.11 382.5462 15234.75 536.3642 9607.23 520.1247
2.7.0 11915.46 612.0381 12371.51 765.871 12521.72 650.8022 15111.65 522.3286 9819.639 518.1418

Entity Loading Query Optimization

By default, a query that returns entities is processed in two phases:

  1. The query is executed, returning only identifiers of the resulting entities
  2. Each resulting entity is loaded using the underlying access API (just as if EntityManager.find was called for the result element)

This makes query result loading typically a little slower and requires more requests to the repository (n+1 where n is the number of result rows). However, it gives JOPA better control over inferred/asserted data to be loaded, cache access and repository contexts.

Nevertheless, it is often more favorable to just load the entities directly from the query result (thus requiring only one request to the repository). Two optimizers are (as of 2.7.1) available for optimized entity loading from query results.

  • Unbound predicate and object optimizer adds, as its name suggests, a triple pattern of the form ?x ?xP ?xV into the query, making the query basically return triples with subject ?x. The entity is then loaded from them. However, this may retrieve more data than expected and could make the query run slow. Therefore, this pattern is used only if the target type has a @Properties field
  • Attribute enumerating optimizer adds optional groups (e.g., OPTIONAL { ?x ex:stringAttribute ?stringAttribute }) for each of the attributes of the target entity class. This narrows the amount of data actually retrieved by the query and should improve the query performance. This is the default optimizer used when the target entity class does not have a @Properties field. This optimizer is also able to work with repository contexts specified by the descriptor passed to the query API.

Entity loading optimizers are not used when the query uses OFFSET or LIMIT, because they both influence the number of query result rows and could thus skew the actual results.

It is also necessary to emphasize that using the loading optimizers means that all inferred data are included in the loaded entity (or the query has to be executed without inference at all), so the loaded entities may contain more data than if they were loaded using the default mechanism.

Entity loading query result optimization can be enabled for individual queries using the cz.cvut.kbss.jopa.query.enableEntityLoadingOptimizer query hint (use the QueryHints class for convenience).

Clone this wiki locally