Skip to main content
Hint

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:

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:

  • DDLCREATE TABLE, DROP TABLE, CREATE INDEX, etc., executed via executeUpdate
  • DMLINSERT, UPDATE, DELETE, use executeUpdate to get affected row count
  • DQLSELECT queries return standard ResultSet with column names corresponding to collection fields
Milvus Special Requirements

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.

Tip

For more usage patterns, see JdbcTemplate. The following features are not supported due to driver limitations:

  • Batch operations
  • Stored procedures
Create JdbcTemplate
JdbcTemplate jdbc = new JdbcTemplate(dataSource);
// or
JdbcTemplate jdbc = new JdbcTemplate(connection);
Create collection and index
// 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");
Insert data
// 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, ...) });
Scalar conditional query
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");
Vector search (KNN)
// 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, ...) });
Vector range search
// 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, ...) });
Hybrid query (scalar filter + vector search)
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 and delete
// 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.

Initialize
LambdaTemplate lambda = new LambdaTemplate(dataSource);
Insert
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();
Query
BookVector book = lambda.query(BookVector.class)
.eq(BookVector::getBookId, 1L)
.queryForObject();

List<BookVector> books = lambda.query(BookVector.class)
.gt(BookVector::getWordCount, 500)
.queryForList();
Update
int result = lambda.update(BookVector.class)
.eq(BookVector::getBookId, 1L)
.updateTo(BookVector::getWordCount, 1200)
.doUpdate();
Delete
int result = lambda.delete(BookVector.class)
.eq(BookVector::getBookId, 1L)
.doDelete();

Paginated Query

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.

Define object
@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
}
Define Mapper interface
@SimpleMapper
public interface BookVectorMapper extends BaseMapper<BookVector> {
}
CRUD with Mapper
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

Tip

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.

1. Define object
@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
}
2. Define Mapper interface
@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);
}
3. Create and use Mapper
Configuration config = new Configuration();
Session session = config.newSession(dataSource);

BookVectorMapper mapper = session.createMapper(BookVectorMapper.class);
Consistency Level

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.

Mapper definition
@SimpleMapper
public interface BookVectorMapper {
@Query("SELECT * FROM book_vectors WHERE word_count > 0")
List<BookVector> queryAll(Page page);
}
Call with pagination
Page page = new PageObject();
page.setPageSize(10);

List<BookVector> list = mapper.queryAll(page);

File Mode (Mapper File)

1. Define object
@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
}
2. Define Mapper file
<?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>
3. Define Mapper interface
@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);
}

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:

L2 Distance TopK Search
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();
KNN Search with Scalar Pre-filtering
List<BookVector> results = lambda.query(BookVector.class)
.gt(BookVector::getWordCount, 500)
.orderByL2(BookVector::getBookIntro, target)
.initPage(5, 0)
.queryForList();
Cosine Distance Search
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:

L2 Distance Range Filter
List<Float> target = Arrays.asList(0.1f, 0.2f);

List<BookVector> results = lambda.query(BookVector.class)
.vectorByL2(BookVector::getBookIntro, target, 0.8)
.queryForList();
Hybrid: Range Search + Scalar Filter
List<BookVector> results = lambda.query(BookVector.class)
.vectorByL2(BookVector::getBookIntro, target, 0.8)
.gt(BookVector::getWordCount, 500)
.queryForList();

API Quick Reference

Query ModeMethodDescription
KNN SortorderByL2(property, vector)Sort by L2 Euclidean distance
KNN SortorderByCosine(property, vector)Sort by Cosine distance
KNN SortorderByIP(property, vector)Sort by Inner Product distance
KNN SortorderByMetric(MetricType, property, vector)Generic metric interface
Range FiltervectorByL2(property, vector, threshold)L2 distance < threshold
Range FiltervectorByCosine(property, vector, threshold)Cosine distance < threshold
Range FiltervectorByIP(property, vector, threshold)Inner Product distance < threshold
SQL Command Mode

To use SQL-style syntax for vector search directly (e.g., ORDER BY field <-> vector, vector_range(), etc.), see Supported Command List.