JDBI (3) 对Bean的查询与插入是否存在非对称支持?
Great question—this is a common point of confusion when working with JDBI 3, so let’s break this down clearly.
First: Yes, This Difference Is Intentional
You’re absolutely right that JDBI handles bean queries more "automatically" than bean inserts. Here’s why:
- For queries: JDBI can leverage ResultSet metadata (from your database) and the bean’s field/getter methods to map columns to bean properties without you explicitly listing every field. It’s safe to do this because the query result’s structure directly aligns with what you’re selecting, and JDBI just maps matching names.
- For inserts: JDBI doesn’t auto-generate the full INSERT statement by default. This is intentional for a few reasons:
- Safety: You might not want to insert every bean property (e.g., transient fields, computed values, or fields that map to auto-generated columns like
id). - Explicitness: SQL INSERT statements require precise column lists to avoid mismatches between bean properties and table columns (like order, data types, or missing columns).
- Safety: You might not want to insert every bean property (e.g., transient fields, computed values, or fields that map to auto-generated columns like
Solutions to Avoid Manual Field Enumeration
While you can’t get full auto-mapping for inserts out of the box like you do for queries, there are ways to reduce the boilerplate:
1. Use JDBI Codegen Plugin
The easiest way to automate this is with the jDBI3-codegen-maven-plugin (or Gradle equivalent). This plugin scans your beans and database schema to generate DAO interfaces with pre-written INSERT (and other) statements that include all relevant fields. You won’t have to manually list columns anymore—the plugin handles it based on your bean and table structure.
2. Dynamic SQL with Reflection
If you prefer not to use codegen, you can use reflection to dynamically build your INSERT statement’s column list and placeholders. For example:
import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; // Get all non-transient fields from your bean List<String> fieldNames = Arrays.stream(User.class.getDeclaredFields()) .filter(field -> !field.isAnnotationPresent(Transient.class)) .map(Field::getName) .collect(Collectors.toList()); // Build the SQL string String columns = String.join(", ", fieldNames); String placeholders = String.join(", ", fieldNames.stream().map(f -> ":" + f).toList()); String insertSql = String.format("INSERT INTO users (%s) VALUES (%s)", columns, placeholders); // Execute the insert with bindBean handle.createUpdate(insertSql) .bindBean(user) .execute();
Just be sure to filter out any fields you don’t want to insert (like auto-increment IDs) and handle edge cases like different column names (use @ColumnName on your bean fields to map if needed).
3. @BindBean with Template SQL
For SQL Object interfaces, you can use @BindBean alongside a template that references bean properties. Even though you still list columns, it’s cleaner than manual parameter binding:
@SqlUpdate("INSERT INTO users (name, email, created_at) VALUES (:user.name, :user.email, :user.createdAt)") void insertUser(@BindBean("user") User user);
While you still list columns, you avoid repeating parameter names and leverage JDBI’s bean binding.
Final Thoughts
The query/insert difference exists because of the inherent differences between reading from a result set and writing to a table—JDBI prioritizes safety and explicitness for writes. But with codegen, reflection, or cleaner template binding, you can minimize the manual work and get close to the convenience of query-time auto-mapping.
内容的提问来源于stack exchange,提问作者Torque




