I want to set only fixed parameters from DTO. I have 3 Entity (tables) which are connected with FK or PK. So when I am getting data from database using ID of Table A. Jpa will give me All data with its child that's exactly I want.
public class AEntity{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "a_id")
private Long aId;
private String name;
private String model;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "b_id")
private bEntity bentity;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "c_uid")
private cEntity centity;
#JoinColumn(name = "d_id")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<dEntity> d;
}
Like this I have B,C,D Entity also. now here I am getting Data by A_id. And created aDTO like:
public class aDTO {
private Long a_Id;
#JsonProperty(required=true)
private String name;
#JsonProperty(required=true)
private String model;
#Valid
private List<AllbDTO> bList;
private List<AllcDTO> cList;
}
And using this code:
{
Type targetListType = new TypeToken<List<aDTO>>() {
}.getType();
List<aDTO> aDTOs = mapper.map(AllDAtaByQuery, targetListType);
return aDTOs ;
}
now "AllDAtaByQuery" its an entity object to storedata from query and all are working fine. Its giving me list of a then inner list with b tables data and so on with all field.
Q: Is it possible that I tell mapper to map specific fields i want So i can send response with specific field?
Like b table have 6 fields but I want only 2 fields data in response by using same DTO. so i will use same dto for all other query and manipulate the response according need.
Related
I am Using Spring Boot on Java to create user's order on his checkout. A new Orders object is created which has a Linked Set of Items. Those items are user's cart contents.
Order is created, but its set of Items is null. The set size is 0. I checked that in JUnit tests. Can you help me to find out what is wrong? Maybe I have defined entities incorrectly? Have a look at the picture of the database:
And check the entities, Orders:
#Entity
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime submitedAt;
#NotEmpty
private String orderName;
#NotEmpty
#Column(name="`User`")
private String username;
#Enumerated(EnumType.STRING)
#Column
private OrderStatus status;
#OneToMany(mappedBy = "orders", cascade = { CascadeType.ALL}, fetch = FetchType.LAZY)
private Set<Item> items;
Item:
#Entity
public class Item {
#Id
private Integer id;
#Column(name="`NAME`")
private String dishName;
#Column(name = "`DESCRIPTION`", length = 2000)
private String dishDescription;
#Column(name = "`QUANTITY`")
private Integer quantityInCart;
#Column(name = "`USER`")
private String username;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinColumn(name = "ORDERS_ID")
private Orders orders;
How to do entities relation correctly? Should it be one direction or bi-directional relationship?
What are differences of these relations? And what kind of relationship I should use? Why?
I was doing JUnit tests for the Orders service methods. It turns out that it can create orders. And Order items from user's cart.
But when it is time to show order (GetMapping) then it returns Orders entity with empty items set.
I think it happens because JPA cannot find foreign key of items for its designated order. It is null.
Why is it null?
And this is the service method that creates such order by user request:
#Transactional
public ResponseEntity<String> createOrder (String username) {
User user = userService.findByUsername(username);
List<CartItem> items = cartRepo.findByUser(user);
if(items.size() > 0) {
Orders newOrder = new Orders();
Set<Item> orderItems = new LinkedHashSet<>();
for(CartItem item : items) {
// new Item(Integer id, String dishName, String dishDescription, Integer quantityInCart, String username)
Item orderItem = new Item(item.getId(), item.getDish().getName(),
item.getDish().getDescription(), item.getQuantity(), item.getUser().getUsername());
orderItems.add(orderItem);
}
newOrder.setItems(orderItems);
newOrder.setOrderName(user.getUsername()+"'s order");
newOrder.setStatus(OrderStatus.SUBMIT);
newOrder.setSubmitedAt();
newOrder.setUsername(username);
orderDao.save(newOrder);
cartService.removeAllUserProducts(username);
LOG.info("[{}]: A new order is created successfully.", username);
return new ResponseEntity<String>("A new order is created successfully.", HttpStatus.CREATED);
}
//...
}
I tried to do one direction relationship for other entities and it really created foreign keys on joined column fields. But I want to find out why my bidirectional way of joining is wrong. Maybe someone who really knows can explain.
The Order class should be like this:
#Entity
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime submitedAt;
#NotEmpty
private String orderName;
#NotEmpty
#Column(name="`User`")
private String username;
#Enumerated(EnumType.STRING)
#Column
private OrderStatus status;
#OneToMany(cascade = { CascadeType.ALL}, fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(name="ORDERS_ID")
private Set<Item> items;
And Item class without Orders class and its ManyToOne relationship.
Now relationship is unidirectional. Item entity has foreign keys column name ORDERS_ID that has id's of Orders for which Items belong.
The user can search for products if any product shown in the result exists in the user_favorites table so the show flag tells the front-end this product was added for this user by user_id and product_id. with spring boot and spring data.
My Entity :
#Id
#Column(name = "catId")
private Integer catId;
#Column(name = "cat_no")
private String catNo;
#Column(name = "cat_sn")
private String catSn;
#Column(name = "doc_ref")
private String docRef;
#Column(name = "user_id")
private Integer userId;
#Column(name = "updated_at")
private String updatedAt;
#Column(name = "created_at")
private String createdAt;
I tried that using #Formula but nothing happing always returns null. and if it's done by #Formula how can i add parameters to #Formula
#Formula(value = "SELECT count(*) as checker FROM fb_user_favorites WHERE cat_id = 34699 AND user_id = '52') ")
#Transient
private String checker;
#Transient is part of JPA spec. In Hibernate fields marked with this annotation just simply ignored/excluded from any JPA engine/runtime logic.
#Formula is part of Hibernate. Fields, marked with it, don't persisted by Hibernate (first argument do not use #Transient as redundant), values are calculated by provided SQL when executing query for entity.
So for Hibernate to see this fields, they should not be excluded by #Transient
TL;DR remove #Transient annotation
Complicated but fast working way.
Adding isFavorite field to the entity:
#Transient
private boolean isFavorite;
Create an entity linking Product and User:
public class ProductFavorite {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#ManyToOne(optional = false, fetch = LAZY)
private Product product;
#ManyToOne(optional = false, fetch = LAZY)
private User user;
}
Then create a repository with a method to find the user's favorite products:
#Repository
public interface ProductLikeRepository extends JpaRepository<ProductFavorite, Long> {
#Query("select f.product.id from ProductFavorite f where f.product in ?1 and f.user = ?2")
Set<Integer> findProductIdsByIdsAndUser(List<Product> products, User user);
}
And at the end, write a method that will fill in the isFavorite field:
public void fillFavorite(List<Product> products, User user) {
if (products.isEmpty()) {
return;
}
var likedIds = favoriteRepository.findProductIdsByIdsAndUser(products, user);
for (Product product : products) {
product.setFavorite(likedIds.contains(product.getId()));
}
}
You need to call it manually:
List<Product> products = productRepository.findAll();
fillFavorite(products, currentUser());
I have table/entity which has varchar(255) field that can store values of multiple data types and type field which indicates what kind of value type it is. I want to perform join only if data type is of certain value ie. document.
Example:
#Entity
#Table(name = "ACT_HI_DETAIL")
public class TaskDetailsVariable implements Serializable {
#Id
#Column(name = "ID_")
private String id;
#Column(name = "TEXT_")
private String value;
#Column(name = "VAR_TYPE_")
private String type;
#ManyToOne
#JoinColumn(name = "TEXT_")
#WhereJoinTable(clause = "VAR_TYPE_ = 'document'") // this doesn't work
private Document document; // this should be joined only if type is document
}
When I try the example above, I get the error because it tries to join all LONG_ values. I have also tried #JoinFormula and #Where.
Hi I have couple of Entity classes as below, using lombok for getter and setters
Parent Entity Class have
#Table(name = "PARTY")
#Entity
public class Party {
#Id
#Column(name = "PARTY_ID")
private Long partyId;
#OneToMany(targetEntity = DVLoanParticipants.class,cascade = CascadeType.ALL)
#JoinColumn(name = "PARTY_ID")
#MapKey(name="dvpParticipantName")
#LazyCollection(LazyCollectionOption.FALSE)
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
}
Child Entity Class have
#Table(name = "DV_LOAN_PARTICIPANTS")
#Entity
public class DVLoanParticipants implements Serializable {
#Id
#Column(name = "PARTY_ID")
private Long partyId;
#Id
#Column(name = "DVP_PARTICIPANT_NAME")
private String dvpParticipantName;
#Column(name = "DVP_PARTICIPANT_TYPE")
private String dvpParticipantType;
}
In service class i am calling save operation as
repository.save(parentEntityObject);
I am able to execute update statements ,but when i try to insert new row for child entity class i am getting an error saying
cannot insert NULL into ("ABC"."DV_LOAN_PARTICIPANTS"."PARTY_ID")
But if i print the parentEntityObject just before the save operation i see the values like
(partyId=12345678, dvpParticipantName=XYZ, dvpParticipantType=VKP)
I see the query formed as
insert
into
DV_LOAN_PARTICIPANTS
(DVP_PARTICIPANT_TYPE, PARTY_ID, DVP_PARTICIPANT_NAME)
values
(?, ?, ?)
Just before te save i am seeing valules in the Object
Builder=DVLoanParticipants(partyId=123456, dvpParticipantName=Builder,
dvpParticipantType=Individual)
Update
This is the setting part for values
DVLoanParticipants dvLoanParticipants = new
DVLoanParticipants();
dvLoanParticipants.setPartyId(Long.valueOf(partyId));
dvLoanParticipants.setDvpParticipantName("Builder");
dvLoanParticipants.setDvpParticipantType("Individual");
Party party = new Party();
Map<String, DVLoanParticipants> dvLoanParticipantsMap = new
java.util.HashMap<>();
dvLoanParticipantsMap.put("Builder", dvLoanParticipants);
party.setPartyId(Long.valueOf(partyId));
party.setDvLoanParticipantsMap(dvLoanParticipantsMap);
repository.save(party);
What is the mistake i am doing ?
The root cause of your problem in this part:
#OneToMany(targetEntity = DVLoanParticipants.class,cascade = CascadeType.ALL)
#JoinColumn(name = "LOAN_ID")
#MapKey(name="dvpParticipantName")
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
actually for your case the column name in the #JoinColumn means:
If the join is for a unidirectional OneToMany mapping using a foreign key mapping strategy, the foreign key is in the table of the target entity.
So, assuming for the clarity that you want to map the following schema:
create table PARTY
(
PARTY_ID int,
-- ...
primary key (PARTY_ID)
);
create table DV_LOAN_PARTICIPANTS
(
PARTY_ID int,
DVP_PARTICIPANT_NAME varchar(50),
DVP_PARTICIPANT_TYPE varchar(10),
-- ...
primary key (PARTY_ID, DVP_PARTICIPANT_NAME),
foreign key (PARTY_ID) references PARTY(PARTY_ID)
);
You can use the following mapping:
#Entity
#Table(name = "PARTY")
public class Party
{
#Id
#Column(name = "PARTY_ID")
private Long partyId;
// I use fetch = FetchType.EAGER instead of deprecated #LazyCollection(LazyCollectionOption.FALSE)
// targetEntity = DVLoanParticipants.class is redundant here
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "PARTY_ID") // this is DV_LOAN_PARTICIPANTS.PARTY_ID column
#MapKey(name = "dvpParticipantName")
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
public Party()
{
dvLoanParticipantsMap = new HashMap<>();
}
// getters / setters
public void addParticipant(DVLoanParticipants p)
{
this.dvLoanParticipantsMap.put(p.getDvpParticipantName(), p);
p.setPartyId(getPartyId());
}
}
#Entity
#Table(name = "DV_LOAN_PARTICIPANTS")
public class DVLoanParticipants implements Serializable
{
#Id
#Column(name = "PARTY_ID")
private Long partyId;
#Id
#Column(name = "DVP_PARTICIPANT_NAME")
private String dvpParticipantName;
#Column(name = "DVP_PARTICIPANT_TYPE")
private String dvpParticipantType;
// getters / setters
}
and example how to save:
Party party = new Party();
party.setPartyId(2L);
// ...
DVLoanParticipants part1 = new DVLoanParticipants();
part1.setDvpParticipantName("Name 3");
part1.setDvpParticipantType("T1");
DVLoanParticipants part2 = new DVLoanParticipants();
part2.setDvpParticipantName("Name 4");
part2.setDvpParticipantType("T1");
party.addParticipant(part1);
party.addParticipant(part2);
repository.save(party);
and several notes:
The LazyCollectionOption.TRUE and LazyCollectionOption.FALSE values are deprecated since you should be using the JPA FetchType attribute of the #OneToMany association.
You use hibernate specific approach for mapping сomposite identifiers. As it's mentioned in the hibernate documentation:
The restriction that a composite identifier has to be represented by a primary key class (e.g. #EmbeddedId or #IdClass) is only JPA-specific.
Hibernate does allow composite identifiers to be defined without a primary key class via multiple #Id attributes.
But if you want to achieve more portability you should prefer one of the jpa allowed approaches.
I have two entities. One of them is a child of the other one with a relation with OneToMany. Is it possible to implement search criteria that looks up simultaneously in both the main entity and all the child entities?
Example: I have a Company with many employees. If I search with some text, I want to retrieve all the companies, which title contains that text or its employee's names contain that text.
Here are the example entities:
#Entity
public class Company extends AbstractEntity {
#Column(nullable = false, unique = true)
private String uuid;
#Column(nullable = false)
private String companyName;
#OneToMany(mappedBy = “company”, cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
protected Set<Employee> employees = new HashSet<>();
}
#Entity
public class Employee extends AbstractEntity {
#Column(nullable = false, unique = true)
private String uuid;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = “company_id”, nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Company company;
}
Here is the example query, that I want to transform into Specification criteria
#Query(value = “SELECT DISTINCT c from Company c left outer join c.employees e
WHERE c.companyName LIKE CONCAT('%',:text,'%')
or e.firstName LIKE CONCAT('%',:text,'%')
or e.lastName LIKE CONCAT('%',:text,'%')”)
If you are using Spring JPA data repository, your interface method would look like this.
Company findByCompanyNameConatainingOrEmployeesFirstNameConatainingOrEmployeeslastNameConataining(String searchTextCompanyTitle, String searchTextEmployeeFName, String searchTextEmployeeLName);
If you are not using data repository, please explain your data access design to get an accurate answer.