Skip to content

Commit

Permalink
--wip-- small tweaks, added joined inheritance test
Browse files Browse the repository at this point in the history
Still needs:
 - interface redesign
 - test and make sure it's working across all dbs / dialects
  • Loading branch information
mbladel committed Nov 20, 2023
1 parent e42f2ca commit b816138
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ public static GeneratedValues getGeneratedValues(

final GeneratedValuesImpl generatedValues = new GeneratedValuesImpl( generatedModelParts );
for ( ModelPart modelPart : generatedModelParts ) {
assert modelPart instanceof SelectableMapping : "Unsupported non-selectable generated value";

// todo marco : would be nice to avoid the cast here, but if we want to keep
// the options open (for Components and/or other attribute types) we're going to need it
final SelectableMapping selectable = getActualSelectableMapping( modelPart, persister );
final JdbcMapping jdbcMapping = selectable.getJdbcMapping();
final Object value = jdbcMapping.getJdbcValueExtractor().extract( resultSet, columnIndex(
Expand Down Expand Up @@ -131,6 +127,9 @@ private static int columnIndex(
}

public static SelectableMapping getActualSelectableMapping(ModelPart modelPart, EntityPersister persister) {
// todo marco : would be nice to avoid the cast here, but if we want to keep
// the options open (for Components and/or other attribute types) we're going to need it
assert modelPart instanceof SelectableMapping : "Unsupported non-selectable generated value";
final ModelPart actualModelPart = modelPart.isEntityIdentifierMapping() ?
persister.getRootEntityDescriptor().getIdentifierMapping() :
modelPart;
Expand Down Expand Up @@ -192,7 +191,6 @@ public static List<String> getGeneratedColumnNames(
boolean unquote) {
final List<? extends ModelPart> generated = persister.getGeneratedProperties( timing );
return generated.stream().map( modelPart -> {
assert modelPart instanceof SelectableMapping : "Unsupported non-selectable generated value";
final SelectableMapping selectableMapping = getActualSelectableMapping( modelPart, persister );
final String selectionExpression = selectableMapping.getSelectionExpression();
return unquote ? StringHelper.unquote( selectionExpression, dialect ) : selectionExpression;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* @deprecated This is not used anymore in any of the
* {@link org.hibernate.generator.values.GeneratedValuesMutationDelegate} implementations.
*/
@Deprecated( since = "7.0" ) // todo marco : correct ?
@Deprecated( since = "7.0" )
public class InsertSelectIdentityInsert extends IdentifierGeneratingInsert {
protected String identityColumnName;

Expand All @@ -45,7 +45,6 @@ public Insert addGeneratedColumns(String[] columnNames, OnExecutionGenerator gen
return super.addGeneratedColumns( columnNames, generator );
}

// todo marco : deprecate appendIdentitySelectToInsert() too ?
public String toStatementString() {
return getDialect().getIdentityColumnSupport()
.appendIdentitySelectToInsert( identityColumnName, super.toStatementString() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,8 @@ default Object loadByUniqueKey(String propertyName, Object uniqueKey, SharedSess
/**
* Persist an instance
*/
// todo marco : can I change this ?
// todo marco : how can I change this ? If we change only return value we
// can't create new method and deprecate the old one, so just modify this?
Object insert(Object id, Object[] fields, Object object, SharedSessionContractImplementor session);

/**
Expand Down Expand Up @@ -930,7 +931,7 @@ default List<? extends ModelPart> getGeneratedProperties(EventType timing) {
return timing == EventType.INSERT ? getInsertGeneratedProperties() : getUpdateGeneratedProperties();
}

// todo marco : move this to PostInsertIdentityPersister and rename the interface ?
// todo marco : move this to PostInsertIdentityPersister and rename / move the interface ?
default List<? extends ModelPart> getInsertGeneratedProperties() {
return Collections.emptyList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
* 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.orm.test.mapping.generated;
package org.hibernate.orm.test.mapping.generated.delegate;

import java.util.Calendar;
import java.util.Date;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Generated;
Expand Down Expand Up @@ -39,16 +39,16 @@
import static org.assertj.core.api.Assertions.assertThat;

@DomainModel( annotatedClasses = {
GeneratedValueMutationDelegateIdentityTest.IdentityOnly.class,
GeneratedValueMutationDelegateIdentityTest.IdentityAndValues.class,
GeneratedValueMutationDelegateIdentityTest.IdentityAndValuesAndRowId.class,
GeneratedValueMutationDelegateIdentityTest.IdentityAndValuesAndRowIdAndNaturalId.class,
MutationDelegateIdentityTest.IdentityOnly.class,
MutationDelegateIdentityTest.IdentityAndValues.class,
MutationDelegateIdentityTest.IdentityAndValuesAndRowId.class,
MutationDelegateIdentityTest.IdentityAndValuesAndRowIdAndNaturalId.class,
} )
@SessionFactory( useCollectingStatementInspector = true )
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsIdentityColumns.class )
public class GeneratedValueMutationDelegateIdentityTest {
public class MutationDelegateIdentityTest {
@Test
public void testInsertGenerationIdentityOnly(SessionFactoryScope scope) {
public void testInsertGeneratedIdentityOnly(SessionFactoryScope scope) {
final GeneratedValuesMutationDelegate delegate = getDelegate( scope, IdentityOnly.class, MutationType.INSERT );
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
inspector.clear();
Expand All @@ -67,7 +67,7 @@ public void testInsertGenerationIdentityOnly(SessionFactoryScope scope) {
}

@Test
public void testInsertGenerationValuesAndIdentity(SessionFactoryScope scope) {
public void testInsertGeneratedValuesAndIdentity(SessionFactoryScope scope) {
final GeneratedValuesMutationDelegate delegate = getDelegate(
scope,
IdentityAndValues.class,
Expand All @@ -92,7 +92,7 @@ public void testInsertGenerationValuesAndIdentity(SessionFactoryScope scope) {
}

@Test
public void testUpdateGenerationAndIdentity(SessionFactoryScope scope) {
public void testUpdateGeneratedValuesAndIdentity(SessionFactoryScope scope) {
final GeneratedValuesMutationDelegate delegate = getDelegate(
scope,
IdentityAndValues.class,
Expand Down Expand Up @@ -124,7 +124,7 @@ public void testUpdateGenerationAndIdentity(SessionFactoryScope scope) {
}

@Test
public void testInsertGenerationValuesAndIdentityAndRowId(SessionFactoryScope scope) {
public void testInsertGeneratedValuesAndIdentityAndRowId(SessionFactoryScope scope) {
final GeneratedValuesMutationDelegate delegate = getDelegate(
scope,
IdentityAndValuesAndRowId.class,
Expand Down Expand Up @@ -172,7 +172,7 @@ public void testInsertGenerationValuesAndIdentityAndRowId(SessionFactoryScope sc
}

@Test
public void testInsertGenerationValuesAndIdentityAndRowIdAndNaturalId(SessionFactoryScope scope) {
public void testInsertGeneratedValuesAndIdentityAndRowIdAndNaturalId(SessionFactoryScope scope) {
final GeneratedValuesMutationDelegate delegate = getDelegate(
scope,
IdentityAndValuesAndRowIdAndNaturalId.class,
Expand Down Expand Up @@ -247,7 +247,7 @@ public static class IdentityAndValues {
private String name;

@UpdateTimestamp( source = SourceType.DB )
private Calendar updateDate;
private Date updateDate;

private String data;

Expand All @@ -259,7 +259,7 @@ public String getName() {
return name;
}

public Calendar getUpdateDate() {
public Date getUpdateDate() {
return updateDate;
}

Expand All @@ -282,7 +282,7 @@ public static class IdentityAndValuesAndRowId {
private String name;

@UpdateTimestamp( source = SourceType.DB )
private Calendar updateDate;
private Date updateDate;

private String data;

Expand All @@ -294,7 +294,7 @@ public String getName() {
return name;
}

public Calendar getUpdateDate() {
public Date getUpdateDate() {
return updateDate;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*
* 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.orm.test.mapping.generated.delegate;

import java.util.Date;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.SourceType;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.generator.EventType;
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
import org.hibernate.sql.model.MutationType;

import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Marco Belladelli
*/
@DomainModel( annotatedClasses = {
MutationDelegateJoinedInheritanceTest.BaseEntity.class,
MutationDelegateJoinedInheritanceTest.ChildEntity.class,
} )
@SessionFactory( useCollectingStatementInspector = true )
public class MutationDelegateJoinedInheritanceTest {
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from BaseEntity" ).executeUpdate() );
}

@Test
public void testInsertBaseEntity(SessionFactoryScope scope) {
final GeneratedValuesMutationDelegate delegate = getDelegate(
scope,
BaseEntity.class,
MutationType.INSERT
);
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
inspector.clear();

scope.inTransaction( session -> {
final BaseEntity entity = new BaseEntity();
session.persist( entity );
session.flush();

assertThat( entity.getId() ).isNotNull();
assertThat( entity.getName() ).isEqualTo( "default_name" );

inspector.assertIsInsert( 0 );
inspector.assertExecutedCount(
delegate != null && delegate.supportsArbitraryValues() ? 1 : 2
);
} );
}

@Test
public void testInsertChildEntity(SessionFactoryScope scope) {
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
inspector.clear();

scope.inTransaction( session -> {
final ChildEntity entity = new ChildEntity();
session.persist( entity );
session.flush();

assertThat( entity.getId() ).isNotNull();
assertThat( entity.getName() ).isEqualTo( "default_name" );
assertThat( entity.getChildName() ).isEqualTo( "default_child_name" );

inspector.assertIsInsert( 0 );
inspector.assertIsInsert( 1 );
// Note: this is a current restriction, mutation delegates only retrieve generated values
// on the "root" table, and we expect other values to be read through a subsequent select
inspector.assertIsSelect( 2 );
inspector.assertExecutedCount( 3 );
} );
}

@Test
public void testUpdateBaseEntity(SessionFactoryScope scope) {
final GeneratedValuesMutationDelegate delegate = getDelegate(
scope,
BaseEntity.class,
MutationType.UPDATE
);
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
final Integer id = scope.fromTransaction( session -> {
final BaseEntity entity = new BaseEntity();
session.persist( entity );
session.flush();
return entity.getId();
} );

inspector.clear();

scope.inTransaction( session -> {
final BaseEntity entity = session.find( BaseEntity.class, id );
entity.setData( "changed" );
session.flush();

assertThat( entity.getUpdateDate() ).isNotNull();

inspector.assertIsSelect( 0 );
inspector.assertIsUpdate( 1 );
inspector.assertExecutedCount(
delegate != null && delegate.supportsArbitraryValues() ? 2 : 3
);
} );
}

@Test
public void testUpdateChildEntity(SessionFactoryScope scope) {
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
final Integer id = scope.fromTransaction( session -> {
final ChildEntity entity = new ChildEntity();
session.persist( entity );
session.flush();
return entity.getId();
} );

inspector.clear();

scope.inTransaction( session -> {
final ChildEntity entity = session.find( ChildEntity.class, id );
entity.setData( "changed" );
session.flush();

assertThat( entity.getUpdateDate() ).isNotNull();
assertThat( entity.getChildUpdateDate() ).isNotNull();

inspector.assertIsSelect( 0 );
inspector.assertIsUpdate( 1 );
// Note: this is a current restriction, mutation delegates only retrieve generated values
// on the "root" table, and we expect other values to be read through a subsequent select
inspector.assertIsSelect( 2 );
inspector.assertExecutedCount( 3 );
} );
}

private static GeneratedValuesMutationDelegate getDelegate(
SessionFactoryScope scope,
Class<?> entityClass,
MutationType mutationType) {
final EntityMutationTarget entityDescriptor = (EntityMutationTarget) scope.getSessionFactory()
.getMappingMetamodel().findEntityDescriptor( entityClass );
return entityDescriptor.getMutationDelegate( mutationType );
}

@Entity( name = "BaseEntity" )
@Inheritance( strategy = InheritanceType.JOINED )
@SuppressWarnings( "unused" )
public static class BaseEntity {
@Id
@GeneratedValue( strategy = GenerationType.IDENTITY )
private Integer id;

@Generated( event = EventType.INSERT )
@ColumnDefault( "'default_name'" )
private String name;

@UpdateTimestamp( source = SourceType.DB )
private Date updateDate;

@SuppressWarnings( "FieldCanBeLocal" )
private String data;

public Integer getId() {
return id;
}

public String getName() {
return name;
}

public Date getUpdateDate() {
return updateDate;
}

public void setData(String data) {
this.data = data;
}
}

@Entity( name = "ChildEntity" )
@SuppressWarnings( "unused" )
public static class ChildEntity extends BaseEntity {
@Generated( event = EventType.INSERT )
@ColumnDefault( "'default_child_name'" )
private String childName;

@UpdateTimestamp( source = SourceType.DB )
private Date childUpdateDate;

public String getChildName() {
return childName;
}

public Date getChildUpdateDate() {
return childUpdateDate;
}
}
}
Loading

0 comments on commit b816138

Please sign in to comment.