What Morphium Offers

Morphium maps Java objects to MongoDB documents with annotations. @Entity, @Id, @Cache, @Index — four annotations turn a POJO into a cached, indexed document. Embedded sub-documents, field name mapping and write safety are all declarative.

The Challenge

Traditional ODMs require XML mapping files or manual codec registries. The MongoDB Java driver gives you raw BSON documents with no type safety. Morphium eliminates both problems with a pure-annotation approach that works at the field level.

Morphium Features Used

@Entity Maps a Java class to a MongoDB collection. The collectionName attribute sets the collection explicitly. Import: de.caluga.morphium.annotations.Entity MongoDBAtlasCosmosDB @Id (MorphiumId) Primary key → MongoDB _id. Auto-generated on first store() if null. Type MorphiumId is Morphium's ObjectId implementation. Import: de.caluga.morphium.annotations.Id MongoDBAtlasCosmosDB @Embedded Stores the object inline as a sub-document (no separate collection). The embedded class must NOT have @Entity. Import: de.caluga.morphium.annotations.Embedded MongoDBAtlasCosmosDB @Cache (LRU) Activates the LRU read cache. maxEntries limits cache size, timeout invalidates entries after N ms. Writes through Morphium auto-invalidate. Import: de.caluga.morphium.annotations.caching.Cache MongoDBAtlasCosmosDB @Index Creates a MongoDB index for faster queries. Field-level: single index. Class-level: compound index. Import: de.caluga.morphium.annotations.Index MongoDBAtlasCosmosDB @Property Overrides the MongoDB field name. Java field 'description' becomes MongoDB field 'product_description'. Import: de.caluga.morphium.annotations.Property MongoDBAtlasCosmosDB @WriteSafety Controls MongoDB write concern. NORMAL = acknowledged writes (w:1). WAIT_FOR_ALL_SLAVES = wait for all replicas. There is also MAJORITY = w:majority. Import: de.caluga.morphium.annotations.WriteSafety MongoDBAtlasCosmosDB @DefaultReadPreference Specifies which replica set member to read from. PRIMARY = strongest consistency. SECONDARY_PREFERRED = distribute read load. Import: de.caluga.morphium.annotations.DefaultReadPreference MongoDBAtlasCosmosDB @FieldNameConstants Lombok annotation (not Morphium!). Generates Product.Fields.name, .price etc. at compile time for type-safe queries. Import: lombok.experimental.FieldNameConstants MongoDBAtlasCosmosDB

Prerequisites & Key Concepts

  • Lombok @FieldNameConstants generates an inner class Product.Fields with constants like Fields.name, Fields.price. These are used in Morphium queries instead of hard-coded strings: query.f(Product.Fields.name).
  • MorphiumId is Morphium's own ObjectId type, compatible with MongoDB's native ObjectId. Import: de.caluga.morphium.driver.MorphiumId.
  • @Embedded goes on the sub-document class (e.g. Category), NOT on the field in the parent. An @Embedded class must NOT also have @Entity.
  • @Cache requires the Morphium cache to be enabled. In Quarkus: quarkus.morphium.cache.read-cache-enabled=true (default). The cache is auto-invalidated on writes through the same Morphium instance.
  • Compound Index @Index on the class creates a compound index — optimal for price-range queries sorted by name.

Entity Relationship

Product (@Entity)
_id: MorphiumId @Id
name: String @Index
product_description: String @Property
price: double @Index
stock: int
category: Category @Embedded
tags: List<String>
@Embedded →
Category (@Embedded)
name: String
description: String
@Entity @Embedded @Reference

Entity Source Code

Product.java Java
import de.caluga.morphium.annotations.Entity;
import de.caluga.morphium.annotations.Id;
import de.caluga.morphium.annotations.Index;
import de.caluga.morphium.annotations.Property;
import de.caluga.morphium.annotations.caching.Cache;
import de.caluga.morphium.annotations.caching.Cache.ClearStrategy;
import de.caluga.morphium.annotations.WriteSafety;
import de.caluga.morphium.annotations.SafetyLevel;
import de.caluga.morphium.annotations.DefaultReadPreference;
import de.caluga.morphium.annotations.ReadPreferenceLevel;
import de.caluga.morphium.driver.MorphiumId;
import lombok.Data;
import lombok.experimental.FieldNameConstants;
import java.util.List;

@Entity(collectionName = "products") 1
@Index("-price, name") 2
@Cache(maxEntries = 100, strategy = ClearStrategy.LRU, timeout = 30000) 3
@WriteSafety(level = SafetyLevel.NORMAL) 4
@DefaultReadPreference(ReadPreferenceLevel.PRIMARY) 5
@Data @FieldNameConstants
public class Product {

    @Id 6
    private MorphiumId id;

    @Index
    private String name;

    @Property(fieldName = "product_description") 7
    private String description;

    @Index
    private double price;

    private int stock;

    private Category category; 8

    private List<String> tags;
}
1 Maps this class to the products MongoDB collection
2 Compound index on price (descending) and name — optimizes sorted range queries
3 LRU read cache: max 100 entries, auto-invalidated after 30s or on writes
4 Write concern: acknowledged writes (w:1)
5 Always read from the primary replica
6 Primary key → MongoDB _id. Auto-generated on first store()
7 Overrides the MongoDB field name: Java description → MongoDB product_description
8 Embedded sub-document — stored inline, no separate collection
Category.java Java
import de.caluga.morphium.annotations.Embedded;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@Embedded 1
@Data @NoArgsConstructor @AllArgsConstructor
public class Category {
    private String name;
    private String description;
}
1 Marks this class as an embedded sub-document — must NOT also have @Entity

Service Code

CRUD Operations

Morphium API — CRUD Java
import de.caluga.morphium.Morphium;

@Inject Morphium morphium;

// Store (insert or upsert) a product
morphium.store(product);

// Bulk insert
morphium.storeList(products);

// Delete a single entity
morphium.delete(product);

// Find by ID
morphium.findById(Product.class, id);

Query Examples

Morphium API — Queries Java
// Find all products
morphium.createQueryFor(Product.class).asList();

// Search by name (regex, case-insensitive)
morphium.createQueryFor(Product.class)
    .f(Product.Fields.name).matches("(?i)laptop")
    .asList();

// Price range query with sort
morphium.createQueryFor(Product.class)
    .f(Product.Fields.price).gte(min)
    .f(Product.Fields.price).lte(max)
    .sort(Map.of(Product.Fields.price, 1))
    .asList();

// Query embedded sub-document fields
morphium.createQueryFor(Product.class)
    .f("category.name").eq("Electronics")
    .asList();

// Distinct values
morphium.createQueryFor(Product.class)
    .distinct(Product.Fields.name);

Related Documentation