HHH-1657 - hql update generate wrong sql with joined subclass hierarcy

This commit is contained in:
Steve Ebersole 2012-03-20 21:52:49 -05:00
parent 8f7b8e10d0
commit 23f49f19b5
4 changed files with 250 additions and 13 deletions

View File

@ -345,13 +345,20 @@ class FromElementType {
LOG.debugf( "toColumns(%s,%s) : subquery = %s", tableAlias, path, subquery );
return new String[]{"(" + subquery + ")"};
}
if (forceAlias) {
return propertyMapping.toColumns(tableAlias, path);
} else if (fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.SELECT) {
}
if (fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.SELECT) {
return propertyMapping.toColumns(tableAlias, path);
} else if (fromElement.getWalker().getCurrentClauseType() == HqlSqlTokenTypes.SELECT) {
}
if (fromElement.getWalker().getCurrentClauseType() == HqlSqlTokenTypes.SELECT) {
return propertyMapping.toColumns(tableAlias, path);
} else if (fromElement.getWalker().isSubQuery()) {
}
if (fromElement.getWalker().isSubQuery()) {
// for a subquery, the alias to use depends on a few things (we
// already know this is not an overall SELECT):
// 1) if this FROM_ELEMENT represents a correlation to the
@ -366,15 +373,24 @@ class FromElementType {
// table name as the column qualification
// 2) otherwise (not correlated), use the given alias
if (isCorrelation()) {
if (isMultiTable()) return propertyMapping.toColumns(tableAlias, path);
if (isMultiTable()) {
return propertyMapping.toColumns(tableAlias, path);
}
return propertyMapping.toColumns(extractTableName(), path);
}
return propertyMapping.toColumns(tableAlias, path);
} else {
String[] columns = propertyMapping.toColumns( path );
LOG.tracev( "Using non-qualified column reference [{0} -> ({1})]", path, ArrayHelper.toString( columns ) );
return columns;
}
if ( isManipulationQuery() && isMultiTable() && inWhereClause() ) {
// the actual where-clause will end up being ripped out the update/delete and used in
// a select to populate the temp table, so its ok to use the table alias to qualify the table refs
// and safer to do so to protect from same-named columns
return propertyMapping.toColumns( tableAlias, path );
}
String[] columns = propertyMapping.toColumns( path );
LOG.tracev( "Using non-qualified column reference [{0} -> ({1})]", path, ArrayHelper.toString( columns ) );
return columns;
}
private boolean isCorrelation() {
@ -394,12 +410,19 @@ class FromElementType {
return fromElement.getQueryable().getTableName();
}
private boolean isManipulationQuery() {
return fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.UPDATE
|| fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.DELETE;
}
private boolean inWhereClause() {
return fromElement.getWalker().getCurrentTopLevelClauseType() == HqlSqlTokenTypes.WHERE;
}
private static final List SPECIAL_MANY2MANY_TREATMENT_FUNCTION_NAMES = java.util.Arrays.asList(
new String[] {
CollectionPropertyNames.COLLECTION_INDEX,
CollectionPropertyNames.COLLECTION_MIN_INDEX,
CollectionPropertyNames.COLLECTION_MAX_INDEX
}
CollectionPropertyNames.COLLECTION_INDEX,
CollectionPropertyNames.COLLECTION_MIN_INDEX,
CollectionPropertyNames.COLLECTION_MAX_INDEX
);
PropertyMapping getPropertyMapping(String propertyName) {

View File

@ -0,0 +1,54 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.hql.joinedSubclass;
import javax.persistence.Entity;
/**
* @author Steve Ebersole
*/
@Entity
public class Employee extends Person {
private String employeeNumber;
public Employee() {
}
public Employee(String name) {
super( name );
}
public Employee(String name, String employeeNumber) {
super( name );
this.employeeNumber = employeeNumber;
}
public String getEmployeeNumber() {
return employeeNumber;
}
public void setEmployeeNumber(String employeeNumber) {
this.employeeNumber = employeeNumber;
}
}

View File

@ -0,0 +1,92 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.hql.joinedSubclass;
import org.hibernate.Session;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
/**
* @author Steve Ebersole
*/
public class JoinedSubclassBulkManipTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Employee.class };
}
@Test
@TestForIssue( jiraKey = "HHH-1657" )
public void testHqlDeleteOnJoinedSubclass() {
Session s = openSession();
s.beginTransaction();
// syntax checking on the database...
s.createQuery( "delete from Employee" ).executeUpdate();
s.createQuery( "delete from Person" ).executeUpdate();
s.createQuery( "delete from Employee e" ).executeUpdate();
s.createQuery( "delete from Person p" ).executeUpdate();
s.createQuery( "delete from Employee where name like 'S%'" ).executeUpdate();
s.createQuery( "delete from Employee e where e.name like 'S%'" ).executeUpdate();
s.createQuery( "delete from Person where name like 'S%'" ).executeUpdate();
s.createQuery( "delete from Person p where p.name like 'S%'" ).executeUpdate();
// now the forms that actually fail from problem underlying HHH-1657
// which is limited to references to properties mapped to column names existing in both tables
// which is normally just the pks. super critical ;)
s.createQuery( "delete from Employee where id = 1" ).executeUpdate();
s.createQuery( "delete from Employee e where e.id = 1" ).executeUpdate();
s.createQuery( "delete from Person where id = 1" ).executeUpdate();
s.createQuery( "delete from Person p where p.id = 1" ).executeUpdate();
s.getTransaction().commit();
s.close();
}
@Test
@TestForIssue( jiraKey = "HHH-1657" )
public void testHqlUpdateOnJoinedSubclass() {
Session s = openSession();
s.beginTransaction();
// syntax checking on the database...
s.createQuery( "update Employee set name = 'Some Other Name' where employeeNumber like 'A%'" ).executeUpdate();
s.createQuery( "update Employee e set e.name = 'Some Other Name' where e.employeeNumber like 'A%'" ).executeUpdate();
s.createQuery( "update Person set name = 'Some Other Name' where name like 'S%'" ).executeUpdate();
s.createQuery( "update Person p set p.name = 'Some Other Name' where p.name like 'S%'" ).executeUpdate();
// now the forms that actually fail from problem underlying HHH-1657
// which is limited to references to properties mapped to column names existing in both tables
// which is normally just the pks. super critical ;)
s.createQuery( "update Employee set name = 'Some Other Name' where id = 1" ).executeUpdate();
s.createQuery( "update Employee e set e.name = 'Some Other Name' where e.id = 1" ).executeUpdate();
s.createQuery( "update Person set name = 'Some Other Name' where id = 1" ).executeUpdate();
s.createQuery( "update Person p set p.name = 'Some Other Name' where p.id = 1" ).executeUpdate();
s.getTransaction().commit();
s.close();
}
}

View File

@ -0,0 +1,68 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.hql.joinedSubclass;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import org.hibernate.annotations.GenericGenerator;
/**
* @author Steve Ebersole
*/
@Entity
@Inheritance( strategy = InheritanceType.JOINED )
public class Person {
private Integer id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Id
@GeneratedValue( generator = "increment" )
@GenericGenerator( strategy = "increment", name = "increment" )
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}