The Two-Layer Adapter of dbVisitor
This article is generated by AI translation.
This article delves into the unique "Two-Layer Adapter" architecture of dbVisitor, revealing how it breaks the barriers between RDBMS and NoSQL through dual abstraction at the application layer and the protocol layer.
Abstract
When using Java for modern application development, mixing relational databases (such as MySQL, PostgreSQL) and non-relational databases (such as MongoDB, Elasticsearch) has become the norm. However, this hybrid architecture often leads to a fragmented technology stack: developers need to frequently switch between JDBC/MyBatis and various NoSQL proprietary clients.
The "Two-Layer Adapter" architecture proposed by dbVisitor aims to solve this pain point.
- Layer 1 (Application Adapter): At the API level, it shields underlying syntax differences (SQL vs DSL) through unified LambdaTemplate and Mapper interfaces.
- Layer 2 (Protocol Adapter): At the driver level, it implements standard JDBC interfaces, encapsulating NoSQL data sources as standard JDBC drivers.
This design not only realizes the vision of "One API Access Any DataBase", but also brings extremely high flexibility: developers can either enjoy the convenience of the full stack of dbVisitor, or use only its JDBC driver, allowing existing MyBatis/Hibernate projects to instantly possess the ability to operate NoSQL.
Two-Layer Adapter
1. First Layer Adapter: Unified Abstraction at Application Layer (API Adapter)
"Application Layer Adapter" solves the problem of "How to write".
At this layer, dbVisitor shields the differences in underlying syntax through highly abstract APIs. Whether the backend is MySQL's SQL, MongoDB's BSON filters, or even Elasticsearch's Query DSL, developers face the same set of Java APIs.
dbVisitor provides 5 kinds of different styles of APIs to meet various scenario needs from simple CRUD to complex report analysis:
1. Programmatic API (JdbcTemplate)
This is the most basic form, closely adhering to the JDBC standard. It is suitable for scenarios requiring fine-grained control over SQL execution, or performing simple and direct database operations. For NoSQL databases, you can even use their native query scripts (such as Mongo Shell) directly here.
// Traditional SQL way
jdbcTemplate.executeUpdate("insert into user_info (id, name) values (?, ?)", 1, "mali");
// Mongo Shell way (Direct passthrough)
jdbcTemplate.executeUpdate("db.user_info.insert({_id: 1, name: 'mali'})");
2. Declarative API (Interface)
By defining Java interfaces and cooperating with annotations like @Query, data access logic is separated from business code. This approach makes the code structure clearer and easier to maintain.
@SimpleMapper
public interface UserMapper {
@Query("select * from user_info where age > :age")
List<User> findByAge(@Param("age") int age);
}
3. General Mapper (BaseMapper)
This is an enhanced version of the declarative API. By inheriting BaseMapper<T>, you can obtain standard CRUD capabilities without writing any code.
The framework will automatically generate corresponding Select/Insert/Update/Delete statements or instructions based on the generic entity T.
// Just one inheritance to own a full set of CRUD methods
public interface UserMapper extends BaseMapper<UserInfo> {
}
// Usage
userMapper.insert(new UserInfo("1001", "Tom"));
UserInfo user = userMapper.selectById("1001");
4. Constructor API (LambdaTemplate)
This is the currently most recommended usage. It utilizes Java's Lambda expressions to implement type-safe query construction. The biggest advantage is: when you refactor the property names of Java entity classes, query conditions will automatically update, without worrying about "hidden bombs" caused by hardcoded strings.
// Automatically translated to corresponding query statements for SQL or NoSQL
List<UserInfo> users = lambdaTemplate.lambdaQuery(UserInfo.class)
.eq(UserInfo::getAge, 18)
.likeRight(UserInfo::getName, "Tom")
.list();
5. File Mapper (XML/DSL)
When encountering extremely complex queries (such as hundreds of lines of reporting SQL, or extremely complex ES aggregation queries), managing SQL/DSL in XML files is the best choice. This not only keeps Java code clean but also supports a powerful dynamic rule engine.
<!-- UserMapper.xml -->
<mapper namespace="com.example.UserMapper">
<select id="findComplexUsers">
select * from user_info
@{and, age > :minAge}
@{and, name like :namePattern}
</select>
</mapper>
Second Layer Adapter: Data Standardization at Protocol Layer (Driver Adapter)
"Protocol Layer Adapter" solves the problems of "How to connect" and "How to transmit".
This is the most innovative part of dbVisitor. Unlike most frameworks that only encapsulate at the API layer, dbVisitor goes deep down to the driver layer and fully implements Java's java.sql.Driver interface.
The "Relational" Disguise of NoSQL
At this layer, dbVisitor "disguises" various non-relational databases as standard JDBC interfaces:
- Table Mapping : MongoDB's
Collectionand Elasticsearch'sIndexare mapped to JDBC'sTable. - Row Mapping: Documents are mapped to Rows, and fields are mapped to Columns.
- SQL Parsing : The driver has a built-in command parser. When you send a native SQL/DSL to the driver, the driver will automatically translate it into SDK API calls conforming to the data source.
This underlying application means: Any tool or framework that supports JDBC can theoretically connect to NoSQL databases through dbVisitor.
Flexible Combination of Dual-Layer Architecture (Synergy)
This Application Layer (API) + Protocol Layer (Driver) dual-layer design brings great flexibility to project architecture. You can choose "Full Stack Mode" or "Driver Mode" according to your team's habits and the status of existing code.
Mode 1: Full Stack Mode (Best Practice)
Use dbVisitor's API and Driver at the same time. This is the smoothest way to use it, you will get a unified development experience, best performance, and complete type safety support.
Applicable Scenarios: New project development, or projects that wish to completely unify the data access layer.
Mode 2: Driver Mode (Integration)
"Old wine in a new bottle". Only use dbVisitor's JDBC driver, while continuing to use your familiar ORM frameworks (such as MyBatis, Hibernate, Spring Data JDBC).
Imagine you have a MyBatis project that has been running for 5 years and now needs to access MongoDB to store logs. You don't need to learn a new MongoTemplate, nor do you need to introduce the heavy Spring Data Mongo. You only need to:
- Modify the JDBC URL to dbVisitor's JDBC format.
- Write MyBatis Mapper XML just like writing MySQL.
dbVisitor driver will silently convert Command commands sent by MyBatis into MongoDB instructions in the background.
<!-- MyBatis Mapper XML -->
<!-- This is a query operating on MongoDB, but in the eyes of MyBatis it is standard SQL -->
<select id="selectLogs" resultType="LogDoc">
test.user_info.find({_id: ObjectId(#{id})})
</select>
Summary
dbVisitor's two-layer adapter architecture is essentially a tribute and extension to the JDBC Standard.
Through the encapsulation of the API Layer, it liberates developers from complex heterogeneous syntax to focus on business logic; Through the implementation of the Driver Layer, it breaks the physical boundaries between RDBMS and NoSQL, allowing data flow to be no longer limited by protocols.
Whether you are a "Full Stack Faction" pursuing extreme development efficiency, or an "Integration Faction" sticking to the existing technology stack, dbVisitor can provide you with a robust and unified data access foundation.