2016-09-07 10 views
11

Tôi đang cố gắng để có được các truy vấn gốc để làm việc với InheritanceType.JOINED. Từ Hibernate documentation Tôi đã tìm thấy:Thêm đối tượng có InheritanceType.JOINED vào truy vấn gốc

13.1.6. Xử lý thừa kế

Truy vấn SQL gốc truy vấn đối tượng được ánh xạ như một phần của kế thừa phải bao gồm tất cả các thuộc tính cho bảng phân lớp và tất cả các lớp con của nó.

Sử dụng hai đối tượng sau đây:

@Data 
@Entity 
@Table(name = "my_super") 
@EqualsAndHashCode(of = {"id"}) 
@Inheritance(strategy = InheritanceType.JOINED) 
public abstract class MySuper { 

    @Id 
    @Column(name = "id") 
    @GeneratedValue(strategy = GenerationType.SEQUENCE) 
    private long id; 

} 

Và:

@Data 
@Entity 
@Table(name = "my_sub_a") 
@EqualsAndHashCode(callSuper = true) 
public class MySubA extends MySuper { 

    @Column(name = "x") 
    private int x; 

} 

Khi tôi cố gắng tạo ra một truy vấn nguồn gốc sử dụng:

Object actual = session 
    .createNativeQuery("SELECT {s.*} FROM my_super {s} LEFT JOIN my_sub_a {a} USING (id)") 
    .addEntity("s", MySuper.class) 
    .getSingleResult(); 

Nó dịch với truy vấn :

SELECT s.id as id1_1_0_, s_1_.x as x1_0_0_, case when s_1_.id is not null then 1 when s.id is not null then 0 end as clazz_0_ FROM my_super s LEFT JOIN my_sub_a a USING (id) 

Và sau đó không thành công với:

javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not prepare statement 
Caused by: org.hibernate.exception.SQLGrammarException: could not prepare statement 
Caused by: org.h2.jdbc.JdbcSQLException: Column "S_1_.X" not found; SQL statement: 

Vì vậy, chúng ta quan sát tiêm bí danh làm công việc của mình, tìm ra rằng nó có thể cần x cột của my_sub_a là tốt. Tuy nhiên, không thể tìm ra bí danh cho my_sub_a. Mã của tôi nên được sửa đổi như thế nào để bí danh này được kết nối đúng cách?

Mã của tôi có sẵn tại https://gist.github.com/JWGmeligMeyling/51e8a305f3c268eda473511e202f76e8 để dễ dàng tái tạo sự cố của tôi.

(Tôi biết rằng truy vấn này có thể dễ dàng được thể hiện bằng JPQL hoặc HQL là tốt, và thậm chí có thể đạt được bằng cách sử dụng EntityManagerSession API. Tôi tuy nhiên muốn sử dụng này trong một truy vấn phức tạp hơn, mà tôi đơn giản hóa tất cả các chi tiết cần thiết cho câu hỏi này).

+0

Bạn nên có lẽ một trong hai thay thế '{a}' với 'a', hoặc thêm một '.addEntity ("a", MySubA .class) ', vì nó rối tung với các bí danh. Nhưng đây chỉ là một thử –

+0

Những gì tôi nhận được từ các tài liệu bạn đã trích dẫn là bạn cần phải bao gồm các thuộc tính của một trong lựa chọn vì vậy có thể 'SELECT {s. *}, {A. *} [...]'. Ngoài ra tôi nghĩ bạn nên thử '.addEntity (" a ", MySubA.class)' mà không có lớp siêu vì nó có thể sẽ không khởi tạo một lớp trừu tượng. – drakyoko

+0

Không, không phải vậy, nếu tôi sử dụng '.addEntity (" a ", MySubA.class)', Hibernate giả định nó cần tìm nạp hai thực thể riêng biệt, thay vì tìm ra các thuộc tính thuộc về cùng một thực thể. Rằng tôi sử dụng một '{a}' không được trả lời trong truy vấn của tôi thực sự là một lỗi sao chép-dán. –

Trả lời

2

Sự cố có vẻ liên quan đến phương ngữ cơ sở dữ liệu cơ bản đang được sử dụng, nói cách khác là một số phần truy vấn "kỳ lạ". Hành vi bạn mô tả có thể sao chép với truy vấn được cung cấp của bạn nhưng với một số tweek nhỏ xíu nó đang chạy mà không có lỗi - tùy thuộc vào phương ngữ bạn muốn sử dụng.

Trong ví dụ của bạn, bạn sử dụng cơ sở dữ liệu H2, nhưng tôi cho rằng đó không phải là phương ngữ sản xuất của bạn, phải không? Tôi cũng đã thử nó với cơ sở dữ liệu PostgresSQL (trong phiên bản 9.5).

Với truy vấn gốc, hành vi giống nhau trên H2 và PostgreSQL. Nhưng nếu bạn loại bỏ các dấu ngoặc nhọn khỏi các cột và bí danh của bạn (có vẻ như một số ODBC escape sequences) và thay đổi USING clause thành điều kiện ON a.id = s.id rõ ràng, truy vấn có thể thực thi mà không có bất kỳ ngoại lệ nào.

Để xác minh hành vi tôi đã tạo một số thử nghiệm với các truy vấn khác nhau bằng cách sử dụng phiên Hibernate hoặc EntityManager, vì sau khi xem mã ví dụ được liên kết của bạn, tôi đã nhầm lẫn bằng cách sử dụng hỗn hợp Hibernate Session interfaceEntityManager methods like createNativeQuery. Trong trường hợp nghi ngờ tôi đã thử cả hai. Tôi đã sử dụng cùng một thực thể và nhiều hay ít cấu hình và mã kiểm tra giống như bạn đã làm trong ví dụ của mình nhưng trong một Spring Boot environment, chỉ cho mục đích thuận tiện.Để chuyển đổi giữa các cơ sở dữ liệu tôi đã sử dụng Spring Boot Profiles, chỉ cần kích hoạt/bỏ ghi chú phần @ActiveProfiles("postgres") nếu bạn có cấu hình cho cả hai cơ sở dữ liệu tại chỗ.

Dưới đây là các bài kiểm tra, tôi hy vọng rằng sẽ giúp một chút:

import static org.assertj.core.api.Assertions.assertThat; 

import java.util.List; 

import javax.persistence.EntityManager; 
import javax.persistence.PersistenceContext; 

import org.hibernate.Session; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.test.context.ActiveProfiles; 
import org.springframework.test.context.junit4.SpringRunner; 
import org.springframework.transaction.annotation.Transactional; 

@RunWith(SpringRunner.class) 
@SpringBootTest 
@Transactional 
//@ActiveProfiles("postgres") // activate for PostgreSQL tests 
public class InheritanceDemoApplicationTests { 

    private static final String QUERY = "SELECT {s.*} FROM my_super {s} LEFT JOIN my_sub_a {a} USING (id)"; 
    private static final String QUERY_WITHOUT_ODBC_ESCAPES = "SELECT s.* FROM my_super s LEFT JOIN my_sub_a a USING (id)"; 
    private static final String QUERY_WITHOUT_USING_KEYWORD = "SELECT {s.*} FROM my_super {s} LEFT JOIN my_sub_a {a} ON a.id = s.id"; 
    private static final String QUERY_WITHOUT_ODBC_ESCAPES_AND_WITHOUT_USING_KEYWORD = "SELECT s.* FROM my_super s LEFT JOIN my_sub_a a ON a.id = s.id"; 

    @PersistenceContext 
    private EntityManager entityManager; 

    @Test 
    public void sessionQuery() { 
     validateQueryViaSession(QUERY); 
    } 

    @Test 
    public void entityManagerQuery() { 
     validateQueryViaEntityManager(QUERY); 
    } 

    @Test // works for PostgreSQL 
    public void sessionQueryWithoutOdbc() { 
     validateQueryViaSession(QUERY_WITHOUT_ODBC_ESCAPES); 
    } 

    @Test // works for PostgreSQL 
    public void entityManagerQueryWithoutOdbc() { 
     validateQueryViaEntityManager(QUERY_WITHOUT_ODBC_ESCAPES); 
    } 

    @Test 
    public void sessionQueryWithoutUsing() { 
     validateQueryViaSession(QUERY_WITHOUT_USING_KEYWORD); 
    } 

    @Test // works for H2 
    public void entityManagerQueryWithoutUsing() { 
     validateQueryViaEntityManager(QUERY_WITHOUT_USING_KEYWORD); 
    } 

    @Test // works for H2 & PostgreSQL 
    public void sessionQueryWithoutOdbcAndWithoutUsing() { 
     validateQueryViaSession(QUERY_WITHOUT_ODBC_ESCAPES_AND_WITHOUT_USING_KEYWORD); 
    } 

    @Test // works for H2 & PostgreSQL 
    public void entityManagerQueryWithoutOdbcAndWithoutUsing() { 
     validateQueryViaEntityManager(QUERY_WITHOUT_ODBC_ESCAPES_AND_WITHOUT_USING_KEYWORD); 
    } 

    @SuppressWarnings("rawtypes") 
    private void validateQueryViaSession(final String queryString) { 
     final MySubA match = persistMySubA(); 
     List result = entityManager.unwrap(Session.class).createSQLQuery(queryString).addEntity("s", MySuper.class) 
       .list(); 
     assertThat(result.iterator().next()).isEqualToComparingFieldByField(match); 
    } 

    @SuppressWarnings("rawtypes") 
    private void validateQueryViaEntityManager(final String queryString) { 
     final MySubA match = persistMySubA(); 
     List result = entityManager.createNativeQuery(queryString, MySuper.class).getResultList(); 
     assertThat(result.iterator().next()).isEqualToComparingFieldByField(match); 
    } 

    private MySubA persistMySubA() { 
     final MySubA mySubA = new MySubA(); 
     mySubA.setX(1); 
     entityManager.persist(mySubA); 
     entityManager.flush(); 
     return mySubA; 
    } 

} 
+0

Thú vị bắt! Các dấu ngoặc nhọn có nguồn gốc từ ví dụ Hibernate, tôi nhận thấy nó được yêu cầu cho ngủ đông để phát hiện các trường nhưng dường như nó chỉ làm mọi thứ tồi tệ hơn. Bây giờ tôi nghĩ về nó quá, nó có ý nghĩa Hibernate đã không biết về xây dựng unionon (Tôi sẽ không nghĩ rằng nó sẽ sử dụng bất cứ điều gì ngoài việc chiếu nhưng có vẻ như để xử lý toàn bộ truy vấn sau đó). Dù sao, cảm ơn rất nhiều vì đã điều tra điều này! –

+0

Bạn được chào đón. :) Để thỏa mãn việc tạo thực thể, tất cả các trường phải là một phần của phép chiếu, các dấu ngoặc nhọn không cần thiết ở đây. Bạn cũng có thể cung cấp một hàm tạo riêng và tận dụng 'chọn mới ...' https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html –

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