Java Hibernate:使用带命名参数的select插入实体

Java Hibernate:使用带命名参数的select插入实体,java,hibernate,jpa,hql,insert-into,Java,Hibernate,Jpa,Hql,Insert Into,我有一个非常具体的问题,我必须做一些类似的事情(在HQL中): 使用Query.setParameter(字符串,对象)()传递参数,它们是字符串、字符串、字符串、日期和枚举 尽管参数编号正确(五个参数对应五个字段),Hibernate仍不断引发以下异常: ... Caused by: org.hibernate.QueryException: number of select types did not match those for insert [insert into ...]

我有一个非常具体的问题,我必须做一些类似的事情(在HQL中):

使用
Query.setParameter(字符串,对象)
()传递参数,它们是字符串、字符串、字符串、日期和枚举

尽管参数编号正确(五个参数对应五个字段),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)
    ...
。。。
原因:org.hibernate.QueryException:选择的类型数与插入[插入到…]的类型数不匹配
在org.hibernate.hql.ast.tree.IntoClause.validateTypes(IntoClause.java:116)上
位于org.hibernate.hql.ast.tree.InsertStatement.validate(InsertStatement.java:57)
位于org.hibernate.hql.ast.HqlSqlWalker.postprocessinert(HqlSqlWalker.java:701)
位于org.hibernate.hql.antlr.HqlSqlBaseWalker.insertStatement(HqlSqlBaseWalker.java:513)
位于org.hibernate.hql.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:255)
位于org.hibernate.hql.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:254)
位于org.hibernate.hql.ast.QueryTranslatorImpl.docomfile(QueryTranslatorImpl.java:185)
位于org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)
在org.hibernate.engine.query.HQLQueryPlan.(HQLQueryPlan.java:101)
在org.hibernate.engine.query.HQLQueryPlan.(HQLQueryPlan.java:80)
位于org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:94)
位于org.hibernate.impl.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:156)
位于org.hibernate.impl.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:135)
位于org.hibernate.impl.SessionImpl.createQuery(SessionImpl.java:1651)
...
我正在使用Hibernate 3.3.2 GA


提前谢谢。

我想Hibernate对此没有准备。为什么需要将表EntityB的列名设置为参数?您是否想过将查询字符串连接起来?

试试以下方法:

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();
Class entityAClass=EntityA.Class;
field1=entityAClass.getDeclaredField(“paramForField1”);
字段1.setAccessible(true);
字符串paramForField1=field1.getName();
field2=entityAClass.getDeclaredField(“paramForField2”);
字段2.setAccessible(true);
字符串paramForField2=field2.getName();
field3=entityAClass.getDeclaredField(“paramForField3”);
字段3.setAccessible(true);
字符串paramForField3=field3.getName();
String hqlisert=String.format(
“插入实体(%1$s,%2$s,%3$s)”+
“从实体b中选择c.%1$s、c.%2$s、c.%3$s”+
“哪里……”,
paramForField1、paramForField2、paramForField3);
int createdEntities=s.createQuery(hqlInsert)
.executeUpdate();
Hibernate不支持参数化插入或选择。只能在WHERE子句中使用参数

任何SQL字符串格式都容易受到影响,这就是为什么需要使用我建议的Java反射习惯用法。如果未提供有效的EntityA字段名,则该字段将无法解析,并将引发异常


通过这种方式,您可以构建动态查询,还可以确保您的代码不会暴露于SQL注入。

从hibernate 4.3开始,这是可能的。hibernate 4.3之前的版本不支持select子句中的参数。

谢谢您的回答。实际上,“paramForFieldX”表示字段x的值,而不是字段本身。因此,我无法想象反射如何能使我的代码成为SQL注入的证明。我猜“DROP TABLE EntityA”不会被解析为类字段,对吗?但是使用字符串连接可以很容易地删除数据库。只需考虑新闻实体的“标题”字段。我的实体的字段名为“title”,但“paramfortletelfield”可能类似于“javarocks”。因此,使用paramForTitleField的反射永远不会工作。根据您的SQL查询,它没有任何意义。您的选择是:“从EntityB选择:paramForField1”。因此“:paramForField1”只能是EntityB的字段。因此,我给了你一个解决方案。事实上,它是有意义的。想象一下下面的原生SQL:从MyTable中选择'a',1这是一个正确的SQL。参数与EntityB完全没有关系。EntityB只是一个只有一行的“硬编码”表。根据选择的结果,insert插入一行或零行。必须这样才能保证操作的原子性。串接字符串的问题是,SQL注入是不可接受的。很高兴知道!!谢谢
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();