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

This commit is contained in:
Andrea Boriero 2020-08-25 08:38:06 +01:00
commit 062a462b0c
16 changed files with 457 additions and 15 deletions

View File

@ -24,7 +24,7 @@ buildscript {
plugins {
id 'me.champeau.buildscan-recipes' version '0.2.3'
id 'org.hibernate.build.xjc' version '2.0.1' apply false
id 'org.hibernate.build.maven-repo-auth' version '3.0.2' apply false
id 'org.hibernate.build.maven-repo-auth' version '3.0.3' apply false
id 'org.jetbrains.gradle.plugin.idea-ext' version '0.5'
id 'biz.aQute.bnd' version '5.1.1' apply false
}

View File

@ -731,7 +731,7 @@ See the <<chapters/domain/basic_types.adoc#mapping-column-read-and-write-example
[[annotations-hibernate-columntransformers]]
==== `@ColumnTransformers`
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/ColumnTransformers.html[`@ColumnTransformers`] annotation iis used to group multiple <<annotations-hibernate-columntransformer>> annotations.
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/ColumnTransformers.html[`@ColumnTransformers`] annotation is used to group multiple <<annotations-hibernate-columntransformer>> annotations.
[[annotations-hibernate-creationtimestamp]]
==== `@CreationTimestamp`

View File

@ -657,7 +657,7 @@ Enables the query cache. You still need to set individual queries to be cachable
Enable/disable the second-level cache, which is enabled by default, although the default `RegionFactor` is `NoCachingRegionFactory` (meaning there is no actual caching implementation).
`*hibernate.cache.query_cache_factory*` (e.g. fully-qualified class name)::
A custom https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/cache/spi/QueryCacheFactory.html[`QueryCacheFactory`] interface. The default is the built-in `StandardQueryCacheFactory`.
A custom https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/cache/spi/TimestampsCacheFactory.html[`TimestampsCacheFactory`] interface. The default is the built-in `StandardTimestampsCacheFactory`.
`*hibernate.cache.region_prefix*` (e.g. A string)::
A prefix for second-level cache region names.

View File

@ -43,7 +43,7 @@ Besides provider specific configuration, there are a number of configurations op
`hibernate.cache.query_cache_factory`::
Query result caching is handled by a special contract that deals with staleness-based invalidation of the results.
The default implementation does not allow stale results at all. Use this for applications that would like to relax that.
Names an implementation of `org.hibernate.cache.spi.QueryCacheFactory`.
Names an implementation of `org.hibernate.cache.spi.TimestampsCacheFactory`.
`hibernate.cache.use_minimal_puts`::
Optimizes second-level cache operations to minimize writes, at the cost of more frequent reads. Providers typically set this appropriately.
`hibernate.cache.region_prefix`::

View File

@ -1,2 +1,2 @@
INSERT INTO Phone (phone_number, phone_type, id)
VALUES ('123-456-78990', 2, 1)
VALUES ('123-456-78990', 1, 1)

View File

@ -26,6 +26,7 @@ import org.hibernate.mapping.Property;
* @author Steve Ebersole
*/
public class LazyAttributesMetadata implements Serializable {
/**
* Build a LazyFetchGroupMetadata based on the attributes defined for the
* PersistentClass
@ -83,6 +84,8 @@ public class LazyAttributesMetadata implements Serializable {
private final Map<String, LazyAttributeDescriptor> lazyAttributeDescriptorMap;
private final Map<String,Set<String>> fetchGroupToAttributeMap;
private final Set<String> fetchGroupNames;
private final Set<String> lazyAttributeNames;
public LazyAttributesMetadata(String entityName) {
this( entityName, Collections.emptyMap(), Collections.emptyMap() );
@ -95,6 +98,8 @@ public class LazyAttributesMetadata implements Serializable {
this.entityName = entityName;
this.lazyAttributeDescriptorMap = lazyAttributeDescriptorMap;
this.fetchGroupToAttributeMap = fetchGroupToAttributeMap;
this.fetchGroupNames = Collections.unmodifiableSet( fetchGroupToAttributeMap.keySet() );
this.lazyAttributeNames = Collections.unmodifiableSet( lazyAttributeDescriptorMap.keySet() );
}
public String getEntityName() {
@ -110,11 +115,14 @@ public class LazyAttributesMetadata implements Serializable {
}
public Set<String> getLazyAttributeNames() {
return lazyAttributeDescriptorMap.keySet();
return lazyAttributeNames;
}
/**
* @return an immutable set
*/
public Set<String> getFetchGroupNames() {
return fetchGroupToAttributeMap.keySet();
return fetchGroupNames;
}
public boolean isLazyAttribute(String attributeName) {
@ -137,6 +145,10 @@ public class LazyAttributesMetadata implements Serializable {
return list;
}
/**
* @deprecated This method is not being used and as such will be removed
*/
@Deprecated
public Set<String> getAttributesInSameFetchGroup(String attributeName) {
final String fetchGroupName = getFetchGroupName( attributeName );
return getAttributesInFetchGroup( fetchGroupName );

View File

@ -32,7 +32,7 @@ public class BeanValidationIntegrator implements Integrator {
public static final String APPLY_CONSTRAINTS = "hibernate.validator.apply_to_ddl";
public static final String BV_CHECK_CLASS = "javax.validation.Validation";
public static final String BV_CHECK_CLASS = "javax.validation.ConstraintViolation";
public static final String MODE_PROPERTY = "javax.persistence.validation.mode";

View File

@ -3722,4 +3722,14 @@ public abstract class Dialect implements ConversionContext {
protected String getDropSequenceString(String sequenceName) throws MappingException {
return "drop sequence " + sequenceName;
}
/**
* Annotation to be appended to the end of each COLUMN clause for temporary tables.
*
* @param sqlTypeCode The SQL type code
* @return The annotation to be appended (e.g. "COLLATE DATABASE_DEFAULT" in SQLServer SQL)
*/
public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) {
return "";
}
}

View File

@ -61,6 +61,7 @@ public class H2Dialect extends Dialect {
private final int version;
private final boolean supportsTuplesInSubqueries;
private final SequenceInformationExtractor sequenceInformationExtractor;
private final String querySequenceString;
@ -78,11 +79,13 @@ public class H2Dialect extends Dialect {
//Note: H2 'bit' is a synonym for 'boolean', not a proper bit type
// registerColumnType( Types.BIT, "bit" );
final int majorVersion = version / 100;
final int minorVersion = version % 100 / 10;
if ( version < 120 || version == 120 && buildId < 139 ) {
LOG.unsupportedMultiTableBulkHqlJpaql( version / 100, version % 100 / 10, buildId );
}
LOG.unsupportedMultiTableBulkHqlJpaql( majorVersion, minorVersion, buildId );
}
supportsTuplesInSubqueries = majorVersion > 1 || minorVersion > 4 || buildId >= 198;
// Prior to 1.4.200 the 'cascade' in 'drop table' was implicit
cascadeConstraints = version > 140 || version == 140 && buildId >= 200;
@ -353,7 +356,7 @@ public class H2Dialect extends Dialect {
@Override
public boolean supportsTuplesInSubqueries() {
return false;
return supportsTuplesInSubqueries;
}
@Override

View File

@ -24,6 +24,8 @@ import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.StringType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.sql.SmallIntTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
@ -611,4 +613,19 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
return "cast('" + date + "' as date)";
}
@Override
public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) {
switch (sqlTypeCode) {
case Types.CHAR:
case Types.NCHAR:
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.LONGVARCHAR:
case Types.LONGNVARCHAR:
return "collate database_default";
default:
return "";
}
}
}

View File

@ -847,7 +847,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
@Override
public Object getLoadedCollectionOwnerOrNull(PersistentCollection collection) {
final CollectionEntry ce = getCollectionEntry( collection );
if ( ce.getLoadedPersister() == null ) {
if ( ce == null || ce.getLoadedPersister() == null ) {
return null;
}

View File

@ -0,0 +1,65 @@
package org.hibernate.query.hhh14154;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Archie Cobbs
* @author Nathan Xu
*/
@RequiresDialect( H2Dialect.class )
@TestForIssue( jiraKey = "HHH-14154" )
public class HHH14154Test extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { HHH14154Test.Foo.class };
}
@Test
public void testNoExceptionThrown() {
doInJPA( this::sessionFactory, em -> {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Foo> cq = cb.createQuery( Foo.class );
final Root<Foo> foo = cq.from( Foo.class );
cq.select(foo)
.where(
cb.lessThanOrEqualTo(
cb.concat(
cb.function( "FORMATDATETIME", String.class, foo.get( "startTime" ), cb.literal( "HH:mm:ss" ) ),
""
),
"17:00:00"
)
);
em.createQuery( cq ).getResultList();
} );
}
@Entity(name = "Foo")
@Table(name = "Foo")
public static class Foo {
@Id
private long id;
private Date startTime;
}
}

View File

@ -0,0 +1,77 @@
package org.hibernate.query.hhh14156;
import java.io.Serializable;
import java.util.Objects;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
/**
* @author Nathan Xu
* @author Christian Beikov
*/
@TestForIssue( jiraKey = "HHH-14156" )
public class HHH14156Test extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { EntityWithCompositeId.class };
}
@Test
public void testNoExceptionThrown() {
inTransaction( session ->
session.createQuery(
"from EntityWithCompositeId e where e in (select e2 from EntityWithCompositeId e2)",
EntityWithCompositeId.class
).getResultList()
);
}
@Entity(name = "EntityWithCompositeId")
@Table(name = "EntityWithCompositeId")
public static class EntityWithCompositeId implements Serializable {
@EmbeddedId
private PK id;
@Embeddable
public static class PK implements Serializable {
private String id1;
private String id2;
public PK(String id1, String id2) {
this.id1 = id1;
this.id2 = id2;
}
private PK() {
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
PK pk = (PK) o;
return Objects.equals( id1, pk.id1 ) &&
Objects.equals( id2, pk.id2 );
}
@Override
public int hashCode() {
return Objects.hash( id1, id2 );
}
}
}
}

View File

@ -0,0 +1,161 @@
/*
* 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.dialect.functional;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.SQLServer2005Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.testing.AfterClassOnce;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
/**
* @author Nathan Xu
*/
@RequiresDialect( SQLServer2005Dialect.class )
@TestForIssue( jiraKey = "HHH-3326" )
public class SQLServerDialectTempTableCollationTest extends BaseCoreFunctionalTestCase {
private String originalDBCollation;
private final String changedDBCollation = "SQL_Latin1_General_CP437_BIN";
private boolean collationChanged;
@Override
protected Configuration constructConfiguration() {
Configuration configuration = super.constructConfiguration();
configuration.setProperty( AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED, Boolean.TRUE.toString() );
return configuration;
}
@AfterClassOnce
protected void revertBackOriginalDBCollation() {
if ( originalDBCollation != null && collationChanged && !changedDBCollation.equals( originalDBCollation ) ) {
BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry();
StandardServiceRegistryImpl serviceRegistry = buildServiceRegistry(
bootRegistry,
constructConfiguration()
);
try (Connection connection = serviceRegistry.getService( JdbcServices.class )
.getBootstrapJdbcConnectionAccess()
.obtainConnection();
Statement statement = connection.createStatement()) {
connection.setAutoCommit( true );
statement.executeUpdate( "ALTER DATABASE CURRENT COLLATE " + originalDBCollation );
}
catch (SQLException e) {
throw new RuntimeException( "Failed to revert back database collation to " + originalDBCollation, e );
}
finally {
serviceRegistry.destroy();
}
}
}
protected void buildSessionFactory() {
BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry();
StandardServiceRegistryImpl serviceRegistry = buildServiceRegistry( bootRegistry, constructConfiguration() );
try {
try ( Connection connection = serviceRegistry.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess().obtainConnection();
Statement statement = connection.createStatement() ) {
connection.setAutoCommit( true );
try ( ResultSet rs = statement.executeQuery( "SELECT SERVERPROPERTY('collation')" ) ) {
rs.next();
String instanceCollation = rs.getString( 1 );
Assert.assertNotEquals( instanceCollation, changedDBCollation );
}
}
catch (SQLException e) {
log.debug( e.getMessage() );
}
try ( Connection connection = serviceRegistry.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess().obtainConnection();
Statement statement = connection.createStatement() ) {
connection.setAutoCommit( true );
try ( ResultSet rs = statement.executeQuery( "SELECT CONVERT (varchar(256), DATABASEPROPERTYEX(DB_NAME(),'collation'))" ) ) {
rs.next();
originalDBCollation = rs.getString( 1 );
}
}
catch (SQLException e) {
log.debug( e.getMessage() );
}
try ( Connection connection = serviceRegistry.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess().obtainConnection();
Statement statement = connection.createStatement() ) {
connection.setAutoCommit( true );
statement.executeUpdate( "ALTER DATABASE CURRENT COLLATE " + changedDBCollation );
collationChanged = true;
}
}
catch ( SQLException e ) {
throw new RuntimeException( e );
}
finally {
serviceRegistry.destroy();
}
super.buildSessionFactory();
}
@Test
public void testTemporaryTableCreateWithoutCollationConflict() {
// without fixing "HHH-3326", the following exception will be thrown:
// Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "SQL_Latin1_General_CP437_BIN" in the equal to operation.
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "update Woman w set w.description = :description where w.age > :age" )
.setParameter( "description", "your are old" )
.setParameter( "age", 30 )
.executeUpdate();
} );
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Human.class,
Woman.class
};
}
@Override
protected boolean rebuildSessionFactoryOnError() {
return false;
}
@Entity(name = "Human")
@Table(name = "Human")
@Inheritance(strategy = InheritanceType.JOINED)
public static abstract class Human {
@Id
String id;
int age;
}
@Entity(name = "Woman")
@Table(name = "Woman")
public static class Woman extends Human {
String description;
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.tool.schema.scripts;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import org.hibernate.dialect.Dialect;
import org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
public class MultiLineImportExtractorTest {
public static final String IMPORT_FILE = "org/hibernate/orm/test/tool/schema/scripts/multi-line-statements2.sql";
private final MultipleLinesSqlCommandExtractor extractor = new MultipleLinesSqlCommandExtractor();
@Test
public void testExtraction() throws IOException {
final ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try (final InputStream stream = classLoader.getResourceAsStream( IMPORT_FILE )) {
assertThat( stream, notNullValue() );
try (final InputStreamReader reader = new InputStreamReader( stream )) {
final List<String> commands = extractor.extractCommands( reader, Dialect.getDialect() );
assertThat( commands, notNullValue() );
assertThat( commands.size(), is( 6 ) );
assertThat( commands.get( 0 ), startsWith( "CREATE TABLE test_data" ) );
assertThat( commands.get( 1 ), is( "INSERT INTO test_data VALUES (1, 'sample')" ) );
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" ) );
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\nVALUES\n" ) );
}
}
}
}

View File

@ -0,0 +1,36 @@
-- Sample file used to test import feature of multiline SQL script (HHH-2403).
-- Contains various SQL instructions with comments.
CREATE TABLE test_data (
id NUMBER NOT NULL PRIMARY KEY -- primary key
, text VARCHAR2(100) /* any other data */
);
INSERT INTO test_data VALUES (1, 'sample');
DELETE
FROM test_data;
/*
* Data insertion...
*/
INSERT INTO test_data VALUES (2, 'Multi-line comment line 1
-- line 2''
/* line 3 */');
/* Invalid insert: INSERT INTO test_data VALUES (1, NULL); */
-- INSERT INTO test_data VALUES (1, NULL);
INSERT INTO test_data VALUES (3 /* 'third record' */, NULL /* value */); -- with NULL value
INSERT INTO test_data
VALUES
(
4 -- another record
, NULL
);
-- comment;
-- comment;