Hibernate: вставка в сущность с использованием select с именованными параметрами

У меня есть очень специфическая проблема, в которой я должен сделать что-то вроде (в HQL):

insert into EntityA(field1, field2, field3, field4, field5)
select
    :paramForField1, 
    :paramForField2,
    :paramForField3,
    :paramForField4,
    :paramForField5
from 
    EntityB
where 
    ...

Параметры передаются с использованием Query.setParameter(String, Object) ( JavaDoc), и они являются String, String, String, Date и Enum.

Хотя номер параметра правильный (пять параметров для пяти полей), Hibernate продолжает вызывать следующее исключение:

...
Caused by: org.hibernate.QueryException: number of select types did not match those for insert [insert into ...]
    at org.hibernate.hql.ast.tree.IntoClause.validateTypes(IntoClause.java:116)
    at org.hibernate.hql.ast.tree.InsertStatement.validate(InsertStatement.java:57)
    at org.hibernate.hql.ast.HqlSqlWalker.postProcessInsert(HqlSqlWalker.java:701)
    at org.hibernate.hql.antlr.HqlSqlBaseWalker.insertStatement(HqlSqlBaseWalker.java:513)
    at org.hibernate.hql.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:255)
    at org.hibernate.hql.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:254)
    at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:185)
    at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)
    at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:101)
    at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
    at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:94)
    at org.hibernate.impl.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:156)
    at org.hibernate.impl.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:135)
    at org.hibernate.impl.SessionImpl.createQuery(SessionImpl.java:1651)
    ...

Я использую Hibernate 3.3.2 GA.

Заранее спасибо.

3 ответа

Решение

Начиная с hibernate 4.3 это возможно. Версии до hibernate 4.3 не поддерживали параметры в предложении select.

Попробуйте это вместо этого:

Class<?> entityAClass = EntityA.class;

Field field1 = entityAClass.getDeclaredField("paramForField1");
field1.setAccessible(true); 
String paramForField1 = field1.getName();

Field field2 = entityAClass.getDeclaredField("paramForField2");
field2.setAccessible(true); 
String paramForField2 = field2.getName();

Field field3 = entityAClass.getDeclaredField("paramForField3");
field3.setAccessible(true); 
String paramForField3 = field3.getName();

String hqlInsert = String.format(
    "insert into EntityA(%1$s, %2$s, %3$s)" + 
    "select c.%1$s, c.%2$s, c.%3$s from EntityB b" + 
    "where ...", 
    paramForField1, paramForField2, paramForField3);
int createdEntities = s.createQuery( hqlInsert )
    .executeUpdate();

Hibernate не поддерживает параметризованные INSERT или SELECT. Вы можете использовать параметры только в предложении WHERE.

Любое форматирование строки SQL подвержено внедрению SQL, поэтому вам нужно использовать предложенную мной идиому Java Reflection. Если вы не укажете правильное имя поля EntityA, поле не будет разрешено, и будет выдано исключение.

Таким образом, вы можете построить динамический запрос, а также убедиться, что вы не подвергаете свой код SQL-инъекции.

Я предполагаю, что Hibernate не готов к этому. Почему вам нужно установить имена столбцов таблицы EntityB в качестве параметров? Вы думали просто о конкатенации строки запроса?

Другие вопросы по тегам