I want to use DTO to communicate with the Angular, but actually it doesn't work. I want to create POST request to add data from my application to the database using Dto model.
You can see my errors on the picture:
My class Customer:
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "name")
private String name;
#OneToMany
private List<Ticket> ticket;
...
Class CustomerDto:
public class CustomerDto {
private String name;
private List<TicketDto> ticket;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<TicketDto> getTicket() {
return ticket;
}
public void setTicket(List<TicketDto> ticket) {
this.ticket = ticket;
}
}
Class CustomerController:
#Autowired
CustomerService customerService;
#PostMapping(value = "/customers/create")
public Customer postCustomer(#RequestBody CustomerDto customerDto, List<TicketDto> ticketDtos) {
//ArrayList<TicketDto> tickets = new ArrayList<>();
ticketDtos.add(customerDto.getName());
ticketDtos.add(customerDto.getTicket());
Customer _customer = customerService.save(new Customer(customerDto.getName(), ticketDtos ));
return _customer;
}
CustomerService:
public interface CustomerService {
void save(CustomerDto customerDto, List<TicketDto> ticketDtos);
}
CustomerServiceImpl:
#Service
public class CustomerServiceImpl implements CustomerService {
#Autowired
CustomerRepository repository;
#Override
public void save(CustomerDto customerDto, List<TicketDto> ticketDtos) {
Customer customer = new Customer();
customer.setName(customerDto.getName());
customer.setTicket(customerDto.getTicket());
List<Ticket> tickets = new ArrayList<>();
for (TicketDto ticketDto : ticketDtos) {
Ticket ticket = new Ticket();
ticket.setDestinationCity(ticketDto.getDepartureCity());
ticket.setDestinationCity(ticketDto.getDestinationCity());
tickets.add(ticket);
}
}
Since you CustomerServiceImpl is taking CustomerDto and list of TicketDtos, you need to change your method call on controller as below:
Class CustomerController:
#Autowired
CustomerService customerService;
#PostMapping(value = "/customers/create")
public Customer postCustomer(#RequestBody CustomerDto customerDto) {
Customer _customer = customerService.save(customerDto));
return _customer;
}
And update CustomerServiceImpl as:
#Service
public class CustomerServiceImpl implements CustomerService {
#Autowired
CustomerRepository repository;
// change save to return saved customer
#Override
public Customer save(CustomerDto customerDto) {
Customer customer = new Customer();
customer.setName(customerDto.getName());
// customer.setTicket(customerDto.getTicket()); // remove this
List<Ticket> tickets = new ArrayList<>();
for (TicketDto ticketDto : customerDto.getTicketDtos) {
Ticket ticket = new Ticket();
ticket.setDestinationCity(ticketDto.getDepartureCity());
ticket.setDestinationCity(ticketDto.getDestinationCity());
tickets.add(ticket);
}
customer.setTickets(tickets); // add this to set tickets on customer
return repository.save(customer);
}
Obviously, you need to change your interface as well:
public interface CustomerService {
Customer save(CustomerDto customerDto);
}
For entity-DTO conversion, we need to use ModelMapper or mapstruct library.
With the help of these libraries, we can easily convert from Dto to entity and entity to dto object. After adding any of the dependency, We are able to use it.
How can we use, Let see...
Define modelMapper bean in spring configuration.
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
Suppose we need to convert List to List obj then we can perform simply like that :
List<TicketDto> ticketDtos = .... //Suppose It is holding some data
List<Ticket> tickets = ticketDtos.stream()
.map(tkt-> mappper.map(tkt, ticket.class))
.collect(Collectors.toList());
It is very simple to use like mappper.map(targetClass, DestinationClass.class)
I used Java8 code here but you can use anyone. I hope It would be very helpful to you.
Related
I created a couple of DTO's and a MapStruct interface for getting the User data:
public class UserDto {
private Long id;
private CountryDto country;
}
public class CountryDto {
private Long id;
private String name;
private List<TimeZoneDto> timeZones = new ArrayList<TimeZoneDto>();
}
#Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
// TODO exclude country timezones
UserDto mapToDto(User entity);
}
I would like to modify UserMapper so the CountryDto timezones list are excluded
{
"id":1,
"country": {
"id": 182,
"name":"Australia"
}
}
I finally found a solution for this, just adding the line below in UserMapper did the trick:
#Mapping(target = "country.timeZones", ignore = true)
UserDto mapToDto(User entity);
I am getting these error while integrating the spring-boot with JPA repository
here is the code
#CrossOrigin(origins = "http://localhost:4200")
#RestController
#RequestMapping("/api")
public class EmpController {
#Autowired
private CrudRepo crud;
#Autowired
private AddrCrudRepo addr;
#Autowired
private EntityManager entity;
//#Autowired
//private ModelMapper mapper;
private static int count = 0;
#Bean
public ModelMapper model() {
return new ModelMapper();
}
//#Autowired
// public EmpController(ModelMapper mapper) {
// this.mapper = mapper;
// }
#RequestMapping(path = "/post-addr", method = RequestMethod.POST)
public List<AddressModel> postAddr(#Valid #RequestBody List<AddressRequest> addr1){
// crud.findById(id)
//AddressModel list = new AddressModel();
EmployeeModel emp = new EmployeeModel();
System.out.println("CALLING THE MAPPER "+addr1);
List<AddressModel> addr_list = ObjectMapperUtils.mapAll(addr1, AddressModel.class);
System.out.println("CALLED THE MAPPER "+addr_list);
addr_list.forEach((a) -> {
crud.findById(a.getEmpId()).ifPresent((b) -> {
System.out.println(this.count++);
a.setEmp_id(b);
b.getAddress().add(a);
});
});
// AddressModel addr_list = model().map(addr1, AddressModel.class);
//
// crud.findById(addr1.getEmp_id()).ifPresent((b) -> {
// addr_list.setEmp_id(b);
//
// });`enter code here`
System.out.println(addr_list.size());
List<AddressModel> addr3 = addr.saveAll(addr_list);
System.out.println(addr3);
return addr_list;
}
getting an error in the postAddr method as when it returns the List<AddressModel> and here is the AddressModel
#Entity
#Table(name="Emp_Address")
public class AddressModel implements Serializable{
#Column(name="address_id")
#Id
private Integer address_id;
#Column(name="city")
private String city;
#Column(name="states")
private String states;
#Transient
private Integer empId;
#ManyToOne
#JoinColumn(name="emp_id")
private EmployeeModel emp_id;
public AddressModel() {
}
//getter and setter
and EmployeeModel
#Entity
#Table(name="Employee")
public class EmployeeModel implements Serializable{
#Column(name="Emp_id")
#Id
private Integer emp_id;
#Column(name="Emp_Name")
private String emp_name;
#OneToMany(mappedBy="emp_id")
private Collection<AddressModel> address = new ArrayList<>();
public EmployeeModel() {
}
//getter and setters
so while saveAll is done properly but when the postAddr method returns the List it throws the StackOverflow
This StackOverflow error is coming because generated toString methods of both classes are circularly dependent on each other.
EmployeeModel tries to print AddressModel but again AddressModel tries to print EmployeeModel and hence the error.
Try removing AddressModel from toString method of EmployeeModel class or reverse, remove EmployeeModel from toString method of AddressModel class.
Using H2 and JPA my REST app worked well before Ansyc, but after implementation breaks the JPA persistence model.
Here is the case:
My repository has a method JpaRepository.save() but when called from a separate thread, it throws InvalidDataAccessApiUsageException error.
My Controller calls the Service which calls the Repository to insert a new object, and I get the following error:
InvalidDataAccessApiUsageException: detached entity passed to persist: TransactionalEntity; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: TransactionalEntity]
CONTROLLER:
#Autowired
#Qualifier("depositIntentService")
private TransactionIntentService depositIntentService;
#PostMapping("/services/transactions/deposit")
public CompletableFuture<ResponseEntity<TransactionIntent>> deposit(#Valid #RequestBody TransactionClientRequest request) {
CompletableFuture<TransactionIntent> depositIntentFuture =
transactionIntentFactory.createDepositIntent(
request.entity.id,
Money.of(CurrencyUnit.of(request.money.currency), request.money.amount));
return depositIntentFuture.thenApply(intent -> {
TransactionIntent publishedIntent = depositIntentService.attemptPublish(intent); //<-- causes error
ResponseEntity.ok(publishedIntent)
});
}
SERVICE:
#Component
#Repository
public abstract class TransactionIntentServiceImpl implements TransactionIntentService{
#Autowired
private TransactionIntentRepository transactionIntentRepo;
#Transactional
public TransactionIntent attemptPublish(TransactionIntent intent){
transactionIntentRepo.save(intent); //<-- Throws error: ...detached entity passed to persist
}
}
REPOSITORY
#Repository
public interface TransactionIntentRepository extends JpaRepository<TransactionIntent, Long>{
}
Any ideas how to support JPA persistance in an Async environment?
Thanks!
Update1
FACTORY
#Component
public class TransactionIntentFactory {
#Autowired
private UserService userService;
#Async("asyncExecutor")
public CompletableFuture<TransactionIntent> createDepositIntent(long beneficiaryId, Money money) {
CompletableFuture<User> bank = userService.findByUsername("bankItself#bank.com");
CompletableFuture<User> user = userService.find(beneficiaryId);
CompletableFuture<Void> allUserFutures = CompletableFuture.allOf(bank, user);
return allUserFutures.thenApply(it -> {
User userSource = bank.join();
User userBeneficiary = user.join();
TransactionIntent intent = new TransactionIntentBuilder()
.status(new TransactionIntentStatus(TRANSFER_STATUS.CREATED, "Deposit"))
.beneficiary(userBeneficiary)
.source(userSource)
.amount(money)
.build();
return intent;
});
}
}
ENTITY
#Entity
public class TransactionIntent {
#Id
#GeneratedValue
public long id;
public final Money amount;
public final Date createdAt;
#OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionIntentStatus status;
#OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionalEntity beneficiary; //to
#OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionalEntity source; //from
TransactionIntent(){
this.amount= null;
this.createdAt = null;
this.status = null;
this.beneficiary = null;
this.source = null;
}
public TransactionIntent(TransactionIntentBuilder builder) {
this.amount = builder.amount;
this.createdAt = new Date();
this.status = builder.status;
this.beneficiary = builder.beneficiary;
this.source = builder.source;
}
}
My Spring boot app has 2 Entities - Document and Card. Card has column dtFrom. Clients have to work with column daysOnDtConfirm (Document.dtConfirm - dtFrom). Annotation #Formula for GET requests works great, but in PUT response returns an old value of daysOnDtConfirm. How return a new value?
#Entity
#Table(name="document")
public class Document extends BaseEntity{
private String name;
#Column(name = "dt_confirm")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#JsonFormat(shape = JsonFormat.Shape.STRING)
private LocalDateTime dtConfirm ;
#Column(name = "contragent_name")
private String contragentName;
....
//CARD
#OneToMany(mappedBy="document" , fetch = FetchType.EAGER)
private List<Card> cards = new ArrayList<Card>();
public List<Card> getCards() {
if (this.cards == null) {
this.cards = new ArrayList<Card>();
}
return this.cards;
}
public void setCard(Card card) {
getCards().add(card);
card.setDocument(this);
}
public int getNrOfCards() {
return getCards().size();
}
....
}
And
#Entity
#Table(name="card")
public class Card extends BaseEntity {
#ManyToOne
#JsonIgnore
#JoinColumn(name = "document_id")
private Document document;
private String name;
private double quantity;
#Column(name = "dt_from")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#JsonIgnore
private LocalDate dtFrom ;
#Formula("(select IFNULL(DATEDIFF(Document.dt_confirm , dt_from), 0) from
Document where Document.id = document_id )")
private int daysOnDtConfirm;
...
public void setDtFrom(LocalDate dtFrom) {
this.dtFrom = dtFrom;
}
public void setDtFrom(int daysOnDtConfirm) {
if (this.document.getDtConfirm() != null){
LocalDate dateTo = this.document.getDtConfirm().toLocalDate();
this.dtFrom = dateTo.minusDays(daysOnDtConfirm);
}
}
...
}
Service :
#Service
public class DocumentServiceImpl implements DocumentService {
#Autowired
DocumentRepository documentRepository;
#Autowired
CardRepository cardRepository;
...
#Override
#Transactional
public void changeCard(Document document, Card card) {
//IF ID is NULL then isNew==true!!!!
if (card.isNew()){
card.setDocument(document);
card.setDtFrom(card.getDaysOnDtConfirm());
document.setCard(card);
cardRepository.saveAndFlush(card);
}
else{
Card cardEdit = cardRepository.findOne(card.getId());
if (cardEdit != null) {
cardEdit.setDocument(document);
cardEdit.setName(card.getName());
cardEdit.setUnit(card.getUnit());
cardEdit.setQuantity(card.getQuantity());
//cardEdit.setDtFrom(card.getDtFrom());
cardEdit.setDtFrom(card.getDaysOnDtConfirm());
cardEdit.setDescription(card.getDescription());
cardRepository.saveAndFlush(cardEdit);
}
}
#Override
#Transactional
public Document changeDocumentAndCards(Document document) {
Document documentEdit = changeDocument(document);
List<Card> cards = document.getCards();
//check if the same rows in DB and Client, DELETE difference
deleteCardsFromDocument(document);
//if not empty received from client rows then change
if (!cards.isEmpty()) {
for (Card card : cards) {
changeCard(documentEdit, card);
}
}
return documentEdit;
}
...
}
RestController:
#RestController
#RequestMapping("/api/docs")
public class DocController {
#Autowired
DocumentService documentService;
#RequestMapping(value = "",
method = RequestMethod.GET,
produces = {"application/json", "application/xml"})
#ResponseStatus(HttpStatus.OK)
public #ResponseBody
List<Document> getAllDocument(HttpServletRequest request, HttpServletResponse response) {
List<Document> list = new ArrayList<>();
Iterable<Document> documents = this.documentService.getDocumentAll();
documents.forEach(list::add);
return list;
}
....
#RequestMapping(value = "/{id}",
method = RequestMethod.PUT,
consumes = {"application/json", "application/xml"},
produces = {"application/json", "application/xml"})
#ResponseStatus(HttpStatus.OK)
public Document updateDocument(//#ApiParam(value = "The ID of the existing Document resource.", required = true)
#PathVariable("id") Long id,
#RequestBody Document document,
HttpServletRequest request, HttpServletResponse response) {
Document documentEdit = documentService.changeDocumentAndCards(document);
return documentEdit;
}
...
}
The issue seems to come from the changeDocument(Document document) method. The return value of saveAndFlush() call should be assigned back to documentEdit
UPDATE
The issue is that hibernate will not re-calculate the #Formula field after it is updated. It just fetches it from cache.
The only way I managed to get this working on my machine was to refresh the card entity after updating it. For that to work I needed to add an entity manager in the service class.
In your DocumentServiceImpl (actually could be any service class) class add the following:
public class DocumentServiceImpl implements DocumentService {
//...
#PersistenceContext
private EntityManager em;
#Transactional
public void refreshEntity(Object entity) {
em.refresh(entity);
}
Then, you should call this refreshEntity() method after an update, so that hibernate doesn't fetch it from cache.
This way it worked for me. Hope it helps you.
I want to delete an record based on Id in Spring.
but in database id value is object
EX:-
id: Object(34562341112313)
How to delete this record in Spring?
You do like this:
public void deleteRecord() {
MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");
Query searchQuery = new Query(Criteria.where("id").is(34562341112313));
mongoOperation.remove(searchQuery, Your_entity_class.class);
logger.info("Delete success");
}
This is my realistic example:
/**
* Delete by condition(s).
*/
public void deleteJob() {
MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");
Query searchQuery = new Query(Criteria.where("company").is("DCV"));
mongoOperation.remove(searchQuery, Job.class);
logger.info("Đã xóa các công việc đăng bởi DCV.");
}
Source: https://github.com/SmartJobVN/MongoDB_SpringDataMongo/blob/master/src/main/java/vn/smartJob/jobs/MongoSpringJavaConfigApplication.java#L132
Reference: http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/
You should delete it like this:
#Repository
public class AppDaoClass{
#Autowired
MongoTemplate mongoTemplate;
#Override
public void deleteSomething(String somethingId) {
mongoTemplate.remove(Query.query(Criteria.where("somethingId").is(somethingId)), Ticket.class);
}
}
The first "somethingId" is the name you gave it in your model, and the second somethingId is for the Parametar you are giving in you method.
And your Domain Model:
#Document
public class Model {
#Id
private String somethingId;
private String someName;
private String someOtherName;
}
Be sure to user proper annotations for your classes #Document and #Repository. And add an #Id annotation to your ID field.
Hope this helps.
This is the way you can delete records in spring data mongoDB using MongoTemplate
WriteResult writeResult=mongoTemplate.remove(query,"collection_name");
OR
WriteResult writeResult=mongoTemplate.remove(query,EntityClassName.class);
You can also use repository Pattern
#Document(collection = "user")
public class User {
#Id
private String id;
private String username;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
#Repository
public interface UserRepository extend MongoRepository<User, String>{
public void delete(String id);
public void delete(User user);
public void deleteByUsername(String username);
}
you can use these method anywhere to delete records also u can write your custom methods
#Query(value = "{'_id' : ?0}", delete = true)
void deleteById(String id);