HHH-11028 - {h-schema} is not replaced in SQLDelete, SQLInsert and SQLUpdate code enhancement

This commit is contained in:
Laabidi Raissi 2016-08-08 23:25:37 +02:00 committed by Vlad Mihalcea
parent 1cb10729b0
commit 090d18dcc8
4 changed files with 254 additions and 8 deletions

View File

@ -60,6 +60,10 @@ public class SQLQueryParser {
return aliasesFound>0; return aliasesFound>0;
} }
protected String getOriginalQueryString() {
return originalQueryString;
}
public String process() { public String process() {
String processedSql = substituteBrackets( originalQueryString ); String processedSql = substituteBrackets( originalQueryString );
processedSql = substituteParams( processedSql ); processedSql = substituteParams( processedSql );
@ -68,7 +72,7 @@ public class SQLQueryParser {
// TODO: should "record" how many properties we have reffered to - and if we // TODO: should "record" how many properties we have reffered to - and if we
// don't get'em'all we throw an exception! Way better than trial and error ;) // don't get'em'all we throw an exception! Way better than trial and error ;)
private String substituteBrackets(String sqlQuery) throws QueryException { protected String substituteBrackets(String sqlQuery) throws QueryException {
StringBuilder result = new StringBuilder( sqlQuery.length() + 20 ); StringBuilder result = new StringBuilder( sqlQuery.length() + 20 );
int left, right; int left, right;
@ -126,7 +130,7 @@ public class SQLQueryParser {
throw new QueryException( "Unknown placeholder ", aliasPath ); throw new QueryException( "Unknown placeholder ", aliasPath );
} }
} }
else { else if (context != null) {
int firstDot = aliasPath.indexOf( '.' ); int firstDot = aliasPath.indexOf( '.' );
if ( firstDot == -1 ) { if ( firstDot == -1 ) {
if ( context.isEntityAlias( aliasPath ) ) { if ( context.isEntityAlias( aliasPath ) ) {

View File

@ -70,7 +70,6 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.PersistenceContext.NaturalIdHelper; import org.hibernate.engine.spi.PersistenceContext.NaturalIdHelper;
import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.ValueInclusion; import org.hibernate.engine.spi.ValueInclusion;
@ -87,6 +86,7 @@ import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.Expectation;
import org.hibernate.jdbc.Expectations; import org.hibernate.jdbc.Expectations;
import org.hibernate.jdbc.TooManyRowsAffectedException; import org.hibernate.jdbc.TooManyRowsAffectedException;
import org.hibernate.loader.custom.sql.SQLQueryParser;
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder; import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.CascadeEntityLoader; import org.hibernate.loader.entity.CascadeEntityLoader;
import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder; import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder;
@ -3927,16 +3927,16 @@ public abstract class AbstractEntityPersister
for ( int j = 0; j < joinSpan; j++ ) { for ( int j = 0; j < joinSpan; j++ ) {
sqlInsertStrings[j] = customSQLInsert[j] == null ? sqlInsertStrings[j] = customSQLInsert[j] == null ?
generateInsertString( getPropertyInsertability(), j ) : generateInsertString( getPropertyInsertability(), j ) :
customSQLInsert[j]; substituteBrackets( customSQLInsert[j]);
sqlUpdateStrings[j] = customSQLUpdate[j] == null ? sqlUpdateStrings[j] = customSQLUpdate[j] == null ?
generateUpdateString( getPropertyUpdateability(), j, false ) : generateUpdateString( getPropertyUpdateability(), j, false ) :
customSQLUpdate[j]; substituteBrackets( customSQLUpdate[j]);
sqlLazyUpdateStrings[j] = customSQLUpdate[j] == null ? sqlLazyUpdateStrings[j] = customSQLUpdate[j] == null ?
generateUpdateString( getNonLazyPropertyUpdateability(), j, false ) : generateUpdateString( getNonLazyPropertyUpdateability(), j, false ) :
customSQLUpdate[j]; substituteBrackets( customSQLUpdate[j]);
sqlDeleteStrings[j] = customSQLDelete[j] == null ? sqlDeleteStrings[j] = customSQLDelete[j] == null ?
generateDeleteString( j ) : generateDeleteString( j ) :
customSQLDelete[j]; substituteBrackets( customSQLDelete[j]);
} }
tableHasColumns = new boolean[joinSpan]; tableHasColumns = new boolean[joinSpan];
@ -3959,7 +3959,7 @@ public abstract class AbstractEntityPersister
.getInsertGeneratedIdentifierDelegate( this, getFactory().getDialect(), useGetGeneratedKeys() ); .getInsertGeneratedIdentifierDelegate( this, getFactory().getDialect(), useGetGeneratedKeys() );
sqlIdentityInsertString = customSQLInsert[0] == null sqlIdentityInsertString = customSQLInsert[0] == null
? generateIdentityInsertString( getPropertyInsertability() ) ? generateIdentityInsertString( getPropertyInsertability() )
: customSQLInsert[0]; : substituteBrackets( customSQLInsert[0] );
} }
else { else {
sqlIdentityInsertString = null; sqlIdentityInsertString = null;
@ -3968,6 +3968,10 @@ public abstract class AbstractEntityPersister
logStaticSQL(); logStaticSQL();
} }
private String substituteBrackets(String sql) {
return new SubstituteBracketSQLQueryParser( sql, getFactory() ).process();
}
public final void postInstantiate() throws MappingException { public final void postInstantiate() throws MappingException {
doLateInit(); doLateInit();
@ -5559,5 +5563,15 @@ public abstract class AbstractEntityPersister
// }; // };
} }
private static class SubstituteBracketSQLQueryParser extends SQLQueryParser {
SubstituteBracketSQLQueryParser(String queryString, SessionFactoryImplementor factory) {
super( queryString, null, factory );
}
@Override
public String process() {
return substituteBrackets( getOriginalQueryString() );
}
}
} }

View File

@ -0,0 +1,118 @@
/*
* 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.persister.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Loader;
import org.hibernate.annotations.NamedNativeQuery;
import org.hibernate.annotations.Persister;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLInsert;
import org.hibernate.annotations.SQLUpdate;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* @author Laabidi RAISSI
*/
@RequiresDialect(H2Dialect.class)
public class CustomSqlSchemaResolvingIdentityTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[]{
CustomEntity.class, Dummy.class
};
}
@Test
public void testSchemaNotReplacedInCustomSQL() throws Exception {
String className = CustomEntity.class.getName();
final AbstractEntityPersister persister = (AbstractEntityPersister) sessionFactory().getEntityPersister( className );
String insertQuery = persister.getSQLInsertStrings()[0];
String updateQuery = persister.getSQLUpdateStrings()[0];
String deleteQuery = persister.getSQLDeleteStrings()[0];
assertEquals( "Incorrect custom SQL for insert in Entity: " + className,
"INSERT INTO FOO (name) VALUES (?)", insertQuery );
assertEquals( "Incorrect custom SQL for delete in Entity: " + className,
"DELETE FROM FOO WHERE id = ?", deleteQuery );
assertEquals( "Incorrect custom SQL for update in Entity: " + className,
"UPDATE FOO SET name = ? WHERE id = ? ", updateQuery );
CustomEntity _entitty = doInHibernate( this::sessionFactory, session -> {
CustomEntity entity = new CustomEntity();
session.persist( entity );
return entity;
} );
doInHibernate( this::sessionFactory, session -> {
CustomEntity entity = session.find( CustomEntity.class, 1 );
assertNotNull(entity);
entity.name = "Vlad";
} );
doInHibernate( this::sessionFactory, session -> {
CustomEntity entity = session.find( CustomEntity.class, _entitty.id );
session.delete( entity );
} );
doInHibernate( this::sessionFactory, session -> {
CustomEntity entity = session.find( CustomEntity.class, _entitty.id );
assertNull(entity);
} );
}
@Entity(name = "CardWithCustomSQL")
@Persister( impl = SingleTableEntityPersister.class )
@Loader(namedQuery = "find_foo_by_id")
@NamedNativeQuery(
name = "find_foo_by_id",
query = "SELECT id, name FROM {h-schema}FOO WHERE id = ?",
resultClass = CustomEntity.class
)
@SQLInsert(sql = "INSERT INTO {h-schema}FOO (name) VALUES (?)", callable = true)
@SQLDelete(sql = "DELETE FROM {h-schema}FOO WHERE id = ?", check = ResultCheckStyle.COUNT)
@SQLUpdate(sql = "UPDATE {h-schema}FOO SET name = ? WHERE id = ? ")
public static class CustomEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer id;
private String name;
}
@Entity(name = "Dummy")
@Table(name = "FOO")
public static class Dummy {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer id;
private String name;
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.persister.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Loader;
import org.hibernate.annotations.NamedNativeQuery;
import org.hibernate.annotations.Persister;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLInsert;
import org.hibernate.annotations.SQLUpdate;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* @author Laabidi RAISSI
*/
public class CustomSqlSchemaResolvingTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[]{
CustomEntity.class, Dummy.class
};
}
@Test
public void testSchemaNotReplacedInCustomSQL() throws Exception {
String className = CustomEntity.class.getName();
final AbstractEntityPersister persister = (AbstractEntityPersister) sessionFactory().getEntityPersister( className );
String insertQuery = persister.getSQLInsertStrings()[0];
String updateQuery = persister.getSQLUpdateStrings()[0];
String deleteQuery = persister.getSQLDeleteStrings()[0];
assertEquals( "Incorrect custom SQL for insert in Entity: " + className,
"INSERT INTO FOO (name, id) VALUES (?, ?)", insertQuery );
assertEquals( "Incorrect custom SQL for delete in Entity: " + className,
"DELETE FROM FOO WHERE id = ?", deleteQuery );
assertEquals( "Incorrect custom SQL for update in Entity: " + className,
"UPDATE FOO SET name = ? WHERE id = ? ", updateQuery );
doInHibernate( this::sessionFactory, session -> {
CustomEntity entity = new CustomEntity();
entity.id = 1;
session.persist( entity );
} );
doInHibernate( this::sessionFactory, session -> {
CustomEntity entity = session.find( CustomEntity.class, 1 );
assertNotNull(entity);
entity.name = "Vlad";
} );
doInHibernate( this::sessionFactory, session -> {
CustomEntity entity = session.find( CustomEntity.class, 1 );
session.delete( entity );
} );
doInHibernate( this::sessionFactory, session -> {
CustomEntity entity = session.find( CustomEntity.class, 1 );
assertNull(entity);
} );
}
@Entity(name = "CardWithCustomSQL")
@Persister( impl = SingleTableEntityPersister.class )
@Loader(namedQuery = "find_foo_by_id")
@NamedNativeQuery(
name = "find_foo_by_id",
query = "SELECT id, name FROM {h-schema}FOO WHERE id = ?",
resultClass = CustomEntity.class
)
@SQLInsert(sql = "INSERT INTO {h-schema}FOO (name, id) VALUES (?, ?)", callable = true)
@SQLDelete(sql = "DELETE FROM {h-schema}FOO WHERE id = ?", check = ResultCheckStyle.COUNT)
@SQLUpdate(sql = "UPDATE {h-schema}FOO SET name = ? WHERE id = ? ")
public static class CustomEntity {
@Id
public Integer id;
private String name;
}
@Entity(name = "Dummy")
@Table(name = "FOO")
public static class Dummy {
@Id
public Integer id;
private String name;
}
}