I have this problem now and I would like to ask for help, I can't acess te value of dto.getIdCliente(), but this is passed like JSON for the ServicoPrestadoDTO dto.
Codes bellow...
ServicoPrestadoController.java:
#RestController
#RequestMapping("/api/servicos-prestados")
#RequiredArgsConstructor
public class ServicoPrestadoController {
private final ClienteRepository clienteRepository;
private final ServicoPrestadoRepository servicoPrestadoRepository;
private final BigDecimalConverter bigDecimalConverter;
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public ServicoPrestado salvar(#RequestBody ServicoPrestadoDTO dto){
LocalDate data = LocalDate.parse(dto.getData(), DateTimeFormatter.ofPattern("dd/MM/yyyy"));
Integer idCliente = dto.getIdCLiente();
Cliente cliente =
clienteRepository.findById(idCliente)
.orElseThrow(() ->
new ResponseStatusException(
HttpStatus.NOT_FOUND, "Cliente não encontrado!"));
ServicoPrestado servicoPrestado = new ServicoPrestado();
servicoPrestado.setDescricao(dto.getDescricao());
servicoPrestado.setData(data);
servicoPrestado.setCliente(cliente);
servicoPrestado.setValor(bigDecimalConverter.converter(dto.getPreco()));
return servicoPrestado;
}
#GetMapping
public List<ServicoPrestado> pesquisar(
#RequestParam(value = "nome", required = false) String nome,
#RequestParam(value = "mes", required = false) Integer mes
){
return servicoPrestadoRepository.findByNomeClienteAndMes("%" + nome + "%", mes);
}
}
ServicoPrestadoDTO.java:
#Data
#NoArgsConstructor
public class ServicoPrestadoDTO {
private String descricao;
private String preco;
private String data;
private Integer idCLiente;
}
ServicoPrestado.java:
#Entity
#Data
public class ServicoPrestado {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false, length = 150)
private String descricao;
#ManyToOne
#JoinColumn(name = "id_cliente")
private Cliente cliente;
#Column
private BigDecimal valor;
#Column
#JsonFormat(pattern = "dd/MM/yyyy")
private LocalDate data;
}
I don't undertand why is not getting the servico send by my servico-prestado-form.ts like JSON.
Your ServicoPrestadoDTO class has a field idCLiente and not idCliente (you have an uppercase "L").
Try changing it to the proper casing of idCliente.
Does that fix it for you?
While using lombok, add the #AllArgsConstructor annotation to your ServicoPrestadoDTO class.
This way, Jackson deserializer will be able to deserialize your JSON into an object.
Related
In my postgresql, I have a table in which there is a column of type jsonb. This is my java class of that table:
#Table(schema = "public", name = "vw_user_site_role_permission")
#Data
#Entity
#TypeDef(
name = "json",
typeClass = JsonType.class
)
public class ViewUserSiteRolePermission extends AuditableEntity{
#Column(name = "user_site_id")
private Long userSiteId;
#Column(name = "user_role_id")
private Long userRoleId;
#Column(name = "user_id")
private Long userId;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "site_id")
private Long siteId;
#Column(name = "site_name")
private String siteName;
#Column(name = "code")
private String code;
#Column(name = "role_id")
private Long roleId;
#Column(name = "role_name")
private String roleName;
#Column(name = "permission_group_id")
private Long permissionGroupId;
#Column(name = "title")
private String title;
#Type(type = "json")
#Column(name = "permission", columnDefinition = "jsonb")
private JsonNode permission;
}
Now everything works good for other objects but i have to implement search on a field which is present in jsonb object.
I am using querydsl in my repository,
#Repository
public interface ViewUserSiteRolePermissionRepository extends
JpaRepository<ViewUserSiteRolePermission, Long>,
QuerydslPredicateExecutor<ViewUserSiteRolePermission> {
}
This is my method to implement filtering and searching on other objects:
#Override
public Page<ViewUserSiteRolePermission> getALLViewUserSiteRolePermission(List<Long> userIds,
List<Long> userRoleIds, List<Long> siteIds, Long roleId, String search,String permissions,
Pageable pageable) {
BooleanBuilder filter = new BooleanBuilder();
if (!ObjectUtils.isEmpty(userIds)) {
filter.and(QViewUserSiteRolePermission.viewUserSiteRolePermission.userId.in(userIds));
}
if (!ObjectUtils.isEmpty(userRoleIds)) {
filter.and(QViewUserSiteRolePermission.viewUserSiteRolePermission.userRoleId.in(userRoleIds));
}
if (!ObjectUtils.isEmpty(siteIds)) {
filter.and(QViewUserSiteRolePermission.viewUserSiteRolePermission.siteId.in(siteIds));
}
if (!ObjectUtils.isEmpty(roleId)) {
filter.and(QViewUserSiteRolePermission.viewUserSiteRolePermission.roleId.eq(roleId));
}
if (StringUtils.isNotBlank(search)) {
BooleanBuilder booleanBuilder = new BooleanBuilder();
Arrays.asList(search.split(" ")).forEach(nm ->
booleanBuilder.or(QViewUserSiteRolePermission.viewUserSiteRolePermission.siteName.containsIgnoreCase(nm))
);
filter.and(booleanBuilder);
}
return viewUserSiteRolePermissionRepository.findAll(filter, pageable);
}
Now how to apply filter on jsonb object using this approach?
Unfortunately querydsl can't access fields inside your jsonb object, just the object as a whole. But there is a way around. You can write a native query inside your repository and access it that way. Let's assume your field inside your permission object is called 'permissionTitle'.
Repo:
import org.springframework.data.jpa.repository.Query;
#Repository public interface ViewUserSiteRolePermissionRepository extends JpaRepository<ViewUserSiteRolePermission, Long>, QuerydslPredicateExecutor<ViewUserSiteRolePermission> {
#Query(
value =
"SELECT * "
+ "FROM view_user_site_role_permission "
+ "WHERE CAST(permission ->> 'permissionTitle' AS VARCHAR) = ?1",
nativeQuery = true)
List<ViewUserSiteRolePermission> findByPermissionTitle(final String permissionTitle);
}
And now inside your getALLViewUserSiteRolePermission function you can do something like this:
if (StringUtils.isNotBlank(permissionTitle)) {
final List<ViewUserSiteRolePermission> found = this.viewUserSiteRolePermissionRepository.findByPermissionTitle(permissionTitle);
filter.and(QViewUserSiteRolePermission.viewUserSiteRolePermission.in(found);
}
How can I map the one to many relationship using org.mapstruct framework?
DTO classes:
#Data
public class ScheduledJobDTO {
private String jobName;
private String jobGroup;
private String jobClass;
private String cronExpression;
private Boolean cronJob;
private Long repeatTime;
private Integer repeatCount;
private Set<ScheduledJobParamsDTO> paramtersDTOs;
}
#Data
#EqualsAndHashCode
public class ScheduledJobParamsDTO {
String name;
String value;
}
Domain Classes -
#Data
#Entity
#Table(name = "scheduled_job")
public class ScheduledJob {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "job_name")
private String jobName;
#Column(name = "job_group")
private String jobGroup;
#Column(name = "job_class")
private String jobClass;
#Column(name = "cron_expression")
private String cronExpression;
#Column(name = "is_cron_job")
private Boolean cronJob;
#Column(name = "repeat_time")
private Long repeatTime;
#Column(name = "repeat_count")
private Integer repeatCount;
#Column(name = "trigger_start_date")
private LocalDate triggerStartDate;
#Column(name = "trigger_end_date")
private LocalDate triggerEndDate;
#Column(name = "created_at")
private LocalDate createdAt;
#Column(name = "modified_at")
private LocalDate modifiedAt;
#Column(name = "is_active")
private Boolean active;
#OneToMany(mappedBy = "scheduledJob")
private Set<ScheduledJobParams> parameters;
}
#Data
#Entity
#Table(name = "scheduled_job_params")
#EqualsAndHashCode
public class ScheduledJobParams {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "scheduled_job_id", nullable = false)
ScheduledJob scheduledJob;
String name;
String value;
}
Mapper Class -
#Mapping(source = ".", target = ".")
#Mapping(source = "paramtersDTOs", target = "parameters")
ScheduledJob mapToDomain(ScheduledJobDTO scheduledJobDTO);
Now, the above mapper is mapping the ScheduledJob & ScheduledJobParams, but the ScheduledJobParams has reference of ScheduledJob.
How can I map the reference ScheduledJob to ScheduledJobParams?
You can achieve that through #AfterMapping with #MappedTarget. This is described in the reference documentation: 12.2. Mapping customization with before-mapping and after-mapping methods.
// Java 8+ otherwise you need to use an abstract class and a for-loop instead
#Mapper(componentModel = "spring")
public interface ScheduledJobMapper {
#Mapping(target = "parameters", source = "paramtersDTOs")
ScheduledJob mapToDomain(ScheduledJobDTO dto);
#AfterMapping
default void after(#MappingTarget ScheduledJob domain, ScheduledJobDTO dto) {
domain.getParameters().forEach(scheduledJobParams -> {
scheduledJobParams.setScheduledJob(domain);
});
}
}
However, I am sure you don't need to fill the bidirectional relationship when you map back from the DTO into the entity (this is what I understand as you refer to "domain"). Note printing out or serializing such object i.e. into JSON or XML throws java.lang.StackOverflowError if not properly handled.
I am using Spring boot with the PagingAndSortingRepository.
My Post entity:
#Setter
#Getter
#Entity
#NoArgsConstructor
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotNull
private int branch;
#NotNull
#Size(min = 240, max = 10000)
private String article;
#NotNull
private String featuredImage;
#NotNull
private int authorId;
private Date datePublished = new Date();
private boolean commentsEnabled = true;
private boolean enabled = false;
private int views;
private String snippetTitle;
private String snippetDescription;
#ManyToMany
Set<Category> categories;
#Transient
private Set<Comment> comments;
public Post(int branch , String article, String featuredImage, int authorId){
this.branch = branch;
this.article = article;
this.featuredImage = featuredImage;
this.authorId = authorId;
}
My PostRepository:
public interface PostRepository extends PagingAndSortingRepository<Post, Integer> {
#RestResource(path = "/byAuthorId")
Page<Post> getPostsByAuthorId (Pageable pageable, int authorId);
#Override
#RestResource(exported = false)
Page<Post> findAll(Pageable pageable);
#Override
#RestResource(exported = false)
Optional<Post> findById(Integer integer);
}
I am trying to request the Posts by authorId with the getPostsByAuthorId method, with this URL: http://localhost:8081/posts/byAuthorId?page=1&size=1&authorId=1
Unfortunately I keep getting this error:
Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "byAuthorId"]
I already tried adding #RequestParam("authorId") int authorId.
I already tried re-writing the method to check if Intellij still provides getPostsByAuthorId as method and not something like getPostsByAuthor_Id
I haven't done anything with the controller regarding this method.
The problem was that autogenerated methods automatically get /search prefix.
Answer: http://localhost:8081/posts/search/byAuthorId?page=1&size=1&authorId=1
I'm working on an API endpoint that returns a Springframework Page response. I want the front end to be able to sort the data but I can't expect the front end to know that the column they want to sort on is actually inside a composite primary key.
In the example below (a simplified version of what I'm working on) you can see that the startDate column is inside a RouteEntityPk class, which is linked to the RouteEntity class with the #EmbeddedId annotation. To Sort on that column the front end would need to add ?sort=pk.startdate,asc to the request. I want the front end to only have to provide ?sort=startdate,asc.
Is there a way - using Spring magic - of having the repository know that startdate == pk.startdate, or will I have to write a translator which will remove the pk when showing the sort column to the front end, and add it where necessary when reading it from the request?
Controller:
#GetMapping(value = "routes/{routeId}", produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Page<Route>> getRouteByRouteId(#PathVariable(value = "routeId") final String routeId,
#PageableDefault(size = 20) #SortDefault.SortDefaults({
#SortDefault(sort = "order", direction = Sort.Direction.DESC),
#SortDefault(sort = "endDate", direction = Sort.Direction.DESC)
}) final Pageable pageable) {
return ResponseEntity.ok(routeService.getRouteByRouteId(routeId, pageable));
}
Service:
public Page<Route> getRouteByRouteId(String routeId, Pageable pageable) {
Page<RouteEntity> routeEntities = routeRepository.findByRouteId(routeId, pageable);
return new PageImpl<>(
Collections.singletonList(routeTransformer.toRoute(routeId, routeEntities)),
pageable,
routeEntities.getContent().size()
);
}
Repository:
#Repository
public interface RouteRepository extends JpaRepository<RouteEntity, RouteEntityPk> {
#Query(value = " SELECT re FROM RouteEntity re"
+ " AND re.pk.routeId = :routeId")
Page<RouteEntity> findByRouteId(#Param("routeId") final String routeId,
Pageable pageable);
}
Entities:
Route:
#Data
#Entity
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "ROUTE", schema = "NAV")
public class RouteEntity {
#EmbeddedId
private RouteEntityPk pk;
#Column(name = "NAME")
private String name;
#Column(name = "ORDER")
private Integer order;
#Column(name = "END_DTE")
private LocalDate endDate;
}
RoutePk:
#Data
#Builder(toBuilder = true)
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
public class RouteEntityPk implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "ROUTE_ID")
private String routeId;
#Column(name = "STRT_DTE")
private LocalDate startDate;
}
Models:
Route:
#Data
#Builder
public class Route {
public String name;
public String routeId;
public List<RouteItem> items;
}
Item:
#Data
#Builder
public class Item {
public Integer order;
public LocalDate startDate;
public LocalDate endDate;
}
Transformer:
public Route toRoute(String routeId, Page<RouteEntity> routeEntities) {
return Route.builder()
.name(getRouteName(routeEntities))
.routeId(routeId)
.items(routeEntities.getContent().stream()
.map(this::toRouteItem)
.collect(Collectors.toList()))
.build();
}
private Item toRouteItem(RouteEntity item) {
return ParcelshopDrop.builder()
.order(item.getOrder())
.startDate(item.getStartDate())
.endDate(item.getEndDate())
.build();
}
So it looks like the way to do this is to use the other way you can deal with composite primary key's in JPA, the annotation #IdClass. This way you can put the fields in the main entity and refer to them as such.
Below is a link to the baeldung article I followed and the changes to the entities I posted above that make this work:
https://www.baeldung.com/jpa-composite-primary-keys
Entities:
Route:
#Data
#Entity
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
#IdClass(RouteEntityPk.class)
#Table(name = "ROUTE", schema = "NAV")
public class RouteEntity {
#Id
#Column(name = "ROUTE_ID")
private String routeId;
#Id
#Column(name = "STRT_DTE")
private LocalDate startDate;
#Column(name = "NAME")
private String name;
#Column(name = "ORDER")
private Integer order;
#Column(name = "END_DTE")
private LocalDate endDate;
}
RoutePk:
#Data
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
public class RouteEntityPk implements Serializable {
private static final long serialVersionUID = 1L;
private String routeId;
private LocalDate startDate;
}
This is one solution, probably not the best, but you can transform Pageable object in order to replace the field name like this :
In your controller getRouteByRouteId method :
List<Order> orders = pageable.getSort().stream().map(o -> o.getProperty().equals("startdate") ? new Order(o.getDirection(), "pk.startdate"): o).collect(Collectors.toList());
Then you can call the service with the modified object :
return ResponseEntity.ok(routeService.getRouteByRouteId(routeId, PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(orders))));
I have two tables one is parent and other one is child. When I am trying to save initially, I am able to insert values in both the tables if values not present in Parent table. But at the time of update/insert the values in child table, it is inserting duplicate values.
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class RuleApi {
Long id;
private String market;
private int modelYear;
private String vehicleLine;
private String vehicleLineName;
private String locale;
private String binding;
private String description;
private String createUser;
private String updateUser;
}
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class DescriptorSaveRequest {
#Valid
#NotNull
RuleApi rule;
}
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "MNAVS03_DESCRIPTOR_CONTEXT")
#EntityListeners(AuditingEntityListener.class)
public class DescriptorContext implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Setter(value = AccessLevel.NONE)
#Column(name = "NAVS03_DESCRIPTOR_CONTEXT_K")
private Long id;
#Column(name = "NAVS03_MARKET_N")
private String market;
#Column(name = "NAVS03_MODEL_YEAR_R")
private Integer modelYear;
#Column(name = "NAVS03_VEHICLE_LINE_C")
private String vehicleLine;
#Column(name = "NAVS03_VEHICLE_LINE_N")
private String vehicleLineName;
#Column(name = "NAVS03_LOCALE_N")
private String locale;
#Column(name = "NAVS03_CREATE_USER_C", nullable = false)
private String createUserId;
#CreationTimestamp
#Column(name = "NAVS03_CREATE_S")
private Timestamp createTimestamp;
#Column(name = "NAVS03_LAST_UPDT_USER_C", nullable = false)
private String updateUserId;
#UpdateTimestamp
#Column(name = "NAVS03_LAST_UPDT_S")
private Timestamp updateTimestamp;
}
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "MNAVS04_DESCRIPTOR_RULE")
#EntityListeners(AuditingEntityListener.class)
public class DescriptorRule implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Setter(value = AccessLevel.NONE)
#Column(name = "NAVS04_DESCRIPTOR_RULE_K")
private Long id;
#JoinColumn(name = "NAVS03_DESCRIPTOR_CONTEXT_K", nullable = false)
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
private DescriptorContext descriptorContextId;
#Column(name = "NAVS04_BINDING_N",
unique = true)
private String binding;
#Column(name = "NAVS04_DESCRIPTOR_RULE_X")
private String description;
#Column(name = "NAVS04_CREATE_USER_C", nullable = false)
private String createUserId;
#CreationTimestamp
#Column(name = "NAVS04_CREATE_S")
private Timestamp createTimestamp;
#Column(name = "NAVS04_LAST_UPDT_USER_C", nullable = false)
private String updateUserId;
#UpdateTimestamp
#Column(name = "NAVS04_LAST_UPDT_S")
private Timestamp updateTimestamp;
}
#ApiOperation(value = "Create/Update Feature Descriptions", notes = "Create/Update a descriptions based on the given input")
#PostMapping("/descriptor/saveFeatures")
public ResponseEntity<BaseBodyResponse<String>> saveFeatureDescriptions(#Valid #RequestBody DescriptorSaveRequest descriptorSaveRequest) throws Exception {
this.descriptorContextService.saveFeatureDescriptions(
this.descriptorContextMapper.mapDescriptorContext(descriptorSaveRequest),
this.descriptorContextMapper.mapDescriptorRule(descriptorSaveRequest)
);
return ResponseEntity.ok(BaseBodyResponse.result("Saved Successfully"));
}
#Service
public class DescriptorContextService {
//SaveFeatureDescriptions
public void saveFeatureDescriptions(DescriptorContext descriptorContext, DescriptorRule descriptorRule) throws Exception {
DescriptorContext descriptorContext1 =
this.descriptorContextRepository.findByMarketAndModelYearAndVehicleLineAndVehicleLineNameAndLocale(
descriptorContext.getMarket(),
descriptorContext.getModelYear(),
descriptorContext.getVehicleLine(),
descriptorContext.getVehicleLineName(),
descriptorContext.getLocale());
if (descriptorContext1 == null) {
// add a new context
descriptorContext1 = descriptorContextRepository.save(DescriptorContext.builder()
.market(descriptorContext.getMarket())
.modelYear(descriptorContext.getModelYear())
.vehicleLine(descriptorContext.getVehicleLine())
.vehicleLineName(descriptorContext.getVehicleLineName())
.locale(descriptorContext.getLocale())
.createUserId(descriptorContext.getCreateUserId())
.updateUserId(descriptorContext.getUpdateUserId())
.build());
}
Long contextId = descriptorContext1.getId();
List<DescriptorRule> rule = this.descriptorRuleRepository.findByDescriptorContextId(contextId);
if (rule.size() == 0) {
// add a new rule
this.descriptorRuleRepository.save(DescriptorRule.builder()
.descriptorContextId(descriptorContext1)
.binding(descriptorRule.getBinding())
.description(descriptorRule.getDescription())
.createUserId(descriptorContext.getCreateUserId())
.updateUserId(descriptorContext.getUpdateUserId())
.build());
} else {
// update a existing rule
for (DescriptorRule descriptorRule1 : rule) {
if (descriptorRule1.getBinding().equals(descriptorRule.getBinding())) {
descriptorRule1.setDescription(descriptorRule.getDescription());
descriptorRule1.setupdateUserId(descriptorRule.getupdateUserId());
this.descriptorRuleRepository.save(descriptorRule1);
} else {
this.descriptorRuleRepository.save(DescriptorRule.builder()
.descriptorContextId(descriptorContext1)
.binding(descriptorRule.getBinding())
.description(descriptorRule.getDescription())
.createUserId(descriptorContext.getCreateUserId())
.updateUserId(descriptorContext.getUpdateUserId())
.build());
}
}
}
}
}
}
#Component
public class DescriptorContextMapper {
public DescriptorContext mapDescriptorContext(DescriptorSaveRequest descriptorSaveRequest) {
return DescriptorContext.builder()
.market(descriptorSaveRequest.getRule().getMarket())
.vehicleLine(descriptorSaveRequest.getRule().getVehicleLine())
.vehicleLineName(descriptorSaveRequest.getRule().getVehicleLineName())
.modelYear(descriptorSaveRequest.getRule().getModelYear())
.locale(descriptorSaveRequest.getRule().getLocale())
.createUserId(descriptorSaveRequest.getRule().getCreateUser())
.updateUserId(descriptorSaveRequest.getRule().getUpdateUser())
.build();
}
public DescriptorRule mapDescriptorRule(DescriptorSaveRequest descriptorSaveRequest) {
return DescriptorRule.builder()
.id(descriptorSaveRequest.getRule().getId())
.binding(descriptorSaveRequest.getRule().getBinding())
.description(descriptorSaveRequest.getRule().getDescription())
.createUserId(descriptorSaveRequest.getRule().getCreateUser())
.updateUserId(descriptorSaveRequest.getRule().getUpdateUser())
.build();
}
}
{
"rule": {
"binding": "5003",
"description": "Test new 5003-2023 Escape",
"locale": "fr_CA",
"market": "WANAC",
"modelYear": 2023,
"vehicleLine": "TMC",
"vehicleLineName": "Escape",
"createUser": "rdongre",
"updateUser": "rdongre"
}
}
If I am passing this request and values are not present in both the tables then it should insert the values in both the tables which is working as expected with above code. But at the time of update it is going inside the loop and inserting duplicate values. I am trying to update DESCRIPTION in child table if BINDING is present if not it should insert BINDING plus DESCRIPTION
I fixed this by separating Save and Update methods. Thanks to all.