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

This commit is contained in:
Andrea Boriero 2020-09-01 09:44:22 +01:00
commit b1ba79105d
11 changed files with 845 additions and 11 deletions

View File

@ -340,8 +340,8 @@ task copyResourcesToIntelliJOutFolder(type: Task, dependsOn: project.tasks.proce
task setDataBase {
inputs.property( "db", db )
doLast {
processTestResources.execute()
copyResourcesToIntelliJOutFolder.execute()
processTestResources
copyResourcesToIntelliJOutFolder
println( 'Setting current database to ' + db )
}

View File

@ -449,4 +449,13 @@ public interface PersistentCollection {
Collection getOrphans(Serializable snapshot, String entityName);
void initializeEmptyCollection(CollectionPersister persister);
/**
* Is the collection newly instantiated?
*
* @return {@code true} if the collection is newly instantiated
*/
default boolean isNewlyInstantiated() {
return getKey() == null && !isDirty();
}
}

View File

@ -39,6 +39,7 @@ import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.type.StandardBasicTypes;
import java.sql.CallableStatement;
import java.sql.ResultSet;
@ -202,7 +203,10 @@ public class MySQLDialect extends Dialect {
CommonFunctionFactory.weekQuarter( queryEngine );
CommonFunctionFactory.daynameMonthname( queryEngine );
CommonFunctionFactory.lastDay( queryEngine );
CommonFunctionFactory.dateTimeTimestamp( queryEngine );
CommonFunctionFactory.date( queryEngine );
CommonFunctionFactory.timestamp( queryEngine );
time( queryEngine );
CommonFunctionFactory.utcDateTimeTimestamp( queryEngine );
CommonFunctionFactory.rand( queryEngine );
CommonFunctionFactory.crc32( queryEngine );
@ -241,6 +245,13 @@ public class MySQLDialect extends Dialect {
}
}
private void time(QueryEngine queryEngine) {
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "time" )
.setExactArgumentCount( 1 )
.setInvariantType( StandardBasicTypes.STRING )
.register();
}
@Override
public int getFloatPrecision() {
//according to MySQL docs, this is

View File

@ -839,17 +839,29 @@ public class CommonFunctionFactory {
}
public static void dateTimeTimestamp(QueryEngine queryEngine) {
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "date" )
.setExactArgumentCount( 1 )
.setInvariantType( StandardBasicTypes.DATE )
date( queryEngine );
time( queryEngine );
timestamp( queryEngine );
}
public static void timestamp(QueryEngine queryEngine) {
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "timestamp" )
.setArgumentCountBetween( 1, 2 )
.setInvariantType( StandardBasicTypes.TIMESTAMP )
.register();
}
public static void time(QueryEngine queryEngine) {
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "time" )
.setExactArgumentCount( 1 )
.setInvariantType( StandardBasicTypes.TIME )
.register();
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "timestamp" )
.setArgumentCountBetween( 1, 2 )
.setInvariantType( StandardBasicTypes.TIMESTAMP )
}
public static void date(QueryEngine queryEngine) {
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "date" )
.setExactArgumentCount( 1 )
.setInvariantType( StandardBasicTypes.DATE )
.register();
}

View File

@ -545,8 +545,9 @@ public final class Cascade {
final boolean deleteOrphans = style.hasOrphanDelete()
&& action.deleteOrphans()
&& elemType.isEntityType()
&& child instanceof PersistentCollection
// a newly instantiated collection can't have orphans
&& child instanceof PersistentCollection;
&& ! ( (PersistentCollection) child ).isNewlyInstantiated();
if ( deleteOrphans ) {
final boolean traceEnabled = LOG.isTraceEnabled();

View File

@ -269,7 +269,7 @@ public abstract class AbstractSaveEventListener
boolean substitute = substituteValuesIfNecessary( entity, id, values, persister, source );
if ( persister.hasCollections() ) {
substitute = substitute || visitCollectionsBeforeSave( entity, id, values, types, source );
substitute = visitCollectionsBeforeSave( entity, id, values, types, source ) || substitute;
}
if ( substitute ) {

View File

@ -0,0 +1,665 @@
package org.hibernate.event.service.internal;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;
import org.hibernate.FlushMode;
import org.hibernate.Transaction;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* @author Artem K.
* @author Nathan Xu
*/
@TestForIssue( jiraKey = "HHH-14178" )
public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunctionalTestCase {
private UnversionedParent up;
private VersionedParent vp;
private Child c;
private VersionedMappingUnversionedParent vmup;
private VersionedMappingVersionedParent vmvp;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
VersionedParent.class,
UnversionedParent.class,
Child.class,
VersionedMappingUnversionedParent.class,
VersionedMappingVersionedParent.class
};
}
@Before
public void setup() {
up = new UnversionedParent();
vp = new VersionedParent();
c = new Child();
vmup = new VersionedMappingUnversionedParent();
vmup.addChild( c );
vmvp = new VersionedMappingVersionedParent();
vmvp.addChild( c );
}
@After
public void cleanup() {
inTransaction( s -> {
if (up.getId() != null) {
s.delete( up );
}
if (vp.getId() != null) {
s.delete( vp );
}
if (c.getId() != null) {
s.delete( c );
}
} );
}
@Test
public void VersionedMappingVersionedParentSaveUpdate() {
inSession( s -> {
s.setHibernateFlushMode( FlushMode.MANUAL );
Transaction trx = s.beginTransaction();
try {
// Associate the mapping to parent
vp.addChild( vmvp );
// Persist Child associated with versioned parent
s.saveOrUpdate( c );
Assert.assertNotEquals( Integer.valueOf(0), c.getId() );
// Persist VersionParent
s.saveOrUpdate( vp );
Assert.assertNotEquals( Integer.valueOf(0), vp.getId() );
// Persist versioned mapping now that parent id is generated
s.saveOrUpdate( vmvp );
Assert.assertNotNull( vmvp.getId() );
Assert.assertNotEquals( Integer.valueOf(0), vmvp.getId().getParentId() );
Assert.assertNotEquals( Integer.valueOf(0), vmvp.getId().getChildId() );
s.flush();
trx.commit();
} catch (RuntimeException e) {
// Transaction is rolled back so we do not want delete code in cleanup to execute.
// Reset any possible ID assignments
vp.setId( null );
c.setId( null );
if (trx.isActive()) {
trx.rollback();
}
throw e;
}
} );
}
@Test
public void VersionedMappingUnversionedParentSaveUpdate() {
inSession( s -> {
s.setHibernateFlushMode( FlushMode.MANUAL );
Transaction trx = s.beginTransaction();
try {
// Associate the mapping to parent
up.addVersionedMappings( vmup );
// Persist child associated with versioned mapping of unversioned parent
s.saveOrUpdate( c );
Assert.assertNotEquals( Integer.valueOf(0), c.getId() );
// Persist unversioned parent
s.saveOrUpdate( up );
Assert.assertNotEquals( Integer.valueOf(0), up.getId() );
// Persist versioned mapping
s.saveOrUpdate( vmup );
Assert.assertNotNull( vmup.getId() );
Assert.assertNotEquals( Integer.valueOf(0), vmup.getId().getParentId() );
Assert.assertNotEquals( Integer.valueOf(0), vmup.getId().getChildId() );
s.flush();
trx.commit();
} catch (RuntimeException e) {
// Transaction is rolled back so we do not want delete code in cleanup to execute.
// Reset any possible ID assignments
up.setId( null );
c.setId( null );
if (trx.isActive()) {
trx.rollback();
}
throw e;
}
} );
}
@Entity(name = "Child")
@Table(name = "Child")
@DynamicUpdate
public static class Child {
private Integer id;
private Long version;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id", nullable = false)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Version
@Column(name = "Version", nullable = false)
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
@Override
public int hashCode() {
return 31;
}
@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( !( obj instanceof Child ) ) {
return false;
}
Child other = (Child) obj;
return Objects.equals( getId(), other.getId() );
}
}
@Embeddable
public static class MappingId implements Serializable {
private static final long serialVersionUID = -4896032953810358940L;
private Integer parentId;
private Integer childId;
@Column(name="ParentId", nullable=false)
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
@Column(name="ChildId", nullable=false)
public Integer getChildId() {
return childId;
}
public void setChildId(Integer childId) {
this.childId = childId;
}
@Override
public int hashCode() {
return Objects.hash(getParentId(), getChildId());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof MappingId)) {
return false;
}
MappingId other = (MappingId) obj;
return Objects.equals(getParentId(), other.getParentId()) && Objects.equals(getChildId(), other.getChildId());
}
@Override
public String toString() {
return "[" + getParentId() + " | " + getChildId() + "]";
}
}
@Entity(name = "UnversionedParent")
@Table(name = "UnversionedParent")
@DynamicUpdate
public static class UnversionedParent {
private Integer id;
private Set<VersionedMappingUnversionedParent> versionedMappings;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="Id", nullable=false)
public Integer getId() {
return id;
}
public void setId(Integer id) {
if (!Objects.equals(id, getId())) {
this.id = id;
getVersionedMappings().forEach(c -> {
if (c.getId() == null) {
c.setId(new MappingId());
}
c.getId().setParentId(id);
});
}
}
@OneToMany(mappedBy="parent", cascade={ javax.persistence.CascadeType.DETACH, javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.REFRESH, javax.persistence.CascadeType.REMOVE }, orphanRemoval=true)
@Cascade({ org.hibernate.annotations.CascadeType.DELETE, org.hibernate.annotations.CascadeType.LOCK, org.hibernate.annotations.CascadeType.REPLICATE })
protected Set<VersionedMappingUnversionedParent> getVersionedMappings() {
if (versionedMappings == null) {
versionedMappings = new HashSet<>();
}
return this.versionedMappings;
}
protected void setVersionedMappings(Set<VersionedMappingUnversionedParent> value) {
if (value == null && this.versionedMappings != null) {
this.versionedMappings.clear();
} else {
this.versionedMappings = value;
}
}
@Transient
public Collection<VersionedMappingUnversionedParent> getVersionedMappingsCollection() {
return new ArrayList<>(getVersionedMappings());
}
public void addVersionedMappings(VersionedMappingUnversionedParent addValue) {
if (addValue != null && !this.getVersionedMappings().contains(addValue)) {
this.versionedMappings.add(addValue);
addValue.addParent(this);
}
}
public void removeVersionedMappings(VersionedMappingUnversionedParent removeValue) {
if (this.versionedMappings != null && this.versionedMappings.contains(removeValue)) {
this.versionedMappings.remove(removeValue);
removeValue.removeParent();
}
}
@Override
public int hashCode() {
return 17;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof UnversionedParent)) {
return false;
}
UnversionedParent other = (UnversionedParent) obj;
return Objects.equals(getId(), other.getId());
}
}
@Entity(name = "VersionedParent")
@Table(name = "VersionedParent")
@DynamicUpdate
public static class VersionedParent {
private Integer id;
private Long version;
private Set<VersionedMappingVersionedParent> children;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="Id", nullable=false)
public Integer getId() {
return id;
}
public void setId(Integer id) {
if (!Objects.equals(id, getId())) {
this.id = id;
getChildren().forEach(c -> {
if (c.getId() == null) {
c.setId(new MappingId());
}
c.getId().setParentId(id);
});
}
}
@Version
@Column(name="Version", nullable=false)
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
@OneToMany(mappedBy="parent", cascade={ javax.persistence.CascadeType.DETACH, javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.REFRESH, javax.persistence.CascadeType.REMOVE }, orphanRemoval=true)
@Cascade({ org.hibernate.annotations.CascadeType.DELETE, org.hibernate.annotations.CascadeType.LOCK, org.hibernate.annotations.CascadeType.REPLICATE })
protected Set<VersionedMappingVersionedParent> getChildren() {
if (children == null) {
children = new HashSet<>();
}
return this.children;
}
protected void setChildren(Set<VersionedMappingVersionedParent> value) {
if (value == null && this.children != null) {
this.children.clear();
} else {
this.children = value;
}
}
@Transient
public Collection<VersionedMappingVersionedParent> getChildrenCollection() {
return new ArrayList<>(getChildren());
}
public void addChild(VersionedMappingVersionedParent addValue) {
if (addValue != null && !this.getChildren().contains(addValue)) {
this.children.add(addValue);
addValue.addParent(this);
}
}
public void removeChild(VersionedMappingVersionedParent removeValue) {
if (this.children != null && this.children.contains(removeValue)) {
this.children.remove(removeValue);
removeValue.removeParent();
}
}
@Override
public int hashCode() {
return 31;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof VersionedParent)) {
return false;
}
VersionedParent other = (VersionedParent) obj;
return Objects.equals(getId(), other.getId());
}
}
@Entity(name = "VersionedMappingUnversionedParent")
@Table(name = "VersionedMappingUnversionedParent")
@DynamicUpdate
public static class VersionedMappingUnversionedParent {
private MappingId id;
private Child child;
private Long version;
@EmbeddedId
public MappingId getId() {
return this.id;
}
public void setId(MappingId id) {
this.id = id;
}
@Version
@Column(name="Version", nullable=false)
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
protected UnversionedParent parent;
@ManyToOne(optional=false, fetch=FetchType.LAZY)
@MapsId("parentId")
@JoinColumn(name="ParentId", nullable=false)
public UnversionedParent getParent() {
return this.parent;
}
protected void setParent(UnversionedParent value) {
this.parent = value;
}
public void addParent(UnversionedParent value) {
UnversionedParent oldParent = getParent();
if (!Objects.equals(value, oldParent)) {
if (oldParent != null) {
setParent(null);
oldParent.removeVersionedMappings(this);
}
if (value != null) {
setParent(value);
if (getId() == null) {
setId(new MappingId());
}
getId().setParentId(value.getId());
value.addVersionedMappings(this);
}
}
}
public void removeParent() {
addParent(null);
}
@ManyToOne(optional=false, fetch=FetchType.LAZY)
@MapsId("childId")
@JoinColumn(name="ChildId", nullable=false)
public Child getChild() {
return child;
}
protected void setChild(Child child) {
this.child = child;
}
public void addChild(Child value) {
Child oldChild = getChild();
if (!Objects.equals(value, oldChild)) {
if (oldChild != null) {
setChild(null);
}
if (value != null) {
setChild(value);
if (getId() == null) {
setId(new MappingId());
}
getId().setChildId(value.getId());
}
}
}
public void removeChild() {
addChild(null);
}
@Override
public int hashCode() {
return 17;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof VersionedMappingUnversionedParent)) {
return false;
}
VersionedMappingUnversionedParent other = (VersionedMappingUnversionedParent) obj;
return Objects.equals(getId(), other.getId());
}
}
@Entity(name = "VersionedMappingVersionedParent")
@Table(name = "VersionedMappingVersionedParent")
@DynamicUpdate
public static class VersionedMappingVersionedParent {
private MappingId id;
private Child child;
private Long version;
@EmbeddedId
public MappingId getId() {
return this.id;
}
public void setId(MappingId id) {
this.id = id;
}
@Version
@Column(name="Version", nullable=false)
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
protected VersionedParent parent;
@ManyToOne(optional=false, fetch=FetchType.LAZY)
@MapsId("parentId")
@JoinColumn(name="ParentId", nullable=false)
public VersionedParent getParent() {
return this.parent;
}
protected void setParent(VersionedParent value) {
this.parent = value;
}
public void addParent(VersionedParent value) {
VersionedParent oldParent = getParent();
if (!Objects.equals(value, oldParent)) {
if (oldParent != null) {
setParent(null);
oldParent.removeChild(this);
}
if (value != null) {
setParent(value);
if (getId() == null) {
setId(new MappingId());
}
getId().setParentId(value.getId());
value.addChild(this);
}
}
}
public void removeParent() {
addParent(null);
}
@ManyToOne(optional=false, fetch=FetchType.LAZY)
@MapsId("childId")
@JoinColumn(name="ChildId", nullable=false)
public Child getChild() {
return child;
}
protected void setChild(Child child) {
this.child = child;
}
public void addChild(Child value) {
Child oldChild = getChild();
if (!Objects.equals(value, oldChild)) {
if (oldChild != null) {
setChild(null);
}
if (value != null) {
setChild(value);
if (getId() == null) {
setId(new MappingId());
}
getId().setChildId(value.getId());
}
}
}
public void removeChild() {
addChild(null);
}
@Override
public int hashCode() {
return 17;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof VersionedMappingVersionedParent)) {
return false;
}
VersionedMappingVersionedParent other = (VersionedMappingVersionedParent) obj;
return Objects.equals(getId(), other.getId());
}
}
}

View File

@ -0,0 +1,31 @@
package org.hibernate.query.criteria.internal.hhh11877;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Foo {
private long id;
private boolean bar;
@Column(nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
public long getId() {
return this.id;
}
public void setId(final long id) {
this.id = id;
}
public boolean isBar() {
return this.bar;
}
public void setBar(final boolean bar) {
this.bar = bar;
}
}

View File

@ -0,0 +1,44 @@
package org.hibernate.query.criteria.internal.hhh11877;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Archie Cobbs
* @author Nathan Xu
*/
@TestForIssue( jiraKey = "HHH-11877" )
public class HHH111877Test extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Foo.class };
}
@Test
public void testNoExceptionThrow() {
doInJPA( this::entityManagerFactory, entityManager -> {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Foo> cq = cb.createQuery( Foo.class );
final Root<Foo> foo = cq.from( Foo.class );
cq.select( foo ).where( cb.and( cb.and(), foo.get( Foo_.bar ) ) );
final TypedQuery<Foo> tq = entityManager.createQuery( cq );
// without fixing, the statement below will throw exception:
// java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: . near line 1, column 106 [select generatedAlias0 from org.hibernate.bugs.Foo as generatedAlias0 where ( 1=1 ) and ( generatedAlias0.bar )]
tq.getResultList();
} );
}
}

View File

@ -0,0 +1,13 @@
package org.hibernate.query.criteria.internal.hhh13908;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity(name = "Foo")
@Table(name = "Foo")
public class Foo {
@Id
Long id;
String startTime;
}

View File

@ -0,0 +1,48 @@
package org.hibernate.query.criteria.internal.hhh13908;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Archie Cobbs
* @author Nathan Xu
*/
@RequiresDialect( MySQLDialect.class )
public class HHH13908Test extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Foo.class };
}
@Test
@TestForIssue( jiraKey = "HHH-13908" )
public void testTimeFunctionNotThrowException() {
doInJPA( this::entityManagerFactory, entityManager -> {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Foo> cq = cb.createQuery( Foo.class );
final Root<Foo> foo = cq.from( Foo.class );
cq.select( foo )
.where(
cb.lessThanOrEqualTo(
cb.function( "TIME", String.class, foo.get( Foo_.startTime ) ),
"17:00:00"
)
);
// without fixing, the following exception will be thrown:
// Parameter value [17:00:00] did not match expected type [java.util.Date (n/a)]
//java.lang.IllegalArgumentException: Parameter value [17:00:00] did not match expected type [java.util.Date (n/a)]
entityManager.createQuery( cq ).getResultList();
} );
}
}