mongoTemplate: custom converter with non standard objects - spring

I found an issue when using a custom converter.
Let's say my to objects are:
public class Foo {
private String id;
private Bar bar;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
public class Bar {
private String id;
private String property;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
If they are inserted into the MongoDB, everything works fine, as long as no custom converter is involved.
Bar bar = new Bar();
bar.setProperty("Test");
Foo foo = new Foo();
foo.setBar(bar);
mongoTemplate.insert(bar);
mongoTemplate.insert(foo);
Results:
{
"_id" : {
"$oid" : "51a346059f2c9d656019798e"
},
"_class" : "Bar",
"property" : "Test"
}
{
"_id" : {
"$oid" : "51a346059f2c9d656019798f"
},
"_class" : "Foo",
"bar" : {
"_id" : {
"$oid" : "51a346059f2c9d656019798e"
},
"property" : "Test"
}
}
Now I wrote a custom converter, cause Foo needs to be stored in a special way.
public class FooWriteConverter implements Converter<Foo, DBObject> {
#Override
public DBObject convert(Foo source) {
DBObject dbo = new BasicDBObject();
dbo.put("id", source.getId());
dbo.put("bar", source.getBar());
return dbo;
}
}
Now I get this error.
Caused by: java.lang.IllegalArgumentException: can't serialize class Bar
So, it looks like I can't fall back to the default conversion for properties of an object, that is converted using a custom converter?!
Any useful solutions instead of doing all conversion manually?

In order to use default conversion for properties in custom converter you need a MongoConverter class help.
public class FooWriteConverter implements Converter<Foo, DBObject> {
private MongoConverter mongoConverter;
public FooWriteConverter(MongoConverter mongoConverter) {
this.mongoConverter = mongoConverter;
}
#Override
public DBObject convert(Foo source) {
DBObject dbo = new BasicDBObject();
dbo.put("id", source.getId());
DBObject bar = new BasicDBObject();
mongoConverter.write(source.getBar(), bar);
dbo.put("bar", bar);
return dbo;
}
}

Related

Fix string contraints on JPA entity attribute

I am new in JPA,
I want to set only specific fix department names to attribute in entity as a fix string as constraints.I.e default values to attributes.
How to set it?
I think the best option is to use enumerated as indicated by Dinesh Dontha, try this:
Entity
#Entity
public class MyEntity implements Serializable(){
private MyEnum attribute;
}
Enum
public enum MyEnum {
NAME1("N1")
private String shortName;
private MyEnum(String shortName) {
this.shortName = shortName;
}
public String getShortName() {
return shortName;
}
public static MyEnum fromShortName(String shortName) {
switch (shortName) {
case "N1":
return NacionalidadEnum.NAME1;
default:
throw new IllegalArgumentException("ShortName [" + shortName
+ "] not supported.");
}
}
}
Converter
#Converter(autoApply = true)
public class MyEntityEnumConverter implements AttributeConverter<MyEnum, String> {
#Override
public String convertToDatabaseColumn(MyEnum myEnum) {
return myEnum.getShortName();
}
#Override
public MyEnum convertToEntityAttribute(String dbData) {
return MyEnum.fromShortName(dbData);
}
}

Graphql - Query returns null

I am using the graphql-java-annotations library for java to retrieve data from my spring backend.
<dependency>
<groupId>io.github.graphql-java</groupId>
<artifactId>graphql-java-annotations</artifactId>
<version>7.1</version>
</dependency>
When I call the query it always return null.
This is my Provider class:
GraphQLAnnotations graphqlAnnotations = new GraphQLAnnotations();
GraphQLSchema graphQLSchema = newSchema()
.query(graphqlAnnotations.object(QueryTest.class))
.build();
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
This is the query:
#GraphQLName("queryTest")
public class QueryTest {
#GraphQLField
public static Test byId(final DataFetchingEnvironment env, #GraphQLName("id") Long id) {
return new Test();
}
}
And finally the Test.class
#GraphQLName("Test")
public class Test {
private String id;
private String name;
public Test() {
this("0");
}
public Test(String id) {
this.setName("Name" + id);
this.setId(id);
}
#GraphQLField
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#GraphQLField
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
This is my call:
{
"query" : "query queryTest { byId(id: 2) { getId } }",
"operationName" : "queryTest"
}
And this is the result i get:
{
"data": {
"byId": null
}
}
I debugged into the graphql execution and found out that schema contains TestClass and Test. So type and query are known. With this configuration I don't have a fetcher or a resolver.
Found the solution:
I had to change my Provider class in order to create the schema correctly via the AnnotationsSchemaCreator Builder:
GraphQLSchema graphQLSchema = AnnotationsSchemaCreator.newAnnotationsSchema()
.query(QueryTest.class)
.typeFunction(new ZonedDateTimeFunction())
.build();
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();

Spring Rest -> Hibernate entity to JSON

I am creating REST API using spring framework. My entity is based on one table and REST API is supposed to be invoked using POST operation with below JSON structure. Can someone explain me how to map the entity class so that it can consume below-shown json.
Since my entity is based on only one table, I am not able to understand how can it create nested json objects for same table properties.
{
"process_ar_receipt": {
"message_header": {
"source_system_guid": "DDED-DBCD-REV-E1F4343DB3434",
"source_system": "MeSo_TravelAds"
},
"receipt_header": {
"customer_number": "123",
"source_receipt_number": "TESTRCPT_1523",
}
}
}
you could use Gson to convert the json to a DTO
https://jarroba.com/gson-json-java-ejemplos/
pseudo code
assuming your Entity class as
#Entity(name="foo")
class Data{
#Id
private String source_system_guid;
#Column
private String source_system;
#Column
private String customer_number;
#Column
private String source_receipt_number;
public Data() {}
public Data(String ssId, String sourceSystm, String custNum, String srcRcptNum) {
this.source_system_guid = ssId;
this.source_system = sourceSystm;
this.customer_number = custNum;
this.source_receipt_number = srcRcptNum;
}
public String getSource_system_guid() {
return source_system_guid;
}
public void setSource_system_guid(String source_system_guid) {
this.source_system_guid = source_system_guid;
}
public String getSource_system() {
return source_system;
}
public void setSource_system(String source_system) {
this.source_system = source_system;
}
public String getCustomer_number() {
return customer_number;
}
public void setCustomer_number(String customer_number) {
this.customer_number = customer_number;
}
public String getSource_receipt_number() {
return source_receipt_number;
}
public void setSource_receipt_number(String source_receipt_number) {
this.source_receipt_number = source_receipt_number;
}
}
Now since your DTO/BO i.e. Data Transfer Object or Business Object is different from the actual entity we will create the required BO object as below
class DataTO{
#JsonProperty("process_ar_receipt")
private ReceiptTO receiptTO=new ReceiptTO();
public ReceiptTO getReceiptTO() {
return receiptTO;
}
public void setReceiptTO(ReceiptTO receiptTO) {
this.receiptTO = receiptTO;
}
}
class ReceiptTO{
#JsonProperty("message_header")
private MessageHeader messageHeder = new MessageHeader();
#JsonProperty("receipt_header")
private ReceiptHeader receiptHeder = new ReceiptHeader();
public MessageHeader getMessageHeder() {
return messageHeder;
}
public void setMessageHeder(MessageHeader messageHeder) {
this.messageHeder = messageHeder;
}
public ReceiptHeader getReceiptHeder() {
return receiptHeder;
}
public void setReceiptHeder(ReceiptHeader receiptHeder) {
this.receiptHeder = receiptHeder;
}
}
class MessageHeader{
#JsonProperty("source_System_Guid")
private String sourceSystemId;
#JsonProperty("system_Id")
private String systemId;
public String getSourceSystemId() {
return sourceSystemId;
}
public void setSourceSystemId(String sourceSystemId) {
this.sourceSystemId = sourceSystemId;
}
public String getSystemId() {
return systemId;
}
public void setSystemId(String systemId) {
this.systemId = systemId;
}
}
class ReceiptHeader{
#JsonProperty("customer_number")
private String customerNumber;
#JsonProperty("source_rcpt_number")
private String sourceReceiptNumber;
public String getCustomerNumber() {
return customerNumber;
}
public void setCustomerNumber(String customerNumber) {
this.customerNumber = customerNumber;
}
public String getSourceReceiptNumber() {
return sourceReceiptNumber;
}
public void setSourceReceiptNumber(String sourceReceiptNumber) {
this.sourceReceiptNumber = sourceReceiptNumber;
}
}
The #JsonProperty annotation is imported from org.codehaus.jackson.annotate.JsonProperty; i.e from jackson jar
Now a Simple Test class to demo DTO/BO back and forth Entity conversion
public class Test{
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
List<Data> datas = new ArrayList<Data>();
datas.add(new Data("DDED-DBCD-REV-E1F4343DB3434","MeSo_TravelAds","123","TESTRCPT_1523"));
datas.add(new Data("ADED-EWQD-REV-E1F4343YG3434","FooSo_MusicAds","132","TESTRCPT_1523"));
datas.add(new Data("YDED-YUTR-REV-E1F43UIDB3434","BarSo_HealthAds","143","TESTRCPT_1523"));
List<DataTO> dataTOs = new ArrayList<DataTO>();
for (Data data : datas) {
DataTO dataTO = new DataTO();
dataTO.getReceiptTO().getMessageHeder().setSourceSystemId(data.getSource_system_guid());
dataTO.getReceiptTO().getMessageHeder().setSystemId(data.getSource_system());
dataTO.getReceiptTO().getReceiptHeder().setCustomerNumber(data.getCustomer_number());
dataTO.getReceiptTO().getReceiptHeder().setSourceReceiptNumber(data.getSource_receipt_number());
dataTOs.add(dataTO);
}
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(dataTOs);
System.out.println(str);
}
}
This will give you below result
[
{
"process_ar_receipt":{
"message_header":{
"source_System_Guid":"DDED-DBCD-REV-E1F4343DB3434",
"system_Id":"MeSo_TravelAds"
},
"receipt_header":{
"customer_number":"123",
"source_rcpt_number":"TESTRCPT_1523"
}
}
},
{
"process_ar_receipt":{
"message_header":{
"source_System_Guid":"ADED-EWQD-REV-E1F4343YG3434",
"system_Id":"FooSo_MusicAds"
},
"receipt_header":{
"customer_number":"132",
"source_rcpt_number":"TESTRCPT_1523"
}
}
},
{
"process_ar_receipt":{
"message_header":{
"source_System_Guid":"YDED-YUTR-REV-E1F43UIDB3434",
"system_Id":"BarSo_HealthAds"
},
"receipt_header":{
"customer_number":"143",
"source_rcpt_number":"TESTRCPT_1523"
}
}
}
]
similarly the other conversion
String input = "{ \r\n" +
" \"process_ar_receipt\":{ \r\n" +
" \"message_header\":{ \r\n" +
" \"source_System_Guid\":\"ADED-EWQD-REV-E1F4343YG3434\",\r\n" +
" \"system_Id\":\"FooSo_MusicAds\"\r\n" +
" },\r\n" +
" \"receipt_header\":{ \r\n" +
" \"customer_number\":\"132\",\r\n" +
" \"source_rcpt_number\":\"TESTRCPT_1523\"\r\n" +
" }\r\n" +
" }\r\n" +
" }";
DataTO dataTO = mapper.readValue(input, DataTO.class);
System.out.println(dataTO.getReceiptTO().getMessageHeder().getSourceSystemId());
System.out.println(dataTO.getReceiptTO().getMessageHeder().getSystemId());
System.out.println(dataTO.getReceiptTO().getReceiptHeder().getCustomerNumber());
System.out.println(dataTO.getReceiptTO().getReceiptHeder().getSourceReceiptNumber());
this will print
ADED-EWQD-REV-E1F4343YG3434
FooSo_MusicAds
132
TESTRCPT_1523
You dont have to use the mapper code you can directly add the jackson converter as HttpMessageConverted which will convert the JSON to java object automatically
#Configuration
#EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
... other configurations
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
builder.serializationInclusion(Include.NON_EMPTY);
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
}

how can get the last inserted value in spring mongodb?

i have a class Named Device
#Document
public class Devise
{
#Id
public String id;
public String reference;
#DBRef
public List<Mesures> listMes;
public Devise() {
// TODO Auto-generated constructor stub
}
public Devise(String reference, List<Mesures> listMes) {
super();
this.reference = reference;
this.listMes = listMes;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getReference() {
return reference;
}
public void setReference(String reference) {
this.reference = reference;
}
public List<Mesures> getListMes() {
return listMes;
}
public void setListMes(List<Mesures> listMes) {
this.listMes = listMes;
}
}
another class named Mesures
#Document
public class Mesures {
#Id
private String id;
private Double value;
private Date date;
#DBRef
private Devise devise;
public Mesures() {
// TODO Auto-generated constructor stub
}
public Mesures(Double value, Date date, Devise devise) {
super();
this.value = value;
this.date = date;
this.devise = devise;
}
public Mesures(Double value, Date date) {
super();
this.value = value;
this.date = date;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Devise getDevise() {
return devise;
}
public void setDevise(Devise devise) {
this.devise = devise;
}
and this is the MesuresRepository who extend from Mongo Repository
public interface MesureRepository extends MongoRepository<Mesures, String>{
public Mesures findTopByDeviseOrderByDateDesc(String DeviseId);
}
and this is the main app (Spring boot app)
ApplicationContext ctx = SpringApplication.run(UiApplication.class, args);
MesureDAO MDAO = ctx.getBean(MesureDAO.class);
DeviceDAO DDAO = ctx.getBean(DeviceDAO.class);
DateFormat df = new SimpleDateFormat("yyyy-mm-dd-hh-mm-ss");
Mesures m1 = new Mesures(10.0, df.parse("2016-11-12-12-51-42"));
Mesures m2 = new Mesures(15.2,df.parse("2016-11-12-12-52-42"));
//add Mesure
MDAO.addMesure(m1);
MDAO.addMesure(m2);
List<Mesures>listMes = new ArrayList<>();
listMes.add(m1);
listMes.add(m2);
//add Device
Devise D = new Devise("G2fgG123", listMes);
DDAO.addDevice(D);
and the database look like :
db.devise.find().pretty()
{
"_id" : ObjectId("58bc8218d980530898a0b047"),
"_class" : "demo.model.Devise",
"reference" : "G2fgG123",
"listMes" : [
DBRef("mesures", ObjectId("58bc8218d980530898a0b045")),
DBRef("mesures", ObjectId("58bc8218d980530898a0b046"))
]
}
and the restController like this
#RestController
public class MesuressController {
#Autowired
MesureRepository mesureRepo;
#RequestMapping(value = "/mesure/{DeviseId}")
public Mesures findByDeviseIdOrderByDate(#PathVariable("DeviseId")String DeviseId) {
return mesureRepo.findTopByDeviseIdOrderByDateDesc(DeviseId);
}}
and this is the measure Doc
db.mesures.find().pretty()
{
"_id" : ObjectId("58bc8218d980530898a0b045"),
"_class" : "demo.model.Mesures",
"value" : 10,
"date" : ISODate("2016-01-11T23:51:42Z")
}
{
"_id" : ObjectId("58bc8218d980530898a0b046"),
"_class" : "demo.model.Mesures",
"value" : 15.2,
"date" : ISODate("2016-01-11T23:52:42Z")
}
and this the device document after changing
db.devise.find().pretty()
{
"_id" : ObjectId("58bd306500ff6d1670916a7d"),
"_class" : "demo.model.Devise",
"reference" : "G2fgG123",
"listMes" : [
{
"_id" : ObjectId("58bd306500ff6d1670916a7b"),
"value" : 10,
"date" : ISODate("2016-01-11T23:51:42Z")
},
{
"_id" : ObjectId("58bd306500ff6d1670916a7c"),
"value" : 15.2,
"date" : ISODate("2016-01-11T23:52:42Z")
}
]
Try to use mongodb's $natural operator:
Query query = new Query().with(new Sort(Direction.ASC, "$natural"));
BasicQuery basicQuery = new BasicQuery(new BasicDBObject());
basicQuery.setSortObject(new BasicDBObject("$natural", -1));

How to post to a URL with hash + range key Spring Data DynamoDB

As in spring-data-dynamoDB demo, I have created my application with hash and range keys, but am unable to post any data into my Table using POST because the following exception,
{cause: {cause: {cause: null,message: null}, message: "N/A (through reference chain: pkg.Test["id"])"}, message: "Could not read JSON: N/A (through reference chain: pkg.Test["id"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: N/A (through reference chain: pkg["id"])"
}
My Domain Class,
#DynamoDBTable(tableName = "test")
public class Test implements Serializable{
private static final long serialVersionUID = 1L;
#Id private TestId testId;
private String description;
private String testing;
#DynamoDBHashKey(attributeName="id")
public String getId() {
return testId != null ? testId.getId() : null;
}
public void setId(String id) {
if(testId == null){
testId = new TestId();
}
this.setId(id);
}
#DynamoDBRangeKey(attributeName="name")
public String getName() {
return testId != null ? testId.getName() : null;
}
public void setName(String name) {
if(testId == null){
testId = new TestId();
}
this.setName(name);
}
#DynamoDBAttribute(attributeName="description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#DynamoDBAttribute(attributeName="testing")
public String getTesting() {
return testing;
}
public void setTesting(String testing) {
this.testing = testing;
}
public TestId getTestId() {
return testId;
}
public void setTestId(TestId testId) {
this.testId = testId;
}
}
and my TestId Class,
public class TestId implements Serializable{
private String id;
private String name;
#DynamoDBHashKey
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#DynamoDBRangeKey
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I think I have created Domain class correctly but What is the correct procedure to Post data into it. I have tried,
URL:
http://localhost:8080/tests
Request Body:
{"testId": {"id": "test", "name": "z"}, "description": "Awesome Guy", "testing": "x"}
and
{"id": "test", "name": "z", "description": "Awesome Guy", "testing": "x"}
But all shows the exception as I mentioned above but I have given id attribute in requestbody correctly.
What is the correct procedure to POST the data into my table? and Is there anything problem with spring-data-rest parsing? as mentioned here
The setId() method seems to be self-calling. You may want to call testId.setId() instead of this.setId().

Resources