how to specify List of entites in an entity using JPA - spring-boot

I have two entities. A vulnerability can have multiple vulnerability identifiers.
#Entity
#JsonInclude(Include.NON_NULL)
#ApiModel(parent = ApprovableEntity.class)
public class Vulnerability {
...
#JsonProperty("vulnerabilityIdentifiers")
#JoinColumn(name = "vulnerabilityidentifier_id")
#JsonView(JsonViews.BasicChangeLogView.class)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<VulnerabilityIdentifier> vulnerabilityIdentifiers;
...
}
#Entity
#ApiModel(parent = ApprovableEntity.class)
public class VulnerabilityIdentifier {
...
#ManyToOne
#JoinColumn(name = "vulnerability_id", referencedColumnName = "id")
#NotNull(message = "vulnerability is required")
#JsonView({JsonViews.BasicApprovableView.class, JsonViews.BasicChangeLogView.class,
JsonViews.ChangeLogAnswerView.class, JsonViews.DraftAnswerView.class})
#ApiModelProperty(hidden = true)
private Vulnerability vulnerability;
#Column(name = "type")
#JsonProperty("type")
#Size(max = 12)
#NotNull(message = "CVEID type required")
#ApiModelProperty(accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String cveIdType;
#Column(name = "value")
#JsonProperty("value")
#Size(max = 24)
#NotNull(message = "value is required")
#ApiModelProperty(accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String value;
...
}
Now when I send in a json request to the endpoint like as under, the application throws exception that it cannot map the type and value fields in the vulnerabilityIdentifier field.
A sample json request
{
"vulnerabilityImpacts": {
},
"vulnerabilityIdentifiers": [{"type": "cveId", "value": "CVE-1234-12345"}],
"vulnerableThreeppcomponents": [],
"internalSource": "**",
"cveId": "*****",
......
}

Both the cveId and value properties are annotated with #ApiModelProperty(accessMode = ApiModelProperty.AccessMode.READ_ONLY) which means they are ignored when deserializing. Remove this annotation from both properties.

Related

Multipartfile charset=UTF-8 is not supported spring boot api rest

The code was working normally and I've tried in every way to solve it and I couldn't, it may be that after I transformed the MultipartFile into an array this happened
#RestController
#RequestMapping("products")
public class ProductController {
#Autowired
private ProductService productService;
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#Transactional
public ResponseEntity<ShowProductsDTO> registerProduct(
#RequestBody #Valid ProductDTO dto,
#RequestParam(name = "files", required = true) MultipartFile[] files,
UriComponentsBuilder uriBuilder) {
ShowProductsDTO showProductsDTO = null;
try {
showProductsDTO = productService.save(dto, files);
} catch (IOException e) {
e.printStackTrace();
}
var uri = uriBuilder.path("/products/{id}").buildAndExpand(showProductsDTO.id()).toUri();
return ResponseEntity.created(uri).body(showProductsDTO);
}
DTO
public record ProductDTO(
#NotBlank
String name,
#NotBlank
String description,
#NotNull
#NumberFormat
BigDecimal price,
#NumberFormat
#NotNull
Integer quantity,
#NotNull
Boolean active,
#NotNull
Long sub_category_id
) {
}
Error console
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException:
Content-Type
'multipart/form-data;boundary=--------------------------816548045966415708649211;charset=UTF-8'
is not supported]
Postman body > raw > json
{
"name": "Nome do produto",
"description": "descricao do produto",
"price": "2500.00",
"quantity": "2",
"active": "true",
"sub_category_id": "1"
}
Postman > body > form-data
KEY "files", TYPE file, VALUE uma imagem minha em png
Error postman
{
"timestamp": "2023-01-11T06:15:43.455+00:00",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content-Type 'multipart/form-data;boundary=--------------------------056640214920648036756520;charset=UTF-8' is not supported.",
"path": "/products"
}
Product entity
#Table(name = "products")
#Entity(name = "Product")
#Getter
#Setter
#NoArgsConstructor
#EqualsAndHashCode(of = "id")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(length = 100, unique = true, nullable = false)
private String name;
#Column(nullable = false, columnDefinition = "TEXT")
private String description;
#Column(length = 8, nullable = false, columnDefinition = "NUMERIC(8,2)")
private BigDecimal price;
#Column(nullable = false, columnDefinition = "INT")
private Integer quantity;
#Column(nullable = false, columnDefinition = "BOOLEAN")
private Boolean active;
#CollectionTable(name = "products_files",
joinColumns =
#JoinColumn(name = "product_id", referencedColumnName = "id"))
private List<String> productFiles;
#JoinColumn(name = "sub_category_id")
#ManyToOne(fetch = FetchType.EAGER)
private SubCategory subCategory;
how do I fix this error?
Change your attribute to #PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
and call your api using Postman body > raw > json.
The thing is, Content-Type: form-data handles file requests.

SpringDoc generate OpenApi spec with multiple media types

In my Spring project I'm using Springdoc to generate a OpenApiSpecification doc.
I created my Api with these annotations. I want to have the same endpoint url with different mediatype to handle the POST of different objects.
#Validated
#Tag(name = "Calendar", description = "Api for Calendar resource")
public interface CalendarApi {
#Operation(summary = "Add an appointment to the calendar", description = "Add an appointment to the calendar", tags = {"appointment"})
#ApiResponses(value = {
#ApiResponse(responseCode = "201", description = "Successful operation", content = #Content(mediaType = "application/json+widget", schema = #Schema(implementation = AppointmentWidgetDto.class))),
#ApiResponse(responseCode = "400", description = "Invalid input")
})
#PostMapping(value = "/appointments", consumes = "application/json+widget")
ResponseEntity<Appointment> saveFromWidget(#Parameter(description = "The new appointment to save", required = true) #Valid #RequestBody AppointmentWidgetDto appointmentDto);
#Operation(summary = "Add an appointment to the calendar", description = "Add an appointment to the calendar", tags = {"appointment"})
#ApiResponses(value = {
#ApiResponse(responseCode = "201", description = "Successful operation", content = #Content(mediaType = "application/json", schema = #Schema(implementation = Appointment.class))),
#ApiResponse(responseCode = "400", description = "Invalid input")
})
#PostMapping(value = "/appointments", consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Appointment> save(#Parameter(description = "The new appointment to save", required = true) #Valid #RequestBody Appointment appointmentDto);
}
The generated Open Api Spec document is:
/api/v1/appointments:
post:
tags:
- Calendar
summary: Add an appointment to the calendar
description: Add an appointment to the calendar
operationId: save_1
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Appointment'
application/json+widget:
schema:
$ref: '#/components/schemas/AppointmentWidgetDto'
required: true
responses:
'201':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/Appointment'
'400':
description: Invalid input
content:
'*/*':
schema:
$ref: '#/components/schemas/Appointment'
I've a couple of porblems:
the endpoint name is not meaningful (save_1)
when I use Open Api generator to generate the Angular client from this specification, I've some warnings that prevent the generation of both methods.
[WARNING] Multiple schemas found in the OAS 'content' section, returning only the first one (application/json)
[WARNING] Multiple MediaTypes found, using only the first one
I know there is this issue opened (https://github.com/OpenAPITools/openapi-generator/issues/3990).
Is there any way to permit to POST two different bodies in the same endpoint url and using OpenApi generator to create client for different languages/platforms?
===== UPDATE =======
This is AppointmentWidgetDTO:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#SuperBuilder
public class AppointmentWidgetDto implements Serializable {
#NotNull(message = "{appointment.store.missing}")
#JsonDeserialize(using = StoreUriDeserializer.class)
private Store store;
#NotNull(message = "{appointment.title.missing}")
#Size(max = 255)
private String title;
#Lob
#Size(max = 1024)
private String description;
#Size(max = 50)
private String type;
#Size(max = 50)
private String icon;
#NotNull(message = "{appointment.startdate.missing}")
private Instant startDate;
#NotNull(message = "{appointment.enddate.missing}")
private Instant endDate;
#JsonDeserialize(using = ContactUriDeserializer.class)
private Contact contact;
#NotBlank(message = "{appointment.contactname.missing}")
private String contactName;
#NotBlank(message = "{appointment.email.missing}")
#Email
private String contactEmail;
#NotBlank(message = "{appointment.phone.missing}")
#PhoneNumber
private String contactPhone;
}
and this is Appointment:
#ScriptAssert(lang = "javascript", script = "_.startDate.isBefore(_.endDate)", alias = "_", reportOn = "endDate", message = "{appointment.invalid.end.date}")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#SuperBuilder
public class Appointment extends AbstractEntity {
#NotNull(message = "{appointment.store.missing}")
#JsonDeserialize(using = StoreUriDeserializer.class)
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "store_id", updatable = false)
private Store store;
#NotNull
#Size(max = 255)
#Column(nullable = false, length = 255)
private String title;
#Lob
#Size(max = 1024)
#Column(length = 1024)
private String description;
#Size(max = 30)
#Column(length = 30)
private String color;
#Size(max = 50)
#Column(length = 50)
private String type;
#Size(max = 50)
#Column(length = 50)
private String icon;
#Size(max = 255)
#Column(length = 255)
private String location;
#NotNull
#Column(nullable = false)
private Instant startDate;
#NotNull
#Column(nullable = false)
private Instant endDate;
#Builder.Default
#NotNull
#Column(nullable = false, columnDefinition = "BIT DEFAULT 0")
private boolean allDay = false;
#JoinColumn(name = "contact_id")
#JsonDeserialize(using = ContactUriDeserializer.class)
#ManyToOne(fetch = FetchType.LAZY)
private Contact contact;
private String contactName;
#Email
private String contactEmail;
#PhoneNumber
private String contactPhone;
#JoinColumn(name = "agent_id")
#JsonDeserialize(using = AgentUriDeserializer.class)
#ManyToOne(fetch = FetchType.LAZY)
private Agent agent;
private String agentName;
#Builder.Default
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
#NotNull
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private AppointmentStatus status = AppointmentStatus.VALID;
With OpenAPI 3, you can not have many operations for the same path.
You will have only one endpoint and only one OpenAPI description.
What you can do is to define the #Operation annotation on the top of one of the methods, where you add the OpenAPI documentation of the merged OpenAPI description of all your other methods as well and add the #Hidden annotation on the others.
Or you can define two different groups: For each one you filter using header matching, option headersToMatch of GroupedOpenApi Bean.

How to map an entity as java.util.Map with spring Data JPA?

I have such entities:
Bonus_Request entity:
#Entity
#Table(name = "bonus_request")
public class BonusRequest {
//some code above...
#OneToMany(fetch = FetchType.EAGER, mappedBy = "bonusRequest")
#JsonManagedReference(value = "parameter-bonus_request")
private Set<BonusRequestParameter> parameters;
}
Bonus_Request_Parameter entity:
#Entity
#Table(name = "bonus_request_parameter")
public class BonusRequestParameter {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(max = 30)
#Column(name = "parameter", nullable = false)
private String parameter;
#Size(max = 50)
#Column(name = "value", nullable = false)
private String value;
#JoinColumn(name = "bonus_request_id", nullable = false)
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference(value = "parameter-bonus_request")
private BonusRequest bonusRequest;
}
I wonder if it is possible to map the BonusRequestParameter entity as a java.util.Map field in the BonusRequest entity.
For example:
#Entity
#Table(name = "bonus_request")
public class BonusRequest {
#OneToMany(fetch = FetchType.EAGER, mappedBy = "bonusRequest")
private Map<String, String> parameters; //String parameter, String value
}
I use:
Spring Data JPA - 2.1.7
PostgreSQL DB - 10.7
This will work. It loads the map eagerly by default.
#Entity
#Table(name = "bonus_request")
public class BonusRequest {
...
#ElementCollection
private Map<String, String> parameters; //String parameter, String value
}
Resolved with this:
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "bonus_request_parameter",
joinColumns = {#JoinColumn(name = "bonus_request_id", referencedColumnName = "id")})
#MapKeyColumn(name = "parameter")
#Column(name = "value")
private Map<String, String> parameters;
Thank you for help.

How to retrieve data based on inverseColumn data using CrudRepository in springboot?

I have two tables i.e. users and events. Users table will be filled when new user will sign up. Later same user can create calendar events. so events table will be filled and users_events will keep mapping of events based on user.
I would like to find all events based on logged in userId. so here is query, it should return data based on it.
select * from events where eventid in (select eventId from users_event where id_user=x ). Here is my Users and Event Entity class.
User.java
#Entity
#Table(name = "users")
public class User {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "family_name", nullable = false)
private String familyName;
#Column(name = "e_mail", nullable = false)
private String email;
#Column(name = "phone", nullable = false)
private String phone;
#Column(name = "language", nullable = false)
private String language;
#Column(name = "id_picture")
private String pictureId;
#Column(name = "login", nullable = false)
private String login;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "birth_date")
private Date birthDate;
#Column(name = "enabled")
private Boolean enabled;
//getter and setter
Event.java
#Entity
#Table(name = "events")
public class Event {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "eventId", nullable = false)
private Long eventId;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "description", nullable = true)
private String description;
#Column(name = "startAt", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date startAt;
#Column(name = "endAt", nullable = true)
#Temporal(TemporalType.TIMESTAMP)
private Date endAt;
#Column(name = "isFullDay", nullable = false)
private Boolean isFullDay;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "users_event", joinColumns = { #JoinColumn(name = "id_event", referencedColumnName = "eventId") }, inverseJoinColumns = { #JoinColumn(name = "id_user", table = "users", referencedColumnName = "id") })
private Set<User> user = new HashSet<User>();
/getter and setter
EventRepo.java
public interface EventRepo extends CrudRepository<Event, Long> {
Event findByUser(Set<User> user);
}
I am trying to implement something, which can give me output of this query.
select * from events where eventid in (select eventId from users_event where id_user=x )
here is my implementation.any input please?
#RequestMapping(value = "/events", method = RequestMethod.GET)
public #ResponseBody List<Event> getEvents() {
logger.debug("get event list");
User x=new User();
x.setId(1);
Set<User> user= new HashSet();
user.add(x);
return (List<Event>) eventRepo.findByUser(user);
}
Just add a following method to your EventRepo:
List<Event> findAllByUserId(Long userId);
And modify your controller to something like this:
#RequestMapping(value = "/events", method = RequestMethod.GET)
public List<Event> getEvents() {
return eventRepo.findAllByUserId(1L);
}

composite pattern, I am not able to retrieve all entities from the backend

I am trying to implement and use the composite pattern in my system.
The problem is that I cant retrieve all the hierarchy of entities from the backend.
I am not sure what is the problem, the fetch is fine. So, I am not sure if is hibernate.
Lets see, these are my entities.
#Entity
#Table(name = "game")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "game")
public class Game extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(name = "name", nullable = false)
private String name;
#Column(name = "detail")
private String detail;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "rule_id")
private GameRule gameRule;
...
In this class I save the main "GameRule"
#Entity
#Table(name = "game_rule")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("rule")
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "_class")
#JsonSubTypes({
#JsonSubTypes.Type(value = SimpleRule.class, name = "SimpleRule"),
#JsonSubTypes.Type(value = CompositeRule.class, name = "CompositeRule") })
public abstract class GameRule implements Serializable {
private static final long serialVersionUID = -4597791997254248990L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private Long id;
private String operator;
In this class I save a list of GameRules
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("group")
public class CompositeRule extends GameRule {
private static final long serialVersionUID = 6197786758476721324L;
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.ALL })
#JoinTable(name = "game_rules_hierarchy",
joinColumns = #JoinColumn(name = "parent_rule_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "child_rule_id", referencedColumnName = "id"))
#OrderBy("id")
private List<GameRule> rules;
public List<GameRule> getRules() { return rules; }
public void setRules(List<GameRule> rules) { this.rules = rules; }
And now the leaf entity.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("simple")
public class SimpleRule extends GameRule {
private static final long serialVersionUID = 6197786758476721324L;
private String variable;
private Double value;
#ManyToOne
#NotNull
private Device device;
Now, the restController to retrieve the data
#RequestMapping(value = "/games/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
#Transactional
public ResponseEntity<Game> getGame(#PathVariable Long id) {
log.debug("REST request to get Game : {}", id);
Game game = gameRepository.findOne(id);
return Optional.ofNullable(game)
.map(result -> new ResponseEntity<>(
result,
HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
Now in the view I am able to receive the CompositeRule objects but nothing about the simpleRule objects.
From the UI (angular), I am using this JSON to load the hierarchy.
vm.game.gameRule = {id: null, operator: "", type:null, _class:"CompositeRule",
rules: [ {id: null, operator: "", type:null, _class:"CompositeRule", rules:
[{id: null, type:null, _class:"SimpleRule", device: "6", variable: "POWER", operator: ">", value: "100"},
{id: null, type:null, _class:"SimpleRule", device: "6", variable: "POWER", operator: ">", value: "100"}]}
]};
This is loaded succesfully in the DB. But the problem is when I try to retrieve the entire hierarchy. Currently I am receiving only the CompositeRule objects, somthing like:
vm.game.gameRule = {id: 1, operator: "", type:null, _class:"CompositeRule",
rules: [ {id: 2, operator: "", type:null, _class:"CompositeRule", rules:[]} ]};]
In the RestController I am able to see all the hierarchy objects. I am not sure what the problem is.
Thanks for reading.
MY BAD!
Everything was fine. My problem is in the console logs. It seems that the console in chrome is not showing all the hierarchy. But if I go to the network view I am able to see everything.
Thanks.

Resources