2010-01-23 49 views
52

làm cách nào tôi có thể đặt tham số Hibernate thành "null"? Ví dụ:Hibernate: Cách đặt giá trị tham số truy vấn NULL bằng HQL?

Query query = getSession().createQuery("from CountryDTO c where c.status = :status and c.type =:type") 
.setParameter("status", status, Hibernate.STRING) 
.setParameter("type", type, Hibernate.STRING); 

Trong trường hợp của tôi, chuỗi trạng thái có thể là rỗng. Tôi đã gỡ lỗi này và ngủ đông sau đó tạo ra một chuỗi SQL/truy vấn như thế này .... status = null ... Tuy nhiên điều này không làm việc trong MYSQL, như câu lệnh SQL đúng phải là "status is null" (Mysql không hiểu tình trạng = null và đánh giá này là false để không có hồ sơ bao giờ sẽ được trả lại cho các truy vấn, theo các tài liệu mysql tôi đã đọc ...)

Câu hỏi của tôi:

  1. Tại sao doesnt Hibernate dịch một chuỗi rỗng một cách chính xác tới "is null" (và đúng hơn là tạo sai "= null")?

  2. Cách tốt nhất để viết lại truy vấn này để truy vấn không an toàn là gì? Với nullsafe tôi có nghĩa là trong trường hợp chuỗi "trạng thái" là null hơn nó nên tạo một "là null"?

Cảm ơn bạn rất nhiều! Tim

+0

Đối với những người quan tâm đến giải pháp, tôi nghĩ API tiêu chí là một cách để đi. Nhưng tôi vẫn không thực sự thuyết phục vì nó bloads mã khủng khiếp và sử dụng HQL sẽ sạch hơn nhiều.Có lẽ giải pháp thực sự là triển khai kiểu Hibernate của riêng bạn (tôi đã thực hiện một kiểu cho ENUM nhưng các kiểu tự thực hiện này, ít nhất là các kiểu cơ bản có nhược điểm lớn hơn bulid trong các kiểu ngủ đông trong Truy vấn với HQL (trừ khi bạn cũng mở rộng HQL) parser?) Điều gì làm cho nó trở thành một projekt lớn và đòi hỏi rất nhiều kiến ​​thức Hibernate ... (tiếp theo trong phần 2) – tim

+0

Part2: Có lẽ dễ nhất (mặt khác defenitely NOT a Best Practice) là chỉnh sửa trực tiếp lớp Hibernate String Type Điều này chỉ nên là một dòng xem để kiểm tra các giá trị chuỗi rỗng và hành động tương ứng ... – tim

+3

Chỉ cần chạy vào này quá ... thần của tôi này là braindead –

Trả lời

37
  1. Tôi tin ngủ đông đầu tiên dịch truy vấn HQL của bạn để SQL và chỉ sau đó nó cố gắng để ràng buộc các thông số của bạn. Điều đó có nghĩa là nó sẽ không thể ghi lại truy vấn từ param = ? đến param is null.

  2. Hãy thử sử dụng Tiêu chuẩn api:

    Criteria c = session.createCriteria(CountryDTO.class); 
    c.add(Restrictions.eq("type", type)); 
    c.add(status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status)); 
    List result = c.list(); 
    
+1

Xin chào Sergey, Cảm ơn câu trả lời của bạn. bạn đăng câu trả lời của bạn) cũng với API tiêu chí.Nhưng tôi vẫn nghĩ rằng đây không phải là "giải pháp lý tưởng" cho vấn đề. Ok phải trung thực, đôi khi "oddities/bugs" không có một giải pháp thực sự tốt .... Tôi đã đánh dấu câu trả lời của bạn là "giải pháp" cho vấn đề. – tim

+0

bạn được chào đón :) nhưng bạn mong đợi gì từ giải pháp lý tưởng? nếu bạn tốt với api tiêu chuẩn thay vì hql, bạn luôn có thể tạo phương thức nhà máy của riêng bạn cho tiêu chí 'không an toàn'. –

+0

Câu trả lời hay! Bản sửa lỗi đơn giản này đã giúp tôi truy vấn một trường không có giá trị mà không cần phải tạo chuỗi truy vấn! – cdeszaq

11

Các javadoc for setParameter(String, Object) là rõ ràng, nói rằng giá trị đối tượng phải không rỗng. Đó là một sự xấu hổ rằng nó không ném một ngoại lệ nếu một null được thông qua trong, mặc dù.

Một cách khác là setParameter(String, Object, Type), mà không cho phép giá trị null, mặc dù tôi không chắc chắn những gì Type tham số sẽ là thích hợp nhất ở đây.

+0

Xin chào skaffman. Cảm ơn bạn rất nhiều vì đã giúp đỡ của bạn! Khi đặt câu hỏi tôi thực sự nghĩ rằng tôi chỉ ngớ ngẩn để tìm một giải pháp. Nhưng có vẻ như đây là một "sự kỳ quặc" thực sự của ngủ đông. Nhiều khả năng tôi sẽ phải viết Nullable-String-Type của riêng mình hoặc tôi sẽ phải chuyển sang Truy vấn Tiêu chí ... Sau khi đào sâu hơn, có vẻ như việc triển khai hiện tại Kiểu Hibernat.STRING không phải là không thể: http: // www.docjar.com/html/api/org/hibernate/type/StringType.java.html. (Nhưng không nên MySQL JConnector thực hiện PreparedStatment.setString một cách chính xác?) Cảm ơn Tim. – tim

+2

Chỉ cần cho thông tin, nó thực sự là một "tính năng" của JDBC: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4312435 Hibernate sử dụng các tham số 'PreparedStatement.setNull()' của JDBC để liên kết các tham số 'null' – axtavt

4

Tôi không thử điều này, nhưng điều gì sẽ xảy ra khi bạn sử dụng :status hai lần để kiểm tra NULL?

Query query = getSession().createQuery(
    "from CountryDTO c where (c.status = :status OR (c.status IS NULL AND :status IS NULL)) and c.type =:type" 
) 
.setParameter("status", status, Hibernate.STRING) 
.setParameter("type", type, Hibernate.STRING); 
+0

Hellp Peter, cảm ơn bạn rất nhiều vì đã giúp bạn! Tôi không biết tại sao, nhưng có vẻ như hibernate luôn luôn đánh giá (c.status =: trạng thái OR (c.status IS NULL VÀ: trạng thái IS NULL) để c.status = null (theo mysql truy vấn đăng nhập của tôi) Tôi cũng đã thay đổi thứ tự thành ((c.status IS NULL VÀ: trạng thái IS NULL) HOẶC c.status =: status) với cùng một kết quả Cảm ơn Tim – tim

4

Có vẻ như bạn phải sử dụng is null trong HQL, (có thể dẫn đến hoán vị phức tạp nếu có nhiều hơn một thông số có tiềm năng null.) nhưng đây là một giải pháp khả thi:

String statusTerm = status==null ? "is null" : "= :status"; 
String typeTerm = type==null ? "is null" : "= :type"; 

Query query = getSession().createQuery("from CountryDTO c where c.status " + statusTerm + " and c.type " + typeTerm); 

if(status!=null){ 
    query.setParameter("status", status, Hibernate.STRING) 
} 


if(type!=null){ 
    query.setParameter("type", type, Hibernate.STRING) 
} 
+3

Hình như tòa nhà truy vấn sql cũ với tôi. Dính vào api tiêu chuẩn cho các công cụ như thế này. Viết tay hql là cực kỳ dễ bị lỗi. – whiskeysierra

3

Đối với một truy vấn HQL thực tế:

FROM Users WHERE Name IS NULL 
1

Bạn có thể sử dụng

Restrictions.eqOrIsNull("status", status) 

insted của

status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status) 
1

Dưới đây là giải pháp tôi tìm thấy trên Hibernate 4.1.9.Tôi đã phải vượt qua một tham số để truy vấn của tôi có thể có giá trị NULL đôi khi. Vì vậy, tôi đã thông qua sử dụng:

setParameter("orderItemId", orderItemId, new LongType()) 

Sau đó, tôi sử dụng sau mệnh đề where trong truy vấn của tôi:

where ((:orderItemId is null) OR (orderItem.id != :orderItemId)) 

Như bạn thấy, tôi đang sử dụng Query.setParameter (String, Object, Loại) phương pháp, nơi tôi không thể sử dụng Hibernate.LONG mà tôi tìm thấy trong tài liệu (có thể là trên các phiên bản cũ hơn). Để có tập hợp đầy đủ các tùy chọn của tham số kiểu, hãy kiểm tra danh sách lớp triển khai của giao diện org.hibernate.type.Type.

Hy vọng điều này sẽ hữu ích!

22

Đây không phải là một vấn đề cụ thể Hibernate (nó chỉ là bản chất SQL), và có, có một giải pháp cho cả hai SQL và HQL:

@ Peter Lang đã có ý tưởng đúng, và bạn có truy vấn HQL đúng . Tôi đoán bạn chỉ cần chạy sạch mới để nhận các truy vấn thay đổi ;-)

Mã dưới đây hoàn toàn hoạt động và nó là tuyệt vời nếu bạn giữ tất cả các truy vấn của bạn trong orm.xml

from CountryDTO c where ((:status is null and c.status is null) or c.status = :status) and c.type =:type

Nếu tham số của bạn String là null thì truy vấn sẽ kiểm tra xem trạng thái của hàng có null không. Nếu không, nó sẽ khu nghỉ mát để so sánh với các dấu bằng.

Ghi chú:

Vấn đề này có thể là một điều không minh bạch MySql cụ thể. Tôi chỉ được thử nghiệm với Oracle.

Các truy vấn trên giả định rằng có hàng bảng nơi c.status là null

Các mệnh đề where được ưu tiên để các tham số được kiểm tra đầu tiên.

Tên thông số 'loại' có thể là một từ dành riêng trong SQL nhưng nó không quan trọng vì nó được thay thế trước khi truy vấn chạy.

Nếu bạn cần bỏ qua: trạng thái where_clause hoàn toàn; bạn có thể mã như vậy:

from CountryDTO c where (:status is null or c.status = :status) and c.type =:type

và nó tương đương với:

sql.append(" where "); 
if(status != null){ 
    sql.append(" c.status = :status and "); 
} 
sql.append(" c.type =:type "); 
2

HQL hỗ trợ coalesce, cho phép giải quyết xấu xí như:

where coalesce(c.status, 'no-status') = coalesce(:status, 'no-status') 
-1

này dường như làm việc như wel ->

@Override 
public List<SomeObject> findAllForThisSpecificThing(String thing) { 
    final Query query = entityManager.createQuery(
      "from " + getDomain().getSimpleName() + " t where t.thing = " + ((thing == null) ? " null" : " :thing")); 
    if (thing != null) { 
     query.setParameter("thing", thing); 
    } 
    return query.getResultList(); 
} 

Btw, tôi khá mới ở đây, vì vậy nếu vì bất kỳ lý do nào thì đây không phải là ý hay, hãy cho tôi biết. Cảm ơn.

+0

Bạn không nên sử dụng toán tử 'is': tức là' ... + "t trong đó t.thing" + ((điều == null)? "Là null": "=: điều") ' – bvdb

+0

Bạn có thể xây dựng trên sự khác biệt giữa hai? –

+2

Việc sử dụng '= null' sẽ gây ra một ngoại lệ. Mặc dù nó có thể phụ thuộc vào dbms bạn chạy này. Trong SQL bạn có cùng một vấn đề, ngoài ra bạn phải sử dụng 'là null' và không phải' = null'. Điều với 'null' là một số cơ sở dữ liệu coi nó không thực sự là một giá trị cho rỗng, mà đúng hơn là một giá trị chưa xác định, có nghĩa là null không bằng null. Do đó, 'null' =' null' sẽ không bao giờ đúng. Xem: http://stackoverflow.com/questions/5066492/hql-is-null-and-null-on-an-oracle-column – bvdb

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