DTO to entity mapping when entity has many to one relationship - java

I'm trying to map business object into entity but got stuck and have no idea how to resolve this.
The problem is when I try to map these attributes from DTO which are attributes of many-to-one relationship with another table.
Here is how my Entity looks like:
#Entity
#Table(name = "t_car")
public class Car extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "pkIdCar")
private Long id;
#Column(nullable = false, columnDefinition="VARCHAR(45)")
private String brand;
#Column(nullable = false)
private Integer productionYear;
#Column(nullable = false)
private Float engineCapacity;
#Column(nullable = false)
private Float power;
#Column(nullable = false)
private Integer distance;
#ManyToOne
#JoinColumn(name = "fkIdType")
private CarType carType;
#ManyToOne
#JoinColumn(name = "fkIdColor")
private Color color;
... }
And here is DTO:
public class CarDto {
private Long id;
private String brand;
private Integer productionYear;
private Float engineCapacity;
private Float power;
private Integer distance;
private CarTypes carType;
private ColorTypes color;
... }
CarTypes and ColorTypes are enums which have assigned number corresponding to their primary key in database. For example CarTypes is:
REGULAR_TWO_DOOR(1),
REGULAR_FOUR_DOOR(2),
STATION_WAGON(3),
MINIVAN(4),
SPORT(5),
LUXURY(6);
And here is the mapper i managed to write. How can I assign proper CarType (that's entity) to carEntity basing on CarType primary key?
public class CarMapper {
...
public static Car carDtoToCar(CarDto carDto, Car carEntity) {
if(carEntity == null) {
carEntity = new Car();
}
carEntity.setBrand(carDto.getBrand());
carEntity.setProductionYear(carDto.getProductionYear());
carEntity.setEngineCapacity(carDto.getEngineCapacity());
carEntity.setPower(carDto.getPower());
carEntity.setDistance(carDto.getDistance());
Long carTypeId = Long.valueOf(carDto.getCarType().getNumber());
Long carColorId = Long.valueOf(carDto.getColor().getNumber());
/* ? */
return carEntity;
}
}
CarType entity:
#Entity
#Table(name = "t_car_type")
public class CarType extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "pkIdType")
private Long id;
#Enumerated(EnumType.STRING)
#Column(nullable = false, columnDefinition="VARCHAR(45)")
private CarTypes type;
... }

Get the CarType entity based on the Cartype Id. For example:
CarType carType = entityManager.find(CarType.class, carTypeId);
And then set this carType in CarEntity as
carEntity.setCarType(carType);

Related

How to get object from OneToMany collection of objects?

I have an Order entity and OrderProduct. I want to show order details on frontend and of course order products in it. So how to fetch product object in OrderProduct JSON. I'm missing product object in products array. I don't need order object one more time and i think it going to be a infinite recursion stuff with it. :)
My Order entity:
#Entity
#Getter
#Setter
#Table(name ="orders")
public class Order{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
private BigDecimal totalPrice;
#OneToMany(mappedBy = "order", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonManagedReference(value="orders")
private List<OrderProduct> products = new ArrayList<>();
private int userId;
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date date = new Date();
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date deliveryDate;
#Enumerated(EnumType.STRING)
private OrderType orderType;
}
My OrderProduct entity:
#Entity
#Setter
#Getter
public class OrderProduct {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JsonBackReference(value="product")
#JoinColumn(name = "product_id")
private Product product;
#ManyToOne
#JsonBackReference(value="orders")
#JoinColumn(name = "order_id")
private Order order;
private Integer quantity;
}
Product entity:
#Entity
#Getter
#Setter
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private String name;
private double price;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
#JsonManagedReference(value="ingredients")
private List<Ingredient> ingredients = new ArrayList<>();
#OneToMany(mappedBy = "product",fetch = FetchType.EAGER)
#JsonManagedReference(value="product")
private List<OrderProduct> products = new ArrayList<>();
private String fileName;
}
This can help annotate one of your entity clases with
#JsonIdentityInfo(
property = "id",
generator = ObjectIdGenerators.PropertyGenerator.class
)
Every time when JSON serialization go in circles object data will be replaced with object id or orher field of entity for your choose.
You can use #JsonViewannotation to define the fields that you need to serialize to JSON
How it works:
You need define class with interfaces. For example:
public class SomeView {
public interface id {}
public interface CoreData extends id {}
public interface FullData extends CoreData {}
}
Mark entity fields with #JsonView(<some interface.class>)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(SomeView.id.class)
private Long id;
#Column(nullable = false)
#JsonView(SomeView.CoreData.class)
private String username;
#Column(nullable = false)
#JsonView(SomeView.FullData.class)
private String email;
}
Annotate endpoint with #JsonView(<some interface.class>)
#GetMapping()
#JsonView(<some interface.class>)
public User getUser() {
return <get user entity somwhere>
}
In case #JsonView(SomeView.id.class) you will get this JSON:
{
id: <some id>
}
In case #JsonView(SomeView.CoreData.class):
{
id: <some id>,
username: <some username>
}
In case #JsonView(SomeView.FullData.class):
{
id: <some id>,
username: <some username>,
email: <some email>
}
#JsonView also works with embeded objects and you can annotate one field with multiply views classes - #JsonView({SomeView.FullData.class, SomeOtherView.OtherData.class})
In your case i think you should annotate all the fields you need except:
#OneToMany(mappedBy = "product",fetch = FetchType.EAGER)
#JsonManagedReference(value="product")
private List<OrderProduct> products = new ArrayList<>();
in Product
to avoid circular serialization
Or as alternative you can just use DTO classes or seralize oject to JSON manualy (https://thepracticaldeveloper.com/java-and-json-jackson-serialization-with-objectmapper/)
This can be done by my library beanknife
// This configure generate a class named ProductInfo which has the same shape with Product without property "products"
#ViewOf(value = Product.class, genName="ProductInfo", includePattern = ".*", excludes = {"products"})
class ProductInfoConfigure {}
// This configure generate a class named OrderProductRelation with the same shape of OrderProduct.
// But it has not order property and the type of its product property is change to ProductInfo generated above.
#ViewOf(value = OrderProduct.class, genName="OrderProductRelation", includePattern = ".*", excludes = {"order"})
class OrderProductRelationConfigure {
#OverrideViewProperty("product")
private ProductInfo product;
}
// This configure generate a class named OrderDetail with the same shape of Order.
// But the type of its products property is change to List<OrderProductRelation>
#ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
class OrderDetailConfigure {
#OverrideViewProperty("products")
private List<OrderProductRelation> products;
}
will generate these classes:
class ProductInfo {
private Long id;
private String name;
private double price;
private List<Ingredient> ingredients; // it is not processed because you have not provide the class Ingredient
private String fileName;
}
public class OrderProductRelation {
private Long id;
private ProductInfo product;
private Integer quantity;
}
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
private Date date = new Date();
private Date deliveryDate;
private OrderType orderType;
}
Then
Order order = ...
OrderDetail orderDetail = OrderDetail.read(order);
// serialize the otherDetail instead of order.
List<Order> orders = ...
List<OrderDetail> orderDetails = OrderDetail.read(orders);
// serialize the orderDetails instead of orders.
Possible problems:
I doesn't use Lombok, so Lombok may need to be adapted because it change the byte code on the fly. But it is not a big problem, I will try to adapt it if someone commit the issue and provide enough use cases.
The generated class does not inherit the annotation on the original class. In next release I will provide a sulotion. At this moment, as a workaround, we can use custom method to convert the property manually. such as
#ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
class OrderDetailConfigure {
#OverrideViewProperty("products")
private List<OrderProductRelation> products;
#OverrideViewProperty("orderType")
public static String orderType(Order source) {
return source.getOrder().name();
}
}
The generated class will be changed to
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
private Date date = new Date();
private Date deliveryDate;
private String orderType;
}
Update
Version 1.2.0 released. Add support of annotation inheritance.
#ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
#UseAnnotation({DateTimeFormat.class, Enumerated.class, JsonProperty.class})
class OrderDetailConfigure {
#OverrideViewProperty("products")
private List<OrderProductRelation> products;
}
generate
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date date;
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date deliveryDate;
#Enumerated(EnumType.STRING)
private OrderType orderType;
}

Entity without Primary Key ID

Hi I want to create an Entity which doesn't have an ID.
#Entity
#Table(name="USER_PROD_LIC_TYPE_ALL")
public class UserProdLicTypeAll {
#EmbeddedId
private UserProdLicTypeAllPK id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="USER_ID")
private User user;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="LICENSE_TYPE_ID")
private LicenseType licType;
....
}
since it doesn't have primary key i created Embeddable class as below:
#Embeddable
public class UserProdLicTypeAllPK {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="USER_ID")
private User user;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="LICENSE_TYPE_ID")
private LicenseType licType;
...
}
The combination of these two fields returns a unique value.
But it doesn't work. I get
org.springframework.beans.factory.UnsatisfiedDependencyException: exception.
Do i need to have references in User and LicenseType entities for both UserProdLicTypeAll and UserProdLicTypeAllPK? I have tried that also but still it doesn't work.
This is my private hell. I'm not sure what the best way to solve it properly.
My best solution is:
#Entity
#Table(name = TableName.AREA_USER)
#IdClass(UserAreaPK.class)
public class UserArea implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = UserAreaPK.C_AREA_ID)
private Long areaId;
#Id
#Column(name = UserAreaPK.C_USER_ID)
private String userId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = UserAreaPK.C_AREA_ID, insertable = false, updatable = false)
private Area area;
And
/**
* Primary key for the relationship User-Area
*/
public class UserAreaPK implements Serializable {
protected static final String C_AREA_ID = "area_id";
protected static final String C_USER_ID = "user_id";
private static final long serialVersionUID = 1L;
#Id
#Column(name = C_AREA_ID)
private Long areaId;
#Id
#Column(name = C_USER_ID)
private String userId;

How to use a child association property as a Map key in JPA parent entity

I'm having two entities Car and CarDescription where CarDescription is depending on another foreign key from the table Language.
What I' trying to accomplish is to have a HashMap in Car such that whenever I'm having a Car entity-object I am able to access all descriptions from the language id.
Entity Car.java
#Entity
#Table(name = "Car")
public class Car extends AbstractTimestampEntity implements Serializable {
private static final long serialVersionUID = -5041816842632017838L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#OneToMany(mappedBy="car")
#MapKeyColumn(name = "language_ID")
// #MapKey(name = "language") // does not work either
private Map<Long, CarDescription> carDescription = new HashMap<>(0);
}
Entity CarDescription.java
#Entity
#Table( name="car_description",
uniqueConstraints = {
#UniqueConstraint(columnNames={"language_id", "name"})
}
)
public class CarDescription extends AbstractTimestampEntity implements Serializable {
private static final long serialVersionUID = 2840651722666001938L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#NotNull
#ManyToOne
private Car car;
#NotNull
#OneToOne
private Language language;
// ..
}
Entity Language.java
#Entity
public class Language implements Serializable {
private static final long serialVersionUID = 3968717758435500381L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID")
private Long id;
// ..
}
The problem I am having is that the mapping gives me a map from each CarDescription.id to CarDescription.
How can I accomplish a correct mapping?
In CarDescription you need to add the languageId property:
#Column(name = "language_id", insertable = false, updatable = false)
private Long languageId;
#NotNull
#OneToOne
#JoinColumn(name = "language_id")
private Language language;
public void setLanguage(Language language) {
this.languageId = language.getId();
this.language = language;
}
Then you can use it in the Car entity like this:
#OneToMany(mappedBy="car")
#MapKey(name = "languageId")
private Map<Long, CarDescription> carDescription = new HashMap<>(0);

Understanding Single Table Inheritance in JPA

I am new to JPA and doing a small sample to learn about it.
But I got one problem below, please help me out, and please explain why:
I have class Customer.java, which is mapped to table customer in db:
#Entity
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id_customer")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
// accountNumber field maps with accountNumber column in Account table
#Column(name = "loginId", unique = true)
private String loginId;
#Column(name = "password")
private String password;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#Column(name = "address")
private String address;
#Column(name = "email")
private String email;
#Column(name = "phone")
private String phone;
#OneToMany(mappedBy="customer")
private List<Account> accountList;
#OneToMany(mappedBy="customer")
private List<Card> cardList;
// getters and setters goes here
}
The above class has two lists, accountList and cardList, their generic Class (Card and Account) extends BaseInfo using Single table Inheritance.
Here is my BaseInfo.java:
#Entity
#Table(name = "account")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.STRING)
public class BaseInfo implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "number")
private String number;
#Column(name = "availableNumber")
private Long availableNumber;
//getter and setter here
}
Class Card.java:
#Entity
#Table(name = "account")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue(value = "C")
public class Card extends BaseInfo implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "cardType")
private String cardType;
#ManyToOne
#JoinColumn(name = "id_customer")
private Customer customer;
//getter and setter
}
And class Account.java:
#Entity
#Table(name = "account")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue(value = "A")
public class Account extends BaseInfo implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "accountName")
private String accountName;
#Column(name = "accountType")
private String accountType;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "dt_created")
private Date createdDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "dt_lst_updt")
private Date lastUpdatedDate;
#ManyToOne
#JoinColumn(name = "id_customer")
private Customer customer;
//getter, setter
}
Then, I do a query that query customer from database with loginid and password, like this:
entityTransaction.begin();
TypedQuery<Customer> query = entityManager.createQuery(
"SELECT c FROM " + Customer.class.getName()
+ " c Where c.loginId= :loginId", Customer.class);
query.setParameter("loginId", loginId);
res = query.getSingleResult();
entityTransaction.commit();
The code run with no error, but the result is somethings strange to me: When I debug (or print out the result to jsp), accountList or cardList contains all Account of that customer, just like they don't care about the 'discriminator' column.
I have 2 questions:
How can I archive the goal that listCard contains only Card (discrimination = c) and listAccount contains only Account (discriminator = a) ?
Is there an alternative way to query listCard or listAccount without query the customer first (like I use) ??
Thank in advance! :D
I'm not sure if it's a JPA restriction or a Hibernate-specific restriction, but you may not use the same column to map two different associations.
You should use something like car_customer_id to map the association between customer and cards, and account_customer_id to map the association between customer and accounts.

java.lang.IllegalStateException: No supertype found on query

I have the following
#Entity
#Table(name = "PROJECTS")
public class Project implements Serializable {
#Id
private Integer SlNo;
#Id
private Long projectNo;
private Date projectDate;
}
and in DAO class
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> countQ = cb.createQuery(Long.class);
Root<Project> empCount = countQ.from(Project.class);
countQ.select(cb.count(empCount));
TypedQuery<Long> countquery = entityManager.createQuery(countQ);// error in this line
I am getting exception java.lang.IllegalStateException: No supertype found in the above line. How can I resolve or workaround this issue? Looks like there is a bug, are there any solution to this?
I am using Hibernate 4.1.0.Final
I have resolved the issue by using #EmbeddedId in Entity class and #Embeddable in Primary Key class.
#Entity
#Table(name = "PROJECTS")
public class Project {
#Column(name = "SL_NO" , insertable = false, updatable = false)
private Integer SlNo;
#Column(name = "PROJECT_NO" , insertable = false, updatable = false)
private Long projectNo;
private Date projectDate;
#EmbeddedId
ProjectPK projectPK;
and Primary Key class
#Embeddable
public class ProjectPK implements Serializable {
#Column(name = "SL_NO")
private Integer SlNo;
#Column(name = "PROJECT_NO")
private Long projectNo;
//with hashCode and equals implementation
for the case Using #EmbeddedId, here is my solution. This code I have written in one class itself, in Entity class.
Class MyEntity - It is my actual Entity class for my table. "OtherFields" are those fields which are not part of primary key.
Class MyEntityPrimaryKeys - It is the class made for my composite key which makes a primary key for my "MyEntity" class. Here ROLLNO and AGE together makes a primary key.
MyEntity.java
#Entity
#Table(name = "myTable")
public class MyEntity extends GenericPersistableEntity implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
MyEntityPrimaryKeys id;//Composite Primary key
//Composite fields can be declared here for getter and setters
#Column(name = "ROLLNO")
private Long RollNo;
//Composite fields can be declared here for getter and setters
#Column(name = "AGE")
private Long age;
#Column(name = "OtherFields"
private Long OtherFields;
//getter and setters comes here
}
#Embeddable
class MyEntityPrimaryKeys implements Serializable{
private static final long serialVersionUID = 1L;
#Column(name = "ROLLNO")
Long RollNo;
#Column(name = "AGE")
Long age;
#Override
public int hashCode() {
HashCodeBuilder hcb = new HashCodeBuilder();
hcb.append(RollNo);
hcb.append(age);
return hcb.toHashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof MyEntityPrimaryKeys)) {
return false;
}
MyEntityPrimaryKeys that = (MyEntityPrimaryKeys) obj;
EqualsBuilder eb = new EqualsBuilder();
eb.append(RollNo, that.RollNo);
eb.append(age, that.age);
eb.append(tonMonth, that.tonMonth);
eb.append(tonYear, that.tonYear);
return eb.isEquals();
}
}

Categories