Skip to content

Invalid class type on nested object - MappingCouchbaseConverter - couchbase transactions #1276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
martin8877 opened this issue Dec 3, 2021 · 5 comments
Labels
status: duplicate A duplicate of another issue type: bug A general bug

Comments

@martin8877
Copy link

Transaction conversion on read, according to the documentation, should work with this code :

TransactionGetResult getResult = ctx.get(couchbaseClientFactory.getDefaultCollection(), "doc-id");

CouchbaseDocument source = new CouchbaseDocument(getResult.id());
source.setContent(getResult.contentAsObject());
Menu menu = mappingCouchbaseConverter.read(Menu.class, source);

When entity contains a list of nested objects then nested field has incorrect type (ArrayList<HashMap>) and app crashes with 'java.lang.ClassCastException: class java.util.HashMap cannot be cast to class com.example.MenuCategory'.

MenuCategory category = menu.getCategories().get(0); // ClassCastException

Document in DB:

{
  "_class": "com.example.Menu",
  "categories": [
    {
      "id": "category_1",
      "products": [
        {
          "id": "product_1"
        },
        {
          "id": "product_2"
        }
      ]
    }
  ],
  "name": "Menu 1"
}

com.example.Menu:

@Document
public class Menu {

    public static final String PREFIX = "menu";

    @IdPrefix
    private final String prefix = PREFIX;

    @Id
    @GeneratedValue(strategy = GenerationStrategy.UNIQUE, delimiter = ID_DELIMITER)
    private String id;

    @Field
    private String name;

    @Field
    private List<MenuCategory> categories;

    // getters / setters
}

com.example.MenuCategory:

public class MenuCategory {

    private String id;

    private List<CategoryProduct> products;

    // getters / setters

    public static class CategoryProduct {

        private String id;

        public CategoryProduct() {
        }

        public CategoryProduct(String id) {
            this.id = id;
        }

        // getters / setters
    }
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 3, 2021
@mikereiche
Copy link
Collaborator

Looking...

@mikereiche
Copy link
Collaborator

I'm not able to reproduce this. Use the latest version - 4.3.0. If you still have issues, post with the complete stack trace.

{ id: doc-id_copy, name:Menu 1, categories:[{id=category_1, products=[{id=product_1}, {id=product_2}]}]}

	@Test
	void menuTest(){
		TransactionConfig txConfig =  TransactionConfigBuilder.create().logDirectly(Event.Severity.INFO).logOnFailure(true, Event.Severity.ERROR)
				.expirationTime(Duration.ofSeconds(10)).durabilityLevel(TransactionDurabilityLevel.NONE).build();;
		Transactions transactions = Transactions.create(couchbaseTemplate.getCouchbaseClientFactory()
				.getCluster(), txConfig);
		transactions.run(ctx -> {
			TransactionGetResult getResult = ctx.get(couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection(), "doc-id_copy");

			CouchbaseDocument source = new CouchbaseDocument(getResult.id());
			source.setContent(getResult.contentAsObject());
			Menu m = couchbaseTemplate.getConverter().read(Menu.class, source);
			System.err.println(m);
		});
	}

@Document
public class Menu {

	public static final String PREFIX = "menu";
	private static final String ID_DELIMITER = "#";

	@IdPrefix private final String prefix = PREFIX;

	@Id @GeneratedValue(strategy = GenerationStrategy.UNIQUE, delimiter = ID_DELIMITER) private String id;

	@Field private String name;

	@Field private List<MenuCategory> categories;

	public void setId(String id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setCategories(List<MenuCategory> categories) {
		this.categories = categories;
	}
	
	public String toString(){
		return "{ id: "+ id+", name:"+name+", categories:"+categories+"}";
	}
}
public class MenuCategory {

  private String id;

  private List<CategoryProduct> products;

  public void setId(String id){
    this.id = id;
  }

  public String toString(){
    return ("{ id:"+id+", products:"+products)+"}";
  }

  public static class CategoryProduct {

    private String id;

    public CategoryProduct() {
    }

    public String toString(){
      return "{ id:"+id+"}";
    }
  }
}
{
  "_class": "com.example.Menu",
  "categories": [
    {
      "id": "category_1",
      "products": [
        {
          "id": "product_1"
        },
        {
          "id": "product_2"
        }
      ]
    }
  ],
  "name": "Menu 1"
}

@mikereiche mikereiche added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Dec 6, 2021
@martin8877
Copy link
Author

I still have issues. Your code is working because there is no casting in toString().

Please try add this line to the end of test:
MenuCategory category = m.getCategories().get(0);

Repo with example code.

Stack trace:

com.couchbase.transactions.error.TransactionFailed: Transaction has failed with cause 'java.lang.ClassCastException: class java.util.HashMap cannot be cast to class com.example.MenuCategory (java.util.HashMap is in module java.base of loader 'bootstrap'; com.example.MenuCategory is in unnamed module of loader 'app')'
	at com.couchbase.transactions.TransactionsReactive.executeHandleErrorsPostRetry(TransactionsReactive.java:320) ~[couchbase-transactions-1.2.2.jar:1.2.2]
	at com.couchbase.transactions.TransactionsReactive.lambda$executeTransaction$4(TransactionsReactive.java:150) ~[couchbase-transactions-1.2.2.jar:1.2.2]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.SerializedSubscriber.onError(SerializedSubscriber.java:124) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.whenError(FluxRetryWhen.java:225) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxRetryWhen$RetryWhenOtherSubscriber.onError(FluxRetryWhen.java:274) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:414) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:251) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxIndex$IndexSubscriber.onNext(FluxIndex.java:101) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:120) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.EmitterProcessor.drain(EmitterProcessor.java:491) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.EmitterProcessor.tryEmitNext(EmitterProcessor.java:299) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.SinkManySerialized.tryEmitNext(SinkManySerialized.java:100) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onError(FluxRetryWhen.java:190) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:106) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onError(MonoIgnoreThen.java:270) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onError(FluxDoFinally.java:136) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onError(MonoIgnoreThen.java:270) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.Operators.error(Operators.java:198) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoError.subscribe(MonoError.java:53) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:236) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2058) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.Operators.complete(Operators.java:137) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:46) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:255) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:255) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onError(FluxPeekFuseable.java:234) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:172) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onError(MonoIgnoreThen.java:270) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onError(MonoIgnoreThen.java:270) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:106) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.Operators.error(Operators.java:198) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoError.subscribe(MonoError.java:53) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:230) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.4.12.jar:3.4.12]
	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:na]
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:99) ~[reactor-core-3.4.12.jar:3.4.12]
		at reactor.core.publisher.Mono.block(Mono.java:1707) ~[reactor-core-3.4.12.jar:3.4.12]
		at com.couchbase.transactions.Transactions.run(Transactions.java:221) ~[couchbase-transactions-1.2.2.jar:1.2.2]
		at com.example.Test.run(Test.java:33) ~[main/:na]
		at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
		at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
		at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
		at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
		at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:344) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:229) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:166) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:938) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.13.jar:5.3.13]
		at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-2.6.1.jar:2.6.1]
		at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412) ~[spring-boot-2.6.1.jar:2.6.1]
		at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) ~[spring-boot-2.6.1.jar:2.6.1]
		at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.1.jar:2.6.1]
		at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[spring-boot-2.6.1.jar:2.6.1]
		at com.example.BugApplication.main(BugApplication.java:10) ~[main/:na]
Caused by: java.lang.ClassCastException: class java.util.HashMap cannot be cast to class com.example.MenuCategory (java.util.HashMap is in module java.base of loader 'bootstrap'; com.example.MenuCategory is in unnamed module of loader 'app')
	at com.example.Test.lambda$run$0(Test.java:41) ~[main/:na]
	at com.couchbase.transactions.TransactionsReactive.lambda$runBlocking$36(TransactionsReactive.java:597) ~[couchbase-transactions-1.2.2.jar:1.2.2]
	at reactor.core.publisher.MonoRunnable.call(MonoRunnable.java:73) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.MonoRunnable.call(MonoRunnable.java:32) ~[reactor-core-3.4.12.jar:3.4.12]
	at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:227) ~[reactor-core-3.4.12.jar:3.4.12]
	... 8 common frames omitted

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 7, 2021
@mikereiche
Copy link
Collaborator

mikereiche commented Dec 7, 2021

I'll keep this open for further investigation as to why the documentation doesn't match - but the following will work.

Menu m = couchbaseTemplate.support().decodeEntity(getResult.id(), new String(getResult.contentAsBytes()), getResult.cas(), Menu.class);
MenuCategory mc = m.getCategories().get(0);
System.err.println(m);

@mikereiche mikereiche added type: bug A general bug and removed status: feedback-provided Feedback has been provided labels Dec 7, 2021
@mikereiche mikereiche added the status: duplicate A duplicate of another issue label Feb 3, 2022
@mikereiche
Copy link
Collaborator

I realize this was first, but please refer to #1312 as it has more information on it. I'll make this the 'duplicate'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants