get API in spring boot with one to many relation - java

I have two models that are having one to many relation (customers have many invoices)
so i create one - many relation on it, this is my customer class :
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "mobile_number")
private String mobileNumber;
#Column(name = "is_deleted")
private boolean isDeleted;
#OneToMany
private Set <Invoice> invoices;
}
and this is invoices class :
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "status")
private String status;
#Column(name = "created_date")
private Timestamp createdDate;
#Column(name = "is_deleted")
private boolean isDeleted;
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
}
and then i create GET API ( get customers ) but it's nor working and return this error :
nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not extract ResultSet (through reference chain: java.util.ArrayList[0]->com.example.invoices.model.Customer["invoices"]), path=/customer/viewList}]
and this is my api :
public List<Customer> getAllCustomers() {
List<Customer> customers = cutomerRepository.findAll();
return customers;
}
and controller :
#GetMapping("/viewList")
public ResponseEntity<List<Customer>> getAllCustomers() {
List<Customer> customers = new ArrayList<>();
customers = customerService.getAllCustomers();
if (customers.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(customers, HttpStatus.OK);
}

You have a Bidirectional relation and therefore an endless loop if json tries to deserialize the Object.
You can use #JsonIgnore to break the loop or use DTOs to return at the endpoint
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "status")
private String status;
#Column(name = "created_date")
private Timestamp createdDate;
#Column(name = "is_deleted")
private boolean isDeleted;
#ManyToOne
#JsonIgnore
#JoinColumn(name = "customer_id")
private Customer customer;
}
DTO would look something like this (I like to use records for this but since I don't know if you use Java 17 I still use class):
Customer:
#Data
public class CustomerDTO {
private final int id;
private final long serialNumber;
private final String firstName;
private final String lastName;
private final String email;
private final String mobileNumber;
private final boolean isDeleted;
private final Set <Invoice> invoices;
public static CustomerDTO fromModel(Customer customer) {
return new CustomerDTO(
customer.getId(),
customer.getSerialNumber(),
customer.getFirstName(),
customer.getLastName(),
customer.getEmail(),
customer.getMobileNumber(),
customer.isDeleted(),
customer.getInvoices()
.stream()
.map(InvoiceDTO::fromModel)
.collect(Collectors.toSet())
);
}
}
Invoice (here you don't show the customer again):
#Data
public class InvoiceDTO {
private final int id;
private final String status;
private final Timestamp createdDate;
private final boolean isDeleted;
public static InvoiceDTO fromModel(Invoice invoice) {
return new InvoiceDTO(
invoice.getId(),
invoice.getStatus(),
invoice.getCreatedDate(),
invoice.isDeleted()
);
}
}
Controller:
#GetMapping("/viewList")
public ResponseEntity<List<CustomerDTO>> getAllCustomers() {
List<CustomerDTO> customers = new ArrayList<>();
customers = customerService.getAllCustomers()
.stream()
.map(CustomerDTO::fromModel)
.toList() //Depending on Java Version .collect(Collectors.toList());
if (customers.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(customers., HttpStatus.OK);
}

Do not open the entity class directly to the outside world
As DTO use for example:
public class InvoiceDTO {
private int id;
private long serialNumber;
private String status;
private Timestamp createdDate;
private boolean isDeleted;
private CustomerDTO customer;
}
See it applied in my GitHub repo FurnitureStoreApplication, example DTO classes in package dto:

Related

SpringBoot microservice InvalidDefinitionException

I developed a microservice and when I go to get to postman ( http://localhost:8080/user/utenti/ ) I get this error InvalidDefinitionException No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.application.dto.UserDto["applicationRoleID"]->com.poste.anagrafica.entity.Role$HibernateProxy$N0iZZA2I["hibernateLazyInitializer"])
Can you help me please?
For clarity I enclose the schema of the database and the classes entity , dto and controller
The Entity classes
"""
#Entity
#Table(name = "USERS")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
public class User implements Serializable {
#Id
#Column(name = "user_id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "created_date")
private LocalDateTime createdDate;
#Column(name = "deleted", nullable = false)
private Long number;
#Column(name = "modified_date")
private LocalDateTime modifiedDate;
#Column(name = "birth_place")
private String birthPlace;
#Column(name = "birthday")
private Date birthDay;
#Column(name = "canNominate")
private Long canNominate;
#Column(name = "email")
private String email;
#Column(name = "firstName")
private String firstName;
#Column(name= "fiscalCode")
private String fiscalCode;
#Column(name = "hiringDate")
private Date hiringDate;
#Column(name ="last_name")
private String lastName;
#Column(name = "matricola")
private String matricola;
#Column(name = "position")
private String position;
#Column(name = "registration_number")
private String registrationNumber;
#Column(name = "replaced")
private Long replaced;
#Column(name = "terminationDate")
private Date terminationDate;
#Column(name = "user_status")
private String userStatus;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "application_role_id")
private Role applicationRoleID;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id")
private Company companyID;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "direction_id")
private Direction directions ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "level_id")
private Levels levelID;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "referent_id")
private User referentID;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "role_id")
private Role roleID;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "task_id")
private EmployeeTask taskID;
}
"""
the UserDTO class
"""
#Data
public class UserDto {
private Long userId;
private LocalDateTime createdDate;
private Long number;
private LocalDateTime modifiedDate;
private String birthPlace;
private Date birthDay;
private Long canNominate;
private String email;
private String firstName;
private String fiscalCode;
private Date hiringDate;
private String lastName;
private String matricola;
private String position;
private String registrationNumber;
private Long replaced;
private Date terminationDate;
private String userStatus;
private Role applicationRoleID;
private Company companyID;
private Direction directions ;
private Levels levelID;
private User referentID;
private Role role;
private EmployeeTask taskID;
}
"""
the controller class
"""
#RestController
#RequestMapping("/user")
#Log
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
#Autowired
UserService userService;
#GetMapping(value = "/cerca/{registration_number}", produces = "application/json")
public ResponseEntity<List<UserDto>> listUserByRegistrationNumber(#PathVariable("registration_number") String registrationNumber)
throws ChangeSetPersister.NotFoundException
{
log.info("****** Ottengo l'user con numeroRegistrazione " + registrationNumber + " *******");
List<UserDto> user = userService.SelByRegistrationNumber(registrationNumber);
return new ResponseEntity<List<UserDto>>(user, HttpStatus.OK);
}
#GetMapping(value = "/utenti", produces = "application/json")
public ResponseEntity<List<UserDto>> listAllUsers ()throws ChangeSetPersister.NotFoundException{
List<UserDto> user = userService.SelTutti();
return new ResponseEntity<List<UserDto>>(user, HttpStatus.OK);
}
}
"""
the application.properties
"""
spring.datasource.url = jdbc:oracle:thin:#192.134.2.82:1521/orcl
spring.datasource.username =admin
spring.datasource.password =zzzx
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
# JPA settings
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect
spring.jpa.hibernate.use-new-id-generator-mappings=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming.implicit- strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
"""
You can try
spring.jackson.serialization.fail-on-empty-beans=false
in your application.properties.

Spring JPA how to create an object with relationship to another entity in Application

I want to create a Person with an Address, each of them are an entity. My Entities seem to work, the part where i begin to struggle is on how to create a Person using the constructor where i also have to put in the Address.
personRepository.save(new Person(new Name("Test","Test"),new Adress("Street","Number","PLZ","Town"),LocalDate.parse("2000-01-01"),"email#email.com","911");
This sadly does not work so my question is how can i create a Person object with the Address.
I'm also wondering how i would add the address if i already got the address in my Address repository, is there a way to get the address or use the adress ID?
adresseRepository.save(new Adresse("Street","Number","PLZ","Town"));
Here's the code for both of the shortend.
Person:
#Entity
#Table(name = "Person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PersonID")
private Long personID;
#Column(name = "FullName")
#Convert(converter = NameConverter.class)
private Name fullName;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="AdresseID")
private Adresse adresse;
#Column(name = "Geburtsdatum")
private LocalDate geburtsdatum;
#Column(name = "EMail")
private String email;
#Column(name = "Telefonnummer")
private String telefonnummer;
private Person() {}
public Person(Name fullName, Adresse adresse, LocalDate geburtsdatum, String email, String telefonnummer) {
this.fullName = fullName;
this.adresse = adresse;
this.geburtsdatum = geburtsdatum;
this.email = email;
this.telefonnummer = telefonnummer;
}
}
Address:
#Entity
#Table(name = "Adresse")
public class Adresse {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "AdresseID")
private Long adresseID;
#Column(name = "Strasse")
private String strasse;
#Column(name = "Hausnummer")
private String hausnummer;
#Column(name = "PLZ")
private String plz;
#Column(name = "Ort")
private String ort;
protected Adresse() {}
public Adresse(String strasse, String hausnummer, String plz, String ort) {
this.strasse = strasse;
this.hausnummer = hausnummer;
this.plz = plz;
this.ort = ort;
}
}
Ralationships are created in hibernate like this:
#Entity
#Table(name="CART")
public class Cart {
//...
#OneToMany(mappedBy="cart")
private Set<Item> items;
// getters and setters
}
Please note that the #OneToMany annotation is used to define the property in Item class that will be used to map the mappedBy variable. That is why we have a property named “cart” in the Item class:
#Entity
#Table(name="ITEMS")
public class Item {
//...
#ManyToOne
#JoinColumn(name="cart_id", nullable=false)
private Cart cart;
public Item() {}
// getters and setters
}
Soin your case you just have to add
#Entity
#Table(name = "address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
//...
#OneToOne(mappedBy = "address")
private User user;
something lilke this to your Adress Table.
Because one Adress also have one user.
For more information visit this site

Update Spring Embedded Entity throws Stack Overflow Entity

I am currently trying to update a Record which contains an Embedded Entity. When trying this, i am getting the following Error:
Request processing failed; nested exception is java.lang.RuntimeException: java.lang.StackOverflowError
When i create the Entity, everything is working fine.
This is what i have right now:
My Main Entity:
#Entity(name = "Account")
#Table(name = "account")
#DynamicUpdate
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
public class Account extends Auditable<String> {
#Id
#Column(name = "Id")
private String id;
#Column(
name = "Description",
length = 512)
private String description;
#Column(
name = "AnnualRevenue",
length = 18)
private String annualRevenue;
#Embedded
#AttributeOverrides(value = {
#AttributeOverride(name = "city", column = #Column(name = "billingAddressCity")),
#AttributeOverride(name = "country", column = #Column(name = "billingAddressCountry")),
#AttributeOverride(name = "latitude", column = #Column(name = "billingAddressLatitude")),
#AttributeOverride(name = "longitude", column = #Column(name = "billingAddressLongitude")),
#AttributeOverride(name = "postalCode", column = #Column(name = "billingAddressPostalCode")),
#AttributeOverride(name = "state", column = #Column(name = "billingAddressState")),
#AttributeOverride(name = "street", column = #Column(name = "billingAddressStreet"))
})
private Address billingAddress;
#Column(name = "Industry")
private String industry;
#Column(name = "IsDeleted")
private boolean isDeleted;
#Column(
name = "Employees",
length = 8
)
private int employees;
#Column(name = "Name")
private String name;
#Column(name = "Phone")
private String phone;
#Column(name = "Type")
private String type;
#Column(name = "Website")
private String website;
#OneToMany(
orphanRemoval = true,
fetch = FetchType.LAZY,
cascade = {
CascadeType.ALL
}
)
#JsonIgnoreProperties("account")
private final List<Contact> contacts = new ArrayList<>();
}
Here, i am using an Embedded Entity for the Address which looks like this:
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
public class Address {
private String city;
private String country;
private Double latitude;
private Double longitude;
private String postalCode;
private String state;
private String street;
}
Furthermore, the Account can have multiple Contacts which is is defined like this:
#Entity(name = "Contact")
#Table(name = "contact")
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#JsonInclude
public class Contact extends Auditable<String> {
#Id
#Column(name = "Id")
private String id;
#Column(name = "Department")
private String department;
#Column(
name = "Email"
)
private String email;
#Column(name = "FirstName")
private String firstName;
#Column(name = "Name")
private String name;
#Column(name = "Salutation")
private String salutation;
#ManyToOne
#JoinColumn(
name = "accountId",
nullable = false,
referencedColumnName = "Id",
foreignKey = #ForeignKey(
name = "AccountContactFK"
)
)
#JsonIgnoreProperties("contacts")
private Account account;
}
For the Account, i am using the DTO Principle to map the Fields which looks like this:
DTO for Creation:
#Getter
#Setter
public class AccountCreationDTO {
private String description;
private String annualRevenue;
private Address billingAddress;
private String industry;
private boolean isDeleted;
private int employees;
private String name;
private String phone;
private String type;
private String website;
}
DTO for Update:
#Getter
#Setter
public class AccountUpdateDTO {
#Id
private String id;
private String description;
private String annualRevenue;
#Embedded
private Address billingAddress;
private String industry;
private boolean isDeleted;
private int employees;
private String name;
private String phone;
private String type;
private String website;
}
And here is my Controller:
#RestController
#RequestMapping("/api/v1")
#RequiredArgsConstructor
#Slf4j
public class AccountController {
private final AccountRepository accountRepository;
#RequestMapping(value = "/createAccount", method = RequestMethod.POST)
public Account createObject(#RequestBody #DTO(AccountCreationDTO.class) Account account) {
//account.setBillingAddress(account.getBillingAddress());
//this.addressRepository.save(account.getBillingAddress());
Account createdAccount = this.accountRepository.saveAndFlush(account);
log.info("Created Account: {}", createdAccount);
return createdAccount;
}
#PutMapping(value = "/updateAcc")
public ResponseEntity<Account> updateAccount(#DTO(AccountUpdateDTO.class) Account account) {
Account updatedAccount = this.accountRepository.saveAndFlush(account);
log.info("Updated Account: {}", updatedAccount);
return ResponseEntity.ok().body(updatedAccount);
}
}

How to return only specific field in spring

In the following code when I used projection it returns me the whole object of Group , But I want to get only roleName of class Group
how can I do this?
Projection: UserWithProfile
#Projection(name="UserWithProfile",types=User.class)
public interface UserWithProfile extends UserGetters {
UserPhoto getProfilePhoto();
}
UserGetters
public interface UserGetters{
Long getId();
String getName();
String getLogonEmail();
boolean isEmailVerified();
Group getGroup();
}
User.class
#Entity
public class User implements Serializable, UserGetters {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, targetEntity = Property.class)
private Set<Property> favouriteProperty;
#OneToOne
private AcceptanceLetter acceptanceLetter;
#Column(unique=true)
private String logonEmail;
private String name;
#JsonProperty(access = Access.WRITE_ONLY)
#Column(updatable=false)
private String password;
private boolean emailVerified;
#ManyToOne
private Group group;
#OneToOne
private Business business;
private String address;
private String postcode;
private String phoneNo;
private String passportNo;
#Column(length=1000)
private String description;
#JsonIgnore
private float meanRating;
#OneToOne(mappedBy="user",targetEntity=UserPhoto.class)
private UserPhoto profilePhoto;
#ManyToOne
private Country country;
Getter and setters... }
First I tried #RestResource(exported = false) its not worked but then I tried
#JsonIgnore it finally works :'D

Hibernate query on many to many mapping

I have been using many to many mapping here is my POJO classes.
Menu.java :
#Entity
#Table(name = "menu")
public class Menu {
#Id
#Column(name = "menuid")
#GeneratedValue
private int menuid;
#Column(name = "parentid")
private int parentid;
#Column(name = "menuname")
private String menuname;
#Column(name = "url")
private String url;
#Column(name = "status")
private String status;
#Column(name = "usertype")
private String usertype;
#Column(name = "isparent")
private boolean isParent;
private ArrayList<Menu> childMenu;
#ManyToMany(mappedBy="menus")
private List<User> users;
public Menu(Integer menuid){
this.menuid=menuid;
}
public Menu(){
}
User.java :
#Entity
#Table(name = "user")
public class User implements Serializable {
#Id
#Column(name = "userid")
#GeneratedValue
private Integer userId;
#Column(name = "OUTLET_ID")
private int outletId;
#Column(name = "NAME")
private String name;
#Column(name = "USERTYPE")
private String userType;
#Column(name = "LOGINID")
private String loginId;
#Column(name = "PASSWORD")
private String password;
#Column(name = "CREATEDDATE")
private String createdDate;
#Column(name = "CONTACTNUMBER")
private String contactNumber;
#Column(name = "EMAILID")
private String emailId;
#Column(name = "OUTLETTYPE")
private String outlettype;
#Transient
private String nsec;
#javax.persistence.Transient
ArrayList<Integer> menuid;
#javax.persistence.Transient
ArrayList<Long> clientid;
#javax.persistence.Transient
ArrayList<String> clientName;
#ManyToMany(fetch=FetchType.EAGER) #JsonIgnore
#JoinTable(name="user_menu",joinColumns={#JoinColumn(name="userid")},
inverseJoinColumns={#JoinColumn(name="menuid")})
public List<Menu> menus;
#ManyToMany(fetch=FetchType.EAGER) #JsonIgnore
#JoinTable(name="user_client",joinColumns={#JoinColumn(name="userid")},
inverseJoinColumns={#JoinColumn(name="outletid")})
public List<Client> clients;
public User() {
}
I have user,menu and third mapping table user_menu which is created automatically, I successfully get result when fire following query in mysql
select * from menu m inner join user_menu um on m.menuid = um.menuid where um.userid = 41;
I want to write this query in hibernate how to this stuff ???
Finally I find my answer, here is my hql query,
String sql = "select m.menuid as menuid,m.parentid as parentid,m.menuname as menuname,m.url as url,m.status as status,m.usertype as usertype,m.isParent as isParent,m.childMenu as childMenu from Menu m join m.users u where u.userId = "+userid +"";
q = session.createQuery(sql).setResultTransformer(Transformers.aliasToBean(Menu.class));

Categories

Resources