Skip to main content

New MongoDB ORM/ODM Tool

· 6 min read
ZhaoYongChun
Maintainers
Hint

This article is generated by AI translation.

Java ORM tools are mature for RDBMS, but MongoDB's document model needs ODM (Object-Document Mapping) for clean Java integration. This post surveys the landscape and introduces dbVisitor as a JDBC-based solution.

Why ORM/ODM?

MongoDB stores flexible BSON documents, but in Java we face:

  1. Type safety: Working directly with Document or Map is error-prone and hard to maintain.
  2. Domain models: Business logic is built on POJOs; we need automatic (de)serialization.
  3. Developer efficiency: Hand-writing BSON builders is clumsy compared to object-centric operations.
  4. Unified style: Projects often mix RDBMS and MongoDB; a consistent API style lowers cognitive load.

ORM vs ODM

  • ORM (Object-Relational Mapping): Bridges OO models and relational tables (MySQL, Oracle, etc.).
  • ODM (Object-Document Mapping): Bridges Java objects and document databases (MongoDB, Elasticsearch, etc.). BSON naturally supports nested structures, so ODM mappings can be simpler, though references are handled differently.

In short: ORM maps “tables,” ODM maps “documents.”

dbVisitor for MongoDB

dbVisitor is a database access toolkit that provides a full JDBC driver for MongoDB (jdbc-mongo). You can operate MongoDB like MySQL using JDBC, raw commands, or MyBatis-style mappers. The Mongo adapter even lets you perform CRUD without writing Mongo commands.

Features

  1. JDBC protocol: Standard JDBC driver (jdbc-mongo); works with any JDBC ecosystem (e.g., HikariCP). Build parameterized queries with PreparedStatement.
  2. Raw commands: Execute native MongoDB commands to reduce the learning curve.
  3. Multiple APIs:
    • JdbcTemplate for direct commands and unstructured data.
    • LambdaTemplate for type-safe builders (MyBatis-Plus–style LambdaQueryWrapper).
    • Mapper interfaces with annotations (@Insert, @Query) or XML—fully MyBatis-compatible.
  4. Dynamic Command: Use <if>, <foreach>, <where>, etc., in XML or annotations to build complex conditions. Choose raw commands for complex queries or LambdaTemplate for single-collection CRUD.

Tool comparison

FeatureMongoClientSpring Data MongoDBMorphiaMongoPlusdbVisitor
PositioningOfficial low-level driverSpring ODMLightweight ODMMyBatis-Plus styleJDBC driver + ORM (no MyBatis dependency)
Dependency levelNone (base lib)Strong Spring dependencyLowSpring required for full featuresVery low (only official driver)
API styleBSON/BuilderRepository / TemplateAnnotation / DatastoreLambda / MapperJDBC / Template / Lambda / Mapper / Annotation
Query languageBSON FiltersCriteria / Query MethodsFluent APIFluent APIFluent API / Query Methods / Raw commands
Learning curveHigh (API-heavy)Medium (Spring Data)High (API-heavy)Medium (MyBatis & MyBatis-Plus)Low (MyBatis / Spring JDBC / native commands)
Dynamic CommandManual BSONWeakNot supportedNot supportedStrong (XML dynamic tags / rules)
JDBC supportNoneNoneNoneNoneNative
Multi-data-sourceNot supportedNeeds other Spring modulesNot supportedNot supportedMongoDB, Redis, MySQL, PostgreSQL, Oracle, etc.

Recommendations

  • Need maximum performance and don’t mind writing BSON? Or the project is very simple? Use MongoClient.
  • Deep in the Spring ecosystem and prefer Repository patterns? Choose Spring Data MongoDB.
  • Prefer the MyBatis style, want XML-managed commands, or need MongoDB in legacy systems lacking NoSQL support? dbVisitor is a strong, practical choice.

dbVisitor bridges RDBMS and NoSQL via JDBC and a unified API, reducing cognitive and maintenance costs in mixed database architectures.

How to use

1) Add dependencies

Add core and MongoDB adapter (Java 8). Current version: 6.7.0

<dependencies>
<dependency>
<groupId>net.hasor</groupId>
<artifactId>dbvisitor</artifactId>
<version>6.3.0</version>
</dependency>
<dependency>
<groupId>net.hasor</groupId>
<artifactId>jdbc-mongo</artifactId>
<version>>6.3.0</version>
</dependency>
</dependencies>

Connection URL example: jdbc:dbvisitor:mongo://127.0.0.1:27017/admin?user=root&password=123456.

2) Raw Mongo commands

Good for full control or quick debugging.

try (Connection c = DriverManager.getConnection(url, user, pwd)) {
JdbcTemplate jdbc = new JdbcTemplate(c);
// Insert
jdbc.execute("db.users.insertOne({name: ?, age: ?})", "Alice", 18);
// Query
Map<String, Object> row = jdbc.queryForMap("db.users.findOne({name: ?})", "Alice");
}

3) Mapper interfaces

Annotate commands to stay in the MyBatis style.

public interface UserMapper {
@Insert("db.users.insertOne({name: :name, age: :age})")
int insert(User user);

@Query("db.users.find({age: {$gt: :age}})")
List<User> findByAge(@Param("age") int age);
}

UserMapper mapper = lambda.getMapper(UserMapper.class);
mapper.insert(new User("Cindy", 22));

4) CRUD builders

No need to handcraft commands; write conditions against entities.

public class User {
private String name;
private int age;

// getters/setters omitted
}

try (Connection c = DriverManager.getConnection(url, user, pwd)) {
User u = new User();
u.setUserId(123);
u.setName("Alice");
u.setAge(18);

LambdaTemplate lambda = new LambdaTemplate(c);
int r1 = lambda.insert(User.class)
.applyEntity(u)
.executeSumResult();

List<User> list = lambda.query(User.class)
.eq(User::getAge, 18)
.queryForList();

// Query by PK
User u2 = lambda.query(User.class)
.eq(User::getUserId, u.getUserId())
.queryForObject();

// Update
int r2 = lambda.update(User.class)
.updateTo(User::getName, 20)
.eq(User::getUserId, u.getUserId())
.doUpdate();

// Delete
int r3 = lambda.delete(User.class)
.eq(User::getUserId, u.getUserId())
.doDelete();
}

5) Map entities

Use @Table / @Column to map collections and fields; supports primary keys, aliases, and TypeHandler.

@Table("users")
public class User {
@Column(value = "userId", primary = true)
private String userId;
@Column("name")
private String name;
@Column("age")
private Integer age;
// getters/setters omitted
}

6) Mapper files

Best for complex dynamic conditions; can coexist with annotations.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//dbvisitor.net//DTD Mapper 1.0//EN"
"https://www.dbvisitor.net/schema/dbvisitor-mapper.dtd">
<mapper namespace="net.hasor.scene.mongodb.dto.UserMapper">
<insert id="saveUser">
db.users.insertOne({name: :name, age: :age})
</insert>

<select id="loadUser" resultType="net.hasor.scene.mongodb.dto.User">
db.users.find({name: #{name}})
</select>

<delete id="deleteUser">
db.users.remove({name: #{name}})
</delete>
</mapper>
@RefMapper("dbvisitor/mapper/user-mapper.xml")
public interface UserMapper {
int saveUser(User info);

User loadUser(@Param("name") String name);

int deleteUser(@Param("name") String name);
}

7) Picking an API

  • JdbcTemplate: maximum freedom for debugging, special commands, or pipelines.
  • Mapper interfaces: light config; good for small/mid projects or fixed statements.
  • LambdaTemplate: type-safe, no template strings; ideal for standard CRUD and medium complexity.
  • Mapper XML: strongest dynamic command; best for complex queries/aggregations and combinations.
  • @Table/@Column: when you need entity-to-document mapping, aliases, or TypeHandlers.

Framework integration

Quick integration examples (see 3.3 Spring Integration for details).

Spring Boot

Add dependencies first. Current version: 6.7.0

<dependency>
<groupId>net.hasor</groupId>
<artifactId>dbvisitor-spring-starter</artifactId>
<version>6.3.0</version>
</dependency>
<dependency>
<groupId>net.hasor</groupId>
<artifactId>jdbc-mongo</artifactId>
<version>>6.3.0</version>
</dependency>

Option 1: configure application.properties

# Spring JDBC data source
spring.datasource.url=jdbc:dbvisitor:mongo://127.0.0.1:27017/admin
spring.datasource.username=root
spring.datasource.password=123456
# Required
dbvisitor.mapper-packages=com.example.demo.dao
dbvisitor.mapper-locations=classpath:dbvisitor/mapper/*.xml

Option 2: configure via annotations on the bootstrap class

@Configuration
@MapperScan(basePackages = "com.example.demo.dao",
mapperLocations = "classpath:dbvisitor/mapper/*.xml")
public class DemoApplication {
...
}

Injecting mappers

Inject mapper
import net.hasor.dbvisitor.lambda.LambdaTemplate;
import net.hasor.dbvisitor.jdbc.core.JdbcTemplate; // different from Spring's JdbcTemplate
import javax.annotation.Resource;

public class ServiceTest {
@Resource // or @Autowired
private UserMapper userMapper;
@Resource // or @Autowired
private JdbcTemplate jdbc;
@Resource // or @Autowired
private LambdaTemplate lambda;
...
}

Summary

dbVisitor brings MongoDB into the same developer experience as relational databases via JDBC. Use raw commands, type-safe Lambda, annotation/XML mappers, or zero-command CRUD. Pick the API that fits the scenario to balance speed, safety, and maintainability. Mixing RDBMS and MongoDB under one API lowers team cognitive load.