HHH-1400 - formula-based property leads to generation of invalid SQL with subselect fetches
(cherry picked from commit 82c5e0ae26
)
This commit is contained in:
parent
32fce20012
commit
10a5f4af15
|
@ -17,6 +17,8 @@ import org.hibernate.persister.entity.PropertyMapping;
|
|||
* @author Gavin King
|
||||
*/
|
||||
public class SubselectFetch {
|
||||
private static final String FROM_STRING = " from ";
|
||||
|
||||
private final Set resultingEntityKeys;
|
||||
private final String queryString;
|
||||
private final String alias;
|
||||
|
@ -39,13 +41,52 @@ public class SubselectFetch {
|
|||
|
||||
//TODO: ugly here:
|
||||
final String queryString = queryParameters.getFilteredSQL();
|
||||
int fromIndex = queryString.indexOf( " from " );
|
||||
int orderByIndex = queryString.lastIndexOf( "order by" );
|
||||
final int fromIndex = getFromIndex( queryString );
|
||||
final int orderByIndex = queryString.lastIndexOf( "order by" );
|
||||
this.queryString = orderByIndex > 0
|
||||
? queryString.substring( fromIndex, orderByIndex )
|
||||
: queryString.substring( fromIndex );
|
||||
}
|
||||
|
||||
private static int getFromIndex(String queryString) {
|
||||
int index = queryString.indexOf( FROM_STRING );
|
||||
|
||||
if ( index < 0 ) {
|
||||
return index;
|
||||
}
|
||||
|
||||
while ( !parenthesesMatch( queryString.substring( 0, index ) ) ) {
|
||||
String subString = queryString.substring( index + FROM_STRING.length() );
|
||||
|
||||
int subIndex = subString.indexOf( FROM_STRING );
|
||||
|
||||
if ( subIndex < 0 ) {
|
||||
return subIndex;
|
||||
}
|
||||
|
||||
index += FROM_STRING.length() + subIndex;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private static boolean parenthesesMatch(String string) {
|
||||
int parenCount = 0;
|
||||
|
||||
for ( int i = 0; i < string.length(); i++ ) {
|
||||
char character = string.charAt( i );
|
||||
|
||||
if ( character == '(' ) {
|
||||
parenCount++;
|
||||
}
|
||||
else if ( character == ')' ) {
|
||||
parenCount--;
|
||||
}
|
||||
}
|
||||
|
||||
return parenCount == 0;
|
||||
}
|
||||
|
||||
public QueryParameters getQueryParameters() {
|
||||
return queryParameters;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.subselectfetch;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class Name implements Serializable {
|
||||
private int id;
|
||||
private String name;
|
||||
private int nameLength;
|
||||
private List values;
|
||||
|
||||
public int getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
public int getNameLength() { return nameLength; }
|
||||
public List getValues() { return values; }
|
||||
|
||||
public void setId(int id) { this.id = id; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public void setNameLength(int nameLength) { this.nameLength = nameLength; }
|
||||
public void setValues(List values) { this.values = values; }
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Name )) return false;
|
||||
Name other = (Name) obj;
|
||||
return other.id == this.id;
|
||||
}
|
||||
|
||||
public int hashCode() { return id; }
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.subselectfetch;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.mapping.Collection;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class SubselectFetchWithFormulaTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected String getBaseForMappings() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getMappings() {
|
||||
return new String[] {
|
||||
"org/hibernate/test/subselectfetch/Name.hbm.xml",
|
||||
"org/hibernate/test/subselectfetch/Value.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
|
||||
Name chris = new Name();
|
||||
chris.setId( 1 );
|
||||
chris.setName( "chris" );
|
||||
Value cat = new Value();
|
||||
cat.setId(1);
|
||||
cat.setName( chris );
|
||||
cat.setValue( "cat" );
|
||||
Value canary = new Value();
|
||||
canary.setId( 2 );
|
||||
canary.setName( chris );
|
||||
canary.setValue( "canary" );
|
||||
|
||||
session.persist( chris );
|
||||
session.persist( cat );
|
||||
session.persist( canary );
|
||||
|
||||
Name sam = new Name();
|
||||
sam.setId(2);
|
||||
sam.setName( "sam" );
|
||||
Value seal = new Value();
|
||||
seal.setId( 3 );
|
||||
seal.setName( sam );
|
||||
seal.setValue( "seal" );
|
||||
Value snake = new Value();
|
||||
snake.setId( 4 );
|
||||
snake.setName( sam );
|
||||
snake.setValue( "snake" );
|
||||
|
||||
session.persist( sam );
|
||||
session.persist(seal);
|
||||
session.persist( snake );
|
||||
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
session.createQuery( "delete Value" ).executeUpdate();
|
||||
session.createQuery( "delete Name" ).executeUpdate();
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void checkSubselectWithFormula() throws Exception {
|
||||
// as a pre-condition make sure that subselect fetching is enabled for the collection...
|
||||
Collection collectionBinding = metadata().getCollectionBinding( Name.class.getName() + ".values" );
|
||||
assertThat( collectionBinding.isSubselectLoadable(), is( true ) );
|
||||
|
||||
// Now force the subselect fetch and make sure we do not get SQL errors
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
List results = session.createCriteria(Name.class).list();
|
||||
for (Object result : results) {
|
||||
Name name = (Name) result;
|
||||
name.getValues().size();
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.subselectfetch;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Value implements Serializable {
|
||||
private int id;
|
||||
private Name name;
|
||||
private String value;
|
||||
|
||||
public int getId() { return id; }
|
||||
public Name getName() { return name; }
|
||||
public String getValue() { return value; }
|
||||
|
||||
public void setId(int id) { this.id = id; }
|
||||
public void setName(Name name) { this.name = name; }
|
||||
public void setValue(String value) { this.value = value; }
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Value )) return false;
|
||||
Value other = (Value) obj;
|
||||
return other.id == this.id;
|
||||
}
|
||||
|
||||
public int hashCode() { return id; }
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ 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>.
|
||||
-->
|
||||
<!DOCTYPE hibernate-mapping
|
||||
PUBLIC "-//Hibernate?Hibernate Mapping DTD//EN"
|
||||
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
|
||||
|
||||
<hibernate-mapping>
|
||||
|
||||
<class name="org.hibernate.test.subselectfetch.Name" table="Name">
|
||||
<id name="id" column="id"/>
|
||||
<property name="name" column="name"/>
|
||||
|
||||
<property name="nameLength" formula="(select length(name) from name where id=name.id)"/>
|
||||
|
||||
<bag name="values" inverse="true" lazy="false" fetch="subselect">
|
||||
<key column="name_id"/>
|
||||
<one-to-many class="org.hibernate.test.subselectfetch.Value"/>
|
||||
</bag>
|
||||
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ 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>.
|
||||
-->
|
||||
<!DOCTYPE hibernate-mapping
|
||||
PUBLIC "-//Hibernate?Hibernate Mapping DTD//EN"
|
||||
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
|
||||
|
||||
<hibernate-mapping>
|
||||
|
||||
<class name="org.hibernate.test.subselectfetch.Value" table="Value">
|
||||
<id name="id" column="id"/>
|
||||
<property name="value" column="value"/>
|
||||
<many-to-one name="name" column="name_id" insert="false" update="false" />
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
Loading…
Reference in New Issue