This article is generated by AI translation.
Milvus Datasource Specifics
dbVisitor accesses Milvus vector database via the JDBC-Milvus driver, based on the JDBC protocol.
Unlike the native command style of MongoDB/ElasticSearch, the Milvus adapter uses SQL-style syntax (CREATE TABLE, INSERT, SELECT, DELETE, etc.), resulting in a lower learning curve.
Supported capabilities:
- Execute SQL-style commands to manipulate data (supported command list)
- JdbcTemplate, Fluent API, BaseMapper, Annotations, Mapper File
- Object Mapping, ResultSet Mapping, Rules, Arguments, ResultSetExtractor/RowMapper
- Vector search: KNN nearest-neighbor search (
ORDER BY field <-> vector), range search (vector_range())
Not supported: executeBatch, stored procedures, Statement.RETURN_GENERATED_KEYS
Concept Analogy
The Milvus adapter uses standard SQL-style syntax, behaving very similarly to traditional RDBMS JDBC:
- DDL —
CREATE TABLE,DROP TABLE,CREATE INDEX, etc., executed viaexecuteUpdate - DML —
INSERT,UPDATE,DELETE, useexecuteUpdateto get affected row count - DQL —
SELECTqueries return standardResultSetwith column names corresponding to collection fields
Milvus requires collections to be loaded into memory before querying — execute LOAD TABLE table_name first.
Additionally, Milvus's Update is essentially "Search-to-Upsert" (search then write), so full-table Updates on massive data are not recommended.
Command Mode (JdbcTemplate)
Use JdbcTemplate to execute SQL-style commands on Milvus directly. Ensure you have properly configured the Milvus data source first — see Milvus Driver Usage Guide.
For more usage patterns, see JdbcTemplate. The following features are not supported due to driver limitations:
- Batch operations
- Stored procedures
JdbcTemplate jdbc = new JdbcTemplate(dataSource);
// or
JdbcTemplate jdbc = new JdbcTemplate(connection);
// Create collection
jdbc.execute("CREATE TABLE book_vectors ("
+ "book_id INT64 PRIMARY KEY, "
+ "title VARCHAR(256) NOT NULL, "
+ "word_count INT32 DEFAULT 0, "
+ "book_intro FLOAT_VECTOR(128)"
+ ")");
// Create vector index
jdbc.execute("CREATE INDEX idx_intro ON book_vectors (book_intro) "
+ "USING \"IVF_FLAT\" WITH (nlist = 1024, metric_type = \"L2\")");
// Load collection into memory (required before search)
jdbc.execute("LOAD TABLE book_vectors");
// Direct insert
jdbc.execute("INSERT INTO book_vectors (book_id, title, word_count, book_intro) "
+ "VALUES (1, 'Thinking in Java', 1000, [0.1, 0.2, ...])");
// Parameterized insert (recommended)
jdbc.execute("INSERT INTO book_vectors (book_id, title, word_count, book_intro) "
+ "VALUES (?, ?, ?, ?)",
new Object[] { 2L, "Design Patterns", 500, Arrays.asList(0.3f, 0.4f, ...) });
List<Map<String, Object>> list = jdbc.queryForList(
"SELECT * FROM book_vectors WHERE word_count > 500 LIMIT 10");
Map<String, Object> book = jdbc.queryForMap(
"SELECT * FROM book_vectors WHERE book_id = 1");
// Use <-> operator + ORDER BY + LIMIT for TopK nearest-neighbor search
List<Map<String, Object>> results = jdbc.queryForList(
"SELECT book_id, title FROM book_vectors "
+ "ORDER BY book_intro <-> ? LIMIT 5",
new Object[] { Arrays.asList(0.1f, 0.2f, ...) });
// Approach 1: vector_range function (recommended)
List<Map<String, Object>> results = jdbc.queryForList(
"SELECT * FROM book_vectors WHERE vector_range(book_intro, ?, 0.8)",
new Object[] { Arrays.asList(0.1f, 0.2f, ...) });
// Approach 2: comparison expression
List<Map<String, Object>> results = jdbc.queryForList(
"SELECT * FROM book_vectors WHERE book_intro <-> ? < 0.8",
new Object[] { Arrays.asList(0.1f, 0.2f, ...) });
List<Map<String, Object>> results = jdbc.queryForList(
"SELECT book_id, title FROM book_vectors "
+ "WHERE word_count > 500 "
+ "ORDER BY book_intro <-> ? LIMIT 3",
new Object[] { Arrays.asList(0.1f, 0.2f, ...) });
// Update by primary key
jdbc.execute("UPDATE book_vectors SET word_count = 1200 WHERE book_id = 1");
// Delete by primary key
jdbc.execute("DELETE FROM book_vectors WHERE book_id = 1");
Fluent API (LambdaTemplate)
The Fluent API provides a type-safe, chained approach for operating Milvus. Usage is identical to RDBMS — see Fluent API for details.
LambdaTemplate lambda = new LambdaTemplate(dataSource);
BookVector book = new BookVector();
book.setBookId(1L);
book.setTitle("Thinking in Java");
book.setWordCount(1000);
book.setBookIntro(Arrays.asList(0.1f, 0.2f));
int result = lambda.insert(BookVector.class)
.applyEntity(book)
.executeSumResult();
BookVector book = lambda.query(BookVector.class)
.eq(BookVector::getBookId, 1L)
.queryForObject();
List<BookVector> books = lambda.query(BookVector.class)
.gt(BookVector::getWordCount, 500)
.queryForList();
int result = lambda.update(BookVector.class)
.eq(BookVector::getBookId, 1L)
.updateTo(BookVector::getWordCount, 1200)
.doUpdate();
int result = lambda.delete(BookVector.class)
.eq(BookVector::getBookId, 1L)
.doDelete();
Paginated Query
PageObject pageInfo = new PageObject(0, 10); // Page 0, 10 rows per page
List<BookVector> page1 = lambda.query(BookVector.class)
.gt(BookVector::getWordCount, 0)
.usePage(pageInfo)
.queryForList();
pageInfo.nextPage();
List<BookVector> page2 = lambda.query(BookVector.class)
.gt(BookVector::getWordCount, 0)
.usePage(pageInfo)
.queryForList();
BaseMapper Interface
BaseMapper provides generic CRUD methods. Usage is identical to RDBMS — see BaseMapper for details.
@Table("book_vectors")
public class BookVector {
@Column(value = "book_id", primary = true)
private Long bookId;
@Column("title")
private String title;
@Column("word_count")
private Integer wordCount;
@Column("book_intro")
private List<Float> bookIntro;
... // getters/setters omitted
}
@SimpleMapper
public interface BookVectorMapper extends BaseMapper<BookVector> {
}
Session session = ...;
BookVectorMapper mapper = session.createMapper(BookVectorMapper.class);
// Insert
mapper.insert(book);
// Query
BookVector loaded = mapper.selectById(1L);
// Update
loaded.setWordCount(1200);
mapper.update(loaded);
// Delete
mapper.deleteById(1L);
Annotations
All annotations provided by the core API work on Milvus data sources, except for the @Call annotation.
Milvus uses SQL-style syntax, so annotation commands are very similar to traditional SQL.
@Table("book_vectors")
public class BookVector {
@Column(value = "book_id", primary = true)
private Long bookId;
@Column("title")
private String title;
@Column("word_count")
private Integer wordCount;
@Column("book_intro")
private List<Float> bookIntro;
... // getters/setters omitted
}
@SimpleMapper
public interface BookVectorMapper {
@Insert("INSERT INTO book_vectors (book_id, title, word_count, book_intro) "
+ "VALUES (#{bookId}, #{title}, #{wordCount}, #{bookIntro})")
int saveBook(BookVector book);
@Query("SELECT * FROM book_vectors WHERE book_id = #{bookId}")
BookVector loadBook(@Param("bookId") Long bookId);
@Delete("DELETE FROM book_vectors WHERE book_id = #{bookId}")
int deleteBook(@Param("bookId") Long bookId);
}
Configuration config = new Configuration();
Session session = config.newSession(dataSource);
BookVectorMapper mapper = session.createMapper(BookVectorMapper.class);
Milvus's default consistency level may cause newly inserted data to not be immediately visible. It is recommended to add the consistencyLevel=Strong parameter in the JDBC connection URL to ensure instant visibility:
jdbc:dbvisitor:milvus://host:port?consistencyLevel=Strong
Paginated Query
Add a Page parameter to Mapper methods to enable paginated queries.
@SimpleMapper
public interface BookVectorMapper {
@Query("SELECT * FROM book_vectors WHERE word_count > 0")
List<BookVector> queryAll(Page page);
}
Page page = new PageObject();
page.setPageSize(10);
List<BookVector> list = mapper.queryAll(page);
File Mode (Mapper File)
@Table("book_vectors")
public class BookVector {
@Column(value = "book_id", primary = true)
private Long bookId;
@Column("title")
private String title;
@Column("word_count")
private Integer wordCount;
@Column("book_intro")
private List<Float> bookIntro;
... // getters/setters omitted
}
<?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="com.example.mapper.BookVectorMapper">
<resultMap id="bookResultMap" type="com.example.dto.BookVector">
<id property="bookId" column="book_id"/>
<result property="title" column="title"/>
<result property="wordCount" column="word_count"/>
<result property="bookIntro" column="book_intro"/>
</resultMap>
<insert id="saveBook">
INSERT INTO book_vectors (book_id, title, word_count, book_intro)
VALUES (#{bookId}, #{title}, #{wordCount}, #{bookIntro})
</insert>
<select id="loadBook" resultMap="bookResultMap">
SELECT * FROM book_vectors WHERE book_id = #{bookId}
</select>
<select id="queryAll" resultMap="bookResultMap">
SELECT * FROM book_vectors LIMIT 100
</select>
<delete id="deleteBook">
DELETE FROM book_vectors WHERE book_id = #{bookId}
</delete>
</mapper>
@RefMapper("/mapper/book-vector-mapper.xml")
public interface BookVectorMapper {
int saveBook(BookVector book);
BookVector loadBook(@Param("bookId") Long bookId);
List<BookVector> queryAll();
int deleteBook(@Param("bookId") Long bookId);
}
Vector Search
Milvus's core capability is vector similarity search. dbVisitor's Fluent API provides type-safe vector search methods — no SQL required. It is recommended to use these first. For complete vector query documentation, see Fluent API - Vector Query.
KNN Nearest-Neighbor Search (orderBy*)
Use orderByL2 / orderByCosine / orderByIP to sort by vector distance, combined with initPage to get the top K most similar records:
LambdaTemplate lambda = new LambdaTemplate(dataSource);
List<Float> target = Arrays.asList(0.1f, 0.2f);
List<BookVector> results = lambda.query(BookVector.class)
.orderByL2(BookVector::getBookIntro, target)
.initPage(10, 0) // TopK = 10
.queryForList();
List<BookVector> results = lambda.query(BookVector.class)
.gt(BookVector::getWordCount, 500)
.orderByL2(BookVector::getBookIntro, target)
.initPage(5, 0)
.queryForList();
List<BookVector> results = lambda.query(BookVector.class)
.orderByCosine(BookVector::getBookIntro, target)
.initPage(10, 0)
.queryForList();
Range Search (vectorBy*)
Use vectorByL2 / vectorByCosine / vectorByIP to filter all records with distance below a threshold. These are WHERE conditions and can be freely combined with other conditions:
List<Float> target = Arrays.asList(0.1f, 0.2f);
List<BookVector> results = lambda.query(BookVector.class)
.vectorByL2(BookVector::getBookIntro, target, 0.8)
.queryForList();
List<BookVector> results = lambda.query(BookVector.class)
.vectorByL2(BookVector::getBookIntro, target, 0.8)
.gt(BookVector::getWordCount, 500)
.queryForList();
API Quick Reference
| Query Mode | Method | Description |
|---|---|---|
| KNN Sort | orderByL2(property, vector) | Sort by L2 Euclidean distance |
| KNN Sort | orderByCosine(property, vector) | Sort by Cosine distance |
| KNN Sort | orderByIP(property, vector) | Sort by Inner Product distance |
| KNN Sort | orderByMetric(MetricType, property, vector) | Generic metric interface |
| Range Filter | vectorByL2(property, vector, threshold) | L2 distance < threshold |
| Range Filter | vectorByCosine(property, vector, threshold) | Cosine distance < threshold |
| Range Filter | vectorByIP(property, vector, threshold) | Inner Product distance < threshold |
To use SQL-style syntax for vector search directly (e.g., ORDER BY field <-> vector, vector_range(), etc.), see Supported Command List.