Merge remote-tracking branch 'upstream/master' into wip/6.0
This commit is contained in:
commit
638a3e234b
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -72,7 +72,7 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl implements Information
|
|||
""
|
||||
)
|
||||
);
|
||||
if ( ! StringHelper.isEmptyOrWhiteSpace( extraPhysycalTableTypesConfig ) ) {
|
||||
if ( ! StringHelper.isBlank( extraPhysycalTableTypesConfig ) ) {
|
||||
this.extraPhysicalTableTypes = StringHelper.splitTrimmingTokens(
|
||||
",;",
|
||||
extraPhysycalTableTypesConfig,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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 " ) ) )
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ) );
|
||||
|
|
Loading…
Reference in New Issue