Select on Northwind database using JPA and JDBC Template - spring-boot

I would like to perform select statement on Northwind database like this bellow.
select distinct b.*, a.CategoryName
from Categories a
inner join Products b on a.CategoryID = b.CategoryID
where b.Discontinued = 'N'
order by b.ProductName;
I have two problems regarding this operation :
I have created POJO for tables categories and products like bellow
Table Products
#Entity
public class Products {
#Id
private Long productid;
private String productname;
private Long supplierid;
#ManyToOne
#JoinColumn(name = "categories", referencedColumnName = "categoryid")
private Categories categoryid;
private String quantityperunit;
private Double unitprice;
private Long unitsinstock;
private Long unitsonorder;
private Long reorderlevel;
private String discontinued;
Table Categories
#Entity
public class Categories {
#Id
private Long categoryid;
private String categoryname;
private String description;
private String picture;
Now I have no idea how to write rowmapper for this tables (please find below ????)
private static final RowMapper<Products> productsRowMapper = (rs, rowNum) ->{
Products products = new Products();
products.setProductid(rs.getLong("ProductID"));
products.setProductname(rs.getString("ProductName"));
products.setSupplierid(rs.getLong("SupplierID"));
products.setCategoryid(rs.?????
products.setQuantityperunit(rs.getString("QuantityPerUnit"));
products.setUnitprice(rs.getDouble("UnitPrice"));
products.setUnitsinstock(rs.getLong("UnitsInStock"));
products.setUnitsonorder(rs.getLong("UnitsOnOrder"));
products.setReorderlevel(rs.getLong("ReorderLevel"));
products.setDiscontinued(rs.getString("Discontinued"));
return products;
};
the second problem is that I don't know if the annotations on the column categoryid in the table products are correct?
After correction
#Repository
public class JdbcProductsDao implements ProductsDao{
private final JdbcTemplate jdbcTemplate;
#Autowired
public JdbcProductsDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private static final RowMapper<Products> productsRowMapper = (rs, rowNum) ->{
Products products = new Products();
products.setProductid(rs.getLong("ProductID"));
products.setProductname(rs.getString("ProductName"));
products.setSupplierid(rs.getLong("SupplierID"));
products.setCategoryid(new Categories(rs.getString("CategoryName")));
products.setQuantityperunit(rs.getString("QuantityPerUnit"));
products.setUnitprice(rs.getDouble("UnitPrice"));
products.setUnitsinstock(rs.getLong("UnitsInStock"));
products.setUnitsonorder(rs.getLong("UnitsOnOrder"));
products.setReorderlevel(rs.getLong("ReorderLevel"));
products.setDiscontinued(rs.getString("Discontinued"));
return products;
};
public Products findByProductName(String productname) {
String sql = "SELECT * FROM products WHERE ProductName = ?";
return jdbcTemplate.queryForObject(sql, productsRowMapper, productname);
}
public List<Products> sortByProductName(){
String sql = "SELECT * FROM products order by ProductName asc";
return jdbcTemplate.query(sql, productsRowMapper);
}
Table Categories
#Entity
public class Categories {
#Id
private Long categoryid;
#Column(name = "CategoryName")
private String categoryname;
private String description;
private String picture;
#OneToMany(mappedBy="categoryid")
private List<Products> products;
Table Products
#Entity
public class Products {
#Id
private Long productid;
#Column(name = "ProductName")
private String productname;
private Long supplierid;
#ManyToOne
private Categories categoryid;
private String quantityperunit;
private Double unitprice;
private Long unitsinstock;
private Long unitsonorder;
private Long reorderlevel;
private String discontinued;

Now I have no idea how to write rowmapper for this tables (please find
below ????)
You have to set an Object like this :
products.setCategoryid(new Categories(rs.getString("a.categoryname"));
I assume you have an constructor Categories(String categoryname)
Note : If you want to get more information, you have to change your query and your constructor as well.
the second problem is that I don't know if the annotations on the
column categoryid in the table products are correct?
I think there are no need to use #JoinColumn(name = "categories", referencedColumnName = "categoryid") just use :
#ManyToOne
private Categories categoryid;
In your Categories entities add this :
#OneToMany(mappedBy="categoryid")
List<Products> products;

Related

How to postmapping in spring boot

I have 5 models:
admin
food
orders
payments
users
admin - admin can view/add/edit/delete food, and getting all users data(this part is done).
users - users can create orders.
so by my understanding, I did the mapping as follows:
users(1)-->orders(M)
orders(1)-->food(M)
orders(1)-->payments(1)
here is my code for all entity.
public class Users {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="user_id")
private Long usersId;
#Column(name="user_fName")
private String firstName;
#Column(name="user_lName")
private String lastName;
#Column(name="user_email")
private String email;
#Column(name="user_password")
private String password;
#Column(name="user_phnNumber")
private String phNumber;
#Column(name="user_address")
private String address;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#OneToMany(cascade = CascadeType.PERSIST,mappedBy= "users")
private Set<Orders> order;
}
public class Payments {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="payment_id")
private Long paymentId;
#Column(name="payment_amount")
private Long paymentAmount;
#Column(name="payment_date")
private Date paymentDate;
#Column(name="payment_transactionId")
private Long paymentTransactionId;
#OneToOne
#JoinColumn(name="order_id")
Orders orders;
public Payments(Long paymentAmount, Date paymentDate, Long paymentTransactionId,Orders orders1) {
super();
this.paymentAmount = paymentAmount;
this.paymentDate = paymentDate;
this.paymentTransactionId = paymentTransactionId;
this.orders = orders1;
}
}
public class Orders {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="order_id")
private Long orderId;
#ManyToOne
#JoinColumn(name="user_id")
Users users;
#OneToMany(cascade = CascadeType.ALL)
private Set<Food> food;
#OneToOne(cascade = CascadeType.ALL)
private Payments payments;
#Column(name="order_quantity")
private Long orderQuantity;
#Column(name="order_date")
private Date orderDate;
#Column(name="order_status")
private Boolean orderStatus;
public Orders(Long orderQuantity, Date orderDate, Boolean orderStatus,Users users1) {
super();
this.orderQuantity = orderQuantity;
this.orderDate = orderDate;
this.orderStatus = orderStatus;
this.users = users1;
}
}
public class Food {
#Id
#SequenceGenerator(
name="food_sequence",
sequenceName="food_sequence",
allocationSize=1
)
#GeneratedValue(
strategy=GenerationType.SEQUENCE,
generator="food_sequence"
)
#Column(name="food_id")
private Long foodId;
#Column(name="food_name")
private String foodName;
#Column(name="food_desc")
private String foodDesc;
#Column(name="food_price")
private String foodPrice;
#Column(name="food_status")
private Boolean foodStatus;
#Column(name="food_img")
private String foodImg;
#ManyToOne
#JoinColumn(name="orders_id")
Orders orders;
public Food(String foodName,
String foodDesc,
String foodPrice,
Boolean foodStatus,
String foodImg,
Orders order1
) {
super();
this.foodName = foodName;
this.foodDesc = foodDesc;
this.foodPrice = foodPrice;
this.foodStatus = foodStatus;
this.foodImg = foodImg;
this.orders = order1;
}
}
So my Question: how to create an order. and when I see food table it has column orders_id. but here first admin will add food and then in the users side user will create orders using those food.
I am stuck at creating an order.
It would be better to remove the Payments column in the Orders class and the Orders column in the Food class.
Every order has a Payment, and you already do this by joining Order inside the Payments class. Likewise, there can be more than one Food in an Order, and you do this inside the Orders class.
To create an order, as you said, first the admin needs to add food to the table. After the food data is added to the table, the user will select food to create an order and you will add the selected food to the Order object's food attribute.(This could be better btw)
Then you will create a Payment with the Order that you created and manage the Payment separately.

JPA ManyToMany fails with "must have same number of columns as the referenced primary key"

I have three tables customer, product and sales. sales is the join table to store the customer's products as shown below:
customer, product and Sales tables
My Entities defined as described below:
Customer.java
#Entity
#Table(name="customer")
public class Customer {
#Id
#Column(name="c_id")
private String customerId;
#Column(name="customer_name")
private String customerName;
#ManyToMany
#JoinTable(
name = "sale",
joinColumns = #JoinColumn(name = "c_id"),
inverseJoinColumns = #JoinColumn(name = "p_id"))
private Set<Product> customerProducts = new HashSet<>();
}
Product.java
#Entity
#Table(name="product")
public class Product {
#Id
#Column(name="p_id")
private String productId;
#Column(name="product_name")
private String productName;
#Column(name="price")
private Double price;
// ... Setters & Getters
}
Sales.java
#Entity
#Table(name="sales")
public class Sales {
#EmbeddedId
private SalesPK salesId;
#Column(name="qty")
private Long qty;
// ... Setters & Getters
}
SalesPK.java
#Embeddable
public class SalesPK implements Serializable {
#Column(name = "c_id")
private String customerId;
#Column(name = "p_id")
private String productId;
public SalesPK() {}
public SalesPK(String customerId, String productId) {
this.customerId = customerId;
this.productId = productId;
}
}
CustomerRepository.java
#Repository
public interface CustomerRepository extends CrudRepository<Customer, String> {
#Query("select customer from Customer customer " +
"left join fetch customer.customerProducts " +
"where customer.customerName = :customerName")
public Customer getCustomerPurchasedProducts(String customerName);
}
My Spring boot application fail to start with following exception:
org.hibernate.MappingException: Foreign key (FK7wwx8x75009xqb1y0tawm8rty:SALES [p_id])) must have same number of
columns as the referenced primary key (SALES [c_id,p_id])
What am I missing here? I have followed the notes as described here in https://www.baeldung.com/jpa-many-to-many
UPDATE:
There is no issue with above solution, I have misspelled "sales" table in #ManyToMany declaration changing from "sale" to "sales" fixed the issue. Strange behavior why it didn't compline about missing table instead it complain about actual composite primary key definition.
Following code Fixed the issue:
#ManyToMany
#JoinTable(
name = "sales",
joinColumns = #JoinColumn(name = "c_id"),
inverseJoinColumns = #JoinColumn(name = "p_id"))
private Set<Product> customerProducts = new HashSet<>();
}
I would map these classes a bit differently:
#Entity
#Table(name="customer")
public class Customer {
#Id
#Column(name="c_id")
private String customerId;
#Column(name="customer_name")
private String customerName;
#OneToMany(mappedBy = "customer")
private Set<Sale> customerSales = new HashSet<>();
}
#Entity
#Table(name="product")
public class Product {
#Id
#Column(name="p_id")
private String productId;
#Column(name="product_name")
private String productName;
#Column(name="price")
private Double price;
}
#Entity
#Table(name="sales")
public class Sales {
#EmbeddedId
private SalesPK salesId;
#MapsId("customerId") // maps customerId attribute of embedded id
#ManyToOne
Customer customer;
#MapsId("productId") // maps productId attribute of embedded id
#ManyToOne
Product product;
#Column(name="qty")
private Long qty;
// ... Setters & Getters
}
#Embeddable
public class SalesPK implements Serializable {
#Column(name = "c_id")
private String customerId;
#Column(name = "p_id")
private String productId;
public SalesPK() {}
public SalesPK(String customerId, String productId) {
this.customerId = customerId;
this.productId = productId;
}
}

Can I get the list of objects in relation #OneToMany?

I've got 2 classes: Device and Category. 1 Device can have 1 assigned category, but 1 category can have assigned many different devices.
#Entity
#Data
#Table(name = "devices")
public class Device implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#Column(name="amount_of_items")
private Integer amountOfItems;
private BigDecimal price;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category_id")
private Category category;
public Device(String name, String description, Integer amountOfItems, BigDecimal price, Category category){
this.name = name;
this.description = description;
this.amountOfItems = amountOfItems;
this.price = price;
this.category = category;
}
public Device() {}
}
#Entity
#Data
#Table(name = "categories")
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#OneToMany(mappedBy = "category", fetch = FetchType.EAGER)
private List<Device> devices = new ArrayList<>();
public Category(String name, String description){
this.name = name;
this.description = description;
}
public Category() { }
}
Can I get the actual list of devices for one Category? The below code returns me a null list of devices:
Category category = new Category("Urzadzenia AGD", "tylko dla klientow premium");
categoryRepository.save(category);
Device device = new Device("pralka", "samoobslugowa", 50, new BigDecimal("220"),
category);
deviceRepository.save(device);
System.out.println(category.getDevies()) ---> returns NULL
Can I do it by calling a getter like above?
save method already return value after save in Database you can use this
Category category = new Category("Urzadzenia AGD", "tylko dla klientow premium");
category= categoryRepository.save(category);
Device device = new Device("pralka", "samoobslugowa", 50, new BigDecimal("220"),
category);
deviceRepository.save(device);
System.out.println(category.getDevies())
and you must be make setter and getter method in your class
after this you have problem stackover flow exciption becouse the all device called category and categore call Devices
you can used #JsonIgnore annotation
Like this :
#OneToMany(mappedBy = "category", fetch = FetchType.EAGER)
#JsonIgnore
private List<Device> devices = new ArrayList<>();

JPA - Join another entity by EmbeddedId with 2 columns

Basically. I have a ProductEntity, like this:
#Entity
#Table(name = "product", schema = "shop")
public class ShopProductEntity {
#EmbeddedId
private ProductEntityId id;
#Column(name = "product_name")
private String name;
#Column(name = "product_category_id")
private int categoryId;
#Column(name = "product_language_id")
private int languageId;
// TODO: Get category by categoryId and languageId.
private CategoryEntity category;
I have another CategoryIdentity:
#Entity
#Table(name = "category", schema = "shop")
public class CategoryEntity {
#EmbeddedId
private CategoryEntityId id;
#Column(name = "category_name")
private String name;
#Column(name = "category_url")
private String url;
It has an EmbeddedId like this:
#Embeddable
public class CategoryEntityId implements Serializable {
#Column(name = "category_id", nullable = false)
private int categoryId;
#Column(name = "language_id", nullable = false)
private int languageId;
public int getCategoryId() {
return categoryId;
}
public int getLanguageId() {
return languageId;
}
Now, every product has a category. Categories are unique by their id and language. The shop connects to a category by both the categoryId and languageId columns. How do I add the CategoryEntity to my ProductEntity so I can use the category's url value for my product?
I tried adding this to ShopProductEntity:
#ManyToOne
#JoinColumns({
#JoinColumn(name="categoryId", referencedColumnName="categoryId"),
#JoinColumn(name="languageId", referencedColumnName="languageId"),
})
private CategoryEntity category;

How to get data from tables in spring data jpa?

I have two tables
#Entity
#Table(name = "TAX_CATEGORY")
public class TaxCategory {
#Id
#GeneratedValue
#Column(name = "ID", nullable = false)
private long id;
#Column(name = "CATEGORY", nullable = false)
private String category;
#Column(name = "TAX", nullable = false)
private Double tax;
#Entity
#Table(name = "PRODUCT")
public class Product {
#Id
#GeneratedValue
#Column(name = "ID", nullable = false)
private long id;
#Column(name = "PRICE", nullable = false)
private Double price;
#Column(name = "NAME", nullable = false)
private String name;
#OneToOne
#JoinColumn(name = "TAX_CATEGORY_ID")
private TaxCategory taxCategory;
Now I want to query
"Select p.name, p.price, t.tax from Product p, TaxCategory t join p.taxCategory.id=t.id"
So List it would return is
ProductName ProductPrice Tax
but I am not able to get this data from two tables. Single table data is working fine.
public interface CustomRepositoryCustom {
public void customMethod();
}
public interface CustomRepository
extends JpaRepository<Account, Long>, CustomRepositoryCustom { }
public class CustomRepositoryImpl implements CustomRepositoryCustom {
public void customMethod() {
Query nativeQuery = entityManager.createNativeQuery("Select p.name, p.price, t.tax from Product p, TaxCategory t join p.taxCategory.id=t.id");
return query.getResultList();
}
}
This throws exception that object is not managed bean. If I create custom object then also it gives similar type of issues.
Use the following JPA query to get the both tables data. Here used jpa query to fetch the product. From product object, can get the taxCategory.
public interface CustomRepository extends JpaRepository<Account, Long>, CustomRepositoryCustom {
Query("select product from Product as product join fetch product.taxCategory as taxCategory where taxCategory.id = :taxCategoryId")
public Product getProductByCategory(#Param Long taxCategoryId);
}
Instead of query method you can directly define JPA method to find products based on category Id as.
#Repository
#RepositoryRestResource
public interface ICountryRepository extends JpaRepository<Product , Long > {
List<Product> findByTaxCategory_Id(#Param Long Id);
}

Resources