Generated Keys (Khóa tự tăng)¶
@Options(useGeneratedKeys = true, ...) yêu cầu JDBC driver trả về Generated Keys (khóa tự tăng / sequence) sau khi thực thi INSERT và tự động gán vào trường của đối tượng tham số.
@Insert("INSERT INTO users (name, email, created_at) VALUES (#{name}, #{email}, #{createdAt})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
int insert(User u);
Mã nguồn Java sinh ra:
private static final String[] KEYS_insert = { "id" };
@Override
public int insert(User u) {
Connection c = s.conn();
try (PreparedStatement ps = c.prepareStatement(SQL_insert, KEYS_insert)) { // (1)!
ps.setString(1, u.getName());
ps.setString(2, u.getEmail());
JdbcCodec.setInstant(ps, 3, u.getCreatedAt());
int n = ps.executeUpdate();
try (ResultSet gk = ps.getGeneratedKeys()) {
if (gk.next()) {
u.setId(gk.getLong(1)); // (2)!
}
}
return n;
} catch (SQLException e) {
throw s.translate(e, SQL_insert);
} finally {
s.release(c);
}
}
- Truyền mảng tên cột
KEYS_inserttường minh vàoprepareStatementthay vì dùng cờ chungRETURN_GENERATED_KEYS. - Lệnh gọi setter
u.setId()được xác định tĩnh từ lúc build.
Tầm quan trọng của keyColumn¶
Luôn khai báo keyColumn tường minh
Lệnh prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) có hành vi không đồng nhất giữa các database:
- Oracle: Trả về ROWID thay vì giá trị sequence thực tế.
- PostgreSQL: Trả về tất cả các cột của bản ghi vừa chèn.
Chỉ khi truyền tên cột cụ thể qua keyColumn (ví dụ keyColumn = "id"), JDBC driver mới trả về đúng giá trị mong đợi trên mọi hệ quản trị database. Nếu bỏ trống keyColumn, compiler sẽ phát cảnh báo lúc build.
Với khóa chính tổ hợp (composite key), phân tách các tên bằng dấu phẩy:
Khi phương thức có nhiều tham số¶
Khi phương thức nhận nhiều tham số, keyProperty phải chỉ rõ tên biến tham số tiền tố:
@Insert("INSERT INTO users (name) VALUES (#{u.name})")
@Options(useGeneratedKeys = true, keyProperty = "u.id", keyColumn = "id")
int insert(@Param("u") User u, @Param("audit") String audit);
Generated Keys trong Batch Insert¶
Khi thực hiện batch insert nhận List<T>, Generated Keys được đọc tuần tự và gán vào từng phần tử trong danh sách:
int n = LarkBatisSql.sum(ps.executeBatch());
try (ResultSet gk = ps.getGeneratedKeys()) {
int i = 0;
while (gk.next() && i < orders.size()) {
orders.get(i).setId(gk.getLong(1));
i++;
}
if (i != orders.size()) {
throw new LarkBatisKeyCountMismatchException(STMT_insertAll, orders.size(), i);
}
}
Nếu số lượng khóa trả về không khớp với số dòng gửi đi, hệ thống lập tức ném LarkBatisKeyCountMismatchException.
Thẻ <selectKey> không được hỗ trợ¶
LarkBatis loại bỏ thẻ <selectKey> vì nó ẩn giấu một câu lệnh truy vấn thứ hai bên trong cấu hình. Thay vào đó, hãy tách thành 2 phương thức mapper tường minh: