2012-07-19 38 views
7

Chúng tôi đang cố gắng lưu trữ một chuỗi mã hóa UTF-16 vào cơ sở dữ liệu Oracle AL32UTF8.Bộ ký tự JDBC Oracle và giới hạn 4000 char

Chương trình của chúng tôi hoạt động hoàn hảo trên cơ sở dữ liệu sử dụng WE8MSWIN1252 làm bộ ký tự. Khi chúng tôi cố gắng chạy nó trên một cơ sở dữ liệu sử dụng AL32UTF8 nó được một java.sql.SQLException: ORA-01461: can bind a LONG value only for insert into a LONG column.

Trong testcase dưới đây mọi thứ hoạt động tốt miễn là dữ liệu đầu vào của chúng tôi không quá dài.

Chuỗi đầu vào có thể vượt quá 4000 ký tự. Chúng tôi muốn giữ lại càng nhiều thông tin càng tốt, mặc dù chúng tôi nhận ra rằng đầu vào sẽ bị cắt bỏ.

Bảng cơ sở dữ liệu của chúng tôi được xác định bằng cách sử dụng từ khóa CHAR (xem bên dưới). Chúng tôi hy vọng rằng điều này sẽ cho phép chúng tôi lưu trữ tới 4000 ký tự của bất kỳ bộ ký tự nào. Việc này có thể thực hiện được không? Nếu vậy, làm thế nào?

Chúng tôi đã thử chuyển đổi Chuỗi thành UTF8 bằng cách sử dụng ByteBuffer mà không thành công. OraclePreparedStatement.setFormOfUse(...) cũng không giúp chúng tôi.

Chuyển sang một số CLOB không phải là một tùy chọn. Nếu chuỗi quá dài cần phải cắt.

Đây là mã của chúng tôi tại thời điểm này:

public static void main(String[] args) throws Exception { 
    String ip ="193.53.40.229"; 
    int port = 1521; 
    String sid = "ora11"; 
    String username = "obasi"; 
    String password = "********"; 

    String driver = "oracle.jdbc.driver.OracleDriver"; 
    String url = "jdbc:oracle:thin:@" + ip + ":" + port + ":" + sid; 
    Class.forName(driver); 

    String shortData = ""; 
    String longData = ""; 
    String data; 

    for (int i = 0; i < 5; i++) 
     shortData += "é"; 

    for (int i = 0; i < 4000; i++) 
     longData += "é"; 

    Connection conn = DriverManager.getConnection(url, username, password); 

    PreparedStatement stat = null; 
    try { 
     stat = conn.prepareStatement("insert into test_table_short values (?)"); 
     data = shortData.substring(0, Math.min(5, shortData.length())); 
     stat.setString(1, data); 
     stat.execute(); 

     stat = conn.prepareStatement("insert into test_table_long values (?)"); 
     data = longData.substring(0, Math.min(4000, longData.length())); 
     stat.setString(1, data); 
     stat.execute(); 
    } finally { 
     try { 
      stat.close(); 
     } catch (Exception ex){} 
    } 
} 

Đây là kịch bản tạo của bảng đơn giản:

CREATE TABLE test_table_short (
    DATA VARCHAR2(5 CHAR); 
); 

CREATE TABLE test_table_long (
    DATA VARCHAR2(4000 CHAR); 
); 

Các trường hợp kiểm tra hoạt động hoàn hảo trên các dữ liệu ngắn. Trên dữ liệu dài tuy nhiên nó vẫn nhận được lỗi. Ngay cả khi longData của chúng tôi chỉ dài 3000 ký tự, nó vẫn không thực thi thành công.

Cảm ơn trước!

Trả lời

7

Trước Oracle 12.1, cột VARCHAR2 bị giới hạn lưu trữ 4000 byte dữ liệu trong bộ ký tự cơ sở dữ liệu ngay cả khi được khai báo VARCHAR2(4000 CHAR). Vì mỗi ký tự trong chuỗi của bạn yêu cầu 2 byte bộ nhớ trong bộ ký tự UTF-8, bạn sẽ không thể lưu trữ hơn 2000 ký tự trong cột. Tất nhiên, con số đó sẽ thay đổi nếu một số ký tự của bạn thực sự chỉ yêu cầu 1 byte dung lượng lưu trữ hoặc nếu một số trong số chúng yêu cầu nhiều hơn 2 byte dung lượng lưu trữ. Khi bộ ký tự cơ sở dữ liệu là Windows-1252, mọi ký tự trong chuỗi của bạn chỉ yêu cầu một byte lưu trữ để bạn có thể lưu trữ 4000 ký tự trong cột.

Vì bạn có chuỗi dài hơn, bạn có thể khai báo cột dưới dạng CLOB thay vì là VARCHAR2 không? Điều đó sẽ (có hiệu quả) loại bỏ các giới hạn chiều dài (có một giới hạn về kích thước của một CLOB mà phụ thuộc vào phiên bản Oracle và kích thước khối nhưng nó ít nhất là trong nhiều GB phạm vi).

Nếu bạn tình cờ sử dụng Oracle 12.1 trở lên, thông số max_string_size cho phép bạn increase the maximum size of a VARCHAR2 column from 4000 bytes to 32767 bytes.

+0

Cảm ơn câu trả lời của bạn. Đáng buồn thay, trong trường hợp này, việc sử dụng clob là câu hỏi cho chúng tôi. Theo [link] (https://forums.oracle.com/forums/thread.jspa?threadID=2369974) đây là câu trả lời đúng. Tuy nhiên, [link] (http://stackoverflow.com/questions/81448/difference-between-byte-and-char-in-column-datatypes) là khá sai lầm trong sự phản đối khiêm tốn của tôi. Bạn có biết điều này được giải thích trong tài liệu này không? Chúng tôi đã tìm kiếm rất nhiều, nhưng không thể tìm thấy điều này. – Arolition

+0

@Arolition - Tôi đã thêm nhận xét vào chuỗi SO. Câu trả lời là chính xác cho đến nay khi nó đi. Nó chỉ không lưu ý rằng nếu một 4000 ký tự đặc biệt đòi hỏi nhiều hơn 4000 byte dung lượng lưu trữ thì giới hạn dung lượng 4000 byte vẫn được khởi động. –

+1

UTF-8 là mã hóa độ dài biến đổi. Nhiều ký tự Châu á yêu cầu ít nhất ba byte để mã hóa. –

4

Giải quyết vấn đề này bằng cách cắt Chuỗi thành độ dài byte yêu cầu.Lưu ý rằng bạn không thể thực hiện việc này chỉ bằng cách sử dụng

stat.substring(0, length) 

vì điều này tạo ra chuỗi UTF-8 có thể dài hơn gấp ba lần cho phép.

while (stat.getBytes("UTF8").length > length) { 
    stat = stat.substring(0, stat.length()-1); 
} 

lưu ý không sử dụng stat.getBytes() vì điều này phụ thuộc vào tập hợp 'file.encoding' và tạo ra Windows-1252 hoặc UTF-8 byte!

Nếu bạn sử dụng Hibernate, bạn có thể thực hiện việc này bằng cách sử dụng org.hibernate.Interceptor!

Các vấn đề liên quan