HHH-11230 - Aggregate on (mappedBy) joined column produces invalid query
This commit is contained in:
parent
36d6563998
commit
080d65de99
|
@ -207,11 +207,14 @@ public class JoinSequence {
|
||||||
subqueryJoinFragment.addFromFragmentString( ".*" );
|
subqueryJoinFragment.addFromFragmentString( ".*" );
|
||||||
|
|
||||||
// Re-alias columns of withClauseJoinAlias and rewrite withClauseFragment
|
// Re-alias columns of withClauseJoinAlias and rewrite withClauseFragment
|
||||||
|
// A list of possible delimited identifier types: https://en.wikibooks.org/wiki/SQL_Dialects_Reference/Data_structure_definition/Delimited_identifiers
|
||||||
Pattern p = Pattern.compile( Pattern.quote( withClauseJoinAlias + "." ) + "(" +
|
Pattern p = Pattern.compile( Pattern.quote( withClauseJoinAlias + "." ) + "(" +
|
||||||
"([a-zA-Z0-9_]+) | " + // Normal identifiers
|
"([a-zA-Z0-9_]+)|" + // Normal identifiers
|
||||||
"('[a-zA-Z0-9_]+' ((''[a-zA-Z0-9_]+)+')?) | " + // Single quoted identifiers
|
// Ignore single quoted identifiers to avoid possible clashes with string literals
|
||||||
"(\"[a-zA-Z0-9_]+\" ((\"\"[a-zA-Z0-9_]+)+\")?) | " + // Double quoted identifiers
|
// and since SQLLite is the only DB supporting that style, we simply decide to not support it
|
||||||
"(`[a-zA-Z0-9_]+` ((``[a-zA-Z0-9_]+)+`)?) | " + // MySQL quoted identifiers
|
//"('[a-zA-Z0-9_]+'((''[a-zA-Z0-9_]+)+')?)|" + // Single quoted identifiers
|
||||||
|
"(\"[a-zA-Z0-9_]+\"((\"\"[a-zA-Z0-9_]+)+\")?)|" + // Double quoted identifiers
|
||||||
|
"(`[a-zA-Z0-9_]+`((``[a-zA-Z0-9_]+)+`)?)|" + // MySQL quoted identifiers
|
||||||
"(\\[[a-zA-Z0-9_\\s]+\\])" + // MSSQL quoted identifiers
|
"(\\[[a-zA-Z0-9_\\s]+\\])" + // MSSQL quoted identifiers
|
||||||
")"
|
")"
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.hql;
|
||||||
|
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Inheritance;
|
||||||
|
import javax.persistence.InheritanceType;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.MappedSuperclass;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Version;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of WithClauseTest.
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class WithClauseJoinRewriteTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[]{
|
||||||
|
AbstractObject.class,
|
||||||
|
AbstractConfigurationObject.class,
|
||||||
|
ConfigurationObject.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-11230")
|
||||||
|
public void testInheritanceReAliasing() {
|
||||||
|
Session s = openSession();
|
||||||
|
Transaction tx = s.beginTransaction();
|
||||||
|
|
||||||
|
// Just assert that the query is successful
|
||||||
|
List<Object[]> results = s.createQuery(
|
||||||
|
"SELECT usedBy.id, usedBy.name, COUNT(inverse.id) " +
|
||||||
|
"FROM " + AbstractConfigurationObject.class.getName() + " config " +
|
||||||
|
"INNER JOIN config.usedBy usedBy " +
|
||||||
|
"LEFT JOIN usedBy.uses inverse ON inverse.id = config.id " +
|
||||||
|
"WHERE config.id = 0 " +
|
||||||
|
"GROUP BY usedBy.id, usedBy.name",
|
||||||
|
Object[].class
|
||||||
|
).getResultList();
|
||||||
|
|
||||||
|
tx.commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table( name = "config" )
|
||||||
|
@Inheritance( strategy = InheritanceType.JOINED )
|
||||||
|
public static abstract class AbstractConfigurationObject<T extends ConfigurationObject> extends AbstractObject {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private Set<ConfigurationObject> uses = new HashSet<>();
|
||||||
|
private Set<ConfigurationObject> usedBy = new HashSet<>();
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToMany( targetEntity = AbstractConfigurationObject.class, fetch = FetchType.LAZY, cascade = {} )
|
||||||
|
public Set<ConfigurationObject> getUses () {
|
||||||
|
return uses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUses(Set<ConfigurationObject> uses) {
|
||||||
|
this.uses = uses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToMany ( targetEntity = AbstractConfigurationObject.class, fetch = FetchType.LAZY, mappedBy = "uses", cascade = {} )
|
||||||
|
public Set<ConfigurationObject> getUsedBy () {
|
||||||
|
return usedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsedBy(Set<ConfigurationObject> usedBy) {
|
||||||
|
this.usedBy = usedBy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table( name = "config_config" )
|
||||||
|
public static class ConfigurationObject extends AbstractConfigurationObject<ConfigurationObject> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@MappedSuperclass
|
||||||
|
public static class AbstractObject {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long version;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
public Long getId () {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Version
|
||||||
|
@Column( nullable = false )
|
||||||
|
public Long getVersion () {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(Long version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -201,6 +201,26 @@ public class WithClauseTest extends BaseCoreFunctionalTestCase {
|
||||||
data.cleanup();
|
data.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-11230")
|
||||||
|
public void testWithClauseAsSubqueryWithEqualOperator() {
|
||||||
|
TestData data = new TestData();
|
||||||
|
data.prepare();
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
Transaction txn = s.beginTransaction();
|
||||||
|
|
||||||
|
// Like testWithClauseAsSubquery but uses equal operator since it render differently in SQL
|
||||||
|
List list = s.createQuery( "from Human h left join h.friends as f with f.nickName = 'bubba' where h.description = 'father'" )
|
||||||
|
.list();
|
||||||
|
assertEquals( "subquery rewriting of join table did not take effect", 1, list.size() );
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
data.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestForIssue(jiraKey = "HHH-9329")
|
@TestForIssue(jiraKey = "HHH-9329")
|
||||||
public void testWithClauseAsSubqueryWithKey() {
|
public void testWithClauseAsSubqueryWithKey() {
|
||||||
|
|
Loading…
Reference in New Issue