I want to create a dynamodb which has following features
PK: orderId
RK: date
shipped: Y|N
details: <nested json structure>
Point 4 is the one which i am really confused about. If i keep details field as a string and try to store json as string, AWS escape " characters i.e., {"onlineStore" : "283"} becomes {\"onlineStore\": \"283\"}
This get's retrieved properly from dynamodb with details as string mappings but if i have to convert it to a pojo using jackson, I have to take care of those \.
So as an alternative, I thought that i could create details a POJO i.e.,
public class OrderDetail{
private int onlineStore;
// rest of the JSON properties
#JsonCreator
public OrderDetail (#JsonProperty("onlineStore") int onlineStore, ...){
this.onlineStore = onlineStore;
}
}
With the above implementation, i get error that DynamoDBMappingException: Couldn't convert attribte.
The OrderDetail type is a common type which is being used between my JSON REST Response as well so i want to avoid putting DynamoDB specific annotation here.
Now the question is what should be proper way to implement it.
If you are using DynamoDB Mapper class to perform the CRUD operation, you can use the annotation #DynamoDBTypeConvertedJson to save the order details.
DynamoDBTypeConvertedJson - Check this link for more details
DynamoDBMapper class:-
The AWS SDK for Java provides a DynamoDBMapper class, allowing you to
map your client-side classes to DynamoDB tables. To use
DynamoDBMapper, you define the relationship between items in a
DynamoDB table and their corresponding object instances in your code.
The DynamoDBMapper class enables you to access your tables, perform
various create, read, update and delete (CRUD) operations, and execute
queries.
Sample Implementation:-
#DynamoDBTable(tableName = "Order")
public class Order implements Serializable {
private static final long serialVersionUID = -3534650012619938612L;
private String orderId;
private OrderDetail orderDetail;
#DynamoDBHashKey(attributeName = "orderId")
#DynamoDBAutoGeneratedKey
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
#DynamoDBTypeConvertedJson
public OrderDetail getOrderDetail() {
return orderDetail;
}
public void setOrderDetail(OrderDetail orderDetail) {
this.orderDetail = orderDetail;
}
}
public class OrderDetail implements Serializable {
private static final long serialVersionUID = 7312390212027563305L;
private Integer onlineStore;
public Integer getOnlineStore() {
return onlineStore;
}
public void setOnlineStore(Integer onlineStore) {
this.onlineStore = onlineStore;
}
#Override
public String toString() {
return "OrderDetail [onlineStore=" + onlineStore + "]";
}
}
public Order loadOrder(String orderId) {
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
Order order = dynamoDBMapper.load(Order.class, orderId,
new DynamoDBMapperConfig(DynamoDBMapperConfig.ConsistentReads.CONSISTENT));
System.out.println("Order : " + order.toString());
System.out.println("Order Id : " + order.getOrderId());
System.out.println("Order Detail : " + order.getOrderDetail());
System.out.println("Online store : " + order.getOrderDetail().getOnlineStore());
return order;
}
Output:-
Order Id : 0beced28-f1de-4c44-8094-6de687d25e97
Order Detail : OrderDetail [onlineStore=1]
Online store : 1
Data in DDB:-
As you mentioned, the order detail will be stored with escape characters. However, when you get the data using DynamoDB mapper, it will be in deserialized form (i.e. as POJO object).
Related
I am trying to implement an API endpoint where a user can request required columns from the API
Basically, this is what I want:
This is my products table entity
#Entity
#Table(name ="products")
class product{
private Long id;
private String itemName;
private String itemDesc;
private double quantity;
private double purchaseRate;
private double saleRate;
private double onlineSaleRate;
.
.
.
constructor()
getter & setter
}
***And my endpoint is:
localhost:8080/api/v1/products
Requirement:
I want to write an api endpoint where i request columns based on requirementsa and get those as response
Example: If i only need - itemName, itemPrice and quantity i'll return those as response only.
if some user has requirement of itemName, purchaseRate, saleRate, quantity he will get those as a response only.
Right now i am writing new endpoints as per requirements, but i think there is some way to do this.
I want to implement this in my application, i tried google for this but there is no search query that is resulting me as per my requirement.
Create a class with all the fields of your entity with the field types of nullable boxed Boolean (for the purpose of the request json).
class ProductColumns {
private Boolean itemName;
private Boolean itemDesc;
...
// setters and getters
}
Then, to construct a custom response, you can use a java Map to acheive this:
public ResponseEntity<Object> getFilteredColumns(ProductColumns columns) {
Map<String, Object> map = new HashMap<String, Object>();
if (columns.getItemName() == true) {
map.put("itemName", [your repo/service method for getting the particular value]);
}
if (columns.getItemDesc() == true) {
map.put("itemDesc", your repo/service method for getting the particular value]);
}
...
return ResponseEntity<Object>(map, HttpStatus.OK);
}
Of course you should wrap it in some try-catch to your liking.
In below query I wanted to group by just on date not time that's why I used TO_DATE function
select TO_DATE(e.created_dt, 'dd-mm-yy'),sum(CURRENT_BAL) from sbill.act_resource_t e group by TO_DATE(e.created_dt, 'dd-mm-yy');
so as of now its working fine with oracle but as per our business requirements application should support both oracle and mysql without write two different queries;
so do we have any solution for that which should works with both oracle and mysql ?
Note :- I am using hql
Below code :
Query query1 = entityManager.createQuery("select TO_DATE(e.createdDt, 'dd-mm-yy'),sum(CURRENT_BAL) from ActT e group by TO_DATE(e.createdDt, 'dd-mm-yy')");
List<Object> result=query1.getResultList();
Since the functions differ between the databases, we need a dynamic query. But we can do it efficiently by creating a custom conversion method, which will select the right implementation based on the current database type. And the query itself will remain common (The solution uses FluentJPA library):
public static final FormatModel DD_MM_YY = Format.dateModel(Format.DD, Format.MM, Format.YY);
public static boolean isOracle() {
return false; //should return the actual value in runtime
}
#Local
// picks the right implementation
public static Function1<String, Date> AS_DATE() {
if (isOracle())
return s -> TO_DATE(s, DD_MM_YY); //oracle
return s -> STR_TO_DATE(s, "%d-%m-%y"); // mysql
}
Now we can write a generic implementation:
#Entity
#Table(name = "act_resource_t", schema = "sbill")
#Data
public static class ActResource {
#Id
private int id;
private int currentBAL;
private String createdDT;
}
// for the result
#Tuple
#Data
public static class BalanceByDate {
private Date date;
private int balance;
}
...
public BalanceByDate balanceByDate() {
FluentQuery query = FluentJPA.SQL((ActResource e) -> {
// with lambda we inject the right implementation
Date createdDate = alias(AS_DATE().apply(e.getCreatedDT()), BalanceByDate::getDate);
Integer balance = alias(SUM(e.getCurrentBAL()), BalanceByDate::getBalance);
SELECT(createdDate, balance);
FROM(e);
GROUP(BY(createdDate));
});
return query.createQuery(em, BalanceByDate.class).getSingleResult();
}
This is the resulting SQL for MySQL:
SELECT STR_TO_DATE(t0.created_dt, '%d-%m-%y') AS date, SUM(t0.current_bal) AS balance
FROM sbill.act_resource_t t0
GROUP BY STR_TO_DATE(t0.created_dt, '%d-%m-%y')
How to do you insert an object array into a database by using JDBCTemplate? I have an object array of variable length coming in from my POST method in my controller.
I have looked at these,
http://www.java2s.com/Tutorial/Java/0417__Spring/PassParameterAsObjectArray.htm
How to insert Integer array into postgresql table using jdbcTemplate in Java Springboot?
As well as others and they do not seem to fit to what I need.
Controller
// Service
#Autowired
private DBService tool;
#PostMapping(value = "/foo")
private void storeData(#RequestBody CustomObject[] customObjects) {
// Calls service then DAO
tool.storeData(customObjects);
}
POJO Object
public class CustomObject {
private Integer id;
private String name;
// Getters & Setters for class attributes
...
}
DAO Is this right? Because I want to store each array element separately, with each element having its own row.
#Autowired
private JdbcTemplate temp;
public void storeData(CustomObject[] customObjects) {
String sql = "INSERT INTO FooBar(name) VALUES(\'" + customObjects.toString() + "\');";
temp.update(sql);
}
Expected
I want to store the array of my custom object from POST into my database with each element having its own row.
Ideally you would want to iterate over the array and save each "CustomObject" .
private JdbcTemplate temp;
public void storeData(CustomObject customObject) {
String sql = "INSERT INTO FooBar VALUES(" + customObject.id + ",\'"+ customObject.name +"\');";
temp.update(sql);
}
#PostMapping(value = "/foo")
private void storeData(#RequestBody CustomObject[] customObjects) {
// Save each record individually
customObjects.forEach { customObject ->
tool.storeData(customObjects);
}
}
Expect the two entities Movie and Genre:
#Entity
public class Movie {
#Id
private long id;
private String name;
private ToMany<Genre> genres;
[...]
}
#Entity
public class Genre {
#Id
private long id;
private String name;
[...]
}
We all know how to create a relation and save it:
Movie movie = new Movie();
movie.setTitle("Star Wars");
movie.getGenres().add(new Genre("Sci-Fi");
box.put(movie);
but is there a possibility to query all Movie-objects with a specific Genre? Like
Box<Movie> box = boxStore.boxFor(Movie.class);
Query query = box.query()
.equal(Genre_.name, "Sci-Fi") // note that I want to query the Movie-Box with Genre-properties
.build();
List movies = query.find();
My goal is to find all movies with a specific genre in a simple way. Does anyone know how to do it or do I have to query all movies and filter the result on my own? Or do I have to adapt my entities in another way?
Update:
I prepared the correct marked answer below to a working example:
final Genre genreSciFi = genreBox.query().equal(Genre_.name, "Sci-Fi").build().findFirst();
List<Movie> filteredMovies = movieBox.query().filter(new QueryFilter<Movie>() {
#Override
public boolean keep(#NonNull Movie entity) {
return entity.getGenres().contains(genreSciFi);
}
}).build().find();
To make the contains-Method work correctly, override equals-Method in your Genre-Entity:
#Override
public boolean equals(Object obj) {
return obj instanceof Genre && ((Genre) obj).getId() == id && ((Genre) obj).getName().equals(name);
}
Unfortunately, this part of the API is not exposed in Java yet. We want to refactor the Query API very soon.
Until this is ready, you can workaround using query filtering. Example using Java/Kotlin-ish code for brevity:
Query query = movieBox.query().filter(movie -> {
return genres.contains(genre -> {
return "Sci-Fi".equals(genre.getName())
}
}).build()
(Will make it similar in Java with the next update.)
I've just started working on a project using the play framework,jongo and MongoDB. The project was initially written in Play 2.1 with pojos with an String id field annotated with both: #Id and #ObjectId This would persist to Mongo as an ObjectId and when deserialized would output the id as: "id":"53fcb9ede4b0b18314098d10" for example.
Since upgrading to Jongo 1.1 and Play 2.3.3 the id attribute is always named "_id" when deserialized, I want the attribute to retain the field name yet I can't use #JsonProperty("custom_name") as the Jongo #Id annotation does #JsonProperty("_id") behind the scenes.
import org.jongo.marshall.jackson.oid.Id;
import org.jongo.marshall.jackson.oid.ObjectId;
public class PretendPojo {
#Id
#ObjectId
private String id;
private String name;
public PretendPojo() {
}
public PretendPojo(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
The POJOs when persisted in MongoDB look like this if I view them via RoboMongo
{
"_id" : ObjectId("53fc984de4b0c34f1905b8ee"),
"name" : "Owen"
}
However when I deserialize them I get the following json if I keep both annotations:
{"name":"Owen","_id":{"time":1409072858000,"date":1409072858000,"timestamp":1409072858,"new":false,"timeSecond":1409072858,"inc":308487737,"machine":-458223042}}
and the following output if I only use the #Id annotation.
{"name":"Owen","_id":"53fcbedae4b0123e12632639"}
I have a test case for working with the PretendPojo show above:
#Test
public void testJongoIdDeserialization() throws UnknownHostException {
DB database = new MongoClient("localhost", 27017).getDB("jongo");
Jongo jongo = new Jongo(database);
MongoCollection collection = jongo.getCollection("jongo");
collection.save(new PretendPojo("Owen"));
PretendPojo pretendPojo = collection.findOne("{name: \"Owen\"}").as(PretendPojo.class);
JsonNode json = Json.toJson(pretendPojo);
assertNotNull(json.get("id"));
}
When trying to use custom deserializers I never can get hold of the object ID I seem to only have access to the date/time/timestamp data that is currently being deserialized.
Ideally the output I'm looking for would be:
{"name":"Owen","id":"53fcbedae4b0123e12632639"}
Any help will be greatly appreciated! :)
ObjectIdSerializer always writes property mapped with #ObjectId to a new instance of ObjectId. This is wrong when you map this property to a String.
To avoid this behaviour, I've write a NoObjectIdSerializer :
public class NoObjectIdSerializer extends JsonSerializer<String> {
#Override
public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(value);
}
}
used like this :
#ObjectId
#JsonSerialize(using = NoObjectIdSerializer.class)
protected final String _id;
There is an open issue.
I think that there is an annotation in jackson that allows you to change the property name, I think is : #JsonProperty but you can see all the possible annotations in this link:
https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations
I hope this solve your problem
You can try using #JsonValue, Jongo does not seem to use them, but without any response from the developers this behaviour might be subject to change in future releases.
#JsonValue
public Map<String, Object> getJson() {
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("id", id);
return map;
}
The more proper solution would be to try combining #JsonView with #Id annotation
Remember to specify which View to use on Jongo's ObjectMapper and your Jackson ObjectMapper (the one to use in REST layer, I presume)
#Id
#JsonView(Views.DatabaseView.class)
private String id;
#JsonView(Views.PublicView.class)
public String getId() {
return id;
}
The Jongo's behaviour has changed since 1.1 for a more consistent handling of its owns annotations.
If your '_id' is a String and you want this field to be stored into Mongo as a String then only #Id is needed.
#Id + #ObjectId on a String property means :
"My String property named 'foo' is a valid ObjectId. This property has to be stored with the name '_id' and have to be handled as an ObjectId."