blaze-persistence icon indicating copy to clipboard operation
blaze-persistence copied to clipboard

Unable to save ManyToOne mapping on underlying entity

Open dsarlo-viso opened this issue 3 years ago • 2 comments

Description

We are trying to map an id to a ManyToOne mapping and have it save properly

Expected behavior

The orgId should be persisted to the db

Actual behavior

Everything seems fine, but orgId isn't persisted to the UserProfile table. OrgId is null even though the value is set when UserProfileView is saved.

Steps to reproduce

Reproduced in Project: https://github.com/dsarlo-viso/blaze-web-implementation-issues

Just have to hit the following endpoints: Create an org POST http://localhost:8080/api/web-mvc/org With no body!

Use the orgId from the response in the request below!

Create a user POST http://localhost:8080/api/web-mvc With the body:

{
    "firstName": "Blaze",
    "lastName": "Entity",
    "orgId": "ORG_ID_HERE"
}

Go to localhost:8080/h2-console to see what is in the db - password is password

orgId on the UserProfile table will be null instead of the id we passed in.

Environment

Version: 1.6.5 JPA-Provider: Hibernate 5.4.32.Final (Spring Data JPA 2.4.13) DBMS: PostgreSQL 13.3 Application Server: Spring Boot 2.4.13

dsarlo-viso avatar Jan 19 '22 20:01 dsarlo-viso

Thanks for the reproducer, I'll look into this!

beikov avatar Jan 20 '22 07:01 beikov

@beikov Just wanted to bump this on your radar (I know we are still waiting on other things first). We are still looking forward to having this fixed. Interest is still alive and well on our side! -- lol

dsarlo-viso avatar Mar 07 '22 22:03 dsarlo-viso

The problem is that you use @UpdatableMapping(updatable = false) on the getOrgId() method. This essentially tells Blaze-Persistence that this entity view attribute should not be mapped to the entity on flush. If you remove the annotation you will see that this is not supported yet as explained by the error:

There is an error for the attribute orgId[com.blaze.spring.example.service.views.UserProfileView.getOrgId]: Invalid dereferencing of collection property 'id' in updatable expression!

There is a typo in the message though. It should obviously say Invalid dereferencing of property 'org' in updatable expression!

Also see https://persistence.blazebit.com/documentation/1.6/entity-view/manual/en_US/#attribute-mappings for the rules here. A common "workaround" for this is to introduce an id-view:

@EntityView(Org.class)
public interface OrgIdView {
    @IdMapping
    UUID getId();
}

and use it like this:

	@Nullable
	default UUID getOrgId() {
		return getOrg() == null ? null : getOrg().getId();
	}
	default void setOrgId(UUID orgId) {
		if (orgId == null) {
			setOrg(null);
		} else {
			setOrg(evm().getReference(OrgIdView.class, orgId)):
		}
	}

	// Special method that gives access to the EVM. See https://persistence.blazebit.com/documentation/1.6/entity-view/manual/en_US/#special-method-attributes
	EntityViewManager evm();

	@Nullable
	OrgIdView getOrg();
	void setOrg(OrgIdView orgId);

Supporting such non-canonical mappings is very hard and I think it's not worth the trouble. Here are some of the questions we'd have to answer for supporting this:

  • When flushing a value to a sub-path e.g. org.id, should a new entity/embeddable be created for org or should the existing one be updated by e.g. calling setId() or should the association be replaced?
  • What should happen if multiple mappings for the same path exist?
  • What should happen if multiple mappings for the same base path e.g. org.id and org.name exist?

As you can see, it's not so simple and involves quite a few changes to internal code. I strongly believe that updatable mappings should be made for the canonical representation and that default methods can serve as helpers for modelling some flat scenarios like yours. I hope you understand that updatable entity views will not receive this feature any time soon.

beikov avatar Oct 22 '22 10:10 beikov

@beikov Just seeing this now somehow! Thank you so much. Going to give this a shot tomorrow. Once again, you're the best!

dsarlo avatar Jan 27 '23 03:01 dsarlo

@beikov Still having a problem here. I'll try to make a reproducer for ya for this one, but essentially I am trying to save this view that we are talking about above by cascading from a parent that also has a parent. So this view is 3 layers deep. The problem I am seeing is when blaze is attempting to flush, it thinks that OrgIdView getOrg(); is in the second layer, not the third. I end up with the error message

Entity view setup = Profile -> OrgConfig -> Type

Type is where the org field actually exists, not on OrgConfig.

org.hibernate.PropertyNotFoundException: Unable to locate property named org on dan.test.OrgConfig
	at org.hibernate.type.ComponentType.getPropertyIndex(ComponentType.java:751)
	at com.blazebit.persistence.integration.hibernate.base.HibernateJpaProvider.getJoinMappingPropertyNames(HibernateJpaProvider.java:1638)
	at com.blazebit.persistence.impl.CachingJpaProvider.getJoinMappingPropertyNames(CachingJpaProvider.java:422)
	at com.blazebit.persistence.view.impl.update.flush.TypeDescriptor.getAttributeElementIdentifier(TypeDescriptor.java:450)
	at com.blazebit.persistence.view.impl.update.flush.TypeDescriptor.forType(TypeDescriptor.java:177)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.createSingularAttributeFlusher(EntityViewUpdaterImpl.java:1158)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.createAttributeFlusher(EntityViewUpdaterImpl.java:763)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.<init>(EntityViewUpdaterImpl.java:295)
	at com.blazebit.persistence.view.impl.EntityViewManagerImpl.getUpdater(EntityViewManagerImpl.java:1412)
	at com.blazebit.persistence.view.impl.entity.AbstractViewToEntityMapper.<init>(AbstractViewToEntityMapper.java:70)
	at com.blazebit.persistence.view.impl.entity.UpdaterBasedViewToEntityMapper.<init>(UpdaterBasedViewToEntityMapper.java:42)
	at com.blazebit.persistence.view.impl.update.flush.TypeDescriptor.createViewToEntityMapper(TypeDescriptor.java:379)
	at com.blazebit.persistence.view.impl.update.flush.TypeDescriptor.forType(TypeDescriptor.java:179)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.createSingularAttributeFlusher(EntityViewUpdaterImpl.java:1158)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.createAttributeFlusher(EntityViewUpdaterImpl.java:763)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.<init>(EntityViewUpdaterImpl.java:295)
	at com.blazebit.persistence.view.impl.EntityViewManagerImpl.getUpdater(EntityViewManagerImpl.java:1412)
	at com.blazebit.persistence.view.impl.entity.AbstractViewToEntityMapper.<init>(AbstractViewToEntityMapper.java:70)
	at com.blazebit.persistence.view.impl.entity.EmbeddableUpdaterBasedViewToEntityMapper.<init>(EmbeddableUpdaterBasedViewToEntityMapper.java:45)
	at com.blazebit.persistence.view.impl.update.flush.TypeDescriptor.createViewToEntityMapper(TypeDescriptor.java:375)
	at com.blazebit.persistence.view.impl.update.flush.TypeDescriptor.forType(TypeDescriptor.java:179)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.createPluralAttributeFlusher(EntityViewUpdaterImpl.java:813)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.createAttributeFlusher(EntityViewUpdaterImpl.java:761)
	at com.blazebit.persistence.view.impl.update.EntityViewUpdaterImpl.<init>(EntityViewUpdaterImpl.java:295)
	at com.blazebit.persistence.view.impl.EntityViewManagerImpl.getUpdater(EntityViewManagerImpl.java:1424)
	at com.blazebit.persistence.view.impl.EntityViewManagerImpl.update(EntityViewManagerImpl.java:1188)
	at com.blazebit.persistence.view.impl.EntityViewManagerImpl.update(EntityViewManagerImpl.java:1153)
	at com.blazebit.persistence.view.impl.EntityViewManagerImpl.save(EntityViewManagerImpl.java:1070)

dsarlo-viso avatar Feb 03 '23 15:02 dsarlo-viso

Looks like a possible bug, yeah. A reproducer would be awesome!

beikov avatar Feb 03 '23 16:02 beikov

@beikov Ah okay it is the same issue as #1503. Will work on a reproducer as soon as I can!

dsarlo-viso avatar Feb 03 '23 17:02 dsarlo-viso