HHH-13169 - Use exact table name for multitable update queries instead of table alias

This commit is contained in:
Moritz Becker 2018-12-19 15:02:44 +01:00 committed by Guillaume Smet
parent 7fdac4eeb5
commit 8f748db9b0
2 changed files with 101 additions and 1 deletions

View File

@ -456,11 +456,16 @@ class FromElementType {
// executors being used (as this subquery will
// actually be used in the "id select" phase
// of that multi-table executor)
// for update queries, the real table name of the updated table must be used if not in the top level where
// clause, typically in a SET clause
// B) otherwise, we need to use the persister's
// table name as the column qualification
// 2) otherwise (not correlated), use the given alias
if ( isCorrelation() ) {
if ( isMultiTable() || isInsertQuery() ) {
if ( isMultiTable() && ( !isUpdateQuery() || inWhereClause() ) ) {
return propertyMapping.toColumns( tableAlias, path );
}
else if ( isInsertQuery() ) {
return propertyMapping.toColumns( tableAlias, path );
}
return propertyMapping.toColumns( extractTableName(), path );
@ -505,6 +510,10 @@ class FromElementType {
return fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.INSERT;
}
private boolean isUpdateQuery() {
return fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.UPDATE;
}
private boolean isManipulationQuery() {
return fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.UPDATE
|| fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.DELETE;

View File

@ -0,0 +1,91 @@
package org.hibernate.test.hql;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.junit.Assert;
import org.junit.Test;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import java.util.List;
import static javax.persistence.InheritanceType.JOINED;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@TestForIssue(jiraKey = "HHH-13169")
public class UpdateJoinedSubclassCorrelationTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Master.class, SubMaster.class, Detail.class };
}
@Test
public void testJoinedSubclassUpdateWithCorrelation() {
// prepare
doInJPA( this::entityManagerFactory, entityManager -> {
Master m1 = new SubMaster( 1, null );
entityManager.persist( m1 );
Detail d11 = new Detail( 10, m1 );
entityManager.persist( d11 );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
// DO NOT CHANGE this query: it used to trigger a very specific bug caused
// by the root table alias being added to the generated subquery instead of the table name
String u = "update SubMaster m set name = (select 'test' from Detail d where d.master = m)";
Query updateQuery = entityManager.createQuery( u );
updateQuery.executeUpdate();
// so check if the name of the SubMaster has been correctly updated
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Master> query = builder.createQuery( Master.class );
query.select( query.from( Master.class ) );
List<Master> masters = entityManager.createQuery( query ).getResultList();
Assert.assertEquals( 1, masters.size() );
Assert.assertEquals( "test", ((SubMaster) masters.get(0)).name );
} );
}
@Inheritance(strategy = JOINED)
@Entity(name = "Master")
public static abstract class Master {
@Id
private Integer id;
public Master() { }
public Master( Integer id ) {
this.id = id;
}
}
@Entity(name = "SubMaster")
public static class SubMaster extends Master {
private String name;
public SubMaster() { }
public SubMaster( Integer id, String name ) {
super(id);
this.name = name;
}
}
@Entity(name = "Detail")
public static class Detail {
@Id
private Integer id;
@ManyToOne(optional = false)
private Master master;
public Detail( Integer id, Master master ) {
this.id = id;
this.master = master;
}
}
}