Merge remote-tracking branch 'upstream/master' into wip/6.0

This commit is contained in:
Andrea Boriero 2020-08-28 12:42:29 +01:00
commit 638a3e234b
15 changed files with 724 additions and 23 deletions

View File

@ -43,7 +43,7 @@ public class FilterSourceImpl
for ( Serializable content : filterElement.getContent() ) {
if ( String.class.isInstance( content ) ) {
final String str = content.toString();
if ( !StringHelper.isEmptyOrWhiteSpace( str ) ) {
if ( !StringHelper.isBlank( str ) ) {
conditionContent = str.trim();
}
}

View File

@ -18,7 +18,6 @@ import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tuple.entity.EntityTuplizer;
@ -37,9 +36,9 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
private final boolean inLineDirtyChecking;
private Set<String> writtenFieldNames;
private boolean initialized;
private Status status;
private boolean initializeBeforeWrite;
private final boolean initializeBeforeWrite;
public EnhancementAsProxyLazinessInterceptor(
String entityName,
@ -63,6 +62,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
// if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to initialise the entity
// because the pre-computed update statement contains even not dirty properties and so we need all the values
initializeBeforeWrite = !inLineDirtyChecking || !entityPersister.getEntityMetamodel().isDynamicUpdate();
status = Status.UNINITIALIZED;
}
public EntityKey getEntityKey() {
@ -72,7 +72,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
@Override
protected Object handleRead(Object target, String attributeName, Object value) {
// it is illegal for this interceptor to still be attached to the entity after initialization
if ( initialized ) {
if ( isInitialized() ) {
throw new IllegalStateException( "EnhancementAsProxyLazinessInterceptor interception on an initialized instance" );
}
@ -125,7 +125,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
isTempSession
);
initialized = true;
setInitialized();
if ( writtenValues != null ) {
// here is the replaying of the explicitly set values we prepared above
@ -189,7 +189,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
// Add an entry for this entity in the PC of the temp Session
session.getPersistenceContextInternal().addEntity(
target,
Status.READ_ONLY,
org.hibernate.engine.spi.Status.READ_ONLY,
// loaded state
ArrayHelper.filledArray(
LazyPropertyInitializer.UNFETCHED_PROPERTY,
@ -215,7 +215,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
@Override
protected Object handleWrite(Object target, String attributeName, Object oldValue, Object newValue) {
if ( initialized ) {
if ( isInitialized() ) {
throw new IllegalStateException( "EnhancementAsProxyLazinessInterceptor interception on an initialized instance" );
}
@ -251,7 +251,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
forceInitialize( target, attributeName );
}
finally {
initialized = true;
setInitialized();
}
if ( inLineDirtyChecking ) {
@ -279,14 +279,14 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
@Override
public void attributeInitialized(String name) {
if ( initialized ) {
if ( status == Status.INITIALIZED ) {
throw new UnsupportedOperationException( "Expected call to EnhancementAsProxyLazinessInterceptor#attributeInitialized" );
}
}
@Override
public boolean isAttributeLoaded(String fieldName) {
if ( initialized ) {
if ( isInitialized() ) {
throw new UnsupportedOperationException( "Call to EnhancementAsProxyLazinessInterceptor#isAttributeLoaded on an interceptor which is marked as initialized" );
}
// Only fields from the identifier are loaded (until it's initialized)
@ -295,7 +295,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
@Override
public boolean hasAnyUninitializedAttributes() {
if ( initialized ) {
if ( isInitialized() ) {
throw new UnsupportedOperationException( "Call to EnhancementAsProxyLazinessInterceptor#hasAnyUninitializedAttributes on an interceptor which is marked as initialized" );
}
return true;
@ -306,8 +306,26 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
return entityKey.getIdentifier();
}
public boolean isInitializing() {
return status == Status.INITIALIZING;
}
public void setInitializing() {
status = Status.INITIALIZING;
}
//Mostly useful for testing
public boolean isInitialized() {
return initialized;
return status == Status.INITIALIZED;
}
private void setInitialized() {
status = Status.INITIALIZED;
}
private enum Status {
UNINITIALIZED,
INITIALIZING,
INITIALIZED
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.hql.internal.ast.exec;
import antlr.RecognitionException;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.hql.internal.ast.HqlSqlWalker;
import org.hibernate.hql.internal.ast.QuerySyntaxException;
import org.hibernate.hql.internal.ast.SqlGenerator;
import org.hibernate.hql.internal.ast.tree.AssignmentSpecification;
import org.hibernate.hql.internal.ast.tree.UpdateStatement;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.Update;
import java.util.List;
import java.util.stream.IntStream;
import static org.hibernate.hql.spi.id.AbstractTableBasedBulkIdHandler.generateIdSelect;
/**
* Executes HQL bulk updates against a single table, using a subselect
* against multiple tables to collect ids, which is needed when the
* where condition of the query touches columns from multiple tables.
*
* @author Gavin King
*/
public class IdSubselectUpdateExecutor extends BasicExecutor {
private final Queryable persister;
private final String sql;
private final List<ParameterSpecification> parameterSpecifications;
public Queryable getPersister() {
return persister;
}
@Override
public String getSql() {
return sql;
}
@Override
public List<ParameterSpecification> getParameterSpecifications() {
return parameterSpecifications;
}
public IdSubselectUpdateExecutor(HqlSqlWalker walker) {
persister = walker.getFinalFromClause().getFromElement().getQueryable();
Dialect dialect = walker.getDialect();
UpdateStatement updateStatement = (UpdateStatement) walker.getAST();
List<AssignmentSpecification> assignments = walker.getAssignmentSpecifications();
String whereClause;
if ( updateStatement.getWhereClause().getNumberOfChildren() == 0 ) {
whereClause = "";
}
else {
try {
SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() );
gen.whereClause( updateStatement.getWhereClause() );
gen.getParseErrorHandler().throwQueryException();
whereClause = gen.getSQL().substring( 7 ); // strip the " where "
}
catch ( RecognitionException e ) {
throw new HibernateException( "Unable to generate id select for DML operation", e );
}
}
String tableAlias = updateStatement.getFromClause().getFromElement().getTableAlias();
String idSelect = generateIdSelect( tableAlias, whereClause, dialect, persister );
String[] tableNames = persister.getConstraintOrderedTableNameClosure();
String[][] columnNames = persister.getContraintOrderedTableKeyColumnClosure();
int[] affectedTables =
IntStream.range( 0, tableNames.length ).filter(
table -> assignments.stream().anyMatch(
assign -> assign.affectsTable( tableNames[table] )
)
).toArray();
if ( affectedTables.length > 1 ) {
throw new AssertionFailure("more than one affected table");
}
int affectedTable = affectedTables[0];
String tableName = tableNames[affectedTable];
String idColumnNames = String.join( ", ", columnNames[affectedTable] );
Update update = new Update( dialect ).setTableName( tableName );
if ( dialect instanceof MySQLDialect) {
//MySQL needs an extra subselect to hack the query optimizer
String selectedIdColumns = String.join( ", ", persister.getIdentifierColumnNames() );
update.setWhere( "(" + idColumnNames + ") in (select " + selectedIdColumns + " from (" + idSelect + ") as ht_ids)" );
}
else {
update.setWhere( "(" + idColumnNames + ") in (" + idSelect + ")" );
}
for ( AssignmentSpecification assignment: assignments ) {
update.appendAssignmentFragment( assignment.getSqlAssignmentFragment() );
}
sql = update.toStatementString();
// now collect the parameters from the whole query
// parameters included in the list
try {
SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() );
gen.statement( walker.getAST() );
parameterSpecifications = gen.getCollectedParameters();
}
catch ( RecognitionException e ) {
throw QuerySyntaxException.convert( e );
}
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.hql.internal.ast.exec;
import antlr.RecognitionException;
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.internal.ast.HqlSqlWalker;
import org.hibernate.hql.internal.ast.QuerySyntaxException;
import org.hibernate.hql.internal.ast.SqlGenerator;
import org.hibernate.hql.internal.ast.tree.InsertStatement;
import org.hibernate.hql.internal.ast.tree.Statement;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.entity.Queryable;
import java.util.List;
/**
* Executes HQL insert statements.
*
* @author Gavin King
*/
public class InsertExecutor extends BasicExecutor {
private final Queryable persister;
private final String sql;
private final List<ParameterSpecification> parameterSpecifications;
@Override
public Queryable getPersister() {
return persister;
}
@Override
public String getSql() {
return sql;
}
@Override
public List<ParameterSpecification> getParameterSpecifications() {
return parameterSpecifications;
}
public InsertExecutor(HqlSqlWalker walker) {
persister = ( (InsertStatement) walker.getAST() ).getIntoClause().getQueryable();
try {
SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() );
gen.statement( walker.getAST() );
sql = gen.getSQL();
gen.getParseErrorHandler().throwQueryException();
parameterSpecifications = gen.getCollectedParameters();
}
catch ( RecognitionException e ) {
throw QuerySyntaxException.convert( e );
}
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.hql.internal.ast.exec;
import antlr.RecognitionException;
import org.hibernate.AssertionFailure;
import org.hibernate.hql.internal.ast.HqlSqlWalker;
import org.hibernate.hql.internal.ast.QuerySyntaxException;
import org.hibernate.hql.internal.ast.SqlGenerator;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.entity.Queryable;
import java.util.List;
/**
* Executes HQL bulk updates against a single table, where the
* query only touches columns from the table it's updating, and
* so we don't need to use a subselect.
*
* @author Gavin King
*/
public class SimpleUpdateExecutor extends BasicExecutor {
private final Queryable persister;
private final String sql;
private final List<ParameterSpecification> parameterSpecifications;
@Override
public Queryable getPersister() {
return persister;
}
@Override
public String getSql() {
return sql;
}
@Override
public List<ParameterSpecification> getParameterSpecifications() {
return parameterSpecifications;
}
public SimpleUpdateExecutor(HqlSqlWalker walker) {
persister = walker.getFinalFromClause().getFromElement().getQueryable();
if ( persister.isMultiTable() && walker.getQuerySpaces().size() > 1 ) {
throw new AssertionFailure("not a simple update");
}
try {
SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() );
gen.statement( walker.getAST() );
gen.getParseErrorHandler().throwQueryException();
// workaround for a problem where HqlSqlWalker actually generates
// broken SQL with undefined aliases in the where clause, because
// that is what MultiTableUpdateExecutor is expecting to get
String alias = walker.getFinalFromClause().getFromElement().getTableAlias();
sql = gen.getSQL().replace( alias + ".", "" );
parameterSpecifications = gen.getCollectedParameters();
}
catch ( RecognitionException e ) {
throw QuerySyntaxException.convert( e );
}
}
}

View File

@ -539,8 +539,30 @@ public final class StringHelper {
return string == null || string.isEmpty();
}
public static boolean isEmptyOrWhiteSpace(String string) {
return isEmpty( string ) || isEmpty( string.trim() );
public static boolean isBlank(String string) {
//TODO use Java 11's more efficient String#isBlank - currently we still require Java 8 compatibility
if ( string == null || string.isEmpty() ) {
return true;
}
else {
//Else: we need to check all characters, preferably without using String#trim() so to
//not allocate temporary strings
for ( int i = 0; i < string.length(); i++ ) {
if ( ! Character.isWhitespace( string.charAt( i ) ) ) {
return false;
}
}
return true;
}
}
/**
* @deprecated use {@link #isBlank(String)}
* @return
*/
@Deprecated
public static boolean isEmptyOrWhitespace(String string) {
return isBlank(string);
}
public static String qualify(String prefix, String name) {
@ -669,7 +691,7 @@ public final class StringHelper {
}
public static String moveAndToBeginning(String filter) {
if ( filter.trim().length() > 0 ) {
if ( !isBlank( filter ) ) {
filter += " and ";
if ( filter.startsWith( " and " ) ) {
filter = filter.substring( 4 );

View File

@ -0,0 +1,28 @@
/*
* 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.tool.schema.ast;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.ValidIdRange;
/**
* @author Steve Ebersole
*/
@ValidIdRange( )
public class SqlScriptLogging {
public static final String SCRIPT_LOGGER_NAME = "org.hibernate.orm.tooling.schema.script";
public static final Logger SCRIPT_LOGGER = Logger.getLogger( SCRIPT_LOGGER_NAME );
public static final boolean TRACE_ENABLED = SCRIPT_LOGGER.isTraceEnabled();
public static final boolean DEBUG_ENABLED = SCRIPT_LOGGER.isDebugEnabled();
public static final String AST_LOGGER_NAME = SCRIPT_LOGGER_NAME + ".graph";
public static final Logger AST_LOGGER = Logger.getLogger( AST_LOGGER_NAME );
public static final boolean AST_TRACE_ENABLED = AST_LOGGER.isTraceEnabled();
}

View File

@ -72,7 +72,7 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl implements Information
""
)
);
if ( ! StringHelper.isEmptyOrWhiteSpace( extraPhysycalTableTypesConfig ) ) {
if ( ! StringHelper.isBlank( extraPhysycalTableTypesConfig ) ) {
this.extraPhysicalTableTypes = StringHelper.splitTrimmingTokens(
",;",
extraPhysycalTableTypesConfig,

View File

@ -26,6 +26,7 @@ import javax.persistence.Table;
import javax.persistence.Version;
import org.hibernate.boot.MetadataSources;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.testing.TestForIssue;
@ -114,7 +115,7 @@ public class EmbeddableWithManyToMany_HHH_11302_Test
result += "id: " + id;
}
result += ", version: " + version;
if ( type != null && !type.trim().isEmpty() ) {
if ( !StringHelper.isBlank( type ) ) {
result += ", type: " + type;
}
return result;

View File

@ -26,6 +26,7 @@ import javax.persistence.Table;
import javax.persistence.Version;
import org.hibernate.boot.MetadataSources;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.testing.TestForIssue;
@ -114,7 +115,7 @@ public class EmbeddableWithOneToMany_HHH_11302_Test
result += "id: " + id;
}
result += ", version: " + version;
if ( type != null && !type.trim().isEmpty() ) {
if ( !StringHelper.isBlank( type ) ) {
result += ", type: " + type;
}
return result;

View File

@ -0,0 +1,259 @@
/*
* 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.bytecode.enhancement.lazy.proxy;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
import org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.NoDirtyCheckEnhancementContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
/**
* @author Andrea Boriero
*/
@RunWith(BytecodeEnhancerRunner.class)
@CustomEnhancementContext({ DirtyCheckEnhancementContext.class, NoDirtyCheckEnhancementContext.class })
public class SharingReferenceTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" );
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
}
@Override
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
super.configureSessionFactoryBuilder( sfb );
sfb.applyStatisticsSupport( true );
sfb.applySecondLevelCacheSupport( false );
sfb.applyQueryCacheSupport( false );
}
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Ceo.class );
sources.addAnnotatedClass( Manager.class );
sources.addAnnotatedClass( Supervisor.class );
sources.addAnnotatedClass( Worker.class );
}
@Before
public void setUp() {
inTransaction(
session -> {
Ceo ceo = new Ceo();
ceo.setName( "Jill" );
session.persist( ceo );
Manager m1 = new Manager();
m1.setName( "Jane" );
m1.setCeo( ceo );
session.persist( m1 );
Manager m2 = new Manager();
m2.setName( "Jannet" );
m2.setCeo( ceo );
session.persist( m2 );
Supervisor s1 = new Supervisor();
s1.setName( "Bob" );
s1.getManagers().add( m1 );
s1.getManagers().add( m2 ); //comment out this line and the test will pass
s1.setCeo( ceo );
session.persist( s1 );
Worker worker = new Worker();
worker.setName( "James" );
worker.setSupervisor( s1 );
session.persist( worker );
}
);
}
@Test
public void testFind() {
inTransaction(
session -> {
Ceo ceo = session.find( Ceo.class, 1L );
assertEquals( "Jill", ceo.getName() );
Worker worker = session.find( Worker.class, 1L );
assertEquals( worker.getName(), "James" );
Supervisor supervisor = worker.getSupervisor();
Manager manager = supervisor.getManagers().get( 0 );
assertSame( ceo, manager.getCeo() );
assertSame( ceo, supervisor.getCeo() );
}
);
}
@Entity(name = "Ceo")
public static class Ceo {
private long id;
private String name;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ceqSeq")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity(name = "Manager")
public static class Manager {
private long id;
private String name;
private Ceo ceo;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "managerSeq")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Ceo getCeo() {
return ceo;
}
public void setCeo(Ceo ceo) {
this.ceo = ceo;
}
}
@Entity(name = "Supervisor")
public static class Supervisor {
private long id;
private String name;
private List<Manager> managers = new ArrayList<>();
private Ceo ceo;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "supervisorSeq")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<Manager> getManagers() {
return managers;
}
public void setManagers(List<Manager> managers) {
this.managers = managers;
}
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Ceo getCeo() {
return ceo;
}
public void setCeo(Ceo ceo) {
this.ceo = ceo;
}
}
@Entity(name = "Worker")
public static class Worker {
private long id;
private String name;
private Supervisor supervisor;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "workerSeq")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Supervisor getSupervisor() {
return supervisor;
}
public void setSupervisor(Supervisor supervisor) {
this.supervisor = supervisor;
}
}
}

View File

@ -11,6 +11,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
@ -55,7 +56,7 @@ public class MyCustomSqlTypeDescriptor implements SqlTypeDescriptor {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
final String valueStr = javaTypeDescriptor.unwrap( value, String.class, options );
if ( valueStr == null || valueStr.trim().isEmpty() ) {
if ( StringHelper.isBlank( valueStr ) ) {
st.setNull( index, getSqlType() );
}
else {
@ -66,7 +67,7 @@ public class MyCustomSqlTypeDescriptor implements SqlTypeDescriptor {
@Override
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
final String valueStr = javaTypeDescriptor.unwrap( value, String.class, options );
if ( valueStr == null || valueStr.trim().isEmpty() ) {
if ( StringHelper.isBlank( valueStr ) ) {
st.setNull( name, getSqlType() );
}
else {

View File

@ -0,0 +1,101 @@
/*
* 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.joinedsubclass;
import java.util.Map;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.junit.Test;
import org.hamcrest.core.StringStartsWith;
import static org.hamcrest.CoreMatchers.not;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertThat;
/**
* @author Nathan Xu
*/
@TestForIssue( jiraKey = "HHH-14153" )
public class SingleTableUpdateQueryTest extends BaseEntityManagerFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
public String[] getMappings() {
return new String[] { "org/hibernate/test/joinedsubclass/Person.hbm.xml" };
}
@Override
protected void addConfigOptions(Map options) {
sqlStatementInterceptor = new SQLStatementInterceptor( options );
}
@Test
public void testSingeTableUpdateWithoutOtherTableInvolvement() {
sqlStatementInterceptor.clear();
/*
* update
* JPerson
* set
* name=(name||?)
* where
* sex=?
*/
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createQuery( "UPDATE Person p SET p.name = concat(p.name, :nameSuffix) WHERE p.sex = :sex" )
.setParameter( "nameSuffix", "(f)" )
.setParameter( "sex", 'f' )
.executeUpdate();
} );
sqlStatementInterceptor.getSqlQueries().forEach(
// currently SQLStatementInterceptor is not able to catch id table creation SQL
sql -> assertThat( sql, not( StringStartsWith.startsWith( "insert into " ) ) )
);
}
@Test
public void testSingeTableUpdateWithOtherTableInvolvement() {
sqlStatementInterceptor.clear();
/*
* update
* JPerson
* set
* name=name
* where
* (
* person_id
* ) IN (
* select
* customer0_.person_id as person_id
* from
* JManager customer0_
* inner join
* JPerson customer0_1_
* on customer0_.person_id=customer0_1_.person_id
* )
*/
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createQuery( "UPDATE Customer SET name = name" ) // 'name' is from 'Person' parent entity
.executeUpdate();
} );
sqlStatementInterceptor.getSqlQueries().forEach(
// currently SQLStatementInterceptor is not able to catch id table creation SQL
sql -> assertThat( sql, not( StringStartsWith.startsWith( "insert into " ) ) )
);
}
}

View File

@ -9,6 +9,7 @@ package org.hibernate.test.tool.schema.scripts;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.List;
import org.hibernate.dialect.Dialect;
@ -46,7 +47,7 @@ public class MultiLineImportExtractorTest {
assertThat( commands.get( 1 ), is( "INSERT INTO test_data VALUES (1, 'sample')" ) );
assertThat( commands.get( 2 ), is( "DELETE FROM test_data" ) );
assertThat( commands.get( 2 ), is( "DELETE FROM test_data" ) );
assertThat( commands.get( 3 ), startsWith( "INSERT INTO test_data VALUES (2," ) );
assertThat( commands.get( 3 ), containsString( "-- line 2" ) );
@ -54,8 +55,16 @@ public class MultiLineImportExtractorTest {
assertThat( commands.get( 4 ), startsWith( "INSERT INTO test_data VALUES (3" ) );
assertThat( commands.get( 4 ), not( containsString( "third record" ) ) );
assertThat( commands.get( 5 ), startsWith( "INSERT INTO test_data" + System.lineSeparator() + "VALUES" ) );
assertThat( commands.get( 5 ).replace( "\t", "" ), is( "INSERT INTO test_data VALUES ( 4 , NULL )" ) );
}
}
}
@Test
public void testExtractionFromEmptyScript() {
StringReader reader = new StringReader( "" );
final List<String> commands = extractor.extractCommands( reader, Dialect.getDialect() );
assertThat( commands, notNullValue() );
assertThat( commands.size(), is( 0 ) );
}
}

View File

@ -18,6 +18,7 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@ -44,6 +45,18 @@ public class StringHelperTest extends BaseUnitTestCase {
assertEquals( "internal.util.StringHelper", StringHelper.partiallyUnqualify( STRING_HELPER_FQN, BASE_PACKAGE ) );
}
@Test
public void testIsBlank() {
assertFalse( StringHelper.isBlank( "A" ) );
assertFalse( StringHelper.isBlank( " a" ) );
assertFalse( StringHelper.isBlank( "a " ) );
assertFalse( StringHelper.isBlank( "a\t" ) );
assertTrue( StringHelper.isBlank( "\t\n" ) );
assertTrue( StringHelper.isBlank( null ) );
assertTrue( StringHelper.isBlank( "" ) );
assertTrue( StringHelper.isBlank( " " ) );
}
@Test
public void testBasePackageCollapsing() {
assertNull( StringHelper.collapseQualifierBase( null, BASE_PACKAGE ) );