I have multiple JMolecules managed aggregate roots that use associations. When persisting them with a Spring Data JPA repository the associations are converted properly.
However when persisting the same object and its associations using an entity manager instance, this fails.
The full scenario has also been posted to https://stackoverflow.com/questions/79862584/persisting-a-jmolecules-aggregate-association-does-not-work-when-using-entityman.
A test showing both scenarios is:
@DataJpaTest()
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestPropertySource(properties = """
spring.jpa.show-sql=true
""")
@Import([IntegrationTestConfiguration, CatalogIntegrationTestConfiguration])
class AssociationWithEntityManagerSpec extends AbstractRepositorySpecification {
@Autowired
ArticleRepository repository
@Autowired
MarketDataRepository marketDataRepository
@Autowired
RicRateRepository ricRateRepository
ArticleFactory entityFactory = new ArticleFactory()
RicRateFactory ricRateFactory = new RicRateFactory()
MarketDataFactory marketDataFactory = new MarketDataFactory()
def "find by id with repository"() {
given:
RicRate rate = ricRateFactory.newObject(id: new RicRateIdentifier('HKD'))
rate = ricRateRepository.save(rate)
MarketData marketData = marketDataFactory.newObject(rate: Association.forAggregate(rate))
marketData = marketDataRepository.save(marketData)
Article article = repository.save(entityFactory.newObject(marketData: Association.forAggregate(marketData)))
when:
Optional<AggregateRoot> result = repository.findById(article.id)
then:
result.isPresent()
}
def "find by id with entity manager"() {
given:
RicRate rate = ricRateFactory.newObject(id: new RicRateIdentifier('HKD'))
rate = entityManager.merge(rate)
MarketData marketData = marketDataFactory.newObject(rate: Association.forAggregate(rate))
marketData = entityManager.merge(marketData)
Article article = repository.save(entityFactory.newObject(marketData: Association.forAggregate(marketData)))
when:
Optional<AggregateRoot> result = repository.findById(article.id)
then:
result.isPresent()
}
}
The test using the entity manager is showing the following exception which looks like the database key value HKD for the rate association is used to construct a MarketDataIdentifer which is a UUID. It really should create a RicRateIdentifier
So I assume, without the repository some meta data is missing to read the association properly? Since the byte buddy plugin is enhancing the entity with attribute converters I would expect that it wouldn't matter whether to use the repository or the entity manager.
jakarta.persistence.PersistenceException: Error attempting to apply AttributeConverter
at org.hibernate.type.descriptor.converter.internal.AttributeConverterBean.toDomainValue(AttributeConverterBean.java:103)
at org.hibernate.type.descriptor.converter.internal.AttributeConverterMutabilityPlan.deepCopyNotNull(AttributeConverterMutabilityPlan.java:59)
at org.hibernate.type.descriptor.java.MutableMutabilityPlan.deepCopy(MutableMutabilityPlan.java:48)
at org.hibernate.type.descriptor.java.JavaType.getReplacement(JavaType.java:140)
at org.hibernate.type.internal.ConvertedBasicTypeImpl.replace(ConvertedBasicTypeImpl.java:337)
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:152)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:292)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:440)
at org.hibernate.event.internal.DefaultMergeEventListener.merge(DefaultMergeEventListener.java:192)
at org.hibernate.event.internal.DefaultMergeEventListener.doMerge(DefaultMergeEventListener.java:136)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:120)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:77)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:138)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:797)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:782)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:317)
at at.dig.wertekasse.catalog.internal.AssociationWithEntityManagerSpec.find by id with entity manager(AssociationWithEntityManagerSpec.groovy:67)
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.UUID] for value [HKD]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:181)
at org.jmolecules.spring.PrimitivesToIdentifierConverter.prepareSource(PrimitivesToIdentifierConverter.java:188)
at org.jmolecules.spring.PrimitivesToIdentifierConverter.lambda$detectConstructor$14(PrimitivesToIdentifierConverter.java:174)
at org.jmolecules.spring.PrimitivesToIdentifierConverter.convert(PrimitivesToIdentifierConverter.java:128)
at org.jmolecules.spring.PrimitivesToAssociationConverter.resolveIdentifier(PrimitivesToAssociationConverter.java:123)
at org.jmolecules.spring.PrimitivesToAssociationConverter.convert(PrimitivesToAssociationConverter.java:101)
at org.jmolecules.spring.jpa.JakartaPersistenceAssociationAttributeConverter.convertToEntityAttribute(JakartaPersistenceAssociationAttributeConverter.java:60)
at org.jmolecules.spring.jpa.JakartaPersistenceAssociationAttributeConverter.convertToEntityAttribute(JakartaPersistenceAssociationAttributeConverter.java:30)
at org.hibernate.type.descriptor.converter.internal.AttributeConverterBean.toDomainValue(AttributeConverterBean.java:97)
... 16 more
Caused by: java.lang.IllegalArgumentException: Invalid UUID string: HKD
at java.base/java.util.UUID.fromString1(UUID.java:282)
at java.base/java.util.UUID.fromString(UUID.java:260)
at org.springframework.core.convert.support.StringToUUIDConverter.convert(StringToUUIDConverter.java:37)
at org.springframework.core.convert.support.StringToUUIDConverter.convert(StringToUUIDConverter.java:33)
at org.springframework.core.convert.support.GenericConversionService$ConverterAdapter.convert(GenericConversionService.java:356)
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
... 25 more
@NoArgsConstructor
@Getter
public class MarketData implements AggregateRoot<MarketData, MarketDataIdentifier> {
private MarketDataIdentifier id;
@Column(name = "rate_id")
private Association<RicRate, RicRate.RicRateIdentifier> rate;
public record MarketDataIdentifier(UUID id) implements Identifier {
public MarketDataIdentifier() {
this(UUID.randomUUID());
}
}
}
@Getter
@NoArgsConstructor
public class Article implements AggregateRoot<Article, ArticleIdentifier> {
private ArticleIdentifier id;
@Column(name = "market_data_id")
private Association<MarketData, MarketDataIdentifier> marketData;
public record ArticleIdentifier(UUID id) implements Identifier {
public ArticleIdentifier() {
this(UUID.randomUUID());
}
}
}
I have multiple JMolecules managed aggregate roots that use associations. When persisting them with a Spring Data JPA repository the associations are converted properly.
However when persisting the same object and its associations using an entity manager instance, this fails.
The full scenario has also been posted to https://stackoverflow.com/questions/79862584/persisting-a-jmolecules-aggregate-association-does-not-work-when-using-entityman.
A test showing both scenarios is:
The test using the entity manager is showing the following exception which looks like the database key value HKD for the rate association is used to construct a MarketDataIdentifer which is a UUID. It really should create a RicRateIdentifier
So I assume, without the repository some meta data is missing to read the association properly? Since the byte buddy plugin is enhancing the entity with attribute converters I would expect that it wouldn't matter whether to use the repository or the entity manager.