Migration v5 → v6¶
Requirements¶
- Java 21+ (mandatory)
- MongoDB 5.0+ for production deployments
- BSON library: Morphium 6 uses MongoDB's BSON library (version 4.7.1+)
SSL/TLS Support¶
Native SSL/TLS support was introduced in the Morphium driver in v6.0.
Breaking Changes¶
1. Configuration API Changes¶
Old (v5):
MorphiumConfig cfg = new MorphiumConfig();
cfg.setDatabase("mydb");
cfg.setHosts("localhost:27017");
cfg.setDriverName("PooledDriver");
New (v6):
MorphiumConfig cfg = new MorphiumConfig();
cfg.connectionSettings().setDatabase("mydb");
cfg.clusterSettings().addHostToSeed("localhost", 27017);
cfg.driverSettings().setDriverName("PooledDriver");
Prefer the new nested settings objects:
- connectionSettings() - database name, credentials, timeouts
- clusterSettings() - hosts, replica set configuration
- driverSettings() - driver selection and configuration
- messagingSettings() - messaging implementation and settings
- cacheSettings() - caching configuration
- encryptionSettings() - encryption keys
2. Driver Selection¶
- v5 and v6: select drivers by name via
MorphiumConfig.driverSettings().setDriverName(name). - Built‑in driver names:
PooledDriver- Connection-pooled MongoDB driver (default for production)SingleMongoConnectDriver- Single-connection driverInMemDriver- In-memory driver for testing (no MongoDB required)- Custom drivers must be annotated with
@de.caluga.morphium.annotations.Driver(name = "YourName")to be discoverable.
3. Messaging API¶
Old (v5):
New (v6):
// Use factory method for correct configuration
MorphiumMessaging m = morphium.createMessaging();
m.setSenderId("myapp");
m.start();
m.addListenerForTopic("topic", (mm, msg) -> {
// process message
return null;
});
- Always use
Morphium.createMessaging()so configuration and implementation selection happen correctly - The messaging implementation is chosen by name from
MorphiumConfig.messagingSettings().getMessagingImplementation() - Built-in implementations:
StandardMessaging(default) - Single-collection messagingMultiCollectionMessaging- Multi-collection messaging for high-volume scenarios- Custom implementations must be annotated with
@de.caluga.morphium.annotations.Messaging(name = "YourName")
Message "name" field renamed to "topic"¶
In v6, the Msg class field and methods have been renamed from "name" to "topic" to align with standard messaging terminology:
Old (v5):
Msg msg = new Msg("myMessageName", "command", "data");
String name = msg.getName();
msg.setName("newName");
New (v6):
Msg msg = new Msg("myTopic", "command", "data");
String topic = msg.getTopic();
msg.setTopic("newTopic");
Backward compatibility:
- The MongoDB field is backward compatible via @Aliases({"name"}) annotation
- V5 and V6 messaging instances can communicate if SingleCollectionMessaging (now called StandardMessaging) is used in V6
- Existing MongoDB documents with the "name" field will continue to work
Removed deprecated methods:
- getName() → use getTopic()
- setName(String) → use setTopic(String)
- sendAnswer(Messaging, Msg) → removed (use messaging instance methods instead)
4. Null Value Handling Changes¶
Morphium v6 changes how null values are handled during serialization and deserialization to be more consistent with MongoDB behavior and other ORMs.
New Default Behavior (v6)¶
Field missing from MongoDB document: - Java field is NOT updated (preserves default values or existing values) - This is consistent behavior regardless of annotations
Field present in MongoDB with explicit null value:
- By default, Java field IS set to null (overwrites default values)
- Use @IgnoreNullFromDB to protect specific fields from null contamination
Old (v5):
@Entity
public class MyEntity {
private Integer counter = 42; // null from DB would be ignored (protected)
@UseIfnull
private String name; // null from DB would be accepted
}
New (v6):
@Entity
public class MyEntity {
private Integer counter = 42; // null from DB IS accepted (becomes null)
@IgnoreNullFromDB
private Integer protectedCounter = 99; // null from DB is ignored (stays 99)
}
Migration Strategy¶
The @UseIfnull annotation is now deprecated. The behavior has been inverted:
Old behavior (v5):
- Fields WITHOUT @UseIfnull: ignored nulls from DB (protected)
- Fields WITH @UseIfnull: accepted nulls from DB
New behavior (v6):
- Fields WITHOUT @IgnoreNullFromDB: accept nulls from DB (standard behavior)
- Fields WITH @IgnoreNullFromDB: ignore nulls from DB (protected)
To migrate:
1. Remove @UseIfnull from fields that should accept nulls (this is now the default)
2. Add @IgnoreNullFromDB to fields that previously did NOT have @UseIfnull if you want to maintain the old protection behavior
Example: Field Missing vs. Explicit Null¶
@Entity
public class Example {
private String regularField = "default";
@IgnoreNullFromDB
private String protectedField = "default";
}
Scenario 1: MongoDB document is {} (field missing entirely)
- regularField stays "default"
- protectedField stays "default"
Scenario 2: MongoDB document is { regularField: null, protectedField: null }
- regularField becomes null (default overwritten)
- protectedField stays "default" (protected from null)
This makes the behavior more consistent: if a field doesn't exist in MongoDB, the Java object is not modified.
5. Removed or Deprecated Methods from Morphium Class¶
In v6, many methods in the Morphium class that perform query-based operations have been marked as deprecated or moved to the Query class. The design principle in Morphium is:
- Morphium class: Entity-based operations (save, delete, updates to specific entities)
- Query class: Query-based operations (reads and updates based on query criteria)
Query-Based Update Operations: Use Query Methods¶
While many methods still exist for backward compatibility, you should migrate to the Query class equivalents.
Deprecated or to-be-removed methods:
- morphium.set(toSet, collection, values, upsert, callback) - use query.set()
- morphium.unset(toSet, collection, field, callback) - use query.unset()
- morphium.inc(fieldsToInc, matching, upsert, multiple, callback) - use query.inc()
- morphium.push(query, field, value, ...) - use query.push()
- morphium.pull(entity, field, value, ...) - use query.pull()
Migration:
Old (v5):
// Setting values on entities matching a query
Map<Enum, Object> values = new HashMap<>();
values.put(MyEntity.Fields.status, "active");
morphium.set(entity, null, values, false, null);
// Unsetting a field
morphium.unset(entity, null, "fieldName", null);
// Incrementing values
Map<Enum, Number> increments = new HashMap<>();
increments.put(MyEntity.Fields.counter, 1);
morphium.inc(increments, query, false, true, null);
New (v6):
// Use Query.set() for query-based updates
Query<MyEntity> query = morphium.createQueryFor(MyEntity.class);
query.f("status").eq("pending"); // Query criteria
query.set("status", "active");
// Use Query.unset() for query-based field removal
query.unset("fieldName");
// Use Query.inc() for query-based increments
query.inc(MyEntity.Fields.counter, 1);
Note: Entity-based operations remain in Morphium:
- morphium.setInEntity(entity, collection, values, ...) - updates a specific entity
- morphium.unsetInEntity(entity, collection, field, ...) - unsets field in a specific entity
- morphium.store(entity) - saves/updates a specific entity
Use Query methods when updating based on query criteria, use Morphium methods when updating a specific entity instance.
Query Creation: Unified Method¶
Removed methods:
- createQueryFor(Class, String collectionName) - two-parameter version removed
- createQueryByTemplate(T template, String... fields) - template-based queries removed
- findByTemplate(T template, String... fields) - template-based find removed
Migration:
Old (v5):
// Creating query with custom collection name
Query<MyEntity> query = morphium.createQueryFor(MyEntity.class, "custom_collection");
// Template-based queries
MyEntity template = new MyEntity();
template.setStatus("active");
List<MyEntity> results = morphium.findByTemplate(template, "status");
New (v6):
// Use single-parameter createQueryFor and specify collection on query
Query<MyEntity> query = morphium.createQueryFor(MyEntity.class);
query.setCollectionName("custom_collection");
// Template-based queries: use standard query syntax
Query<MyEntity> query = morphium.createQueryFor(MyEntity.class);
query.f("status").eq("active");
List<MyEntity> results = query.asList();
Other Deprecated or Removed Methods¶
Read operations:
- readAll(Class) - deprecated (use createQueryFor(Class).asList())
- findByField(Class, field, value) - deprecated (use query with field filter)
- findById(Class, id, collection) - three-parameter version deprecated
Index operations:
- ensureIndex(Class, callback, Enum... fields) - deprecated (use ensureIndices(Class))
- getIndexesFromMongo(Class) - deprecated (use driver-specific methods)
Other operations:
- flush(Class) - deprecated (use buffered writer methods directly)
- createAggregator(Class, resultClass) - exists (supported)
- mapReduce(Class, map, reduce) - exists (supported, but MongoDB deprecated it)
Migration approach:
1. Replace morphium.set/unset/inc/push/pull with equivalent query.set/unset/inc/push/pull methods
2. Use Query for all update operations instead of direct Morphium methods
3. Use standard query syntax instead of template-based queries
4. For simple operations like findById, use query.f(Query.ID_FIELD).eq(id).get()
6. InMemoryDriver Improvements (v6)¶
The InMemoryDriver received major enhancements in v6.1:
Change Streams¶
- Full change stream support with document snapshots
- Proper event isolation preventing dirty reads
- Database-scoped driver sharing with reference counting
- Multiple Morphium instances can share the same InMemoryDriver when using the same database name
Message Queue Testing¶
// Multiple messaging instances can now share InMemoryDriver correctly
MorphiumMessaging msg1 = morphium1.createMessaging();
MorphiumMessaging msg2 = morphium2.createMessaging();
// Both will receive change stream events from the shared driver
Known Limitations¶
- No replica set simulation (single-instance only)
- No sharding support
- Limited geospatial operations compared to MongoDB
- See
docs/howtos/inmemory-driver.mdfor detailed feature coverage
New Features in v6¶
1. Virtual Threads Support¶
Morphium 6 uses Java 21 virtual threads for: - Change stream event dispatching - Async operation handling - Lightweight concurrent processing
2. Enhanced Configuration¶
- URI-based configuration:
MONGODB_URIenvironment variable - System properties:
-Dmorphium.driver=inmem - Properties file:
morphium-test.properties
3. Test Infrastructure¶
- Tag-based test organization (
@Tag("core"),@Tag("messaging"),@Tag("inmemory")) - Improved test runner:
./runtests.shwith retry logic and filtering - Better isolation between tests with proper driver lifecycle management
Migration Checklist¶
- [ ] Update to Java 21+
- [ ] Update pom.xml dependency to
6.1.1or higher - [ ] Replace flat config setters with nested settings objects
- [ ] Change messaging instantiation to
morphium.createMessaging() - [ ] Update messaging code: replace
getName()/setName()withgetTopic()/setTopic() - [ ] Replace removed
Morphiummethods: - [ ]
morphium.set/unset/inc()→ usequery.set/unset/inc()instead - [ ]
morphium.findByTemplate()→ use standard query syntax - [ ]
morphium.readAll()→ usequery.asList() - [ ]
morphium.findByField()→ use query with field filter - [ ]
morphium.createQueryFor(Class, collection)→ usequery.setCollectionName() - [ ] Review null handling behavior: consider adding
@IgnoreNullFromDBto fields that need protection from null values - [ ] Remove deprecated
@UseIfnullannotations (behavior is now inverted) - [ ] Review custom driver/messaging implementations for annotation requirements
- [ ] Update test infrastructure to use tags if using parameterized tests
- [ ] Test with InMemoryDriver to verify change stream handling
JMS Support¶
- The JMS classes provided are experimental/incomplete and should not be relied upon for full JMS compatibility
- May be completed in upcoming versions
Getting Help¶
- Check
docs/troubleshooting-guide.mdfor common issues - Review
docs/howtos/for specific use cases - Join our Slack: https://join.slack.com/t/team-morphium/shared_invite/...