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
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
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.
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",
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
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.