Mapping issue on Hibernate annotation - java

I have the above tables i need to write the HIbernate entity class with annotation
and mapping
i have a problem while getting the List of ObjectAttributes..
Class are written as below
#Entity
public class Object {
#Id
private int id;
private String name;
#OneToMany(mappedBy="object",fetch=FetchType.LAZY)
private List<ObjectAttribute> attrubuteList;
}
#Entity
public class ObjectAttribute {
#Id
private int id;
#ManyToOne
#JoinColumn(name="objectId")
private Object object;
private String name;
}
#Entity
public class Filter {
#Id
private int filterId;
#ManyToOne
#JoinColumn(name="ObjectId")
private Object object;
private String filterName;
#OneToMany(mappedBy="filter")
private Set<FilterAttribute> filterValues;
}
#Entity
public class FilterAttribute implements Serializable {
#Id
private int filterAttrId;
#Id
#ManyToOne
#JoinColumn(name="objectId")
private Object object;
#Id
#ManyToOne
#JoinColumn(name="filterId")
private Filter filter;
#Id
#ManyToOne
#JoinColumn(name="attributeId")
private ObjectAttribute attribute;
private String value;
}
Note not added getter and setters
and test code as below
List<Object> list = sess.createCriteria(Object.class).list();
for(Object ob: list)
{
System.out.println("Object name : "+ ob.getName());
List<ObjectAttribute> attList = ob.getAttrubuteList();
for (Iterator iterator = attList.iterator(); iterator.hasNext();) {
ObjectAttribute objectAttribute = (ObjectAttribute) iterator
.next();
System.out.println(objectAttribute.getName());
}
}
attList = ob.getAttrubuteList(); returns null

While creating the Object in my test code as below
Object obj = new Object();
obj.setId(1);
obj.setName("Employee");
sess.save(obj);
ObjectAttribute ageAtt = new ObjectAttribute();
ageAtt.setId(1);
ageAtt.setName("age");
ageAtt.setObject(obj);
sess.save(ageAtt);
ObjectAttribute depAtt = new ObjectAttribute();
depAtt.setId(2);
depAtt.setName("department");
depAtt.setObject(obj);
sess.save(depAtt);
ObjectAttribute attName = new ObjectAttribute();
attName.setId(3);
attName.setName("name");
attName.setObject(obj);
sess.save(attName);
I have not added these line
obj.getAttrubuteList().add(depAtt);
obj.getAttrubuteList().add(attName);
obj.getAttrubuteList().add(ageAtt);
But i dont know why we need to do this!!

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;
}

Mapstruct bidirectional mapping throwing out of memory error

//Entities and DTOs
public class JourneyType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "journey_type_id")
private Long journeyTypeId;
private String type;
// bi-directional many-to-one association to JourneyRent
#OneToMany(mappedBy = "journeyType")
private Set<JourneyRent> journeyRents;
}
public class JourneyTypeTO implements Serializable {
private Long journeyTypeId;
private String type;
private Set<JourneyRentTO> journeyRents;
}
public class JourneyRent implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "journey_rent_id")
private Long journeyRentId;
#Column(name = "min_max_travel_km")
private Double minMaxTravelKm;
#Column(name = "rent_charges")
private BigDecimal rentCharges;
// bi-directional many-to-one association to JourneyType
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "journey_type_id")
private JourneyType journeyType;
// bi-directional many-to-one association to VehicleType
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "vehicle_type_id")
private VehicleType vehicleType;
}
public class JourneyRentTO implements Serializable {
private Long journeyRentId;
private Double minMaxTravelKm;
private BigDecimal rentCharges;
private JourneyTypeTO journeyType;
private VehicleTypeTO vehicleType;
}
public class VehicleType implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="vehicle_type_id")
private Long vehicleTypeId;
private String type;
//bi-directional many-to-one association to JourneyRent
#OneToMany(mappedBy="vehicleType")
private Set<JourneyRent> journeyRents;
}
public class VehicleTypeTO implements Serializable {
private Long vehicleTypeId;
private String type;
private Set<JourneyRentTO> journeyRents;
}
// Mapper interfaces
#Mapper(uses = {JourneyRentMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface JourneyTypeMapper {
JourneyTypeMapper INSTANCE = Mappers.getMapper(JourneyTypeMapper.class);
#Mapping(target = "journeyBookings", ignore = true)
JourneyTypeTO toDTO(JourneyType journeyType, #Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
#Mapping(target = "journeyBookings", ignore = true)
JourneyType toEntity(JourneyTypeTO journeyType);
}
#Mapper(uses = {JourneyTypeMapper.class, VehicleTypeMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface JourneyRentMapper {
JourneyRentMapper INSTANCE = Mappers.getMapper(JourneyRentMapper.class);
#Mapping(target = "journeyType", ignore = true)
JourneyRentTO toDTO(JourneyRent journeyRent, #Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
#Mapping(target = "journeyBookingVehicles", ignore = true)
JourneyRent toEntity(JourneyRentTO journeyRentTo);
}
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface VehicleTypeMapper {
VehicleTypeMapper INSTANCE = Mappers.getMapper(VehicleTypeMapper.class);
VehicleTypeTO toDTO(VehicleType vehiType);
VehicleType toEntity(VehicleTypeTO vehiTypeTo);
}
public class CycleAvoidingMappingContext {
private final Map<Object, Object> knownInstances = new IdentityHashMap<>();
#BeforeMapping
public <T> T getMappedInstance(Object source, #TargetType Class<T> targetType) {
return targetType.cast(knownInstances.get(source));
}
#BeforeMapping
public void storeMappedInstance(Object source, #MappingTarget Object target) {
knownInstances.put(source, target);
}
}
There is a bi-directional relationship between JourneyType and JourneyRent. Now JPQL query returns JourneyType Object which has a reference of
JourneyRent object and in turn JourneyRent object has a reference of JourneyType Object.
Usage :
List<JourneyTypeTO> journeyTypeTos = new ArrayList<>();
for (JourneyType journeyType : journeyTypes) {
journeyTypeTos.add(JourneyTypeMapper.INSTANCE.toDTO(journeyType, new CycleAvoidingMappingContext()));
}
When I try to map Entity to TO, I'm getting out of memory error. How can I map the objects for this scenario ?
I see that you are using the example of the CycleAvoidingMappingContext. The reason why it isn't working to you is because you are not passing it in your VehicleMapper. MapStruct will use the VehicleMapper to map your vehicles and there is no context that would store the cycles.

Loading DTO with collection

#Entity
#Table(name = "person")
public class Consignment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "person_id")
private String personId;
#Column(name = "person_name")
private String personName;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person")
#Column(name = "cars_owned")
private Set<Cars> casrsowned = new HashSet<>();
}
#Entity
#Table(name = "cars")
public class Cars implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "cars_id")
private String carsId;
#ManyToOne
#JoinColumn(name = "person")
private Person person;
#OneToOne
private CarsDetail carsDetail;
}
#Entity
#Table(name = "carsDetail")
public class CarsDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "brand")
private String brand;
#Column(name = "color")
private String color;
#Column(name = "model")
private String model;
}
class CarModelDTO {
String personName;
List<String> models;
}
In the above relation, want to return CarModelDTO
JPA query where,
#Query("Select CarModelDTO(p.personName, p.casrsowned.carsDetail.model) from Person as p where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id);
I tried multiple ways but it gives
org.hibernate.QueryException: illegal attempt to dereference collection
As I have already described Retrieve List from repository interface to DTO list you should go through the following step :
first create a constructor using the fields you want to be returned from the query output
in you query you should create new instance of your dto and pass the field from db to new instalnce :
so you need these changes:
1. In the constructor:
You should not use a list as List<String> models; as you should consider that your dto as a result row of DB. so you need to have a simple String model;
public CarModelDTO (String name,String model){
this.name=name;
this.model=model;
}
2. In the #Query:
you should use multi inner join appropriately
you should also append your package name to CarModelDTO in the query (here i used com.example you should change it)
#Query("Select com.example.CarModelDTO(p.personName, d.model ) from Person as p inner join p.carsowned c inner join c.carDetail d where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id)

DTO to entity mapping when entity has many to one relationship

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);

JPA, Eclipselink - Relations are deleted on new transaction

I'm building a small eclipse rcp with a little bit of JPA. Now something strange happens:
I create some TopCategories with some SubCategories, this works as intended. The inserts are printed in the log. I close my application and now the problem raises up:
The Categories have a relation to books
Book.java
#Entity
public class Book implements Serializable, PropertyChangeListener {
private static final long serialVersionUID = 4646743297687986216L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private boolean active = true;
#Temporal(TemporalType.TIMESTAMP)
private Date updated;
#Lob
private Set<Group> allowedGroups;
#Column(columnDefinition = "TEXT")
private String text;
private BookType type;
#ManyToOne
private TopCategory topCategory;
#ManyToOne
private SubCategory subCategory;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private BookAttachment attachment;
#Transient
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
// ordinary getter/setter
#PrePersist
#PreUpdate
private void updateUpdated() {
this.updated = new Date();
}
}
After restart and querying Book with this select b from Book all SubCategories which aren't used getting deleted. If a SubCategory has a relation to Book it stays in my DB. Why this occures?
Category.java
#MappedSuperclass
public class Category implements Serializable {
private static final long serialVersionUID = 6091963773161164543L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#Temporal(TemporalType.TIMESTAMP)
private Date updated;
#Enumerated(EnumType.STRING)
private CategoryType type;
#Transient
private List<Snippet> snippets = new LinkedList<Snippet>();
// ordinary getter/setter
#PrePersist
#PreUpdate
public void updateUpdated() {
this.updated = new Date();
}
}
TopCategory.java
#Entity
public class TopCategory extends Category {
#OneToMany(cascade = CascadeType.ALL)
private List<SubCategory> subCategories;
public TopCategory() {
setName("");
setSubCategories(new ArrayList<SubCategory>());
}
public List<SubCategory> getSubCategories() {
return subCategories;
}
public void setSubCategories(List<SubCategory> subCategories) {
this.subCategories = subCategories;
}
#Override
public void setType(CategoryType type) {
super.setType(CategoryType.topCategory);
}
SubCategory.java
#Entity
public class SubCategory extends Category {
#ManyToOne(cascade = CascadeType.ALL)
private TopCategory topCategory;
public TopCategory getTopCategory() {
return topCategory;
}
public void setTopCategory(TopCategory topCategory) {
this.topCategory = topCategory;
}
#Override
public void setType(CategoryType type) {
super.setType(CategoryType.subCategory);
}
}
I'm using Eclipselink 2.1.1.
Regards
Alright - I've found the problem: One of the result lists from my model is modified from a contentprovider - this is were not used SubCategories are removed, in case that the entity objects weren't detached, it caused the entitymanger to update.

Categories