Merge remote-tracking branch 'upstream/master' into wip/6.0
This commit is contained in:
commit
dc5894db42
|
@ -1,8 +1,9 @@
|
|||
# Typically *NIX text editors, by default, append '~' to files on saving to make backups
|
||||
*~
|
||||
|
||||
# Gradle work directory
|
||||
# Gradle work directory and caches
|
||||
.gradle
|
||||
.gradletasknamecache
|
||||
|
||||
# Build output directies
|
||||
/target
|
||||
|
|
|
@ -275,6 +275,14 @@ Please report your mapping that causes the problem to us so we can examine the d
|
|||
+
|
||||
The default value is `false` which means Hibernate will use an algorithm to determine if the insert can be delayed or if the insert should be performed immediately.
|
||||
|
||||
`*hibernate.id.sequence.increment_size_mismatch_strategy*` (e.g. `LOG`, `FIX` or `EXCEPTION` (default value))::
|
||||
This setting defines the `org.hibernate.id.SequenceMismatchStrategy` used when
|
||||
Hibernate detects a mismatch between a sequence configuration in an entity mapping
|
||||
and its database sequence object counterpart.
|
||||
+
|
||||
The default value is given by the `org.hibernate.id.SequenceMismatchStrategy#EXCEPTION`,
|
||||
meaning that an Exception is thrown when detecting such a conflict.
|
||||
|
||||
==== Quoting options
|
||||
|
||||
`*hibernate.globally_quoted_identifiers*` (e.g. `true` or `false` (default value))::
|
||||
|
@ -511,6 +519,30 @@ Raises an exception when in-memory pagination over collection fetch is about to
|
|||
+
|
||||
Disabled by default. Set to true to enable.
|
||||
|
||||
`*hibernate.query.immutable_entity_update_query_handling_mode*` (e.g. `EXCEPTION` or `WARNING` (default value))::
|
||||
Defines how `Immutable` entities are handled when executing a bulk update query.
|
||||
+
|
||||
By default, the (`ImmutableEntityUpdateQueryHandlingMode#WARNING`) mode is used, meaning that
|
||||
a warning log message is issued when an `@Immutable` entity is to be updated via a bulk update statement.
|
||||
+
|
||||
If the (`ImmutableEntityUpdateQueryHandlingMode#EXCEPTION`) mode is used, then a `HibernateException` is thrown instead.
|
||||
|
||||
`*hibernate.query.in_clause_parameter_padding*` (e.g. `true` or `false` (default value))::
|
||||
By default, the IN clause expands to include all bind parameter values.
|
||||
+
|
||||
However, for database systems supporting execution plan caching,
|
||||
there's a better chance of hitting the cache if the number of possible IN clause parameters lowers.
|
||||
+
|
||||
For this reason, we can expand the bind parameters to power-of-two: 4, 8, 16, 32, 64.
|
||||
This way, an IN clause with 5, 6, or 7 bind parameters will use the 8 IN clause,
|
||||
therefore reusing its execution plan.
|
||||
|
||||
`*hibernate.query.omit_join_of_superclass_tables*` (e.g. `false` or `true` (default value))::
|
||||
When you use `javax.persistence.InheritanceType#JOINED` strategy for inheritance mapping and query
|
||||
a value from an entity, all superclass tables are joined in the query regardless you need them.
|
||||
+
|
||||
With this setting set to true only superclass tables which are really needed are joined.
|
||||
|
||||
==== Multi-table bulk HQL operations
|
||||
|
||||
`*hibernate.hql.bulk_id_strategy*` (e.g. A fully-qualified class name, an instance, or a `Class` object reference)::
|
||||
|
|
|
@ -15,10 +15,12 @@ import java.util.Collection;
|
|||
* processed by auto-flush based on the table to which those entities are mapped and which are
|
||||
* determined to have pending state changes.
|
||||
*
|
||||
* In a similar manner, these query spaces also affect how query result caching can recognize invalidated results.
|
||||
* In a similar manner, these query spaces also affect how query result caching can recognize
|
||||
* invalidated results.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings( { "unused", "UnusedReturnValue", "RedundantSuppression" } )
|
||||
public interface SynchronizeableQuery<T> {
|
||||
/**
|
||||
* Obtain the list of query spaces the query is synchronized on.
|
||||
|
@ -36,6 +38,18 @@ public interface SynchronizeableQuery<T> {
|
|||
*/
|
||||
SynchronizeableQuery<T> addSynchronizedQuerySpace(String querySpace);
|
||||
|
||||
/**
|
||||
* Adds one-or-more synchronized spaces
|
||||
*/
|
||||
default SynchronizeableQuery<T> addSynchronizedQuerySpace(String... querySpaces) {
|
||||
if ( querySpaces != null ) {
|
||||
for ( int i = 0; i < querySpaces.length; i++ ) {
|
||||
addSynchronizedQuerySpace( querySpaces[i] );
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a table expression as a query space.
|
||||
*/
|
||||
|
@ -43,6 +57,13 @@ public interface SynchronizeableQuery<T> {
|
|||
return addSynchronizedQuerySpace( tableExpression );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one-or-more synchronized table expressions
|
||||
*/
|
||||
default SynchronizeableQuery<T> addSynchronizedTable(String... tableExpressions) {
|
||||
return addSynchronizedQuerySpace( tableExpressions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity name for (a) auto-flush checking and (b) query result cache invalidation checking. Same as
|
||||
* {@link #addSynchronizedQuerySpace} for all tables associated with the given entity.
|
||||
|
@ -55,6 +76,18 @@ public interface SynchronizeableQuery<T> {
|
|||
*/
|
||||
SynchronizeableQuery<T> addSynchronizedEntityName(String entityName) throws MappingException;
|
||||
|
||||
/**
|
||||
* Adds one-or-more entities (by name) whose tables should be added as synchronized spaces
|
||||
*/
|
||||
default SynchronizeableQuery<T> addSynchronizedEntityName(String... entityNames) throws MappingException {
|
||||
if ( entityNames != null ) {
|
||||
for ( int i = 0; i < entityNames.length; i++ ) {
|
||||
addSynchronizedEntityName( entityNames[i] );
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity for (a) auto-flush checking and (b) query result cache invalidation checking. Same as
|
||||
* {@link #addSynchronizedQuerySpace} for all tables associated with the given entity.
|
||||
|
@ -65,5 +98,18 @@ public interface SynchronizeableQuery<T> {
|
|||
*
|
||||
* @throws MappingException Indicates the given class could not be resolved as an entity
|
||||
*/
|
||||
@SuppressWarnings( "rawtypes" )
|
||||
SynchronizeableQuery<T> addSynchronizedEntityClass(Class entityClass) throws MappingException;
|
||||
|
||||
/**
|
||||
* Adds one-or-more entities (by class) whose tables should be added as synchronized spaces
|
||||
*/
|
||||
default SynchronizeableQuery<T> addSynchronizedEntityClass(Class<?>... entityClasses) throws MappingException {
|
||||
if ( entityClasses != null ) {
|
||||
for ( int i = 0; i < entityClasses.length; i++ ) {
|
||||
addSynchronizedEntityClass( entityClasses[i] );
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,4 +89,11 @@ public @interface NamedNativeQuery {
|
|||
* Whether the results should be read-only. Default is {@code false}.
|
||||
*/
|
||||
boolean readOnly() default false;
|
||||
|
||||
/**
|
||||
* The query spaces to apply for the query.
|
||||
*
|
||||
* @see org.hibernate.SynchronizeableQuery
|
||||
*/
|
||||
String[] querySpaces() default {};
|
||||
}
|
||||
|
|
|
@ -143,4 +143,17 @@ public class QueryHints {
|
|||
*/
|
||||
public static final String PASS_DISTINCT_THROUGH = "hibernate.query.passDistinctThrough";
|
||||
|
||||
/**
|
||||
* Hint for specifying query spaces to be applied to a native (SQL) query.
|
||||
*
|
||||
* Passed value can be any of:<ul>
|
||||
* <li>List of the spaces</li>
|
||||
* <li>array of the spaces</li>
|
||||
* <li>String "whitespace"-separated list of the spaces</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see org.hibernate.SynchronizeableQuery
|
||||
*/
|
||||
public static final String NATIVE_SPACES = "org.hibernate.query.native.spaces";
|
||||
|
||||
}
|
||||
|
|
|
@ -6,10 +6,14 @@
|
|||
*/
|
||||
package org.hibernate.boot.model.source.internal.hbm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.boot.MappingException;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmColumnType;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmFilterType;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmManyToOneType;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmRootEntityType;
|
||||
|
@ -72,17 +76,58 @@ public abstract class AbstractPluralAttributeSourceImpl
|
|||
|
||||
Optional<JaxbHbmManyToOneType> jaxbHbmManyToOneTypeOptional = Optional.empty();
|
||||
|
||||
if ( pluralAttributeJaxbMapping.isInverse() && pluralAttributeJaxbMapping.getOneToMany() != null ) {
|
||||
// Our goal here is to find the inverse side of a one to many to figure out against what to join
|
||||
if ( pluralAttributeJaxbMapping.isInverse() && pluralAttributeJaxbMapping.getOneToMany() != null && pluralAttributeJaxbMapping.getKey().getPropertyRef() == null ) {
|
||||
String childClass = pluralAttributeJaxbMapping.getOneToMany().getClazz();
|
||||
|
||||
if ( childClass != null ) {
|
||||
// We match by columns as defined in the key
|
||||
final List<String> keyColumnNames;
|
||||
if ( pluralAttributeJaxbMapping.getKey().getColumnAttribute() == null ) {
|
||||
keyColumnNames = new ArrayList<>( pluralAttributeJaxbMapping.getKey().getColumn().size() );
|
||||
for ( JaxbHbmColumnType jaxbHbmColumnType : pluralAttributeJaxbMapping.getKey().getColumn() ) {
|
||||
keyColumnNames.add( jaxbHbmColumnType.getName() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
keyColumnNames = new ArrayList<>( 1 );
|
||||
keyColumnNames.add( pluralAttributeJaxbMapping.getKey().getColumnAttribute() );
|
||||
}
|
||||
jaxbHbmManyToOneTypeOptional = mappingDocument.getDocumentRoot().getClazz()
|
||||
.stream()
|
||||
.filter( (JaxbHbmRootEntityType entityType) -> childClass.equals( entityType.getName() ) )
|
||||
.flatMap( jaxbHbmRootEntityType -> jaxbHbmRootEntityType.getAttributes().stream() )
|
||||
.filter(
|
||||
attribute -> attribute instanceof JaxbHbmManyToOneType &&
|
||||
( (JaxbHbmManyToOneType) attribute ).getPropertyRef() != null )
|
||||
.filter( attribute -> {
|
||||
if ( attribute instanceof JaxbHbmManyToOneType ) {
|
||||
JaxbHbmManyToOneType manyToOneType = (JaxbHbmManyToOneType) attribute;
|
||||
String manyToOneTypeClass = manyToOneType.getClazz();
|
||||
String containerClass = container.getAttributeRoleBase().getFullPath();
|
||||
// Consider many to ones that have no class defined or equal the owner class of the one to many
|
||||
if ( manyToOneTypeClass == null || manyToOneTypeClass.equals( containerClass ) ) {
|
||||
if ( manyToOneType.getColumnAttribute() == null ) {
|
||||
List<Serializable> columns = manyToOneType.getColumnOrFormula();
|
||||
if ( columns.size() != keyColumnNames.size() ) {
|
||||
return false;
|
||||
}
|
||||
for ( int i = 0; i < columns.size(); i++ ) {
|
||||
Serializable column = columns.get( i );
|
||||
String keyColumn = keyColumnNames.get( i );
|
||||
if ( !( column instanceof JaxbHbmColumnType ) || !( (JaxbHbmColumnType) column )
|
||||
.getName()
|
||||
.equals( keyColumn ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return keyColumnNames.size() == 1 && keyColumnNames.get( 0 )
|
||||
.equals( manyToOneType.getColumnAttribute() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map( JaxbHbmManyToOneType.class::cast )
|
||||
.findFirst();
|
||||
}
|
||||
|
@ -91,13 +136,12 @@ public abstract class AbstractPluralAttributeSourceImpl
|
|||
this.keySource = jaxbHbmManyToOneTypeOptional
|
||||
.map( jaxbHbmManyToOneType -> new PluralAttributeKeySourceImpl(
|
||||
sourceMappingDocument(),
|
||||
pluralAttributeJaxbMapping.getKey(),
|
||||
jaxbHbmManyToOneType,
|
||||
container
|
||||
) ).orElseGet( () -> new PluralAttributeKeySourceImpl(
|
||||
sourceMappingDocument(),
|
||||
pluralAttributeJaxbMapping.isInverse() ?
|
||||
pluralAttributeJaxbMapping.getKey() :
|
||||
pluralAttributeJaxbMapping.getKey(),
|
||||
pluralAttributeJaxbMapping.getKey(),
|
||||
container
|
||||
) );
|
||||
|
||||
|
|
|
@ -74,16 +74,31 @@ public class PluralAttributeKeySourceImpl
|
|||
|
||||
public PluralAttributeKeySourceImpl(
|
||||
MappingDocument mappingDocument,
|
||||
final JaxbHbmManyToOneType jaxbKey,
|
||||
final JaxbHbmKeyType jaxbKey,
|
||||
final JaxbHbmManyToOneType jaxbManyToOne,
|
||||
final AttributeSourceContainer container) {
|
||||
super( mappingDocument );
|
||||
|
||||
this.explicitFkName = StringHelper.nullIfEmpty( jaxbKey.getForeignKey() );
|
||||
this.referencedPropertyName = StringHelper.nullIfEmpty( jaxbKey.getPropertyRef() );
|
||||
this.cascadeDeletesAtFkLevel = jaxbKey.getOnDelete() != null
|
||||
&& "cascade".equals( jaxbKey.getOnDelete().value() );
|
||||
this.nullable = jaxbKey.isNotNull() == null || !jaxbKey.isNotNull();
|
||||
this.updateable = jaxbKey.isUpdate();
|
||||
this.explicitFkName = StringHelper.nullIfEmpty( jaxbManyToOne.getForeignKey() );
|
||||
this.referencedPropertyName = StringHelper.nullIfEmpty( jaxbManyToOne.getPropertyRef() );
|
||||
if ( jaxbKey.getOnDelete() == null ) {
|
||||
this.cascadeDeletesAtFkLevel = jaxbManyToOne.getOnDelete() != null && "cascade".equals( jaxbManyToOne.getOnDelete().value() );
|
||||
}
|
||||
else {
|
||||
this.cascadeDeletesAtFkLevel = "cascade".equals( jaxbKey.getOnDelete().value() );
|
||||
}
|
||||
if ( jaxbKey.isNotNull() == null ) {
|
||||
this.nullable = jaxbManyToOne.isNotNull() == null || !jaxbManyToOne.isNotNull();
|
||||
}
|
||||
else {
|
||||
this.nullable = !jaxbKey.isNotNull();
|
||||
}
|
||||
if ( jaxbKey.isUpdate() == null ) {
|
||||
this.updateable = jaxbManyToOne.isUpdate();
|
||||
}
|
||||
else {
|
||||
this.updateable = jaxbKey.isUpdate();
|
||||
}
|
||||
|
||||
this.valueSources = RelationalValueSourceHelper.buildValueSources(
|
||||
sourceMappingDocument(),
|
||||
|
@ -106,7 +121,7 @@ public class PluralAttributeKeySourceImpl
|
|||
|
||||
@Override
|
||||
public List getColumnOrFormulaElements() {
|
||||
return jaxbKey.getColumnOrFormula();
|
||||
return jaxbKey.getColumn();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -396,59 +396,67 @@ public final class AnnotationBinder {
|
|||
context.getMetadataCollector().addIdentifierGenerator( buildIdGenerator( def, context ) );
|
||||
}
|
||||
|
||||
private static void bindQueries(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
|
||||
{
|
||||
SqlResultSetMapping ann = annotatedElement.getAnnotation( SqlResultSetMapping.class );
|
||||
QueryBinder.bindSqlResultSetMapping( ann, context, false );
|
||||
}
|
||||
{
|
||||
SqlResultSetMappings ann = annotatedElement.getAnnotation( SqlResultSetMappings.class );
|
||||
if ( ann != null ) {
|
||||
for ( SqlResultSetMapping current : ann.value() ) {
|
||||
QueryBinder.bindSqlResultSetMapping( current, context, false );
|
||||
}
|
||||
private static void bindNamedJpaQueries(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
|
||||
QueryBinder.bindSqlResultSetMapping(
|
||||
annotatedElement.getAnnotation( SqlResultSetMapping.class ),
|
||||
context,
|
||||
false
|
||||
);
|
||||
|
||||
final SqlResultSetMappings ann = annotatedElement.getAnnotation( SqlResultSetMappings.class );
|
||||
if ( ann != null ) {
|
||||
for ( SqlResultSetMapping current : ann.value() ) {
|
||||
QueryBinder.bindSqlResultSetMapping( current, context, false );
|
||||
}
|
||||
}
|
||||
{
|
||||
NamedQuery ann = annotatedElement.getAnnotation( NamedQuery.class );
|
||||
QueryBinder.bindQuery( ann, context, false );
|
||||
}
|
||||
{
|
||||
org.hibernate.annotations.NamedQuery ann = annotatedElement.getAnnotation(
|
||||
org.hibernate.annotations.NamedQuery.class
|
||||
);
|
||||
QueryBinder.bindQuery( ann, context );
|
||||
}
|
||||
{
|
||||
NamedQueries ann = annotatedElement.getAnnotation( NamedQueries.class );
|
||||
QueryBinder.bindQueries( ann, context, false );
|
||||
}
|
||||
{
|
||||
org.hibernate.annotations.NamedQueries ann = annotatedElement.getAnnotation(
|
||||
org.hibernate.annotations.NamedQueries.class
|
||||
);
|
||||
QueryBinder.bindQueries( ann, context );
|
||||
}
|
||||
{
|
||||
NamedNativeQuery ann = annotatedElement.getAnnotation( NamedNativeQuery.class );
|
||||
QueryBinder.bindNativeQuery( ann, context, false );
|
||||
}
|
||||
{
|
||||
org.hibernate.annotations.NamedNativeQuery ann = annotatedElement.getAnnotation(
|
||||
org.hibernate.annotations.NamedNativeQuery.class
|
||||
);
|
||||
QueryBinder.bindNativeQuery( ann, context );
|
||||
}
|
||||
{
|
||||
NamedNativeQueries ann = annotatedElement.getAnnotation( NamedNativeQueries.class );
|
||||
QueryBinder.bindNativeQueries( ann, context, false );
|
||||
}
|
||||
{
|
||||
org.hibernate.annotations.NamedNativeQueries ann = annotatedElement.getAnnotation(
|
||||
org.hibernate.annotations.NamedNativeQueries.class
|
||||
);
|
||||
QueryBinder.bindNativeQueries( ann, context );
|
||||
}
|
||||
|
||||
QueryBinder.bindQuery(
|
||||
annotatedElement.getAnnotation( NamedQuery.class ),
|
||||
context,
|
||||
false
|
||||
);
|
||||
|
||||
QueryBinder.bindQueries(
|
||||
annotatedElement.getAnnotation( NamedQueries.class ),
|
||||
context,
|
||||
false
|
||||
);
|
||||
|
||||
QueryBinder.bindNativeQuery(
|
||||
annotatedElement.getAnnotation( NamedNativeQuery.class ),
|
||||
context,
|
||||
false
|
||||
);
|
||||
|
||||
QueryBinder.bindNativeQueries(
|
||||
annotatedElement.getAnnotation( NamedNativeQueries.class ),
|
||||
context,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private static void bindQueries(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
|
||||
bindNamedJpaQueries( annotatedElement, context );
|
||||
|
||||
QueryBinder.bindQuery(
|
||||
annotatedElement.getAnnotation( org.hibernate.annotations.NamedQuery.class ),
|
||||
context
|
||||
);
|
||||
|
||||
QueryBinder.bindQueries(
|
||||
annotatedElement.getAnnotation( org.hibernate.annotations.NamedQueries.class ),
|
||||
context
|
||||
);
|
||||
|
||||
QueryBinder.bindNativeQuery(
|
||||
annotatedElement.getAnnotation( org.hibernate.annotations.NamedNativeQuery.class ),
|
||||
context
|
||||
);
|
||||
|
||||
QueryBinder.bindNativeQueries(
|
||||
annotatedElement.getAnnotation( org.hibernate.annotations.NamedNativeQueries.class ),
|
||||
context
|
||||
);
|
||||
|
||||
// NamedStoredProcedureQuery handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bindNamedStoredProcedureQuery(
|
||||
|
|
|
@ -176,11 +176,7 @@ public class CopyIdentifierComponentSecondPass implements SecondPass {
|
|||
final Ejb3JoinColumn joinColumn;
|
||||
String logicalColumnName = null;
|
||||
if ( isExplicitReference ) {
|
||||
final String columnName = column.getName();
|
||||
logicalColumnName = buildingContext.getMetadataCollector().getLogicalColumnName(
|
||||
referencedPersistentClass.getTable(),
|
||||
columnName
|
||||
);
|
||||
logicalColumnName = column.getName();
|
||||
//JPA 2 requires referencedColumnNames to be case insensitive
|
||||
joinColumn = columnByReferencedName.get( logicalColumnName.toLowerCase(Locale.ROOT ) );
|
||||
}
|
||||
|
|
|
@ -92,6 +92,8 @@ import java.sql.*;
|
|||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Date;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.*;
|
||||
|
||||
|
@ -130,6 +132,15 @@ public abstract class Dialect implements ConversionContext {
|
|||
* Characters used as closing for quoting SQL identifiers
|
||||
*/
|
||||
public static final String CLOSED_QUOTE = "`\"]";
|
||||
private static final Pattern SINGLE_QUOTE_PATTERN = Pattern.compile(
|
||||
"'",
|
||||
Pattern.LITERAL
|
||||
);
|
||||
|
||||
private static final Pattern ESCAPE_CLOSING_COMMENT_PATTERN = Pattern.compile( "\\*/" );
|
||||
private static final Pattern ESCAPE_OPENING_COMMENT_PATTERN = Pattern.compile( "/\\*" );
|
||||
|
||||
public static final String TWO_SINGLE_QUOTES_REPLACEMENT = Matcher.quoteReplacement( "''" );
|
||||
|
||||
private final TypeNames typeNames = new TypeNames();
|
||||
private final TypeNames hibernateTypeNames = new TypeNames();
|
||||
|
@ -1372,7 +1383,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
|
||||
/**
|
||||
* Get the string to append to SELECT statements to acquire WRITE locks
|
||||
* for this dialect. Location of the of the returned string is treated
|
||||
* for this dialect. Location of the returned string is treated
|
||||
* the same as getForUpdateString.
|
||||
*
|
||||
* @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait.
|
||||
|
@ -1400,7 +1411,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
|
||||
/**
|
||||
* Get the string to append to SELECT statements to acquire READ locks
|
||||
* for this dialect. Location of the of the returned string is treated
|
||||
* for this dialect. Location of the returned string is treated
|
||||
* the same as getForUpdateString.
|
||||
*
|
||||
* @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait.
|
||||
|
@ -1413,7 +1424,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
/**
|
||||
* Get the string to append to SELECT statements to acquire READ locks
|
||||
* for this dialect given the aliases of the columns to be read locked.
|
||||
* Location of the of the returned string is treated
|
||||
* Location of the returned string is treated
|
||||
* the same as getForUpdateString.
|
||||
*
|
||||
* @param aliases The columns to be read locked.
|
||||
|
@ -1430,8 +1441,8 @@ public abstract class Dialect implements ConversionContext {
|
|||
* Does the <tt>FOR UPDATE OF</tt> clause accept a list of columns
|
||||
* instead of a list of table aliases?
|
||||
*
|
||||
* @return True if the database <tt>FOR UPDATE OF</tt> clause takes
|
||||
* a column list; false otherwise.
|
||||
* @return True if the database supports <tt>FOR UPDATE OF</tt> syntax;
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean forUpdateOfColumns() {
|
||||
// by default we report no support
|
||||
|
@ -1528,7 +1539,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
|
||||
/**
|
||||
* Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>,
|
||||
* whereby a "lock hint" is appends to the table name in the from clause.
|
||||
* whereby a "lock hint" is appended to the table name in the from clause.
|
||||
* <p/>
|
||||
* contributed by <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a>
|
||||
*
|
||||
|
@ -1543,7 +1554,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
}
|
||||
/**
|
||||
* Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>,
|
||||
* whereby a "lock hint" is appends to the table name in the from clause.
|
||||
* whereby a "lock hint" is appended to the table name in the from clause.
|
||||
* <p/>
|
||||
* contributed by <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a>
|
||||
*
|
||||
|
@ -1835,7 +1846,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
* <p/>
|
||||
* It is strongly recommended that specific Dialect implementations override this
|
||||
* method, since interpretation of a SQL error is much more accurate when based on
|
||||
* the a vendor-specific ErrorCode rather than the SQLState.
|
||||
* the vendor-specific ErrorCode rather than the SQLState.
|
||||
* <p/>
|
||||
* Specific Dialects may override to return whatever is most appropriate for that vendor.
|
||||
*
|
||||
|
@ -2604,7 +2615,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
|
||||
/**
|
||||
* Does this dialect require that references to result variables
|
||||
* (i.e, select expresssion aliases) in an ORDER BY clause be
|
||||
* (i.e, select expression aliases) in an ORDER BY clause be
|
||||
* replaced by column positions (1-origin) as defined
|
||||
* by the select clause?
|
||||
|
||||
|
@ -3019,6 +3030,8 @@ public abstract class Dialect implements ConversionContext {
|
|||
|
||||
/**
|
||||
* By default interpret this based on DatabaseMetaData.
|
||||
*
|
||||
* @return The NameQualifierSupport.
|
||||
*/
|
||||
public NameQualifierSupport getNameQualifierSupport() {
|
||||
return null;
|
||||
|
@ -3156,7 +3169,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
* @return escaped String
|
||||
*/
|
||||
protected String escapeLiteral(String literal) {
|
||||
return literal.replace("'", "''");
|
||||
return SINGLE_QUOTE_PATTERN.matcher( literal ).replaceAll( TWO_SINGLE_QUOTES_REPLACEMENT );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3180,7 +3193,15 @@ public abstract class Dialect implements ConversionContext {
|
|||
}
|
||||
|
||||
protected String prependComment(String sql, String comment) {
|
||||
return "/* " + comment + " */ " + sql;
|
||||
return "/* " + escapeComment( comment ) + " */ " + sql;
|
||||
}
|
||||
|
||||
public static String escapeComment(String comment) {
|
||||
if ( StringHelper.isNotEmpty( comment ) ) {
|
||||
final String escaped = ESCAPE_CLOSING_COMMENT_PATTERN.matcher( comment ).replaceAll( "*\\\\/" );
|
||||
return ESCAPE_OPENING_COMMENT_PATTERN.matcher( escaped ).replaceAll( "/\\\\*" );
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@ import static org.hibernate.annotations.QueryHints.FETCH_SIZE;
|
|||
import static org.hibernate.annotations.QueryHints.FLUSH_MODE;
|
||||
import static org.hibernate.annotations.QueryHints.FOLLOW_ON_LOCKING;
|
||||
import static org.hibernate.annotations.QueryHints.NATIVE_LOCKMODE;
|
||||
import static org.hibernate.annotations.QueryHints.NATIVE_SPACES;
|
||||
import static org.hibernate.annotations.QueryHints.PASS_DISTINCT_THROUGH;
|
||||
import static org.hibernate.annotations.QueryHints.READ_ONLY;
|
||||
import static org.hibernate.annotations.QueryHints.TIMEOUT_HIBERNATE;
|
||||
|
@ -26,8 +27,6 @@ import static org.hibernate.annotations.QueryHints.TIMEOUT_JPA;
|
|||
|
||||
/**
|
||||
* Defines the supported JPA query hints
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class QueryHints {
|
||||
/**
|
||||
|
@ -91,26 +90,23 @@ public class QueryHints {
|
|||
*
|
||||
* Note: Currently, attributes that are not specified are treated as FetchType.LAZY or FetchType.EAGER depending
|
||||
* on the attribute's definition in metadata, rather than forcing FetchType.LAZY.
|
||||
*
|
||||
* @deprecated (since 5.4) Use {@link GraphSemantic#FETCH}'s {@link GraphSemantic#getJpaHintName()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String HINT_FETCHGRAPH = GraphSemantic.FETCH.getJpaHintName();
|
||||
|
||||
/**
|
||||
* Hint providing a "loadgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
|
||||
* FetchType.EAGER (via join fetch or subsequent select). Attributes that are not specified are treated as
|
||||
* FetchType.LAZY or FetchType.EAGER depending on the attribute's definition in metadata
|
||||
*
|
||||
* @deprecated (since 5.4) Use {@link GraphSemantic#LOAD}'s {@link GraphSemantic#getJpaHintName()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String HINT_LOADGRAPH = GraphSemantic.LOAD.getJpaHintName();
|
||||
|
||||
public static final String HINT_FOLLOW_ON_LOCKING = FOLLOW_ON_LOCKING;
|
||||
|
||||
public static final String HINT_PASS_DISTINCT_THROUGH = PASS_DISTINCT_THROUGH;
|
||||
|
||||
public static final String HINT_NATIVE_SPACES = NATIVE_SPACES;
|
||||
|
||||
|
||||
private static final Set<String> HINTS = buildHintsSet();
|
||||
|
||||
private static Set<String> buildHintsSet() {
|
||||
|
@ -127,6 +123,7 @@ public class QueryHints {
|
|||
hints.add( HINT_NATIVE_LOCKMODE );
|
||||
hints.add( HINT_FETCHGRAPH );
|
||||
hints.add( HINT_LOADGRAPH );
|
||||
hints.add( HINT_NATIVE_SPACES );
|
||||
return java.util.Collections.unmodifiableSet( hints );
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ import static org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE;
|
|||
import static org.hibernate.jpa.QueryHints.HINT_FLUSH_MODE;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_FOLLOW_ON_LOCKING;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_LOADGRAPH;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_NATIVE_SPACES;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_READONLY;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_TIMEOUT;
|
||||
import static org.hibernate.jpa.QueryHints.SPEC_HINT_TIMEOUT;
|
||||
|
@ -123,13 +124,11 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
|
|||
this.parameterMetadata = parameterMetadata;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MutableQueryOptions getQueryOptions() {
|
||||
return queryOptions;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FlushMode getHibernateFlushMode() {
|
||||
return getQueryOptions().getFlushMode();
|
||||
|
@ -1013,6 +1012,9 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
|
|||
final CacheStoreMode storeMode = value != null ? CacheStoreMode.valueOf( value.toString() ) : null;
|
||||
applied = applyJpaCacheStoreMode( storeMode );
|
||||
}
|
||||
else if ( HINT_NATIVE_SPACES.equals( hintName ) ) {
|
||||
applied = applyQuerySpaces( value );
|
||||
}
|
||||
else if ( QueryHints.HINT_NATIVE_LOCKMODE.equals( hintName ) ) {
|
||||
applied = applyNativeQueryLockMode( value );
|
||||
}
|
||||
|
@ -1064,6 +1066,12 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
|
|||
return this;
|
||||
}
|
||||
|
||||
protected boolean applyQuerySpaces(Object value) {
|
||||
throw new IllegalStateException(
|
||||
"Illegal attempt to apply native-query spaces to a non-native query"
|
||||
);
|
||||
}
|
||||
|
||||
protected void handleUnrecognizedHint(String hintName, Object value) {
|
||||
MSG_LOGGER.debugf( "Skipping unsupported query hint [%s]", hintName );
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import java.util.Iterator;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
|
||||
/**
|
||||
* An SQL <tt>DELETE</tt> statement
|
||||
*
|
||||
|
@ -36,7 +38,7 @@ public class Delete {
|
|||
public String toStatementString() {
|
||||
StringBuilder buf = new StringBuilder( tableName.length() + 10 );
|
||||
if ( comment!=null ) {
|
||||
buf.append( "/* " ).append(comment).append( " */ " );
|
||||
buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " );
|
||||
}
|
||||
buf.append( "delete from " ).append(tableName);
|
||||
if ( where != null || !primaryKeyColumns.isEmpty() || versionColumnName != null ) {
|
||||
|
|
|
@ -94,7 +94,7 @@ public class Insert {
|
|||
public String toStatementString() {
|
||||
StringBuilder buf = new StringBuilder( columns.size()*15 + tableName.length() + 10 );
|
||||
if ( comment != null ) {
|
||||
buf.append( "/* " ).append( comment ).append( " */ " );
|
||||
buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " );
|
||||
}
|
||||
buf.append("insert into ")
|
||||
.append(tableName);
|
||||
|
|
|
@ -71,7 +71,7 @@ public class InsertSelect {
|
|||
|
||||
StringBuilder buf = new StringBuilder( (columnNames.size() * 15) + tableName.length() + 10 );
|
||||
if ( comment!=null ) {
|
||||
buf.append( "/* " ).append( comment ).append( " */ " );
|
||||
buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " );
|
||||
}
|
||||
buf.append( "insert into " ).append( tableName );
|
||||
if ( !columnNames.isEmpty() ) {
|
||||
|
|
|
@ -126,7 +126,7 @@ public class QuerySelect {
|
|||
public String toQueryString() {
|
||||
StringBuilder buf = new StringBuilder( 50 );
|
||||
if ( comment != null ) {
|
||||
buf.append( "/* " ).append( comment ).append( " */ " );
|
||||
buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " );
|
||||
}
|
||||
buf.append( "select " );
|
||||
if ( distinct ) {
|
||||
|
|
|
@ -42,7 +42,7 @@ public class Select {
|
|||
public String toStatementString() {
|
||||
StringBuilder buf = new StringBuilder(guesstimatedBufferSize);
|
||||
if ( StringHelper.isNotEmpty(comment) ) {
|
||||
buf.append("/* ").append(comment).append(" */ ");
|
||||
buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " );
|
||||
}
|
||||
|
||||
buf.append("select ").append(selectClause)
|
||||
|
|
|
@ -151,7 +151,7 @@ public class SimpleSelect {
|
|||
);
|
||||
|
||||
if ( comment != null ) {
|
||||
buf.append( "/* " ).append( comment ).append( " */ " );
|
||||
buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " );
|
||||
}
|
||||
|
||||
buf.append( "select " );
|
||||
|
|
|
@ -166,7 +166,7 @@ public class Update {
|
|||
public String toStatementString() {
|
||||
StringBuilder buf = new StringBuilder( (columns.size() * 15) + tableName.length() + 10 );
|
||||
if ( comment!=null ) {
|
||||
buf.append( "/* " ).append( comment ).append( " */ " );
|
||||
buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " );
|
||||
}
|
||||
buf.append( "update " ).append( tableName ).append( " set " );
|
||||
boolean assignmentsAppended = false;
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* 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.collection.map;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapKey;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class EmbeddableIndexTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { TheOne.class, TheMany.class };
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
TheOne one = new TheOne( "1" );
|
||||
session.save( one );
|
||||
|
||||
TheMapKey theMapKey = new TheMapKey( one );
|
||||
TheMany theMany = new TheMany( theMapKey );
|
||||
session.save( theMany );
|
||||
|
||||
Map<TheMapKey, TheMany> map = new HashMap<>();
|
||||
map.put( theMapKey, theMany );
|
||||
one.setTheManys( map );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIt() {
|
||||
inSession(
|
||||
session -> {
|
||||
TheOne one = session.get( TheOne.class, "1" );
|
||||
TheMapKey theMapKey = one.getTheManys().keySet().iterator().next();
|
||||
assertThat( theMapKey, is( notNullValue() ) );
|
||||
assertThat( theMapKey.getTheOne(), sameInstance( one ) );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "TheOne")
|
||||
public static class TheOne {
|
||||
private String id;
|
||||
private String aString;
|
||||
private Map<TheMapKey, TheMany> theManys = new HashMap<>();
|
||||
|
||||
TheOne() {
|
||||
}
|
||||
|
||||
public TheOne(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Id
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@OneToMany(mappedBy = "theMapKey.theOne")
|
||||
@MapKey(name = "theMapKey")
|
||||
public Map<TheMapKey, TheMany> getTheManys() {
|
||||
return theManys;
|
||||
}
|
||||
|
||||
public void setTheManys(Map<TheMapKey, TheMany> theManys) {
|
||||
this.theManys = theManys;
|
||||
}
|
||||
|
||||
public String getaString() {
|
||||
return aString;
|
||||
}
|
||||
|
||||
public void setaString(String aString) {
|
||||
this.aString = aString;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class TheMapKey implements Serializable {
|
||||
private TheOne theOne;
|
||||
private int anInt;
|
||||
|
||||
TheMapKey() {
|
||||
}
|
||||
|
||||
public TheMapKey(TheOne theOne) {
|
||||
this.theOne = theOne;
|
||||
}
|
||||
|
||||
@ManyToOne
|
||||
public TheOne getTheOne() {
|
||||
return theOne;
|
||||
}
|
||||
|
||||
public void setTheOne(TheOne theOne) {
|
||||
this.theOne = theOne;
|
||||
}
|
||||
|
||||
public int getAnInt() {
|
||||
return anInt;
|
||||
}
|
||||
|
||||
public void setAnInt(int anInt) {
|
||||
this.anInt = anInt;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "TheMany")
|
||||
public static class TheMany {
|
||||
private TheMapKey theMapKey;
|
||||
private String aString;
|
||||
|
||||
TheMany() {
|
||||
}
|
||||
|
||||
public TheMany(TheMapKey theMapKey) {
|
||||
this.theMapKey = theMapKey;
|
||||
}
|
||||
|
||||
@EmbeddedId
|
||||
public TheMapKey getTheMapKey() {
|
||||
return theMapKey;
|
||||
}
|
||||
|
||||
public void setTheMapKey(TheMapKey theMapKey) {
|
||||
this.theMapKey = theMapKey;
|
||||
}
|
||||
|
||||
public String getaString() {
|
||||
return aString;
|
||||
}
|
||||
|
||||
public void setaString(String aString) {
|
||||
this.aString = aString;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.comments;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@Entity
|
||||
public class TestEntity {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private String value;
|
||||
|
||||
public TestEntity() {
|
||||
|
||||
}
|
||||
|
||||
public TestEntity(String id, String value) {
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.comments;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@Entity
|
||||
public class TestEntity2 {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private String value;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.comments;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CompoundSelection;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class UseSqlCommentTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { TestEntity.class, TestEntity2.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addMappings(Map settings) {
|
||||
settings.put( AvailableSettings.USE_SQL_COMMENTS, "true" );
|
||||
settings.put( AvailableSettings.FORMAT_SQL, "false" );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
TestEntity testEntity = new TestEntity();
|
||||
testEntity.setId( "test1" );
|
||||
testEntity.setValue( "value1" );
|
||||
entityManager.persist( testEntity );
|
||||
|
||||
TestEntity2 testEntity2 = new TestEntity2();
|
||||
testEntity2.setId( "test2" );
|
||||
testEntity2.setValue( "value2" );
|
||||
entityManager.persist( testEntity2 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIt() {
|
||||
String appendLiteral = "*/select id as col_0_0_,value as col_1_0_ from testEntity2 where 1=1 or id=?--/*";
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
||||
List<TestEntity> result = findUsingQuery( "test1", appendLiteral, entityManager );
|
||||
|
||||
TestEntity test1 = result.get( 0 );
|
||||
assertThat( test1.getValue(), is( appendLiteral ) );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
||||
List<TestEntity> result = findUsingCriteria( "test1", appendLiteral, entityManager );
|
||||
|
||||
TestEntity test1 = result.get( 0 );
|
||||
assertThat( test1.getValue(), is( appendLiteral ) );
|
||||
} );
|
||||
}
|
||||
|
||||
public List<TestEntity> findUsingCriteria(String id, String appendLiteral, EntityManager entityManager) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<TestEntity> criteria = builder.createQuery( TestEntity.class );
|
||||
Root<TestEntity> root = criteria.from( TestEntity.class );
|
||||
|
||||
Path<Object> idPath = root.get( "id" );
|
||||
CompoundSelection<TestEntity> selection = builder.construct(
|
||||
TestEntity.class,
|
||||
idPath,
|
||||
builder.literal( appendLiteral )
|
||||
);
|
||||
criteria.select( selection );
|
||||
|
||||
criteria.where( builder.equal( idPath, builder.parameter( String.class, "where_id" ) ) );
|
||||
|
||||
TypedQuery<TestEntity> query = entityManager.createQuery( criteria );
|
||||
query.setParameter( "where_id", id );
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
public List<TestEntity> findUsingQuery(String id, String appendLiteral, EntityManager entityManager) {
|
||||
TypedQuery<TestEntity> query =
|
||||
entityManager.createQuery(
|
||||
"select new org.hibernate.test.comments.TestEntity(id, '"
|
||||
+ appendLiteral.replace( "'", "''" )
|
||||
+ "') from TestEntity where id=:where_id",
|
||||
TestEntity.class
|
||||
);
|
||||
query.setParameter( "where_id", id );
|
||||
return query.getResultList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.mapping.hhh14276;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.query.sqm.mutation.internal.inline.InlineStrategy;
|
||||
|
||||
import org.hibernate.test.mapping.hhh14276.entity.PlayerStat;
|
||||
import org.hibernate.test.mapping.hhh14276.entity.Score;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-14276")
|
||||
public class NestedIdClassDerivedIdentifiersTest extends BaseEntityManagerFunctionalTestCase {
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
PlayerStat.class,
|
||||
Score.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS, Boolean.TRUE );
|
||||
options.put( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, InlineStrategy.class.getName() );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInJPA( this::entityManagerFactory, em ->
|
||||
{
|
||||
// do nothing
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedIdClassDerivedIdentifiers() {
|
||||
doInJPA( this::entityManagerFactory, em ->
|
||||
{
|
||||
// do nothing
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.mapping.hhh14276.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "\"PlayerStats\"")
|
||||
@IdClass(PlayerStatId.class)
|
||||
public class PlayerStat implements Serializable {
|
||||
|
||||
@Id
|
||||
@Column(name = "player_id")
|
||||
private Integer playerId;
|
||||
|
||||
@Basic(optional = false)
|
||||
@Column(name = "jersey_nbr")
|
||||
private Integer jerseyNbr;
|
||||
|
||||
@Id
|
||||
@ManyToOne(optional = false, fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "game_id", referencedColumnName = "game_id")
|
||||
@JoinColumn(name = "is_home", referencedColumnName = "is_home")
|
||||
private Score score;
|
||||
|
||||
public PlayerStat() {
|
||||
}
|
||||
|
||||
public Integer getGameId() {
|
||||
return score.getGameId();
|
||||
}
|
||||
|
||||
public void setGameId(Integer gameId) {
|
||||
score.setGameId( gameId );
|
||||
}
|
||||
|
||||
public Boolean getHome() {
|
||||
return score.getHome();
|
||||
}
|
||||
|
||||
public void setHome(Boolean home) {
|
||||
score.setHome( home );
|
||||
}
|
||||
|
||||
public Integer getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public void setPlayerId(Integer playerId) {
|
||||
this.playerId = playerId;
|
||||
}
|
||||
|
||||
public Integer getJerseyNbr() {
|
||||
return jerseyNbr;
|
||||
}
|
||||
|
||||
public void setJerseyNbr(Integer jerseyNbr) {
|
||||
this.jerseyNbr = jerseyNbr;
|
||||
}
|
||||
|
||||
public Score getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScore(Score score) {
|
||||
this.score = score;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.mapping.hhh14276.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class PlayerStatId implements Serializable {
|
||||
|
||||
private Integer playerId;
|
||||
|
||||
// nested composite PK @IdClass: named like relationship in entity class
|
||||
private ScoreId score;
|
||||
|
||||
public PlayerStatId() {
|
||||
}
|
||||
|
||||
public Integer getGameId() {
|
||||
return score.getGameId();
|
||||
}
|
||||
|
||||
public void setGameId(Integer gameId) {
|
||||
score.setGameId( gameId );
|
||||
}
|
||||
|
||||
public Boolean getHome() {
|
||||
return score.getHome();
|
||||
}
|
||||
|
||||
public void setHome(Boolean home) {
|
||||
score.setHome( home );
|
||||
}
|
||||
|
||||
public Integer getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public void setPlayerId(Integer playerId) {
|
||||
this.playerId = playerId;
|
||||
}
|
||||
|
||||
public ScoreId getScoreId() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScoreId(ScoreId scoreId) {
|
||||
this.score = scoreId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.mapping.hhh14276.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "\"Scores\"")
|
||||
@IdClass(ScoreId.class)
|
||||
public class Score implements Serializable {
|
||||
|
||||
@Id
|
||||
@Column(name = "game_id")
|
||||
private Integer gameId;
|
||||
|
||||
@Id
|
||||
@Column(name = "is_home")
|
||||
private Boolean home;
|
||||
|
||||
@Basic(optional = false)
|
||||
@Column(name = "roster_id")
|
||||
private Integer rosterId;
|
||||
|
||||
@Basic
|
||||
@Column(name = "final_score")
|
||||
private Integer finalScore;
|
||||
|
||||
public Score() {
|
||||
}
|
||||
|
||||
public Integer getGameId() {
|
||||
return gameId;
|
||||
}
|
||||
|
||||
public void setGameId(Integer gameId) {
|
||||
this.gameId = gameId;
|
||||
}
|
||||
|
||||
public Boolean getHome() {
|
||||
return home;
|
||||
}
|
||||
|
||||
public void setHome(Boolean home) {
|
||||
this.home = home;
|
||||
}
|
||||
|
||||
public Integer getRosterId() {
|
||||
return rosterId;
|
||||
}
|
||||
|
||||
public void setRosterId(Integer rosterId) {
|
||||
this.rosterId = rosterId;
|
||||
}
|
||||
|
||||
public Integer getFinalScore() {
|
||||
return finalScore;
|
||||
}
|
||||
|
||||
public void setFinalScore(Integer finalScore) {
|
||||
this.finalScore = finalScore;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.mapping.hhh14276.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class ScoreId implements Serializable {
|
||||
|
||||
private Integer gameId;
|
||||
|
||||
private Boolean home;
|
||||
|
||||
public ScoreId() {
|
||||
}
|
||||
|
||||
public Integer getGameId() {
|
||||
return gameId;
|
||||
}
|
||||
|
||||
public void setGameId(Integer gameId) {
|
||||
this.gameId = gameId;
|
||||
}
|
||||
|
||||
public Boolean getHome() {
|
||||
return home;
|
||||
}
|
||||
|
||||
public void setHome(Boolean home) {
|
||||
this.home = home;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* 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.query.sql;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityResult;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.QueryHint;
|
||||
import javax.persistence.SqlResultSetMapping;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.NamedNativeQuery;
|
||||
import org.hibernate.cache.spi.CacheImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.jpa.QueryHints;
|
||||
import org.hibernate.query.sql.spi.NativeQueryImplementor;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SynchronizedSpaceTests extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Test
|
||||
public void testNonSyncedCachedScenario() {
|
||||
// CachedEntity updated by native-query without adding query spaces
|
||||
// - the outcome should be all cached data being invalidated
|
||||
|
||||
checkUseCase(
|
||||
"cached_entity",
|
||||
query -> {},
|
||||
// the 2 CachedEntity entries should not be there
|
||||
false
|
||||
);
|
||||
|
||||
// and of course, let's make sure the update happened :)
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "from CachedEntity", CachedEntity.class ).list().forEach(
|
||||
cachedEntity -> assertThat( cachedEntity.name, is( "updated" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void checkUseCase(
|
||||
String table,
|
||||
Consumer<Query> queryConsumer,
|
||||
boolean shouldExistAfter) {
|
||||
|
||||
checkUseCase(
|
||||
(session) -> {
|
||||
final Query nativeQuery = session.createNativeQuery( "update " + table + " set name = 'updated'" );
|
||||
queryConsumer.accept( nativeQuery );
|
||||
return nativeQuery;
|
||||
},
|
||||
Query::executeUpdate,
|
||||
shouldExistAfter
|
||||
);
|
||||
}
|
||||
|
||||
private void checkUseCase(
|
||||
Function<SessionImplementor,Query> queryProducer,
|
||||
Consumer<Query> executor,
|
||||
boolean shouldExistAfter) {
|
||||
|
||||
// first, load both `CachedEntity` instances into the L2 cache
|
||||
loadAll();
|
||||
|
||||
final CacheImplementor cacheSystem = sessionFactory().getCache();
|
||||
|
||||
// make sure they are there
|
||||
assertThat( cacheSystem.containsEntity( CachedEntity.class, 1 ), is( true ) );
|
||||
assertThat( cacheSystem.containsEntity( CachedEntity.class, 2 ), is( true ) );
|
||||
|
||||
// create a query to update the specified table - allowing the passed consumer to register a space if needed
|
||||
inTransaction(
|
||||
session -> {
|
||||
// notice the type is the JPA Query interface
|
||||
final Query nativeQuery = queryProducer.apply( session );
|
||||
executor.accept( nativeQuery );
|
||||
}
|
||||
);
|
||||
|
||||
// see if the entries exist based on the expectation
|
||||
assertThat( cacheSystem.containsEntity( CachedEntity.class, 1 ), is( shouldExistAfter ) );
|
||||
assertThat( cacheSystem.containsEntity( CachedEntity.class, 2 ), is( shouldExistAfter ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedCachedScenario() {
|
||||
final String tableName = "cached_entity";
|
||||
|
||||
checkUseCase(
|
||||
tableName,
|
||||
query -> ( (NativeQueryImplementor<?>) query ).addSynchronizedQuerySpace( tableName ),
|
||||
// the 2 CachedEntity entries should not be there
|
||||
false
|
||||
);
|
||||
|
||||
// and of course, let's make sure the update happened :)
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "from CachedEntity", CachedEntity.class ).list().forEach(
|
||||
cachedEntity -> assertThat( cachedEntity.name, is( "updated" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonSyncedNonCachedScenario() {
|
||||
// NonCachedEntity updated by native-query without adding query spaces
|
||||
// - the outcome should be all cached data being invalidated
|
||||
|
||||
checkUseCase(
|
||||
"non_cached_entity",
|
||||
query -> {},
|
||||
// the 2 CachedEntity entries should not be there
|
||||
false
|
||||
);
|
||||
|
||||
// and of course, let's make sure the update happened :)
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "from NonCachedEntity", NonCachedEntity.class ).list().forEach(
|
||||
cachedEntity -> assertThat( cachedEntity.name, is( "updated" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenario() {
|
||||
// NonCachedEntity updated by native-query with query spaces
|
||||
// - the caches for CachedEntity are not invalidated - they are not affected by the specified query-space
|
||||
|
||||
final String tableName = "non_cached_entity";
|
||||
|
||||
checkUseCase(
|
||||
tableName,
|
||||
query -> ( (NativeQueryImplementor<?>) query ).addSynchronizedQuerySpace( tableName ),
|
||||
// the 2 CachedEntity entries should still be there
|
||||
true
|
||||
);
|
||||
|
||||
// and of course, let's make sure the update happened :)
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "from NonCachedEntity", NonCachedEntity.class ).list().forEach(
|
||||
cachedEntity -> assertThat( cachedEntity.name, is( "updated" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenarioUsingHint() {
|
||||
// same as `#testSyncedNonCachedScenario`, but here using the hint
|
||||
|
||||
final String tableName = "non_cached_entity";
|
||||
|
||||
checkUseCase(
|
||||
tableName,
|
||||
query -> query.setHint( QueryHints.HINT_NATIVE_SPACES, tableName ),
|
||||
// the 2 CachedEntity entries should still be there
|
||||
true
|
||||
);
|
||||
|
||||
// and of course, let's make sure the update happened :)
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "from NonCachedEntity", NonCachedEntity.class ).list().forEach(
|
||||
cachedEntity -> assertThat( cachedEntity.name, is( "updated" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenarioUsingHintWithCollection() {
|
||||
// same as `#testSyncedNonCachedScenario`, but here using the hint
|
||||
|
||||
final String tableName = "non_cached_entity";
|
||||
final Set<String> spaces = new HashSet<>();
|
||||
spaces.add( tableName );
|
||||
|
||||
checkUseCase(
|
||||
tableName,
|
||||
query -> query.setHint( QueryHints.HINT_NATIVE_SPACES, spaces ),
|
||||
// the 2 CachedEntity entries should still be there
|
||||
true
|
||||
);
|
||||
|
||||
// and of course, let's make sure the update happened :)
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "from NonCachedEntity", NonCachedEntity.class ).list().forEach(
|
||||
cachedEntity -> assertThat( cachedEntity.name, is( "updated" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenarioUsingHintWithArray() {
|
||||
// same as `#testSyncedNonCachedScenario`, but here using the hint
|
||||
|
||||
final String tableName = "non_cached_entity";
|
||||
final String[] spaces = { tableName };
|
||||
|
||||
checkUseCase(
|
||||
tableName,
|
||||
query -> query.setHint( QueryHints.HINT_NATIVE_SPACES, spaces ),
|
||||
// the 2 CachedEntity entries should still be there
|
||||
true
|
||||
);
|
||||
|
||||
// and of course, let's make sure the update happened :)
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "from NonCachedEntity", NonCachedEntity.class ).list().forEach(
|
||||
cachedEntity -> assertThat( cachedEntity.name, is( "updated" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenarioUsingAnnotationWithReturnClass() {
|
||||
checkUseCase(
|
||||
(session) -> session.createNamedQuery( "NonCachedEntity_return_class" ),
|
||||
Query::getResultList,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenarioUsingAnnotationWithResultSetMapping() {
|
||||
checkUseCase(
|
||||
(session) -> session.createNamedQuery( "NonCachedEntity_resultset_mapping" ),
|
||||
Query::getResultList,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenarioUsingAnnotationWithSpaces() {
|
||||
checkUseCase(
|
||||
(session) -> session.createNamedQuery( "NonCachedEntity_spaces" ),
|
||||
Query::getResultList,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenarioUsingJpaAnnotationWithNoResultMapping() {
|
||||
checkUseCase(
|
||||
(session) -> session.createNamedQuery( "NonCachedEntity_raw_jpa" ),
|
||||
Query::getResultList,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncedNonCachedScenarioUsingJpaAnnotationWithHint() {
|
||||
checkUseCase(
|
||||
(session) -> session.createNamedQuery( "NonCachedEntity_hint_jpa" ),
|
||||
Query::getResultList,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private void loadAll() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "from CachedEntity" ).list();
|
||||
|
||||
// this one is not strictly needed since this entity is not cached.
|
||||
// but it helps my OCD feel better to have it ;)
|
||||
session.createQuery( "from NonCachedEntity" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void prepareTest() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.persist( new CachedEntity( 1, "first cached" ) );
|
||||
session.persist( new CachedEntity( 2, "second cached" ) );
|
||||
|
||||
session.persist( new NonCachedEntity( 1, "first non-cached" ) );
|
||||
session.persist( new NonCachedEntity( 2, "second non-cached" ) );
|
||||
}
|
||||
);
|
||||
|
||||
cleanupCache();
|
||||
}
|
||||
|
||||
public void cleanupTest() {
|
||||
cleanupCache();
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete CachedEntity" ).executeUpdate();
|
||||
session.createQuery( "delete NonCachedEntity" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { CachedEntity.class, NonCachedEntity.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean overrideCacheStrategy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Entity( name = "CachedEntity" )
|
||||
@Table( name = "cached_entity" )
|
||||
@Cacheable( true )
|
||||
@Cache( usage = CacheConcurrencyStrategy.READ_WRITE )
|
||||
public static class CachedEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public CachedEntity() {
|
||||
}
|
||||
|
||||
public CachedEntity(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "NonCachedEntity" )
|
||||
@Table( name = "non_cached_entity" )
|
||||
@Cacheable( false )
|
||||
@NamedNativeQuery(
|
||||
name = "NonCachedEntity_return_class",
|
||||
query = "select * from non_cached_entity",
|
||||
resultClass = NonCachedEntity.class
|
||||
)
|
||||
@NamedNativeQuery(
|
||||
name = "NonCachedEntity_resultset_mapping",
|
||||
query = "select * from non_cached_entity",
|
||||
resultSetMapping = "NonCachedEntity_resultset_mapping"
|
||||
)
|
||||
@SqlResultSetMapping(
|
||||
name = "NonCachedEntity_resultset_mapping",
|
||||
entities = @EntityResult( entityClass = NonCachedEntity.class )
|
||||
)
|
||||
@NamedNativeQuery(
|
||||
name = "NonCachedEntity_spaces",
|
||||
query = "select * from non_cached_entity",
|
||||
querySpaces = "non_cached_entity"
|
||||
)
|
||||
@javax.persistence.NamedNativeQuery(
|
||||
name = "NonCachedEntity_raw_jpa",
|
||||
query = "select * from non_cached_entity"
|
||||
)
|
||||
@javax.persistence.NamedNativeQuery(
|
||||
name = "NonCachedEntity_hint_jpa",
|
||||
query = "select * from non_cached_entity",
|
||||
hints = {
|
||||
@QueryHint( name = QueryHints.HINT_NATIVE_SPACES, value = "non_cached_entity" )
|
||||
}
|
||||
)
|
||||
public static class NonCachedEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public NonCachedEntity() {
|
||||
}
|
||||
|
||||
public NonCachedEntity(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue