java-graphql - Unable to match type definition with java type - spring

I am using graphql-spring-boot to serve graphql queries from my spring-boot project. Right now I am working on matching the graphql scheme type definitions with my spring entites. For whatever reason, I am getting the following error:
Caused by: com.coxautodev.graphql.tools.SchemaClassScannerError: Unable to match type definition (ListType{type=TypeName{name='HomestayInfo'}}) with java type (class ninja.familyhomestay.domain.HomestayInfo): Java class is not a List or generic type information was lost: class ninja.familyhomestay.domain.HomestayInfo
at com.coxautodev.graphql.tools.TypeClassMatcher.error(TypeClassMatcher.kt:19)
at com.coxautodev.graphql.tools.TypeClassMatcher.match(TypeClassMatcher.kt:79)
at com.coxautodev.graphql.tools.TypeClassMatcher.match(TypeClassMatcher.kt:25)
Here's my graphql schema defintion for HomestayInfo:
type HomestayInfo{
homestayName: String
homestayShortDescription: String
homestayDescription: String
address: Address
rooms: [Room]
houseImages: [HouseImage]
pets: [Pet]
}
and the corresponding kotlin entity:
#Entity
#Table(name = "homestay_info")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "homestay_info")
data class HomestayInfo(
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
var id: Long? = null,
#Column(name = "homestay_name")
var homestayName: String? = null,
#Column(name = "homestay_short_description")
var homestayShortDescription: String? = null,
#Column(name = "homestay_description")
var homestayDescription: String? = null,
#OneToOne
#JoinColumn(name = "address_id")
var address:Address?=null,
#OneToMany(mappedBy = "homestayInfo", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var rooms: MutableSet<Room> = HashSet(),
#OneToMany(mappedBy = "homestayInfo", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var houseImages: MutableSet<HouseImage> = HashSet(),
#OneToMany(mappedBy = "homestayInfo", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var pets: MutableSet<Pet> = HashSet()
) : Serializable
I don't see anything wrong with the mapping. Any ideas?

Adding scalar Date to the top of your schema.graphqls file should do the trick! So your file would look like such:
scalar Date
schema { // not sure if your file has this, but mine does
query: Query
}
type HomestayInfo{
...

Related

Spring data rest ManyToMany mapping PUT/update operation is not replacing the nested object

I started to learn spring data rest. I'm doing PUT operation and it's not working for the nested objects for ManyToMany relationship, whereas it works fine for OneToMany relation.
Entities structures:
#Table(name="CONFIG_DTLS",schema = "app_txn")
#Entity
public class Config {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name = "NAME", nullable = false, length = 75)
private String name;
/*Unable to replace the data in the MBR_CONFIG_MAPPING table in the put operation.
When the control comes to #HandleBeforeSave annotated method in PUT operation,
the request data contains the existing Member info instead of the one which i'm passing in the PUT request body */
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.EAGER)
#JoinTable(schema = "app_txn", name = "MBR_CONFIG_MAPPING",
joinColumns ={#JoinColumn(name="CONFIG_ID",referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name="MBR_ID",referencedColumnName = "ID")}
)
private Set<Member> members;
//able to replace the notifications completely in PUT operation
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "CONFIG_ID",referencedColumnName = "ID")
private Set<Notification> notifications;
}
Member.java
#Table(name="MBR_DTLS",schema = "app_txn")
#Entity
public class Member {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name = "OTHER_MBR_DATA", updatable = false)
private String otherMbrData;
}
Notification.java
#Table(name="NOTIFICATIONS",schema = "app_txn")
#Entity
public class Notification {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name="LEVEL")
private String level;
#Column(name="EMAIL")
private String email;
}
Interfaces:
#RepositoryRestResource(collectionResourceRel = "configs", path="configs")
public interface ConfigRepo extends PagingAndSortingRepository<Config,UUID> {
}
#RepositoryRestResource(exported=false) // don't want to users to manipulate it directly.
public interface MemberRepo extends PagingAndSortingRepository<Member,Object> {
}
Here I don't want to add or modify anything in the MBR_DTLS table as it is loaded by another backend process. I want to update only the mapping details MBR_CONFIG_MAPPING table whenever user does the PUT/update operation. POST/create operation is working fine. Please share your thoughts on how to fix this and if you have any questions add it in the comment section.
PS: I referred some links online but that does not help much - Spring Data REST - PUT request does not work properly since v.2.5.7

Creating subcategories in kotlin spring boot

I need to implement categories and subcategories within my entities. Here's what I have so far and think it should be:
StockCategory.kt
#Entity
#Table(name = "table_categories")
data class StockCategory(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id")
val id: Long? = null,
#ManyToOne
#JoinColumn(name = "parentid")
val parent: StockCategory? = null,
#ManyToMany(mappedBy = "categories")
var stockItems: MutableList<StockItem> = mutableListOf(),
#OneToMany(mappedBy = "parent")
var childCategories: MutableList<StockCategory> = mutableListOf(),
)
StockItem.kt
#Entity
#Table(name = "table_stock")
data class StockItem(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "stock_item_id")
val id: Long? = null,
#Column(name = "stock_item_name")
var name: String = "New Item",
...
#ManyToMany
#JoinColumn(name = "item_category", referencedColumnName = "category_id")
var categories: MutableList<StockCategory> = mutableListOf(),
...
)
Now at the moment, this looks to be correct... At the very least Spring Boot is not complaining.
However, in terms of what to do next, I'm not sure. I know I need to implement a JpaRepository, of which I have the current:
StockCategoryRepository.kt
interface StockCategoryRepository: JpaRepository<StockCategory, Long> {
}
I also need to implement the relevant methods in my service class.
What exactly do I need to do next in order to get this to work and be able to use the information later on? Please also ELI5 too as although I have a decent amount of knowledge on this, I'm still not where I would like to be when it comes to this.
A few background bits if it makes it easier for you.
I'm using H2 as my database, Spring Boot and Kotlin as my language.

Spring Boot many to many relation - How to get an additional property inside the join table as a property from another entity

Im new in Java Spring and I'm stuck at this point here for a while:
I have an entity model like this:
Entity Relation now
A channellist contains many channels. A channel can be assigned to many channellists. I have setup my code that this works fine. Now I would like to add a property "sort" to my channels, so I would be able to order the channels for every specific channellist in a way I would like to. From the database relation model I know I have to store this information in the joining table "Channellist_Channel".
MY PROBLEM IS: I dont understand how I'm able asign this property to my Entity "Channel" so that every Channel has a "sort"-value depending on the context of the channellist. I read for this case, I have to add a new Entity which will represent the join table "Channellist_Channel" and add the property "sort" there. But the puzzle in my head is just not complete to do it :/
Here are my two entitys Channellist and Channel
CHANNELLIST:
#Entity
#Table(name = "channellist", schema = "stream")
public class Channellist {
#Id
#Column(name = "ID")
private int id;
#Column(name = "DISPLAYNAME")
private String displayName;
#ManyToMany
#JoinTable(
schema = "stream",
name = "Channellist_Channel",
joinColumns = #JoinColumn(name = "channellist_id"),
inverseJoinColumns = #JoinColumn(name = "channel_id")
)
private Set<Channel> channels;
//Constructors...
//Getter Setter...
CHANNEL:
#Entity
#Table(name = "channel", schema = "stream")
public class Channel {
#Id
#Column(name = "id")
private String id;
#Column(name = "display_name")
private String displayName;
#Column(name = "type")
private int type;
#Column(name = "logo_url")
private String logoUrl;
#Column(name = "stream_url")
private String streamUrl;
#OneToMany(mappedBy = "channelId", fetch = FetchType.LAZY)
#OrderBy(value = "timeStart ASC")
private Set<ChannelEpg> programs;
#JsonIgnore
#ManyToMany
#JoinTable(
schema = "stream",
name = "Channellist_Channel",
joinColumns = #JoinColumn(name = "channel_id"),
inverseJoinColumns = #JoinColumn(name = "channellist_id")
)
private Set<Channellist> channellists;
//Constructors...
//Getters Setters...
My JSON Response from "GetAllChannellists: Please Ignore the TAG "programs" under "channels". Its not relevant for my problem.
JSON RESPONSE:
[
{"id":1,
"displayName":"Standard",
"channels":[
{"id":"344143862749137509158c22d1606ad5",
"displayName":"KiKa",
"type":0,
"logoUrl":"https://example.de/test/kika.png",
"streamUrl":"https://example.de/test/kika.m3u8",
"programs":[
{"channelId":"344143862749137509158c22d1606ad5",
"timeStart":"2022-08-09T11:30:00.000+00:00",
"timeEnd":"2022-08-09T11:40:00.000+00:00",
"title":"logo!",
"subTitle":null,
"description":"a verry long description, no one cares"},
{"channelId":"344143862749137509158c22d1606ad5",
"timeStart":"2022-08-09T11:40:00.000+00:00",
"timeEnd":"2022-08-09T12:10:00.000+00:00",
"title":"Tiere bis unters Dach",
"subTitle":"Jojo, der Held",
"description":"another long description, no one cares"},
[...]
{"id":2,
"displayName":"Deluxe",
"channels":[
[...]
My goal is it to make it look like this:
[
{"id":1,
"displayName":"Standard",
"channels":[
{"id":"344143862749137509158c22d1606ad5",
"displayName":"KiKa",
"sort":21,
"type":0,
"logoUrl":"https://example.de/test/kika.png",
"streamUrl":"https://example.de/test/kika.m3u8",

JPA giving a unknown entity reference

My project is using JPA2/hibernate to map properties to their respective tables. As I understand it, I must put the property mappedby in the owner table and put JoinColumn in the child table(many side). I am getting the error as seen here:
Caused by: org.hibernate.AnnotationException: mappedBy reference an
unknown target entity property:
ninja.familyhomestay.domain.HouseImage.homestay_info in
ninja.familyhomestay.domain.HomestayInfo.houseImages
Here is my HomestayInfo class:
#Entity
#Table(name = "homestay_info")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "homestay_info")
data class HomestayInfo(
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
var id: Long? = null,
...
#OneToMany(mappedBy = "homestay_info", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var houseImages: MutableSet<HouseImage> = HashSet(),
...
)
and my houseImage class:
#Entity
#Table(name = "house_image")
#Document(indexName = "house_image")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
class HouseImage(
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
var id: Long? = null,
...
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "homestay_info_id")
var homestayInfo: HomestayInfo? = null
) : Serializable
Any ideas? I am also using liquid to create the tables in the db.
In your HomestayInfo class, while mentioning #OneToMany relationship, you are mentioning value of mappedBy attribute as homestay_info, while in HouseImage class there is no field with the name homestay_info.
You should have same field name mentioned in mappedBy and the attribute specifying the bilateral relationship in the other class.
Either rename homestayInfo to homestay_Info in the below statement :
var homestayInfo: HomestayInfo? = null
or
rename the value in mappedBy field to homestayInfo

Spring JPA cannot map a field with custom setter in a Kotlin data class

I've got a Kotlin data class with a custom setter. The Spring JPA framework cannot seem to map the property with the custom setter. If I remove the custom getter/setter and rename the property to login instead of _login, everything seems to work fine. How can I create the property in the Kotlin data class with a custom setter, so that it is recognised in the JPA framework?
User.kt
#Entity
#Table(name = "jhi_user")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
data class User (
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
var id: Long? = null,
#NotNull
#Pattern(regexp = Constants.LOGIN_REGEX)
#Size(min = 1, max = 50)
#Column(name = "login", length = 50, unique = true, nullable = false)
var _login: String? = null,
#JsonIgnore
#NotNull
#Size(min = 60, max = 60)
#Column(name = "password_hash",length = 60)
var password: String? = null,
...
#JsonIgnore
#ManyToMany
#JoinTable(
name = "jhi_user_authority",
joinColumns = arrayOf(JoinColumn(name = "user_id", referencedColumnName = "id")),
inverseJoinColumns = arrayOf(JoinColumn(name = "authority_name", referencedColumnName = "name")))
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#BatchSize(size = 20)
var authorities: MutableSet<Authority>? = null): AbstractAuditingEntity(), Serializable {
//Lowercase the login before saving it in database
var login: String?
get() = _login
set(value) {
_login = StringUtils.lowerCase(value, Locale.ENGLISH)
}
}
The error I'm getting:
...
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [login] on this ManagedType [com.sample.domain.AbstractAuditingEntity]
at org.hibernate.metamodel.internal.AbstractManagedType.checkNotNull(AbstractManagedType.java:128)
at org.hibernate.metamodel.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:113)
at org.hibernate.metamodel.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:111)
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:569)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.getTypedPath(JpaQueryCreator.java:377)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.build(JpaQueryCreator.java:300)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.toPredicate(JpaQueryCreator.java:205)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:117)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:54)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:111)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:90)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:78)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.<init>(PartTreeJpaQuery.java:135)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$CountQueryPreparer.<init>(PartTreeJpaQuery.java:256)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:72)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:103)
Using custom setters for constructor parameters like this is a bit ugly (but unfortunately the only way I am aware of doing it).
For starters JPA is going to want to register both _login and login as separate columns in your database since neither of them are #Transient. I believe your issue arises here since you have marked the _login property to map to the column "login" whereas the login property has no #Column annotation so it is trying to map to it's default value of "login" which already has the _login property mapped to it.
Therefore I think you probably want to make _login transient and only persist login (I've missed out irrelevant code for brevity and clarity):
...
#Transient
var _login: String? = null,
...
#NotNull
#Pattern(regexp = Constants.LOGIN_REGEX)
#Size(min = 1, max = 50)
#Column(name = "login", length = 50, unique = true, nullable = false)
var login: String?
get() = _login
set(value) {
_login = StringUtils.lowerCase(value, Locale.ENGLISH)
}
If this still doesn't work then I really think it's more hassle than it's worth trying to get this already slightly hacky workaround for using custom setters on constructor properties working with JPA. I would suggest instead to use a #PrePersist/#PreUpdate method to do the lowercasing for you prior to saving it to the database.

Resources