I am using GraphiQL with spring boot for creating services. I have a scenario where I have some fields common to 2 to 3 classes.
type Parent {
field1: String
field2: String
}
#should have field1 and field2 from parent
type Child1 extends Parent {
field3: String
}
#should have field1 and field2 from parent
type Child2 extends Parent {
field4: String
}
I can do it like below,
type Child1 {
field3: String
parent: Parent
}
But it will be an extra class inside Child1. Is it possible to achieve parent and child class like in Java ?
Related
I'm creating an utility in Spring Boot to connect and insert/upsert data into the couchbase in the more generic way possible.
I have something like this:
public interface GenericRepository extends CouchbaseRepository<MyClass, String> {
}
Where I have MyClass I would like to accept any kind of document to insert into couchbase.
I have tried some things like using the generic type T but without success because I got the following error:
Caused by: org.springframework.data.mapping.MappingException: Couldn't
find PersistentEntity for type class java.lang.Object!
My structure is: service (interface/impl) > DAO (interface/impl) > repository
Extra info: Across the above model I am passing a Generic type T. I am calling the service with my Pojo with the #Document annotation.
The goal is to remove the "dependency" of having one repository class for each type of Document.
You can have generic repositories (bit hacky) but with some limitation. Assume you have documents;
#Document
public class MyClass1 extends BaseClass {
private String text1;
public MyClass1() {
super();
setType(Type.MyClass1);
}
// getter/setters
}
#Document
public class MyClass2 extends BaseClass {
private String text2;
public MyClass2() {
super();
setType(Type.MyClass2);
}
// getter/setters
}
with BaseClass;
#Document
public class BaseClass {
private Type type;
// other common fields if any, and getter/setters
public enum Type {
MyClass1, MyClass2
}
}
Then you can have the following repository;
public interface GenericRepository<T extends BaseClass> extends CouchbaseRepository<T, String> {
public List<T> findByType(BaseData.Type type);
public default List<T> findAll(Class<T> clazz) throws IllegalAccessException, InstantiationException {
return findByType(clazz.newInstance().getType());
}
}
and use it like;
#Autowired
private GenericRepository<MyClass1> mc1Repository;
#Autowired
private GenericRepository<MyClass2> mc2Repository;
public void doStuff() {
MyClass1 myClass1 = new MyClass1();
myClass1.setText1("text1");
mc1Repository.save(myClass1);
mc1Repository.findAll(MyClass1.class).forEach(d -> System.out.println(d.getText1()));
MyClass2 myClass2 = new MyClass2();
myClass2.setText2("text2");
mc2Repository.save(myClass2);
mc2Repository.findAll(MyClass2.class).forEach(d -> System.out.println(d.getText2()));
}
will print out;
text1
text2
But be aware that the documents will be all in same collection that is the collection for the BaseClass
Also this won't work with more than one extension (like MyClass1 extends Mid1, and Mid1 extends Base)
UPDATE: You can build a class that does generic manipulation of an entity (save/delete/update) via the CouchbaseOperations class. All you need to do is to inject it in your service or custom repository.
I don't think this is possible via the Spring SDK (Couchbase just implements the Spring's spec). However, you can create a single generic repository using reflection and the standard Java SDK:
Cluster cluster = CouchbaseCluster.create("localhost");
cluster.authenticate("username", "password");
Bucket bucket = cluster.openBucket("bucketname");
// Create a JSON Document
JsonObject arthur = JsonObject.create()
.put("name", "Arthur")
.put("email", "kingarthur#couchbase.com")
.put("interests", JsonArray.from("Holy Grail", "African Swallows"));
// Store the Document
bucket.upsert(JsonDocument.create("u:king_arthur", arthur));
Hello I have the following example:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "EVENT_TYPE")
abstract class BaseEvent {
}
#Entity
#DiscriminatorValue("ACCEPT_EVENT")
class AcceptEvent {
}
#Entity
#DiscriminatorValue("CANCEL_EVENT")
class CancelEvent {
}
I would like to write a repository for this Polymorphic entity. The thing is that I may have many EventTypes so I would prefer If I don't have to write One Repository class per Event Type. I was hoping that #{#entityName} can be applied in a generic repository like this:
interface EventRepositort<T> extends CRUDRepository<T> {
#Query(select e from #{#entityName} e where e.completionDate is null)
public T findIncompleteEvents();
}
I was hoping that if I have a service and I inject the repository with specific generic type it will figure out what the #{#entityName} so I did the following:
#Service
class EventService {
#Autowire
private EventRepository<AcceptEvent> acceptEventRepo;
public runUnfinishedEvents() {
List<AcceptEvent> unfinishedEvents = acceptEventRepo.findIncompleteEvents();
}
}
It apears though that even though I have defined what the generic type is on the event repository the method findIncompleteEvents is not able to resolve the generic declaration with #{#entityName} basicly it returns both the CancelEvent and the AcceptEvent
Now my question:
Is there a way to express what I want without writing 10 different reposities for each subclass of the BaseEvent ?
I have two java classes A and B that I would like to be able to serialize to JSON such that their content looks the same to a consumer.
I ommitted the constructors/getters/setters to keep it minimal.
public class A {
#JsonProperty("b")
private B b;
}
public class B {
#JsonProperty("propertyB")
public String propertyB;
}
When I serialize A I get
{
b: {
propertyB: ''
}
}
But I want it to look like B's serialization:
{
propertyB: ''
}
Is there any way to achieve that simply by configuring the serialization process respectively, i.e. using jackson annotations or some other form of jackson configuration.
Thanks
You can use #JsonUnwrapped
public class A {
#JsonUnwrapped
private B b;
}
https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html
Is there a simple way to use spring data couchbase with documents that do not have _class attribute?
In the couchbase I have something like this in my sampledata bucket:
{
"username" : "alice",
"created" : 1473292800000,
"data" : { "a": 1, "b" : "2"},
"type" : "mydata"
}
Now, is there any way to define mapping from this structure of document to Java object (note that _class attribute is missing and cannot be added) and vice versa so that I get all (or most) automagical features from spring couchbase data?
Something like:
If type field has value "mydata" use class MyData.java.
So when find is performed instead of automatically adding AND _class = "mydata" to generated query add AND type = "mydata".
Spring Data in general needs the _class field to know what to instantiate back when deserializing.
It's fairly easy in Spring Data Couchbase to use a different field name than _class, by overriding the typeKey() method in the AbsctractCouchbaseDataConfiguration.
But it'll still expect a fully qualified classname in there by default
Getting around that will require quite a bit more work:
You'll need to implement your own CouchbaseTypeMapper, following the model of DefaultCouchbaseTypeMapper. In the super(...) constructor, you'll need to provide an additional argument: a list of TypeInformationMapper. The default implementation doesn't explicitly provide one, so a SimpleTypeInformationMapper is used, which is the one that puts FQNs.
There's an alternative implementation that is configurable so you can alias specific classes to a shorter name via a Map: ConfigurableTypeInformationMapper...
So by putting a ConfigurableTypeInformationMapper with the alias you want for specific classes + a SimpleTypeInformationMapper after it in the list (for the case were you serialize a class that you didn't provide an alias for), you can achieve your goal.
The typeMapper is used within the MappingCouchbaseConverter, which you'll also need to extend unfortunately (just to instantiate your typeMapper instead of the default.
Once you have that, again override the configuration to return an instance of your custom MappingCouchbaseConverter that uses your custom CouchbaseTypeMapper (the mappingCouchbaseConverter() method).
You can achive this e.g. by creating custom annotation #DocumentType
#DocumentType("billing")
#Document
public class BillingRecordDocument {
String name;
// ...
}
Document will look like:
{
"type" : "billing"
"name" : "..."
}
Just create following classes:
Create custom AbstractReactiveCouchbaseConfiguration or AbstractCouchbaseConfiguration (depends which varian you use)
#Configuration
#EnableReactiveCouchbaseRepositories
public class CustomReactiveCouchbaseConfiguration extends AbstractReactiveCouchbaseConfiguration {
// implement abstract methods
// and configure custom mapping convereter
#Bean(name = BeanNames.COUCHBASE_MAPPING_CONVERTER)
public MappingCouchbaseConverter mappingCouchbaseConverter() throws Exception {
MappingCouchbaseConverter converter = new CustomMappingCouchbaseConverter(couchbaseMappingContext(), typeKey());
converter.setCustomConversions(customConversions());
return converter;
}
#Override
public String typeKey() {
return "type"; // this will owerride '_class'
}
}
Create custom MappingCouchbaseConverter
public class CustomMappingCouchbaseConverter extends MappingCouchbaseConverter {
public CustomMappingCouchbaseConverter(final MappingContext<? extends CouchbasePersistentEntity<?>,
CouchbasePersistentProperty> mappingContext, final String typeKey) {
super(mappingContext, typeKey);
this.typeMapper = new TypeBasedCouchbaseTypeMapper(typeKey);
}
}
and custom annotation #DocumentType
#Persistent
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
public #interface DocumentType {
String value();
}
Then create TypeAwareTypeInformationMapper which will just check if an entity is annoatated by #DocumentType if so, use value from that annotation, do the default if not (fully qualified class name)
public class TypeAwareTypeInformationMapper extends SimpleTypeInformationMapper {
#Override
public Alias createAliasFor(TypeInformation<?> type) {
DocumentType[] documentType = type.getType().getAnnotationsByType(DocumentType.class);
if (documentType.length == 1) {
return Alias.of(documentType[0].value());
}
return super.createAliasFor(type);
}
}
Then register it as following
public class TypeBasedCouchbaseTypeMapper extends DefaultTypeMapper<CouchbaseDocument> implements CouchbaseTypeMapper {
private final String typeKey;
public TypeBasedCouchbaseTypeMapper(final String typeKey) {
super(new DefaultCouchbaseTypeMapper.CouchbaseDocumentTypeAliasAccessor(typeKey),
Collections.singletonList(new TypeAwareTypeInformationMapper()));
this.typeKey = typeKey;
}
#Override
public String getTypeKey() {
return typeKey;
}
}
In your couchbase configuration class you just need to have :
#Override
public String typeKey() {
return "type";
}
Unfortunately for query derivation (n1ql) the _class or type are still using the class name.Tried spring couch 2.2.6 and it's minus point here.
#Simon, are you aware that something has changed and the support to have the possibility to have custom _class/type value in next release(s)?
#SimonBasle
Inside of class N1qlUtils and method createWhereFilterForEntity we have access to the CouchbaseConverter. On line:
String typeValue = entityInformation.getJavaType().getName();
Why not use the typeMapper from the converter to get the name of the entity when we want to avoid using the class name? Otherwise you have to annotate each method in your repository as follows:
#Query("#{#n1ql.selectEntity} WHERE `type`='airport' AND airportname = $1")
List<Airport> findAirportByAirportname(String airportName);
If createWhereFilterForEntity used the CouchbaseConverter we could avoid annotating with the #Query.
Does Jackson with Jersey support polymorphic classes over JSON?
Let's say, for instance, that I've got a Parent class and a Child class that inherits from it. And, let's say I want to use JSON to send & receive both Parent and Child over HTTP.
public class Parent {
...
}
public class Child extends Parent {
...
}
I've thought about this kind of implementation:
#Consumes({ "application/json" }) // This method supposed to get a parent, enhance it and return it back
public #ResponseBody
Parent enhance(#RequestBody Parent parent) {
...
}
Question: If I give this function (through JSON of course) a Child object, will it work? Will the Child's extra member fields be serialized, as well ?
Basically, I want to know if these frameworks support polymorphic consume & respond.
BTW, I'm working with Spring MVC.
Jackson does support polymorphism,
In your child class annotate with a name:
#JsonTypeName("Child_Class")
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "objectType")
public class Child extends Parent{
....
}
In parent you specify sub types:
#JsonSubTypes({ #JsonSubTypes.Type(value = Child.class), #JsonSubTypes.Type(value = SomeOther.class)})
public class Parent {
....
}