Can not update data in MongoDB Springboot? - java

My MongoDB Schema look like this. I want to update quantity field using uname, prodname and quantity. I tried to write a function in CartService class but it shows error
"The method save(S) in the type CrudRepository<Cart,String> is not applicable for the arguments (Optional)
] with root cause"
Please suggest any other solution or point out my mistake in code.
"Id":"string",
"uname":"string",
"products":[
{
"prodname":"string",
"quantity":"int",
"price":"double"
}],
"tot_amt":"double",
}
This is one of my model Cart.java
public class Cart {
#Id
public String Id;
#Indexed(unique=true)
public String uname;
public List<Product>products;
public double tot_amt;
}
This is another model class Product.java
public class Product {
public String prodname;
public int quantity;
public double price;
}
This is the repository interface CartRepository.java
public interface CartRepository extends MongoRepository<Cart,String>{
#Query("{uname:?0}")
Optional<Cart> findByName(String name);
}
This is Service class
public class CartService {
#Autowired
public CartRepository cartRepo;
public MongoTemplate mt;
public void saveUser(Cart cart) {
List<Double>amt= new ArrayList<>();
List<Product>products=cart.getProducts();
products.forEach(p -> {
double price=p.getPrice();
int quantity=p.getQuantity();
amt.add(price*quantity);
});
double tot_amount = 0;
for (Double i : amt)
tot_amount += i;
cart.setTot_amt(tot_amount);
cartRepo.save(cart);
}
public List<Cart> getdata()
{
return cartRepo.findAll();
}
public Optional<Cart> getDetailsByName(String name) {
Optional<Cart> savedCartData=Optional.of(cartRepo.findByName(name).orElseThrow(()->new RuntimeException(String.format("Not found %s",name))));
return savedCartData;
}
public void updateProd(String name, String prodname, int qty) {
Optional<Cart> cart=cartRepo.findByName(name);
Cart c=cart.get();
List<Product>products=c.getProducts();
for(Product p:products)
{
if (p.getProdname().equals(prodname)) {
p.setQuantity(qty);
break;
}
}
c.setProducts(products);
cart=Optional.of(c);
cartRepo.save(cart);
}
And this is update function in controller class
#PutMapping("/{name}/{prodname}/{qty}")
public String updateProduct(#PathVariable String name,#PathVariable String prodname,#PathVariable
int qty)
{
cartService.updateProd(name,prodname,qty);
return "Product updated";
}

Ok, let me give you steps to achieve this, I won’t take the fun of coding away from you.
Get the cart using username (you already have the repository function for this)
Stream the products in the cart
filter the product which has the input product name.
Update the quantity in above stream (using peek) with the input quantity
Collect the product list from stream.
set the collected product list back to cart.
Save the cart save()

Related

Spring Boot Custom Query In Controller

How do i add a custom query to my Spring boot application and access it in the controller?
I have two tables called CarBrand and YearMade. CarBrand has ID, code and Brand as columns. YearMade also has ID, code and year as columns.
I have written my model classes with setter and getter methods for each entity. I have added my repository interfaces and my service classes.
public interface YearRepository extends JpaRepository<Year, Long> {
}
My Brand Repository
public interface BrandRepository extends JpaRepository<Brand, Long> {
#Query("select b from brand b where brand.brand = ?1")
List<Brand> findVehicleBrand(String brand);
}
Here is my service class
public class YearService {
#Autowired
private YearRepository yearRepository;
public List<Year> listAll(){
return yearRepository.findAll();
}
public void save(Year engineSize){
yearRepository.save(engineSize);
}
public Year get (long id){
return yearRepository.findById(id).get();
}
public void delete (Long id){
yearRepository.deleteById(id);
}
}
My Brand Service
public interface BService {
List<Brand> findVehicleBrand(String name);
}
And this.
#Service
#Transactional
public class BrandService implements BService{
#Autowired
private BrandRepository brandRepository;
public List<Brand> listAll(){
return brandRepository.findAll();
}
public void save(Brand brand){
brandRepository.save(brand);
}
public Brand get (long id){
return brandRepository.findById(id).get();
}
public void delete (Long id){
brandRepository.deleteById(id);
}
#Override
public List<Brand> findVehicleBrand(String name) {
var brand = (List<Brand>) brandRepository.findVehicleBrand(name);
return brand;
}
}
In my controller, I get a path variable with a string, i use substring to break the string into two. The two substrings have the code for brand and year. The first two represent the year and the other three represent the brand. How do i compare the codes to the codes in the database to get the actual year and brand.
http://localhost:8081/vincode/wwQPT
The ww is the code for the year 1990 and QPT is for Honda Motor Company in the database.
I want a JSON response like this
{
Year Made : 1990,
Brand Name : Honda Motor Company
}
Here is the controller class i have so far.
#RequestMapping("/{vincode}")
public #ResponseBody String getAttr(#PathVariable(value="vincode") String vincode) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
System.out.println(yr);
return yr;
}
Where do i add the query and how do i use it in my controller?
Thank you.
If you dont have request mappong wlth value top of the class then http://localhost:8081/vincode/ww/QPT
RequestMapping("/vincode/{code}/{company}") can be more useful
There is no need to use substring maybe code or company key sizes changes.
Also service layer can be injected and used anytime.
Firstly add this statement in BrandRepository interface :
public interface BrandRepository extends JpaRepository<Brand, Long> {
#Query("select b from brand b where brand.brand = ?1")
List<Brand> findVehicleBrand(String brand);
public Brand findByCode(String code);
}
In YearRepository interface :
public interface YearRepository extends JpaRepository<Year, Long> {
public Year findByCode(String code);
}
Then add this method in BrandService Class:
public String findByCode (String code){
return brandRepository.findByCode(code).getBrand();
}
Then add this method in YearService Class:
public String findByCode (String code){
return yearRepository.findByCode(code).getYear;
}
Create Domain Class :
public class YearBrand
{
private String YearMade;
private String BrandName;
public YearBrand(String year, String brand)
{
this.YearMade=year;
this.BrandName=brand;
}
}
Then In Controller Class :
#RequestMapping("/{vincode}")
public YearBrand getAttr(#PathVariable(value="vincode") String vincode) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
return new YearBrand(yearService.findByCode(yr),brandService.findByCode(brand));
}
NOTES:
Make sure your Controller Class is annotated with #RestController
Spring Data JPA derives queries based on method naming conventions.
So, to get year by code in YearMade table, you need to modify your YearReporsitory interface like this (add an abstract method):
public interface YearRepository extends JpaRepository<Year, Long> {
// set return type as required
//find - Do What, ByCode - Criteria.
public Integer findByCode(String code);
}
And, use this method in your YearService just as you've used other methods.
But, you cannot use the same method for getting brand by code requirement. You'll have to write a repo class for it like:
public interface BrandRepository extends JpaRepository<CarBrand, Long> {
public Integer findByCode(String code);
}
You can write these methods for all the members of your Entity class. You've to follow the naming convention to get Spring recognize it.
EDIT (to show how to use this in controller and service class):
YearRepository interface:
public interface YearRepository extends JpaRepository<Year, Long> {
// set return type as required
//find - Do What, ByCode - Criteria.
public Integer findByCode(String code);
}
BrandRepository
public interface BrandRepository extends JpaRepository<Brand, Long> {
/*The below two methods are abstract methods.*/
// it must follow the findby<MemberName> convention
//return CarBrand
CarBrand findByBrand(String brand);
/*return a CarBrand Entity*/
public CarBrand findByCode(String code);
YearService:
public class YearService {
#Autowired
private YearRepository yearRepository;
public List<Year> listAll() {
return yearRepository.findAll();
}
public void save(Year engineSize) {
yearRepository.save(engineSize);
}
public Year get(long id) {
return yearRepository.findById(id).get();
}
public void delete(Long id) {
yearRepository.deleteById(id);
}
public int getYearByCode(String code) {
//here, we're using this method just as you've used the methods above.
//Spring constructs the query at runtime
return yearRepository.findByCode(code); //<-- usage of the custom method
}
}
BService:
public interface BService {
CarBrand findVehicleBrand(String name);
}
BrandService:
#Service
#Transactional
public class BrandService implements BService{
#Autowired
private BrandRepository brandRepository;
public List<Brand> listAll(){
return brandRepository.findAll();
}
public void save(Brand brand){
brandRepository.save(brand);
}
public Brand get (long id){
return brandRepository.findById(id).get();
}
public void delete (Long id){
brandRepository.deleteById(id);
}
#Override
public CarBrand findVehicleBrand(String name) {
//var brand = (List<Brand>) brandRepository.findVehicleBrand(name);
var brand = brandRepository.findByBrand(name); //<-- using the custom method in brandRepository
return brand;
}
}
Your RepsonseDto:
class RepsonseDto {
private String yearMade;
private brandName;
//getters and setters
/*Use #JsonProperty("Year Made") and #JsonProperty("Brand Name") on your getters. Otherwise, you will get json reposnse as: "yearMade" and "brandName"*/
}
Controller:
There are better ways to write controllers and inject dependencies. Let's keep it simple for now.
#RequestController
class YourController {
//inject dependencies
#Autowired
YearService yearService;
#Autowired
BrandService brandService;
#RequestMapping("/{vincode}")
// the definition for ResponseEntity is above
public ResponseEntity<RepsonseDto> getAttr(#PathVariable(value="vincode") String vincode) {
// create a ReponseEntity object
RepsonseDto retEntity = new RepsonseDto();
// do a check for null and expected length of vincode
if(vincode != null && vincode.length() == 5) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
retEntity.setYearMade(yearService.getYearByCode(yr));
retEntity.setBrandName(brandService.findVehicleBrand(brand));
System.out.println(yr);
}
return new ResponseEntity<>(retEntity, HttpStatus.OK)
}
NOTE: I didn't use an IDE to write this. There may be compiler errors. Hope this gives you an idea of it all fits in.

Java Generics - Choosing the correct generic service

Lets say we have a product of different types, the different types have different internal variables and does not share anything beside the fact they are all products.
public interface Product {}
public class ProductA implements Product {
private String productAVariable;
// getters
}
public class ProductB implements Product {
private String productBVariable;
// getters
}
To get the quote/price of a product we must call an external service:
public interface QuoteService<T extends Product> {
ProductPrice getQuote(T product);
}
public class ProductAQuoteService implements QuoteService<ProductA> {
#Override
public ProductPrice getQuote(ProductA product) {
return new ProductPrice(someExternalService.getQuote(product.getProductAVariable()));
}
}
public class ProductBQuoteService implements QuoteService<ProductB> {
#Override
public ProductPrice getQuote(ProductB product) {
return new ProductPrice(someOtherExternalService.getQuote(product.getProductBVariable()));
}
}
So far so good. The last step is to iterate through a List of Products in our shoppingcart. Fetching the price/quote for each one. But how can we do that in a generic way? Currently i have some if-else instance of statements, but I feel there must be something more generic?
List<Product> productsInCart = Lists.asList(new ProductA(), new ProductB());
for (Product product : productsInCart) {
if (product instanceof ProductA) {
productAQuoteService.getQuote((ProductA) product);
} else if (product instanceof ProductB) {
productBQuoteService.getQuote((ProductB) product);
}
}

Persisting objects in SugarORM

I have a Book class:
public class Book extends SugarRecord {
private String mBookName;
private String mAuthorName;
private List<Page> mPageList;
public Book() {
}
public Book(String bookname, String authorName) {
mBookName = bookname;
mAuthorName = authorName;
mPageList = new ArrayList<>();
}
public String getAuthorName() {
return mAuthorName;
}
public void setAuthorName(String authorName) {
mAuthorName = authorName;
}
public String getBookName() {
return mBookName;
}
public void setBookName(String bookName) {
mBookName = bookName;
}
public void addPage(Page page) {
mPageList.add(page);
}
}
and the Page class:
public class Page extends SugarRecord {
private String mText;
public Page() {
}
public Page(String text) {
mText = text;
}
public String getText() {
return mText;
}
public void setText(String text) {
mText = text;
}
}
I am testing it with this:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Book book = new Book("Some Book Title", "John Doe");
Page page1 = new Page("Once upon a time there was a very lonely bunny who wanted some friends.");
Page page2 = new Page("So he found some friends, and everyone was happy.");
Page page3 = new Page("The end!");
book.addPage(page1);
book.addPage(page2);
book.addPage(page3);
book.save();
}
}
However it is not working as expected. It is trying to make mPageList its own column with this .schema:
CREATE TABLE BOOK ( ID INTEGER PRIMARY KEY AUTOINCREMENT , M_AUTHOR_NAME TEXT, M_BOOK_NAME TEXT, M_PAGE_LIST );
What I'd really like it to do is not treat the list as its own column but instead save the Pages to the PAGE table, with additional ids that reference this Book class (so what I am expecting is something like ID, BOOK_ID, M_TEXT). In short, persistence operations that cascade through nested child objects.
Can this be done in SugarORM?
No ORM database(SugarORm, DBFLow etc) supports List column. As you know sql don't have this datatype as well.
That's the reason why you are getting this error. If you ask me how you are saving list to ORM. I use Gson.
Declare Pagelist as string.
String Pagelist;
Before saving it to database convert it to Json string with the help Gson library.
Gson gson = new Gson();
String value = gson.toJson(your_page_list);
when retrieving from database convert the json string to List using Gson.
List<Page> page_list;
Type typeIndicatorForGson = new TypeToken<ArrayList<Page>>() {}.getType();
Gson myGson = new Gson();
page_list = myGson.fromJson(page_json_data_from_database, typeIndicatorForGson);
No List<Object> available on SugarORM. The way you can manage this is a little tricky. In few words, you can manage 1 to N relations, upside down. Take a look to the next example
Lets suppose a Team object which can have N Person objects. Normally you will use a List<Person> in your class Team in this way:
public class Team {
String teamName;
List<Person>
...
}
public class Person {
String name;
String rol;
...
}
Well, it is not possible on SugarORM. But you can add Team as a property in Person, so, any Person's instance should contain a reference to the Team object it belong.
public class Team extends SugarRecord<Team> {
String teamName;
...
}
public class Person extends SugarRecord<Person> {
String name;
String rol;
Team team;
...
}
Then you can get all the Person objects from Team with a method (in the Team class) like:
public class Team extends SugarRecord<Team> {
String teamName;
...
public List<Person> getPersons(){
return Person.find(Person.class, "id = ?", String.valueOf(this.getId()));
}
}
So, you can manage 1 to N relations, but you can't manage N to M relationships (Person belonging to more than one Team object).
IMO the way to manage this is using an Associative Entity in order to split N to M into two 1 to N relationships.
As you can see SugarORM is not allowing you to think just in terms of objects, but, any case you can save a lot of boiler plate coding.

Multiple products in a single client(arrayList)

I've been asked to program a small online sales aplication.
It sounds very simple in theory (but it's been a hell for me). I'm just supposed to have an arrayList with about 5 products and then have a client buy 1 to 5 products and print the sales total.
public class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public String printInfo() {
return "Product: " + name + " Cost: " + price;
}
}
Then I have a client class:
public class Cliente {
private String name;
private int numPedido;
ArrayList<Producto> products = new ArrayList<Producto>();
public void listBuilder() {
Producto shirt = new Producto("Shirt", 30);
Producto tshirt = new Producto("T-Shirt", 40);
Producto sweater = new Producto("Sweater", 50);
}
public Cliente(String name, int numPedido) {
this.name = name;
this.numPedido = numPedido;
}
public Cliente() {
}
public String getName() {
return name;
}
public int getNumPedido() {
return (int) (Math.random() * 100);
}
public void addNewClient() {
name = JOptionPane.showInputDialog("Nombre: ");
}
public String printInfo() {
return "Nombre: " + name;
}
}
Right now I'm stuck thinking on how to make a client select a product and get that attached to him. I was thinking on making an arrayList of an arrayList but I'm sure that would complicate things. I know there is probably an easier way to connect them but I can't think of any. The option I have in mind is a method which shows numbers from 1 to 3(corresponding to each product) and when the user picks one it should return the price of the item.
Still not sure how to implement it in a way that the user can pick multiple products.
EDIT:
I also have an admin class that goes like this:
public class Admin {
private Client[] clientList;
public AdminPedidos() {
clientList = new Client[2];
}
public void AddContact() {
clienteList[0] = addProduct();
clienteList[1] = addProduct();
fillList();
}
public Cliente addProduct() {
String contactoString = JOptionPane.showInputDialog("Are you a new client? Press 1 if yes.");
if (contactoString.equals("1")) {
return new Cliente();
} else {
return new Cliente(); //just for testing
}
}
private void fillList() {
for (Client i : clientList) {
i.addNewClient();
}
}
public void printContact() {
for (Client i : clientList) {
System.out.println(i.printInfo());
}
}
}
You can have some purchaseProduct method attached to each Client.
public void purchaseProduct(Product product) { this.products.add(product); }
Then each Client you instantiate (Client client = new Client(name, id);) can add Products to his/her cart with the purchaseProduct method.
I'm assuming you are using some kind of user input method (Scanner). With that you can read the user's input of which Product they want and accordingly call the function with the right Product.
The listBuilder function doesn't quite make sense to me btw (and after your edit, it's really hard to make sense of what the Admin class should be/represent).
Edit: You would probably want to create an ArrayList<Product> which will be attached to each client, which you already have. I sense that you have a difficulty deciding where to put your actual Products. You should not put them inside your Client class for sure.
You should think about who/where they are going to be used. Probably in main right? So just instantiate them there first and then the Client could choose which one to purchase (via the method I introduced before):
client.purchaseProduct(product);

class objects does not get saved in parse databrowser android

I have started with parse to store the data of my class. I have followed parse guide and tutorials and tried to implement the code. Unfortunately, the objects of class are not getting saved in parse data browser. When I see the data in browser just one object id is shown not the columns of name, desc and qty of my item class. I have created class in dashboard also created columns respective to my data. Unable to get the solution as I am new to android and parse.
Here is my code
Item class
package com.example.owner.newstock;
import com.parse.ParseClassName;
import com.parse.ParseObject;
#ParseClassName("Item")
public class Item extends ParseObject {
public int id;
public String item_name;
public String item_desc;
public String item_qty;
public Item(){}
public Item(int id, String item_name, String item_desc, String item_qty) {
super();
this.item_name = item_name;
this.item_desc = item_desc;
this.item_qty = item_qty;
}
public Item(String item_name, String item_desc, String item_qty){
this.item_name = item_name;
this.item_desc=item_desc;
this.item_qty = item_qty;
}
public int getID(){
return id;
}
public void setID(int id){
this.id= id;
}
public String getItem_name(){
return getString(item_name);
}
public void setItem_name(String item_name)
{
put("item_name", item_name);
}
public String getItem_desc()
{
return getString(item_desc);
}
public void setItem_desc(String item_desc)
{
put("item_desc", item_desc);
}
public String getItem_qty()
{
return getString (item_qty);
}
public void setItem_qty(String item_qty){
put("item_qty", item_qty);
}
}
code of parse in main activity
ParseObject.registerSubclass(Item.class);
Parse.initialize(this, "Kw0dyUgLoqv24QdLE30mvFBVclEzLHRGtR2hQVHA", "5BWc3bAd60EgqU0sFIj31mMYYg7OIX9WKgC0a6oP");
ParseAnalytics.trackAppOpened(getIntent());
code to save the objects
Item i = new Item();
i.setItem_name(item_name);
i.setItem_desc(item_desc);
i.setItem_qty(item_qty);
i.saveInBackground();
Am I missing something?
Rather than creating an item class that extends ParseObject, set up a ParseObject variable, as follows:
ParseObject item = new ParseObject("Item");
Then put data in as follows:
item.put("quantity", yourQuantityVariable);
item.put("description", yourDescriptionVariable);
item.put("name", yourNameVariable);
To save:
item.saveInBackground();
To retrieve data, make use of querying and the getDataType() methods. Specified on https://parse.com/docs/android/guide#objects and https://parse.com/docs/android/guide#queries

Categories

Resources