I'm writing my new java project and a requirement is to represent product that can belong to a category.
I'm using a database in my project and I connect products and categories by a foreign key.
In the code, instead, I've to use SOLID design and I don't understand how can I connect products and categories.
In a first version, the code was
public class Product {
private int ID;
private String name;
private String descr;
private int stock;
private float price;
private int category;
public Product(int anID, String aName, String aDescr, int aStock, float aPrice, int aCategory) {
this.ID = anID;
this.name = aName;
this.descr = aDescr;
this.stock = aStock;
this.price = aPrice;
this.category = aCategory;
}
public int getID() { return this.ID; }
public String getName() { return this.name; }
public String getDescr() { return this.descr; }
public int getStock() { return this.stock; }
public float getPrice() { return this.price; }
public int getCategory() { return this.category; }
public void decreaseStock(int x) { this.stock -= x; }
}
and
public class Category {
private int ID;
private String name;
private String descr;
public Category (int anID, String aName, String aDescr) {
this.ID = anID;
this.name = aName;
this.descr = aDescr;
}
public int getID() { return this.ID; }
public String getName() { return this.name; }
public String getDescr() { return this.descr; }
}
... but I'm thinking that product can implements category, in order to have all information in one object and not jump between two classes...
Which is the best way to write it?
You should not mimic the underlying database table structure in your Java classes verbatim. The correct way to do and which every ORM approach that I worked on until now uses is as follows:
Product class stores a reference to a Category instance.
When fetching the records from a database within the data access layer you would explicitly write code to to create a Category object first and then pass it to the Product class constructor when creating the Product object.
This way the Java class hierarchy reflects the true business relationship between a Product and its related Category. This also has the advantage of abstracting the storage details from the application - consider what would happen with the approach you are currently adopting if the data were to be stored in a NoSQL database. However, by adopting the approach presented in this answer you would only need to change the Data access layers for creating the correct objects - your class design remains intact (The O of Open-Closed principle in SOLID).
Related
I am writing a simple inventory system for practice and I have an item class that holds these values:
private String name;
private int quantity;
private Integer ID;
private Double pricePerUnit;
private Double totalPrice;
I am writing the constructors for this class and I want everything except the name and quantity to be optional, as in the user can opt whether or not to input any data for those fields. Currently I have two constructors that look like this:
public Item(String name, int quantity)
{
this.name=name;
this.quantity=quantity;
}
public Item(String name, int quantity, Integer ID, Double pricePerUnit, Double totalPrice)
{
this(name, quantity);
this.ID=ID;
this.pricePerUnit=pricePerUnit;
this.totalPrice=totalPrice;
}
Is there any way I can make some of the arguments in the second constructor optional, or non-mandatory?
Thanks!
Naturally, in such cases I'd would think of two possibilities to get what you need, Constructor Overloading and Builder Pattern
Although when you have the same data formats, You can't naturally depend on the former. In such cases (as OP's Question) the best alternative is to go for a builder design pattern.
You can build an instance of Item.class as the following
public class Item {
\\... other public functions.. etc
static class ItemBuilder{
private Item item;
public ItemBuilder withNameAndQuantity(String name, int quantity){
item = new Item(); //default constructor or as per your usecase a private constructor
item.setName(name);
item.setQuantity(quantity);
return this;
}
public ItemBuilder withPricePerUnit(Double pricePerUnit){
if(item!=null){
item.setPriceUnit(pricePerUnit);
}
return this;
}
public ItemBuilder withTotalPrice(Double totalPrice){
if(item!=null){
item.setTotalPrice(totalPrice);
}
return this;
}
public Item build(){
if(item!=null){
return item;
}else{
throw new IllegalStateException("item is null, no name or id set");
}
}
}
}
Finally, you could build a new Item by doing the following :
Item item = new Item.ItemBuilder(). withNameAndQuantity("apple",10). withTotalPrice(100).build();
Ideally you would decompose the class into coherent pieces, in much the same way as you might normalise a database schema.
There is the Builder Pattern.
You code could be written better by making the constructor will all the attributes the "canonical constructor* and forward to that. This will also make it easier to switch over to records when they become available.
public Item(String name, int quantity) {
this(name, quantity, null, null, null);
}
public Item(String name, int quantity, Integer id, Double pricePerUnit, Double totalPrice) {
this.name = name;
this.quantity = quantity;
this.ID = ID;
this.pricePerUnit = pricePerUnit;
this.totalPrice = totalPrice; // Shouldn't this be calculated?
}
(Not that nulls are ideal.)
One way is to create more constructors and another is to loose the immutability and introduce setter methods.
Hence, you can use Builder Pattern as Builder Pattern will help you to consume additional attributes while retaining the immutability of Item class.
Below is the coded solution. This uses a additional class ItemBuilder which helps us in building desired Item object with all mandatory attributes and combination of optional attributes, without loosing the immutability.
public class Item {
//All final attributes
private String name; // required
private int quantity; // required
private Integer ID; // optional
private Double pricePerUnit; // optional
private Double totalPrice; // optional
private Item(ItemBuilder builder) {
this.name = builder.name;
this.quantity = builder.quantity;
this.ID = builder.ID;
this.pricePerUnit = builder.pricePerUnit;
this.totalPrice = builder.totalPrice;
}
//All getter, and NO setter to provide immutability
public String getName() {
return name;
}
public int getQuantity() {
return quantity;
}
public Integer getID() {
return ID;
}
public Double getPricePerUnit() {
return pricePerUnit;
}
public Double getTotalPrice() {
return totalPrice;
}
#Override
public String toString() {
return "User: "+this.name+", "+this.quantity+", "+this.ID+", "+this.pricePerUnit+", "+this.totalPrice;
}
public static class ItemBuilder
{
private String name; // required
private int quantity; // required
private Integer ID; // optional
private Double pricePerUnit; // optional
private Double totalPrice; // optional
public ItemBuilder(String name, int quantity) {
this.name = name;
this.quantity = quantity;
}
public ItemBuilder ID(Integer ID) {
this.ID = ID;
return this;
}
public ItemBuilder pricePerUnit(Double pricePerUnit) {
this.pricePerUnit = pricePerUnit;
return this;
}
public ItemBuilder totalPrice(Double totalPrice) {
this.totalPrice = totalPrice;
return this;
}
//Return the finally constructed Item object
public Item build() {
Item item = new Item(this);
validateUserObject(item);
return item;
}
private void validateUserObject(Item item) {
//Do some basic validations to check
//if item object does not break any assumption of system
}
}
}
OR
You can also make use of JsonProperties.
#JsonIgnoreProperties(ignoreUnknown = true)
public record Item(
String name,
Integer quantity,
#JsonInclude(JsonInclude.Include.NON_NULL) Integer ID,
#JsonInclude(JsonInclude.Include.NON_NULL) Double pricePerUnit,
#JsonInclude(JsonInclude.Include.NON_NULL) Double totalPrice) {}
I hope this gives you clarity on how to resolve your problem.
I'm creating a simple web store as part of a larger application, using Spring Boot and Spring Data JPA. I have a number of items (my stock), and an order will consist of a collection of those items (along with shipping information, etc).
It's possible that items in the stock will update - for example, changing the price. I want to be able to do this without the overhead of versioning, etc. However, when I look up past orders I want to get the information that was used at the time.
Is there a way in Spring Data JPA of embedding a copy of the item in my order, rather than linking to the item object?
My classes are below:
ShopItem.java
#Entity
public class ShopItem {
#Id
#GeneratedValue
private Long id;
private String name;
private String description;
private int price;
private int stock;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public void reduceStock(int by){
this.stock = this.stock - by;
}
}
ShopOrder.java
#Entity
public class ShopOrder {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private Member member;
private boolean postage;
#OneToMany
private List<ShopOrderItem> items;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Member getMember(){
return member;
}
public void setMember(Member member) {
this.member = member;
}
public boolean getPostage() {
return postage;
}
public void setPostage(boolean postage) {
this.postage = postage;
}
public List<ShopOrderItem> getItems() {
return items;
}
public void setItems(List<ShopOrderItem> items) {
this.items = items;
}
}
ShopOrderItem.java
#Entity
public class ShopOrderItem {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private ShopItem item;
private int quantity;
public ShopOrderItem(ShopItem item, int quantity){
this.item = item;
this.quantity = quantity;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public ShopItem getItem() {
return item;
}
public void setItem(ShopItem item) {
this.item = item;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getTotalPrice(){
return item.getPrice() * quantity;
}
}
Spring Data JPA is only a way to make your life easier when you use an ORM. and a ORM is only a way to make your life easier when you use a Database.
The thing is, if you are using a relational Database, there is no way to do what you want except by cloning datas.
so, one way is to clone your ShopItem entity and store it as a new entry in your database in a dedicated table.
another way may be to use a NoSQL DB which could handle this better.
There is a number of different ways to store a history of orders, but I can't think of anything from Spring data JPA which could do it out of the box.
I have two classes one is called Modifiers and the other is called Options. Modifiers has an id, name and a group of options associated with it. While Options (or each option) has a name and price. Each option is associated with only one Modifier. What is the best way of Writing these classes to take into account their relationship. In my application, the user has to enter the name of the Modifier, then they have to enter the name and price of each option. Each modifier can have as many options as necessary. Here are the classes and how I am using them for now:the user has to enter the name of the Modifier, then they have to enter the name and price of each option. Each modifier can have as many options as necessary. Here are the classes and how I am using them for now:
public class Modifier {
int id;
String name;
Options [] options;
public Modifier() {
}
public Modifier(String name, Options[] options) {
this.name = name;
this.options = options;
}
public Modifier(int id, String name, Options[] options) {
this.id = id;
this.name = name;
this.options = options;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Options[] getOptions() {
return options;
}
public void setOptions(Options[] options) {
this.options = options;
}
}
I am currently add the group of Options as an array. Here is the Options class.
public class Options {
int id;
String name;
public Options() {
}
public Options(String name, long price) {
this.name = name;
this.price = price;
}
public Options(int id, String name, long price) {
this.id = id;
this.name = name;
this.price = price;
}
long price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
}
And in my main:
Options sausage = new Options("sausage",23);
Options olives = new Options("olives", 24);
Options bacon = new Options("bacon", 25);
Options [] toppings = {sausage, olives, bacon};
Modifier pizza_toppings = new Modifier(1, "Pizza toppings",toppings);
I plan on using an SQLIte DB so I need to find an efficient way of doing this.
you could add one field to Options class which save modifier id, then with sqlite you will create two tables one for modifiers and one for options, and when you retrieve data from it, you use foreign key to build your object. whoever your classes now looks fine, you may just save modifiers ids in options table and the vice versa like this:
modifier table [modifierId, name] - option table [optionId, name, modifierId]
public class MyObject
{
public static enum Type {A, B, C, D;}
public static final int ID_MAIN = 1;
public static final int ID_MAIN_UK = 2;
public static final int ID_MAIN_US = 3;
public static final int ID_SUB = 4;
// lots more constants here
public static final String DESCRIPTION_1 = "Desc Full Name";
public static final String DESCRIPTION_2 = "Desc2 Full Name";
// lots more constants here
private int id;
public MyObject(final int id)
{
this.id = id;
}
//simple getter
public int getID() { return this.id;}
// real responsibility of the class is in the following two methods
public static String getDescription()
{
switch(id)
{
case MyObject.ID_MAIN:
case MyObject.ID_MAIN_UK:
return MyObject.DESCRIPTION_1;
case MyObject.ID_SUB:
return MyObject_Description_2;
default:
// throw IllegalArgException
}
}
public static Type getType(int id)
{
switch(id)
{
case MyObject.ID_MAIN:
case MyObject.ID_SUB:
return Type.A;
case MyObject.ID_MAIN_UK:
case MyObject.ID_MAIN_US:
return Type.B;
default:
return Type.Undefined;
}
}
}
Basically, there is an ID that maps to both a description and a type. This ID is passed in during construction of the class and it should map to a set of constants already contained in the class. If the id is not part of the list of constants, an error is thrown when trying to get the description that maps to the id and an 'Unknown' type is return if the type is queried. The ID maps a description to a set of constants. The same ID maps to a certain Type (defined as an enum).
This code is pretty ugly because there are tons of constants defined at the top, which makes the switch statements pretty bloated. Is there a simple way to refactor this without changing the public interface? It seems trivially simple, but it seems pretty ugly no matter how you slice it. How can I simplify these mappings to make the code more concise?
I was thinking about representing the mappings in a text file and having a manager class that held simple containers in a hashmap. When the manager class is constructed, it would create the objects by reading the text file and map them to an ID. When the manager is queried with the ID, it would just call the corresponding get method, for instance:
class Manager
{
private HashMap<int, MyObject> objectMap;
public Manager() {} //construct the object map
public String getDescription(int id) { return objectMap.get(id).getDescription();}
public Type getType(int id) { return objectMap.get(id).getType();}
}
class DataContainer
{
private String description;
private Type type;
public DataContainer(String desc, Type type) {//set mem vars}
public String getDescription() //simple getter
public Type getType() //simple getter
}
But this solution seems too complicated. Is there a better solution, preferably one that would keep everything in one class?
You can do something like following. This would be much cleaner and manageable.
public enum Type
{
MAIN(1, "Main Description"),
MAIN_UK(2, "Main UK Description"),
//....
//Define all the types
//....
UNKNOWN(-1, "Unknown Type");
private int id;
private String description;
private Type(int id, String description)
{
this.id = id;
this.description = description;
}
public static Type getById(int id)
{
for (Type type : Type.values())
{
if (id == type.getId())
{
return type;
}
}
return Type.UNKNOWN;
}
public final int getId()
{
return id;
}
public final String getDescription()
{
return description;
}
}
public class MyObject
{
private int id;
private Type type;
public MyObject(int id)
{
this.id = id;
this.type = Type.getById(id);
}
public int getId()
{
return id;
}
public Type getType()
{
return type;
}
public String getDescription()
{
return type.getDescription();
}
}
In Java enums can have methods. For example following one accepts ID and description and provides some accessors.
public enum Type {
MAIN(1, "desc1"),
UK(2, "desc2"),
SUB(4, "desc4");
private int id;
private String desc;
Type(int id, String desc) {
this.id = id;
this.desc = desc;
}
public String getDescription() {
return desc;
}
public int getType() {
//return id;
return 1+2 + 3+ id;
}
}
You could use that to improve design.
Suppose I have 2 beans:
Award
Employee
I add both of them in a collection. Based on this collection I want to generate a report using DynamicJasper using JRBeanCollectionDataSource.
I'm able to generate the report for a single bean, but for collection of different bean I cant create a report - I get an error.
Is it possible to create a report for two different beans at a time?
Are there any alternative solution to solve this task?
With the information given, it implies there is no relationship between Award and Employee.
If that is the case you could create a Custom Data Source to handle this for you. For the sake of completeness I am going to include a stubbed out Award, and Employee class along with a custom Data Source called MixedDataSource. (There is going to be a lot code listed here, but stick with me for a second).
Award.java
package test;
public class Award {
private String shortName;
private String description;
public Award(String shortName, String description) {
super();
this.shortName = shortName;
this.description = description;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Employee.java
package test;
public class Employee {
private String name;
private String position;
public Employee(String name, String position) {
super();
this.name = name;
this.position = position;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
}
MixedDataSource.java
package test;
import java.util.List;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
#SuppressWarnings("rawtypes")//have to add this because we are not using generics
public class MixedDataSource implements JRDataSource {
private List rows;
private int index=0;
public MixedDataSource(List rows) {
super();
this.rows = rows;
}
#Override
public Object getFieldValue(JRField arg0) throws JRException {
Object obj = rows.get(index);
if (obj instanceof Award){
Award row = (Award)obj;
//now get the field name
if (arg0.getName().equals("shortName")){
return row.getShortName();
} else if(arg0.getName().equals("description")){
return row.getDescription();
}
} else if (obj instanceof Employee){
Employee row = (Employee)obj;
if (arg0.getName().equals("name")){
return row.getName();
} else if(arg0.getName().equals("position")){
return row.getPosition();
}
}
//means we don't know what to do with it, so just return null
return null;
}
#Override
public boolean next() throws JRException {
//This method is used by jasper to tell us they are moving to the next row.
//So increment the index and return true if there are still more rows, if not
//return false
index = index+1;
if(index < rows.size()){
return true;
}
return false;
}
}
This would work, but probably not the way you want. My assumption is that there is a relationship between an Award and Employee and you are wanting to do some type of grouping and ordering. Essentially you want an Award and Employee on the same row. You may want to group by Award and list the Employees. You may want to do the opposite and group by Employee and list Awards. If that is the case, ignore all that above, it is useless.
What you really need to do is create a new bean (we could call it EmployeeAward) with the information you have. This could be really easy, if you are using a sql query to do this, or may require a little more work if you are using something like hibernate. where you are essentially just saying give all the Awards and it gives you a list. If you are doing the latter, it will probably be easier to step down to SQL or HSQL(I think that what they call it) and write the query by hand.
So create a class called EmployeeAward. Here is mine:
package test;
public class EmployeeAward {
private String employeeName;
private String employeePosition;
private String shortName;
private String description;
public EmployeeAward(String employeeName, String employeePosition,
String shortName, String description) {
super();
this.employeeName = employeeName;
this.employeePosition = employeePosition;
this.shortName = shortName;
this.description = description;
}
public EmployeeAward(Employee employee, Award award) {
super();
this.employeeName = employee.getName();
this.employeePosition = employee.getPosition();
this.shortName = award.getShortName();
this.description = award.getDescription();
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getEmployeePosition() {
return employeePosition;
}
public void setEmployeePosition(String employeePosition) {
this.employeePosition = employeePosition;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Now by whatever means you have create your list of these object so you have a List<EmployeeAward> emplyeeAwards instantiated in there. Each item in this list is going to be a row in your report. Now here is the nice part, create your dataSource:
JRDataSource datasource = new JRBeanCollectionDataSource(employeeAwards);
Then pass that on like normal and your work is done.
In closing, I would like to say you can do a lot of cool things with JasperReports, but what I see a lot of folks doing is making it more complicated than it needs to be. You will find that JasperReports like what I call flat datasources (i.e. same objects in list, each item in list is a row, etc). If you do that, life becomes easier and more enjoyable.
An alternative for this can be use an SubReport and pass the List<Award> to the MainReport and the List<Employee> to the SubReport, doing that, you can set your SubReport as an extension of your MainReport, example:
MainReport:
|FieldAward1|FieldAward2|FieldAward3|...|YOUR SUBREPORT HERE|
SubReport:
|FieldEmployee1|FieldEmployee3|FieldEmployee3|...
Edit:
Even if it seems basic, I have to tell you that you should sort your list in a way to make each Object inside one list matches with its repective Object inside the another one.