I got an exception when I try to basically read a @ManyToMany relation using ebean cache system (with io.ebean:ebean-redis). This is a SpringBoot application.
The versions I am using:
- springBootVersion=2.4.3
- mariaDbVersion=2.7.2
- ebeanVersion=12.7.2
- flywayVersion=7.7.0
The BaseModel Java class:
@MappedSuperclass
public abstract class BaseModel extends Model {
@Id
@GeneratedValue
private Long id;
@WhenCreated
@NotNull
private Instant createdAt;
@WhenModified
@NotNull
private Instant updatedAt;
@NotNull
@SoftDelete
private Boolean deleted = false;
protected BaseModel() {
}
// here the getters and setters
}
The Article Java class:
@Entity
@Table(name = "article")
@Cache
public class DArticle extends BaseModel {
private static final long serialVersionUID = -7120023327129825322L;
@NotNull
@Index
@Length(20)
private String code;
@DbJson
@NotNull
private Map<Locale, String> name;
@NotNull
private Double unitPrice;
@ManyToMany(mappedBy = "articles")
private List<DArticleCategory> articleCategories;
public DArticle(String code, Map<Locale, String> name, Double unitPrice) {
super();
this.code = code;
this.name = name;
this.unitPrice = unitPrice;
}
// here the getters and setters
}
The ArticleCategory Java class:
@Entity
@Table(name = "article_category")
@Cache
public class DArticleCategory extends BaseModel {
private static final long serialVersionUID = 528512691717594544L;
@DbJson
@NotNull
private Map<Locale, String> name;
@ManyToMany
private List<DArticle> articles;
public DArticleCategory(Map<Locale, String> name) {
super();
this.name = name;
}
// here the getters and setters
}
The SQL (I use flyway for the migrations):
create table `article` (
`id` bigint auto_increment not null,
`created_at` datetime(6) not null,
`updated_at` datetime(6) not null,
`deleted` tinyint(1) not null,
`code` varchar(20) not null,
`name` longtext not null,
`unit_price` double not null,
primary key (`id`),
index (`code`)
);
create table `article_category` (
`id` bigint auto_increment not null,
`created_at` datetime(6) not null,
`updated_at` datetime(6) not null,
`deleted` tinyint(1) not null,
`name` longtext not null,
primary key (`id`)
);
The code I try to execute:
DArticleCategory c = new DArticleCategory(getTranslatedText("Category 1", "Catégorie 1", null));
c.save();
DArticleCategory cat = articleCategoryRepository.findById(1l);
for (DArticle article : cat.getArticles()) {
//nothing
}
The exception:
Caused by: java.lang.RuntimeException: Failed to decode cache data
at io.ebean.redis.encode.EncodePrefixKey.encode(EncodePrefixKey.java:26)
at io.ebean.redis.RedisCache.key(RedisCache.java:85)
at io.ebean.redis.RedisCache.get(RedisCache.java:139)
at io.ebeaninternal.server.deploy.BeanDescriptorCacheHelp.manyPropGet(BeanDescriptorCacheHelp.java:277)
at io.ebeaninternal.server.deploy.BeanDescriptorCacheHelp.manyPropLoad(BeanDescriptorCacheHelp.java:297)
at io.ebeaninternal.server.deploy.BeanDescriptor.cacheManyPropLoad(BeanDescriptor.java:1306)
at io.ebeaninternal.server.loadcontext.DLoadManyContext$LoadBuffer.loadMany(DLoadManyContext.java:215)
at io.ebean.common.AbstractBeanCollection.lazyLoadCollection(AbstractBeanCollection.java:101)
at io.ebean.common.BeanList.init(BeanList.java:139)
at io.ebean.common.BeanList.iterator(BeanList.java:335)
at db.migration.dev.V2_0_1__katel_test.migrate(V2_0_1__katel_test.java:200)
at org.flywaydb.core.internal.resolver.java.JavaMigrationExecutor.executeOnce(JavaMigrationExecutor.java:61)
... 59 common frames omitted
Caused by: java.lang.IllegalStateException: Expecting String keys but got type:class java.lang.Long
at io.ebean.redis.encode.EncodePrefixKey.encode(EncodePrefixKey.java:19)
... 70 common frames omitted
It looks a very simple code, but I'm not able to see where I'm wrong...
The annotation @Cacheable is only available for public exposed method that are allowed to be intercepter. But you could get the "CacheManager" services and use it in your code to handle internally the cache in the privated methods if required. But only to solve some "special" problems the usual way is to annotate the public methods.
Also if you use only the starter you are using only the basic and poor implementation of Spring, a simple memory cache.
Think about how your application will work (single app, distributed app, short/long amount of data cached,...) and the memory consumption to add a dependency of any of the supported cache managers like ehCache, Hazelcast,Caffeine,... that meets your requirements and improves your cache performance.