Hi,
I've run into a situation where hibernate seems to generate invalid SQL if I have a property in a compound primary key named 'id'. Hopefully, someone can spot what I'm doing wrong here or confirm this is a bug.
Description
Below I have 2 pojos, A and B. A has a one to many relationship with B. Furthermore, B has a compound key consisting of 2 fields named id and type. The IdClass for B is B.BId. B also has a reference to its A.
The generated tables are as follows: (* denotes primary key)
A(id*, description)
B(id*, type*, a_id)
Source code
I'll omit imports, getters, and setters to try to shorten this post a little...
Code:
@NamedQuery(name = "A.findByB", query = "select a from B b join b.a a where b = ?0")
@Entity
public class A {
@Id
@GeneratedValue
private long id;
@OneToMany(targetEntity = B.class, mappedBy = "a", cascade = CascadeType.ALL)
List<B> bs;
private String description;
}
Code:
@Entity
@IdClass(B.BId.class)
public class B {
@Id
private String type;
@Id
private String id;
@ManyToOne (
targetEntity=A.class,
fetch=FetchType.LAZY,
cascade=CascadeType.ALL)
private A a;
public static class BId implements Serializable {
public String id;
public String type;
}
}
Code:
public class ADao {
@PersistenceContext
private EntityManager entityManager;
A save(A o) {
return entityManager.merge(o);
}
public List<A> findByB(B b) {
Query namedQuery = entityManager.createNamedQuery("A.findByB");
namedQuery.setParameter(0, b);
return (List<A>) namedQuery.getResultList();
}
}
Code:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:applicationContext-server.xml" })
@Transactional
public class ADaoJpaTest extends AbstractJpaTests {
@Autowired
private ADao aDao;
private A a;
private B b;
@Before public void setup() {
a = new A();
a.setDescription("test");
List<B> bs = new ArrayList<B>();
b = new B();
b.setBId("123");
b.setType("test");
bs.add(b);
a.setBs(bs);
A newA = aDao.save(a);
}
@Transactional
@Test public void findByB() {
B b = a.getBs().get(0);
List<A> as = aDao.findByB(b);
Assert.assertTrue(as.size() > 0);
}
}
Here is the relevant excerpt from my applicationContext
Code:
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
</bean>
</property>
<property name="jpaDialect" ref="jpaDialect" />
</bean>
ResultWhen I execute the previous test, the generated SQL for findByB() is
Quote:
Hibernate: insert into A (description) values (?)
Hibernate: select b0_.id as id1_1_, b0_.type as type1_1_, b0_.a_id as a3_1_1_, a1_.id
as id0_0_, a1_.description as descript2_0_0_ from B b0_ left outer join A a1_ on
b0_.a_id=a1_.id where b0_.id=? and b0_.type=?
Hibernate: insert into B (a_id, id, type) values (?, ?, ?)
Hibernate: select a1_.id as id0_, a1_.description as descript2_0_ from B b0_ inner join
A a1_ on b0_.a_id=a1_.id where b0_.id=(?, ?)
Jun 7, 2008 9:51:14 AM org.hibernate.util.JDBCExceptionReporter logExceptions
WARNING: SQL Error: 1241, SQLState: 21000
Jun 7, 2008 9:51:14 AM org.hibernate.util.JDBCExceptionReporter logExceptions
SEVERE: Operand should contain 1 column(s)
I've made the invalid portion bold.
Now for the kicker, if I change B.id and B.BId.id to be named B.bid and B.BId.bid, respectively, then Hibernate generates the following SQL which executes without issue:
Quote:
Hibernate: insert into A (description) values (?)
Hibernate: select b0_.bid as bid1_1_, b0_.type as type1_1_, b0_.a_id as a3_1_1_, a1_.id
as id0_0_, a1_.description as descript2_0_0_ from B b0_ left outer join A a1_ on
b0_.a_id=a1_.id where b0_.bid=? and b0_.type=?
Hibernate: insert into B (a_id, bid, type) values (?, ?, ?)
Hibernate: select a1_.id as id0_, a1_.description as descript2_0_ from B b0_ inner join
A a1_ on b0_.a_id=a1_.id where (b0_.bid, b0_.type)=(?, ?)
So, is this a bug or am I overlooking something?
Hibernate version:I've tried with 3.2.6 and 3.3.0 cr1 I'm using MySQL 5.1.24. I have tried this with HSQL and it throws this exception for the original code and works if I change the id to bid as described above.
Code:
Caused by: org.hibernate.HibernateException: SqlNode's text did not reference expected number of columns
at org.hibernate.hql.ast.tree.BinaryLogicOperatorNode.extractMutationTexts(BinaryLogicOperatorNode.java:156)
at org.hibernate.hql.ast.tree.BinaryLogicOperatorNode.mutateRowValueConstructorSyntax(BinaryLogicOperatorNode.java:94)
at org.hibernate.hql.ast.tree.BinaryLogicOperatorNode.mutateRowValueConstructorSyntaxesIfNecessary(BinaryLogicOperatorNode.java:71)
at org.hibernate.hql.ast.tree.BinaryLogicOperatorNode.initialize(BinaryLogicOperatorNode.java:51)
at org.hibernate.hql.ast.HqlSqlWalker.prepareLogicOperator(HqlSqlWalker.java:1006)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:3992)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:1762)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:776)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:577)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:281)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:229)
at org.hibernate.hql.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:228)
at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:160)
at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:111)
at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:56)
at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:72)
at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:413)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:361)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1304)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:854)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:669)
... 71 more
Thanks for your help!