I am trying to save an object into DynamoDB. I know that the easiest way to do this is to annotate the object with #DynamoDBDocument.
However, in my case, the objects I want to save belong to a package that I can't modify.
I am using the java sdk.
import not.my.package.Outsider;
#DynamoDBTable(tableName = "DynamoTable")
public class DynamoTable {
private Outsider outsider;
//getters...
//setters...
}
Any ideas on how I can save these objects? I do not want to save them as a string as we are using a Dynamo to SQL plugin for our business purposes.
Thanks.
Firstly, the OP doesn't have any information about partition key and sort key. The below code auto generates the partition key using annotation #DynamoDBAutoGeneratedKey. You can change it based on your use case.
Order class - Similar to DynamoTable
#DynamoDBTable(tableName = "Order")
public class Order implements Serializable {
private static final long serialVersionUID = -3534650012619938612L;
private String orderId;
private String productName;
private Integer createDate;
private Outsider outsider;
#DynamoDBHashKey(attributeName = "orderId")
#DynamoDBAutoGeneratedKey
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
#DynamoDBAttribute(attributeName = "productName")
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
#DynamoDBAttribute(attributeName = "createDate")
public Integer getCreateDate() {
return createDate;
}
public void setCreateDate(Integer createDate) {
this.createDate = createDate;
}
#DynamoDBAttribute(attributeName = "outsider")
public Outsider getOutsider() {
return outsider;
}
public void setOutsider(Outsider outsider) {
this.outsider = outsider;
}
}
Outsider class:-
The attributes in outsider class will be saved as Map attribute in DynamoDB table.
#DynamoDBDocument
public class Outsider implements Serializable{
private static final long serialVersionUID = 4449726365885112352L;
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Code to save data:-
This code should work as long as you have dynamoDBClient object. I have used Spring to inject the object to my service class. There are multiple ways.
public Boolean createOrderWithOutsider(String productName, Outsider outsider) {
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
Order order = new Order();
order.setProductName(productName);
order.setOutsider(outsider);
dynamoDBMapper.save(order);
System.out.println("Order id : " + order.getOrderId());
return true;
}
Test code:-
#Test
public void createOrderWithOutsider() {
Outsider outsider = new Outsider();
outsider.setFirstName("John");
outsider.setLastName("Micheal");
Assert.isTrue(tableOperations.createOrderWithOutsider("Pepsi", outsider));
}
Connection sample:-
<bean id="amazonDynamoDB" class="com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient">
<constructor-arg ref="amazonAWSCredentials" />
<property name="endpoint" value="${amazon.dynamodb.endpoint}" />
</bean>
Autowired in service class:-
#Autowired
private AmazonDynamoDBClient dynamoDBClient;
Sample data saved in table:-
Related
I have two tables CustomerDetails and Product, I want to fetch customerid from customer table and add it to #joincolumn(order_id) column in same CustomerDetails table.
CustomerDetails.java
#Entity
#Table(name = "CustomerDetails")
public class CustomerDetails {
#Id
#GeneratedValue
#Column(name="CUSTOMER_ID")
private Long custid;
#Column(name="CUSTOMER_NAME")
private String customerName;
#Column(name="EMAIL")
private String email;
#Column(name="ADDRESS")
private String address;
#Column(name="PHONENO")
private String phoneno;
public CustomerDetails() {
}
#Override
public String toString() {
return "CustomerDetails [custid=" + custid + ", customername=" + customerName + ", email=" + email
+ ", address=" + address + ", phoneno=" + phoneno + "]";
}
public CustomerDetails(String customername, String email, String address, String phoneno) {
super();
this.customerName = customername;
this.email = email;
this.address = address;
this.phoneno = phoneno;
}
public Long getCustid() {
return custid;
}
public void setCustid(Long custid) {
this.custid = custid;
}
public String getName() {
return customerName;
}
public void setName(String name) {
this.customerName = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return address;
}
public void setPassword(String password) {
this.address = password;
}
public String getPhoneno() {
return phoneno;
}
public void setPhoneno(String phoneno) {
this.phoneno = phoneno;
}
}
Product.java
#Entity
#Table(name="Product")
public class Product {
#Id
#GeneratedValue
#Column(name="PRODUCT_ID")
private Long productId;
#Column(name="PRODUCT_NAME")
private String productName;
#Column(name="PRODUCT_BRAND")
private String productBrand;
#Column(name="PRODUCT_PRICE")
private double productPrice;
#OneToOne
private CustomerDetails cd;
public Product(Long productId, String productName, String productBrand, double productPrice, CustomerDetails cd) {
super();
this.productId = productId;
this.productName = productName;
this.productBrand = productBrand;
this.productPrice = productPrice;
this.cd = cd;
}
public Product(String productName, String productType, double productPrice) {
super();
this.productName = productName;
this.productBrand = productType;
this.productPrice = productPrice;
}
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductBrand() {
return productBrand;
}
public void setProductBrand(String productType) {
this.productBrand = productType;
}
public double getProductPrice() {
return productPrice;
}
public void setProductPrice(double productPrice) {
this.productPrice = productPrice;
}
public CustomerDetails getCd() {
return cd;
}
public void setCd(CustomerDetails cd) {
this.cd = cd;
}
public Product() {
//super();
}
#Override
public String toString() {
return "Product [productId=" + productId + ", productName=" + productName + ", productType=" + productBrand
+ ", productPrice=" + productPrice + "]";
}
}
CustomerDetails repository
#Repository
public interface CdRepo extends JpaRepository<CustomerDetails, Long>
{
}
Product repository
#Repository
public interface ProductRepo extends JpaRepository<Product, Long>
{
}
CustomerService
#Service
#Transactional
public class CustomerService {
private final CdRepo cdRepo;
#Autowired
public CustomerService(CdRepo cdRepo) {
this.cdRepo = cdRepo;
}
public void saveCustomer(CustomerDetails cd)
{
cdRepo.save(cd);
}
}
controller
#RequestMapping(value = {"/addCustomerDetails"}, method = RequestMethod.POST)
public ModelAndView addCustomerDetails(CustomerDetails cd)
{
customerService.saveCustomer(cd);
System.out.println(cd.getCustid());
ModelAndView model = new ModelAndView();
model.setViewName("homepage");
return model;
}
In controller using getCustid() I'm getting current customer's id now I want to insert that id into #joinColumn(order_id)
If I've understood correctly, you want to assign a product to a user (e.g Customer).
For a #OneToOne relation you don't need private CustomerDetails cd; in product class. Although I don't know why are you implementing such thing in that way at all!
Generally If you want to assign two things together, let's say you want to assign a product to a user so that the product would be for that user, you should find the product obj from repository or any where (both product and user must have an id generated by db) and then assign it to user.product.
product service
#Service
public class ProductService {
#Autowired
private ProductRepo productRepository;
public Optional<Product> findProductById(Long id) {
return this.productRepository.findByProductId(id);
}
}
customer service
#Service
#Transactional
public class CustomerService {
private final CdRepo cdRepo;
#Autowired
public CustomerService(CdRepo cdRepo) {
this.cdRepo = cdRepo;
}
public CustomerDetails saveCustomer(CustomerDetails cd, Long productId) {
CustomerDetails dbCustomer = customerService.saveCustomer(cd);
// I'm getting the id from path variable, change it if you have other logics
Optional<Product> dbProduct = this.productService.findProductById(productId);
// I don't know how you handle run time errors so I can't write it, don't
// forget to check the dbProduct in case it didn't exist :)
// In case you did not created getters and setters in CustomerDetails,
// use dbCustomer.product = dbProduct.get();
dbCustomer.setProduct(dbProduct.get());
// update our customer using JPA, after customer update JPA handles everything
return this.cdRepo.save(dbCustomer);
}
}
controller
#RequestMapping(value = {"/addCustomerDetails/{productId}"}, method = RequestMethod.POST)
public ModelAndView addCustomerDetails(CustomerDetails cd, #PathVariable Long productId )
{
CustomerDetails dbCustomer = this.customerService.saveCustomer(cd, productId);
// Use the dbCustomer in your logic ...
ModelAndView model = new ModelAndView();
model.setViewName("homepage");
return model;
}
Write getters and setters in each entity or use Lombok annotation #Data.
Usually when I want to deploy an ecommerce with user and product. I use user, cart, product model.
The problem with the code above is that if you assign that product to a user, it's ONLY for that user. if other users want the same product you have to create all of those products for them. Solution to that would be using product as a series or a model.
Imagine a situation that you want to create a website to sell coffee packages. you only have two type of package to sell. you can create an entity like product for those packages with a quantity for each. Then create a #OneToMany relationship in your cart model to products. It will create a join table to store any possible product id there with cart id. After that, create a #ManyToOne relationship in your cart entity and #OneToMany in your user entity. Now each cart is only for a specific user and not the products.
Some Friendly Advice:
Don't populate your controller with logic. Let service layer handle it.
For each entity, create a package with the name of the entity instead and create 3 classes; the entity itself, response POJO and request POJO.
Use getters and setters for your entites. You can use lombok for that matter. It will handle the situation by generating them.
Use convertor components to create and convert requested entity to the entity itself and also convert entity to response entity.
Avoid interacting with Data base as much as you can. hold the object in a variable like dbCustomer for doing operations.
I converted UUID to string (String id) and put the conversion inside a method.
I also declared other String variables such as FirstName etc and put in on an ArrayList:
Code
The code does work. But I'm confused why the string email was showing second on the list.
public class StudentController {
#Autowired
StudentService studentService = new StudentService();
#GetMapping
public List<Student> displayStudent(){
return studentService.getStudent();
}
}
public class StudentService {
Student student = new Student();
private List<Student> studentList = Arrays.asList(
new Student(student.genID(),"Elvis" , "Presley" ,"Elvis#gmail.com")
);
public List<Student> getStudent(){
return studentList;
}
}
public class Student {
UUID uuid = UUID.randomUUID();
private String id;
private String FirstName;
private String LastName;
private String email;
public Student() {}
//Method Converting UUID into string
public String genID(){
id = uuid.toString();
return id;
}
public Student(String id) {
this.id = id;
}
public Student(String id, String firstName, String lastName, String email) {
this.id = id;
FirstName = firstName;
LastName = lastName;
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return FirstName;
}
public void setFirstName(String firstName) {
FirstName = firstName;
}
public String getLastName() {
return LastName;
}
public void setLastName(String lastName) {
LastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Expected
I expected data to be in this order
ID , FirstName , LastName , email
Actual Output JSON
JSON is an unordered collection, as specified on https://www.json.org/json-en.html , so you don't have to worry about it. It might depend on library though.
Specify the serialized order of properties
The order of properties during serialization can be defined in Jackson.
Either at class-level specifically using annotation #JsonPropertyOrder.
Or globally for your ObjectMapper using a feature:
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
Example
In your case you can achieve expected order using the annotation on your class:
#JsonPropertyOrder({'id', 'firstName', 'lastName', 'email'})
public class Student {
// body of your class
}
Or separately with an index on your fields:
public class Student {
#JsonProperty(index=10)
private String id;
// not ordered specifically
private String firstName;
private String lastName;
#JsonProperty(index=20)
private String email;
// remainder of your class
}
See also
Jackson ObjectMapper - specify serialization order of object properties
Order of JSON objects using Jackson's ObjectMapper
Jackson JSON - Using #JsonPropertyOrder annotation to define serialized properties ordering
I have a dynamodb table named opx_user_profiles. The entity is shown below, however the attribute user_profile_id is getting saved as userProfileID in the table, even though the #DynamoDBAttribute(attributeName = USER_PROFILE_ID) is specified on the attribute. Other attributes like date_created are getting saved as expected.
I have read the documentation but still not able to find the root cause of the issue. Is it is a bug in dynamo DB?
#DynamoDBTable(tableName = "opx_user_profiles")
public class UserProfileEntity implements Serializable
{
public static final String USER_PROFILE_ID="user_profile_id";
public static final String DATE_CREATED = "date_created";
public static final String EXPIRY_DATE = "expiry_date";
public static final String USERNAME ="username";
public static final String CONTACT_NAME ="contact_name";
private static final long serialVersionUID = 1L;
#DynamoDBAttribute(attributeName = USER_PROFILE_ID)
private Integer userProfileId;
#DynamoDBAttribute(attributeName = USERNAME)
private String userName;
#DynamoDBAttribute(attributeName = CONTACT_NAME)
private String contactName;
#DynamoDBAttribute(attributeName = DATE_CREATED)
private Date dateCreated;
#DynamoDBAttribute(attributeName = EXPIRY_DATE)
private long expiryDate;
public Integer getUserProfileID()
{
return userProfileId;
}
public void setUserProfileID(Integer userProfileId)
{
this.userProfileId = userProfileId;
}
public String getUserName()
{
return userName;
}
public void setUserName(String userName)
{
this.userName = userName;
}
public String getContactName()
{
return contactName;
}
public void setContactName(String contactName)
{
this.contactName = contactName;
}
public Date getDateCreated()
{
return dateCreated;
}
public void setDateCreated(Date dateCreated)
{
this.dateCreated = dateCreated;
}
}
Even though the official AWS documentation says we may apply the annotation #DynamoDBAttribute class field, I could not make it work like this. However, as seen in this AWS example, I could apply without any problem the annotation to the getter methods.
Please try the following:
#DynamoDBTable(tableName = "opx_user_profiles")
public class UserProfileEntity implements Serializable
{
public static final String USER_PROFILE_ID="user_profile_id";
...
private Integer userProfileId;
...
#DynamoDBAttribute(attributeName = USER_PROFILE_ID)
public Integer getUserProfileID()
{
return userProfileId;
}
public void setUserProfileID(Integer userProfileId)
{
this.userProfileId = userProfileId;
}
...
}
One more option.
If you want to reduce code boilerplates you might use Lombok's #Getter this way:
#Getter(onMethod = #__({#DynamoDbAttribute("address")}))
private String address;
p.s.: It won't impact performance cause the code generation happens not in runtime but the entity looks better IMO.
I am using spring data Mongodb in my project and refer the below classes for my query on grouping the results:
Student class:
#Document(collection = "student")
public class Student {
#Id
private String id;
private String firstName;
private String lastName;
//other fields
//getters & setters
}
StudentResults (dto):
public class StudentResults {
private String firstName;
private List<String> studentIds; //I need List<Student> here
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public List<String> getStudentIds() {
return studentIds;
}
public void setStudentIds(List<String> studentIds) {
this.studentIds = studentIds;
}
}
StudentServiceImpl class:
public class StudentServiceImpl implements StudentService {
#Autowired
private MongoTemplate mongoTemplate;
public List<StudentResults> findStudentsGroupByFirstName() {
TypedAggregation<Student> studentAggregation =
Aggregation.newAggregation(Student.class,
Aggregation.group("firstName").
addToSet("id").as("studentIds"),
Aggregation.project("studentIds").
and("firstName").previousOperation());
AggregationResults<StudentResults> results = mongoTemplate.
aggregate(studentAggregation, StudentResults.class);
List<StudentResults> studentResultsList = results.getMappedResults();
return studentResultsList;
}
}
Using the above code, I am able to retrieve the List<String> studentIds successfully, but I need to retrieve List<Student> students using Aggregation.group()? Can you help?
Change your TypedAggregation part to below and add students field to StudentResults
TypedAggregation<Student> studentAggregation = Aggregation.newAggregation(Student.class,
Aggregation.group("firstName").
push("$$ROOT").as("students"));
$$ROOT will push the whole document.
Update:
TypedAggregation<Student> studentAggregation = Aggregation.newAggregation(Student.class,
Aggregation.group("firstName").
push(new BasicDBObject
("_id", "$_id").append
("firstName", "$firstName").append
("lastName", "$lastName")).as("students"));
I'm new to Java and even more newer to Spring (Boot and JPA) but I was curious, I'm trying to debug an issue that says, "No identifier specified for entity".
For illustartion purposes, I've created the following tables from this diagram:
Originally, there was a M:N relationship between the user and vehicle table, so I created an associative entity (UserVehicleAsso) to split the two up. I was following this guide on M:N mapping in Java, http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/
For the most part, it was pretty straight forward but my question is, within the associative entity (UserVehicleAsso), do I have to use the #Id annotation for each of the foreign keys? I assume that I didn't need to because those were automatically generated from each of the respective tables.
Let me know your thoughts or comments, thanks.
Also, below is the code that I used to generate these models:
For the User table/class:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int userId;
private String fName;
private String lName;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="userVehicleAsso",
joinColumns={#JoinColumn(name="userID")},
inverseJoinColumns={#JoinColumn(name="vehicleID")})
private Set<Vehicle> vehicles = new HashSet<Vehicle>();
//constructor
protected User() {}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getFName() {
return fName;
}
public void setFName(String fName) {
this.fName = fName;
}
public String getLName() {
return lName;
}
public void setLName(String lName) {
this.lName = lName;
}
public Set<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(Set<Vehicle> vehicles) {
this.vehicles = vehicles;
}
#Override
public String toString() {
return getFName() + "," + getLName();
}}
For the Vehicle table/class:
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int vehicleId;
private String brand;
private String model;
//foreign key mappings
//mapping with associative
#ManyToMany(mappedBy="vehicles")
private Set<User> users = new HashSet<User>();
//constructors
protected Vehicle() {}
public Vehicle(int id) {
this.vehicleId = id;
}
public Vehicle (String brand, String model) {
this.brand = brand;
this.model = model;
}
/* public Vehicle() {
}*/
public int getVehicleId() {
return vehicleId;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public void setVehicleId(int vehicleId) {
this.vehicleId = vehicleId;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#Override
public String toString() {
// + setBodyType() + "," +
return getBrand() + "," + getModel();
}
}
And then finally, my associtive table/class:
#Entity
public class UserVehicleAsso{
private int userID;
private int vehicleID;
public int getUserID() {
return userID;
}
public void setUserID(int userID) {
this.userID = userID;
}
public int getVehicleID() {
return vehicleID;
}
public void setVehicleID(int vehicleID) {
this.vehicleID = vehicleID;
}
}
In my opinion, it's not necessary to have an Entity class for the middle table in your case. The table will be generated automatically if configured correctly. In this table, there would not be column ID, only two columns with userID and vehicleID data.
Now, if your middle table has more than what are needed to establish the M:N relationship, then your middle Entity class is needed, and the ID of it, too. For example, if this class is intended to store the time stamp every time a relationship is established, you have to:
Create this Entity class,
Give it an ID field with proper generation strategy,
Map the time stamp with a field with adequate type, annotation/XML mapping and so on.
This part of JPA/Hibernate have confused me a lot and I used to get into them. If my memory serves me well this is the proper/perfect way how things should work.
You can specify a composite primary key class that is mapped to multiple fields or properties of the entity.
Here are sample codes:
public class ActivityRegPK implements Serializable {
private int activityId;
private int memberId;
public int getActivityId() {
return activityId;
}
public void setActivityId(int activityId) {
this.activityId = activityId;
}
public int getMemberId() {
return memberId;
}
public void setMemberId(int memberId) {
this.memberId = memberId;
}
}
associtive table/class:
#IdClass(ActivityRegPK.class)
#Entity
#Table(name="activity_reg")
#NamedQuery(name="ActivityReg.findAll", query="SELECT a FROM ActivityReg a")
public class ActivityReg implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="activity_id")
private int activityId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="ins_date")
private Date insDate;
#Id
#Column(name="member_id")
private int memberId;
}
Activity.class
#Entity
#NamedQuery(name="Activity.findAll", query="SELECT a FROM Activity a")
public class Activity implements Serializable {
// some attributes
}