2013-07-25 27 views
5

Tôi đang làm việc với JDBC và HSQLDB 2.2.9. Cách hiệu quả nhất và chính xác nhất để chèn hàng mới vào DB là gì, và sau đó, giữ lại giá trị id (PK được đặt thành tự động tăng) của bạn? Lý do tôi cần phải làm điều này có lẽ là khá rõ ràng, nhưng tôi sẽ minh họa bằng một ví dụ để thảo luận:Cách lấy giá trị PK ID được tạo tự động trước đó bằng JDBC và HSQLDB

Giả sử có một bảng Customer rằng có một trường PersonId với một hạn chế FK đề cập đến một hàng từ một bảng Person. Tôi muốn tạo một Customer mới, nhưng để làm điều này, trước tiên tôi cần tạo một Person mới và sử dụng giá trị Person.id mới để đặt Customer.PersonId.

Tôi đã nhìn thấy bốn cách tiếp cận này:

  1. Chèn hàng Person đặt trường id để null. HSQLDB tự động tạo giá trị id tiếp theo. Sau đó, thực hiện truy vấn trên bảng Person để nhận giá trị id vừa tạo và sử dụng nó để tạo hàng Customer mới.

    Điều này có vẻ tốn kém chỉ để truy xuất một giá trị số nguyên duy nhất.

  2. Lấy id giá trị tiếp theo trong bảng Person và sử dụng nó trong báo cáo kết quả INSERT để thiết lập giá trị Person.id bằng tay. Sử dụng cùng một giá trị id để đặt Customer.PersonId. Không cần đọc tiếp từ DB.

    Không nhất quán có thể phát sinh nếu giá trị id thu được, nhưng một kết nối khác thực hiện INSERT trong bảng trước khi tuyên bố INSERT INTO Person... của tôi được thực thi.

  3. Thực thi câu lệnh INSERT, như trong tùy chọn 1 ở trên, đặt id=null để cho phép tự động tạo. Sau đó, sử dụng phương thức getGeneratedKeys để truy xuất các khóa được tạo trong câu lệnh cuối cùng.

    Tôi nghĩ rằng điều này nghe có vẻ giống như một lựa chọn tốt, nhưng tôi không thể làm cho nó hoạt động. Dưới đây là một đoạn mã của tôi:

    // PreparedStatement prepared previously... 
    preparedStatement.executeUpdate(); 
    ResultSet genKeys = preparedStatement.getGeneratedKeys(); 
    int id; 
    if (genKeys.next()) { 
        id = genKeys.getInt(1); 
    } 
    // Finish up method... 
    

    Mã này đã được trả lại một trống ResultSet cho genKeys. Tôi có đang sử dụng phương pháp getGeneratedKeys không chính xác không? Nếu tôi có thể làm việc này, đây có thể là cách để đi.

  4. Một lần nữa, hãy thực hiện câu lệnh INSERT cho phép tự động tạo id. Sau đó, ngay lập tức thực hiện CALL IDENTITY() để truy xuất giá trị id mới nhất do kết nối tạo ra (như được giải thích here và được đề cập trong câu hỏi this SO).

    Điều này cũng có vẻ như là một lựa chọn hợp lý, mặc dù tôi phải thực hiện thêm executeQuery. Về mặt tích cực, tôi đã thực sự có thể làm cho nó làm việc với đoạn mã sau:

    // INSERT statement executed here... 
    statement = connection.createStatement(); 
    ResultSet rs = statement.executeQuery("CALL IDENTITY();"); 
    int id; 
    if (rs.next()) id = rs.getInt(1); 
    // Finish up method... 
    

Vì vậy, trong Tóm lại, hai tùy chọn đầu tiên tôi không điên về.Thứ hai có vẻ ok, nhưng tôi chỉ có thể nhận được tùy chọn 4 để làm việc. Tùy chọn nào được ưa thích hơn và tại sao? Nếu tùy chọn 3 là tốt nhất, tôi đang làm gì sai? Ngoài ra, có cách nào tốt hơn mà tôi chưa đề cập? Tôi biết những từ như 'tốt hơn' có thể chủ quan, nhưng tôi đang làm việc với một DB đơn giản và muốn giải pháp trực tiếp nhất không mở DB cho những mâu thuẫn có thể và không làm tăng tỷ lệ thất bại giao dịch (do cố gắng để tạo bản ghi với số id đã tồn tại).

Điều này có vẻ giống như một câu hỏi cơ bản (và cần thiết), nhưng tôi không thể tìm thấy nhiều hướng dẫn về cách tốt nhất để làm điều đó. Cảm ơn.


EDIT: Tôi chỉ tìm thấy this câu hỏi thảo luận về lựa chọn của tôi 3. Theo câu trả lời được chấp nhận, dường như tôi đã bỏ đi Statement.RETURN_GENERATED_KEYS thông số cần thiết để kích hoạt chức năng đó. Tôi không hiển thị phương thức prepareStatement trong đoạn mã của mình, nhưng tôi đã sử dụng phiên bản tham số duy nhất. Tôi cần phải thử lại bằng cách sử dụng phiên bản hai tham số quá tải.

Ngoài ra còn có một số câu hỏi SO khác hiển thị với câu hỏi đó liên quan chặt chẽ đến câu hỏi của tôi. Vì vậy, tôi đoán tôi có thể được coi là một bản sao (không chắc chắn làm thế nào tôi bị mất những câu hỏi khác trước). Nhưng tôi vẫn muốn bất kỳ hướng dẫn nào về việc liệu một giải pháp có được coi là tốt hơn những giải pháp khác hay không. Bây giờ, nếu tôi nhận được tùy chọn 3 để làm việc, tôi có thể sẽ đi với điều đó.

+0

Thành thật mà nói, tôi không hiểu tại sao bạn won't dính vào giải pháp của bạn No1. Thực hiện một SELECT trên ID của hàng mới được chèn vào có giá trị thông thường của một truy vấn, nhưng sau đó một lần nữa, tất cả các tùy chọn khác của bạn cũng làm như vậy. –

+0

Tôi sẽ không được 'SELECT'ing trên' id', mà là trên một loạt các trường khác bao gồm một khóa duy nhất khác. Giá trị 'id' sẽ là thứ tôi đang tìm kiếm. Tuy nhiên, nhận xét của bạn về "chi phí thông thường của truy vấn" vẫn không phụ thuộc vào chi tiết của truy vấn. Tuy nhiên, tôi đã không chắc chắn về chi phí đi kèm trong tùy chọn 3. Tôi nghĩ rằng thông tin sẽ được còn lại từ việc thực hiện tuyên bố trước đó. Đối với tùy chọn 4 Tôi đã không chắc chắn nếu thực hiện một truy vấn của 'CALL IDENTITY()' là bất kỳ rẻ hơn so với một "thường xuyên" 'SELECT' tuyên bố. – neizan

+0

Tôi không biết nếu nó là một trong hai. Cá nhân, tôi sẽ không lo lắng về chỉ một truy vấn sau khi chèn, nhưng tự nhiên tôi không biết các chi tiết cụ thể của nhiệm vụ của bạn. –

Trả lời

1

Không có nhiều hành động ở đây, vì vậy tôi sẽ tiếp tục và trả lời để mang lại kết thúc cho câu hỏi này. Sau khi chơi xung quanh với các tùy chọn khác nhau và sau khi xem câu hỏi this, tôi có thể nhận được tùy chọn 3 của mình để hoạt động. Như tôi đã đề cập trong phần chỉnh sửa cho câu hỏi của mình, tôi sẽ sử dụng tùy chọn 3. Tùy chọn 4 cũng hoạt động tốt, nhưng vì câu trả lời được chấp nhận cho câu hỏi được liên kết được đưa ra bởi một nguồn có uy tín, tôi gắn bó với điều đó. Tôi ước gì tôi đã thấy câu hỏi/câu trả lời đó trước khi bắt đầu câu hỏi này, tôi đã tiết kiệm được chút thời gian!

+3

Tùy chọn 3 là tốt nhất vì khóa được tạo được trả lại khi câu lệnh INSERT hoàn thành, giảm thiểu các hoạt động bổ sung mà các tùy chọn khác yêu cầu. – fredt

+0

Đó là những gì tôi đã tìm ra, và lý do chính tôi đi với lựa chọn đó. Cảm ơn vì bạn đã phản hồi. – neizan

+0

Nếu bạn sẵn sàng sử dụng ORM, họ sẽ thực hiện tất cả công việc cho bạn. Xem ví dụ về [Nhận dạng Sormula Column] (http://www.sormula.org/identity/). –

7

tôi không có đủ uy tín để nhận xét về câu trả lời neizan, nhưng đây là cách tôi giải quyết cùng một vấn đề:

  • Cột trông giống như một cột ID, nhưng nó không được định nghĩa là SẮC;
  • Như đã nói ở trên, bạn cần chỉ định RETURN_GENERATED_KEYS.
  • Dường như nếu bạn thực hiện 2 CH INSN theo thứ tự, chuỗi thứ hai sẽ không trả lại các khóa đã tạo. Sử dụng "CALL IDENTITY()" để thay thế.

Ví dụ sử dụng HSQLDB 2.2.9:

CREATE TABLE MY_TABLE (
ID INTEGER IDENTITY, 
NAME VARCHAR(30) 
) 

Sau đó, trong Java:

PreparedStatement result = cnx.prepareStatement(
    "INSERT INTO MY_TABLE(ID, NAME) VALUES(NULL, 'TOM');", 
    RETURN_GENERATED_KEYS); 
int updated = result.executeUpdate(); 
if (updated == 1) { 
    ResultSet generatedKeys = result.getGeneratedKeys(); 
    if (generatedKeys.next()) { 
     int key = generatedKeys.getInt(1); 
    } 
} 
Các vấn đề liên quan