java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [shapes] on this ManagedType - Spring JPA Inheritance - spring

I am trying to add some optional parameters to request to make dynamic search in table with inheritance.
I created abstract class:
#Entity
#Table(name = "shapes")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
public abstract class ShapeEntity {
#Id
private String id;
#Column(name = "type", insertable = false, updatable = false)
#Enumerated(EnumType.STRING)
private ShapeType type;
}
and child class:
#DiscriminatorValue(value = "CIRCLE")
public class CircleEntity extends ShapeEntity {
private Double radius;
}
ultimately I will have many different shapes. Next I added Spec class with Specification:
public class ShapeSpec {
public static Specification<ShapeEntity> radiusGreaterThan(Double radiusFrom) {
return (root, query, builder) ->
radiusFrom == null ?
builder.conjunction() :
builder.greaterThan(root.get("shapes").get("radius"), radiusFrom);
}
public static Specification<ShapeEntity> radiusLessThan(Double radiusTo) {
return (root, query, builder) ->
radiusTo == null ?
builder.conjunction() :
builder.lessThanOrEqualTo(root.get("shapes").get("radius"), radiusTo);
}
}
but when I send request for example:
http://localhost:8083/shapes/test?radiusTo=30
I am getting error like in title:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [shapes] on this ManagedType [com.example.entity.ShapeEntity]
so as I understand it can't find "radius" attribute in abstract class ShapeEntity. Is there any solution? Should I add "Double radius" field to abstract class?
In database column "radius" is created as in SINGLE_TABLE strategy.

Related

Instructing Sping Data MongoDB to use correct mapping between ObjectId and its class

I cannot retrieve the 2nd level nested objects in Spring Data MongoDB
I have nested collection in MongoDB to retrieve with Spring. Imagine this schema
#Data
#Builder
#Document(collection = "emitted")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Emitter{
#Id
private String id;
#Field("installation")
#DocumentReference(lazy = true)
private Installaton installation;
// other fields
#Data
#Builder
#Document(collection = "installation")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Installation {
#Id
private String id;
#Field("subject")
#DocumentReference(lazy = true)
private Subject subject;
// other fields
#Data
#Builder
#Document(collection = "subject")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Subject {
#Id
private String id;
// other fields
Plus, I have MapStruct to map nested object field to string, for the purpose of avoiding cyclic reference introducing the search by id of the collection:
#ObjectFactory
public <T> T map(#NonNull final String id, #TargetType Class<T> type) {
return mongoTemplate.findById(id, type);
}
Everything works at first level, but at nested level I have this error:
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.bson.types.ObjectId] to type [com.package.collections.Subject]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.doConvert(MappingMongoConverter.java:1826)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.doConvert(MappingMongoConverter.java:1818)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleRead(MappingMongoConverter.java:1337)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleRead(MappingMongoConverter.java:1311)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$DefaultConversionContext.convert(MappingMongoConverter.java:2371)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$ConversionContext.convert(MappingMongoConverter.java:2174)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1936)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readProperties(MappingMongoConverter.java:638)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.populateProperties(MappingMongoConverter.java:549)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:527)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readDocument(MappingMongoConverter.java:491)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:427)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:423)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:120)
at org.springframework.data.mongodb.core.MongoTemplate$ReadDocumentCallback.doWith(MongoTemplate.java:3326)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindOneInternal(MongoTemplate.java:2940)
at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:2618)
at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:2588)
at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:922)
at com.package.myapp.services.mapper.ReferenceMapper.map(ReferenceMapper.java:26)
at com.package.myapp.services.mapper.InstallationMapperImpl.toEntity(InstallationMapperImpl.java:102)
When asking the conversion, the findById works correctly and retrieve the object and the nested one. It fails when the request is for 2nd level nested object, where the ObjectId is retrieved but cannot be converted and fails.
I'm answering myself because I found a solution suited for my problem.
I only needed the entity object with the id, so I wrote a converter:
public class ObjectIdToSubjectConverter implements Converter<ObjectId, Subject> {
#Override
public Subject convert(ObjectId source) {
return Subject.builder().id(source.toString()).build();
}
}
And register it as a bean:
#Configuration
public class MongoConfig {
#Bean
public MongoCustomConversions mongoCustomConversions() {
return new MongoCustomConversions(Collections.singletonList(new ObjectIdToSubjectConverter());
}
}

Spring Jpa Specification unable to locate attribute in sub classes

so I have following hierarchy of entities:
#MappedSuperClass
public abstract class BaseEntity {
private Long id;
private Date createAt;
private Date updateAt;
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Post extends BaseEntity {
private String creatorId;
private String title;
private String content;
}
#Entity
public class Article extends Post {
private String category; // article has category
}
#Entity
public class Journal extends Post {
private Date expiration; // journal has expiration
}
now when I use Spring Jpa Specification to query for articles with certain category, it won't work:
// define specification
public class ArticleSpecifications {
public static Specification<Article> withCategory(String category) {
(root, query, criteriaBuilder) ->
criteriaBuilder.equal(root.get("category"), category)
}
}
// repository
public interface PostRepository<T extends Post> extends JpaRepository<T, Long>, JpaSpecificationExecutor<T> { ... }
// usage: in some service class
#Autowired
private PostRepository<Article> articleRepository;
...
public void someMethod {
...
// error here
articleRepository.findAll(ArticleSpecifications.withCategory("news"), pageable);
...
}
Error message:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [category] on this ManagedType [com.gladdev.galahad.base.BaseEntity]
Just trying to understand here why it tries to look up "category" in BaseEntity.
Every Specification accessing attributes defined in Post works just fine.
Is it some spring jpa specification bug or I missed something?
You can use the CriteriaBuilder.treat() method.
In Criteria API, downcasting an entity to a subclass entity can be performed by using one of the CriteriaBuilder.treat() methods:
See https://docs.oracle.com/javaee/7/api/javax/persistence/criteria/CriteriaBuilder.html#treat-javax.persistence.criteria.Root-java.lang.Class-
public static Specification<Article> withCategory(String category) {
return (root, query, criteriaBuilder) -> {
Root<Article> article = criteriaBuilder.treat(root, Article.class);
return criteriaBuilder.equal(article.get("category"), category);
};
}

Is there an option to add different collection in edge using arangodb-spring-data

In arangoDB, we can create an edge in which we can set #from and #to to different collection since these are all json data. In ArangoDB-Spring-Data library we can create an edge and we have to provide type to #from and #to. I want to add relation between different collection using same edge. For example-
I have a class EntitywithKey-
public class EntityWithKey {
#Id
protected String id;
}
I have 2 classes which extends EntityWIthKey
#Document("actors")
#HashIndex(fields = { "name", "surname" }, unique = true)
public class Actor extends EntityWithKey{
private String name;
private String surname;
}
#Document("characters")
#HashIndex(fields = { "name", "surname" }, unique = true)
public class Character extends EntityWithKey {
private String name;
private String surname;
}
i want to create an edge like below-
#Edge
public class ChildOf {
#Id
private String id;
#From
private EntityWithKey child;
#To
private EntityWithKey parent;
}
So that i can add relationship between Actor-Actor, Actor-Character, Character-Character and Character-Actor.
But i am seeing an error
nested exception is org.springframework.data.mapping.PropertyReferenceException: No property name found for type EntityWithKey!
Is there any option with this library to do that?
I have fixed the issue. The edge will have to be created with java generics-
#Edge
public class ChildOf<T1,T2> {
#Id
private String id;
#From
private T1 child;
#To
private T2 parent;
}
Now i can add any two connectors in single edge relationship

Spring boot JPA: recursion on JSON-view in a self join relationShip

For a project that I am trying to build, I need a manyToMany relationship From the User Class to the Class User.(Users have Friends and Friends are friends of Users).
When trying to get Json out of SpringBoot with JPA. I get a recursive loop on my JSON.(This happens only if two users are friends of each other)
I know what happens but cannot find the solution to the problem.
As you can see I'm using different views to 'filter' the view. The question is: How can I stop the recursion?
#Entity
public class User {
#Id
#GeneratedValue
#JsonView(JsonViews.UserView.class)
private long userId;
#JsonView(JsonViews.UserView.class)
private String username;
#JsonView(JsonViews.UserView.class)
private String password;
#JsonView(JsonViews.UserView.class)
private String email;
#JsonView(JsonViews.UserView.class)
private String location;
//private String avatar;
#JsonView(JsonViews.UserView.class)
private int ranking;
private String UserStatus;
//Friend Relation Many > Many
#JsonView({JsonViews.UserView.class, JsonViews.FriendWith.class})
#ManyToMany()
private List<User> friendsWith;
#ManyToMany(mappedBy = "friendsWith")
private List<User> friendOf;
#JsonView({JsonViews.UserView.class, JsonViews.Player.class})
#OneToMany(mappedBy = "user")
private List<Player> player;
public User() {
this.friendsWith = new ArrayList<>();
this.friendOf = new ArrayList<>();
}
public User(String username, String password, String email, String location, int ranking) {
this();
//this.userId = userId;
this.username = username;
this.password = password;
this.email = email;
this.location = location;
this.ranking = ranking;
}
// and th usual getters and setters
Stack:
2019-12-10 22:17:52.414 ERROR 1618 --- [nio-8084-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service()
for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: org.hibernate.collection.internal.PersistentBag[0]->
nl.hva.ewa.stratego.backend.models.User["friendsWith"]->
org.hibernate.collection.internal.PersistentBag[0]->
nl.hva.ewa.stratego.backend.models.User["friendsWith"]->
org.hibernate.collection.internal.PersistentBag[0]->
nl.hva.ewa.stratego.backend.models.User["friendsWith"]->
org.hibernate.collection.internal.PersistentBag[0]->
nl.hva.ewa.stratego.backend.models.User["friendsWith"]->
ETC.... ETC.....
for completeness the JsonViews class:
public class JsonViews {
public class UserView { };
public class AComplete extends UserView { };
public class BComplete extends UserView { };
public class FriendsWith extends UserView {};
public class FriendsOf extends UserView {};
public class Player extends UserView {};
}
Try the #JsonIdentityInfo annotation, where you can tell Jackson what is the POJO id, so it does not repeat it:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "userId")
public class User {
public long userId;
public String name;
public List<User> friends;
}
That will generate the following JSON:
{
"userId" : 11,
"name" : "A",
"friends" : [ {
"userId" : 22,
"name" : "B",
"friends" : [ 11, {
"userId" : 33,
"name" : "C",
"friends" : [ ]
} ]
} ]
}
Notice that in the inner user B, the friends list contains just the ID if the user has been already added to the JSON body [ 11, {.

How to validate objects that were deserialized with Jackson polymorphic deserialization

Given the following request:
/enterprises-api/{{version}}/enterprises?query={"searchType":"FOUNDERSEARCH","maxResult":"61",...}
OR
/enterprises-api/{{version}}/enterprises?query={"searchType":"NAMEORADDRESSSEARCH","maxResult":"61",...}
I have managed to deserialize query into either a FounderSearch or NameOrAddressSearch object based on the searchType property.
However, Javax validation is ignored because this isn't a toplevel object. How can I resolve this?
Top level object:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "searchType")
#JsonSubTypes({
#JsonSubTypes.Type(value = FounderSearch.class, name = "FOUNDERSEARCH"),
#JsonSubTypes.Type(value = NamerOrAddressSearch.class, name = "NAMEORADDRESSSEARCH")
})
public abstract class Query {
public abstract boolean isFounderSearch();
}
One of the sub-level objects:
#Data
public class FounderSearch extends QueryObject {
#Min(1) #Max(60)
private Integer maxResult = 20;
#Pattern(regexp = "\\d{11}")
private String personNumber;
private List<CodeType> functions;
private Boolean activeFunctions;
public boolean isFounderSearch() {
return true;
}
public SearchType getSearchType() {
return SearchType.FOUNDERSEARCH;
}
}
If you add #Valid annotation on nested objects as well it will work.
something like this, above all the fields that have validation constraints.
#Data
public class FounderSearch extends QueryObject {
#Min(1) #Max(60)
#Valid
private Integer maxResult = 20;
#Pattern(regexp = "\\d{11}")
#Valid
private String personNumber;
private List<CodeType> functions;
private Boolean activeFunctions;
public boolean isFounderSearch() {
return true;
}
public SearchType getSearchType() {
return SearchType.FOUNDERSEARCH;
}
}

Resources