Ответ 1
Проблема, похоже, связана с используемым диалектом базы данных, другими словами, с некоторыми "экзотическими" частями запроса. Поведение, которое вы описали, можно тиражировать с предоставленным вами запросом, но с небольшими тиражами, которые он запускает без ошибок - в зависимости от диалекта, который вы хотите использовать.
В вашем примере вы используете базу данных H2, но я предполагаю, что это не ваш производственный диалект, не так ли? Я также попробовал его с базой данных PostgresSQL (в версии 9.5).
С исходным запросом поведение одинаково для H2 и PostgreSQL. Но если вы удалите фигурные скобки из ваших столбцов и псевдонимов (которые кажутся как escape-последовательности ODBC) и измените USING clause в явное условие ON a.id = s.id
, запрос выполним без каких-либо исключений.
Чтобы проверить поведение, я создал несколько тестов с разными запросами, используя сеанс Hibernate или EntityManager, потому что, взглянув на ваш код связанного примера, я каким-то образом смутился запутанным использованием Hibernate Session interface и Методы EntityManager, такие как createNativeQuery. В случае сомнений я попробовал оба.
Я использовал те же объекты и более или менее тот же конфигурационный и тестовый код, что и в вашем примере, но в Spring Загрузочная среда, только для удобства. Для переключения между базами данных я использовал Spring Boot Profiles, просто активируйте/раскомментируйте @ActiveProfiles("postgres")
если у вас есть конфигурации для обеих баз данных.
Вот тесты, я надеюсь, что это поможет немного:
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;
}
}