I have a class as UserPayload.java
package com.pulse.org.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.UUID;
import javax.validation.constraints.NotNull;
import lombok.Data;
#JsonInclude(value = JsonInclude.Include.NON_EMPTY, content = JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({"id", "firstName", "middleName", "lastName", "email", "countryCode", "mobileNumber", "orgId"})
#Data
#Schema(description = "details of a user")
public class UserPayload {
/**
* id of a user
*/
#JsonProperty("id")
#Schema(name = "id of a user")
private UUID id;
/*
* first name of a user
*/
#NotNull(message = "1020")
#Schema(name = "first name of a user")
private String firstName;
/*
* middle name of a user
*/
#Schema(name = "middle name of a user")
private String middleName;
/*
* last name of a user
*/
#NotNull(message = "1021")
#Schema(name = "last name of a user")
private String lastName;
/*
* email of a user
*/
#NotNull(message = "1022")
#Schema(name = "email of a user")
private String email;
/*
* country code of a user
*/
#Schema(name = "country code of a user")
private Integer countryCode;
/*
* mobile number of a user
*/
#Schema(name = "mobile number of a user")
private Long mobileNumber;
/*
* organization id of a user's organization
*/
#NotNull(message = "1023")
#Schema(name = "organization id of a user's organization")
private UUID orgId;
/**
* extra details of an user
*/
#JsonUnwrapped
private UserExtraPayload userExtraPayload;
}
and a class which is UserExtraPayload.java as a child of UserExtraPayload.
package com.pulse.org.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.UUID;
import lombok.Data;
import lombok.NoArgsConstructor;
#JsonInclude(value = JsonInclude.Include.NON_EMPTY, content = JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({"id", "supervisorId", "secondaryEmail", "department", "subDepartment"})
#Data
#NoArgsConstructor
#Schema(description = "details of a user")
public class UserExtraPayload {
/**
* id of the user
*/
#Schema(name = "id of the user")
private UUID id;
/*
* supervisor's id of a user
*/
#Schema(name = "supervisor's id of a user")
private UUID supervisorId;
/*
* secondary email of a user
*/
#Schema(name = "secondary email of a user")
private String secondaryEmail;
/*
* department of a user
*/
#Schema(name = "department of a user")
private String department;
/*
* sub department of a user
*/
#Schema(name = "sub department of a user")
private String subDepartment;
}
expected json
{
"id": "0125d290-9519-4170-89a8-0beeb6543055",
"firstName": "ritesh",
"lastName": "shakya",
"email": "abbs#xyz.com",
"orgId": "761eda6a-9a17-4047-b64b-2ce6c624fb8d",
"supervisorId": "761eda6a-9a17-4047-b64b-2ce6c624fb8d",
"secondaryEmail": "pqr#xyz",
"department": "ENG",
"subDepartment": "IT"
}
But I need to map same id which is in UserPayload and UserExtraPayload
I tried using #JsonUnwrap but it's not working.
I want to map a single id in both payload in UserExtra and UserPayload......
Related
I am noticing an InvocationException being returned when I execute a JpaRepository.findAll(Example example) on H2 database.
It occurs when I try to configure the foregin key relationship between the 2 tables "Account" and "Transaction" (i.e. An account can have many transactions, but a transaction can only belong to one account).
Before I add the #OneToMany and #ManyToOne annotations, there were no issues.
Welcome for any help, thank you.
Request:
Query is successful but it gives an InvocationException, which in turns give a HTTP500.
Service:
AccountService.java
...
......
public List<Transaction> getAllTransactions(Account account) {
TransactionPK inputTransactionPK = new TransactionPK();
inputTransactionPK.setAccountNum(account.getAccountNum());
Transaction inputTransaction = new Transaction();
inputTransaction.setTransactionPK(inputTransactionPK);
ExampleMatcher matcher = ExampleMatcher.matchingAll().withIgnorePaths("debitAmt", "creditAmt");
Example<Transaction> example = Example.of(inputTransaction, matcher);
List<Transaction> transactionList = transactionRepository.findAll(example);
log.info("##################################################\n"
+ "Retrieved transaction list for account with account number " + account.getAccountNum()
+ "\n##################################################");
return transactionList;
}
...
......
Table models:
Account.java
package com.somecompany.account.model;
import java.sql.Timestamp;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import lombok.Data;
#Entity
#Data
public class Account {
#OneToMany(mappedBy = "account")
private Set<Transaction> transaction;
#Column(name = "cust_id")
#NotEmpty(message = "Customer ID cannot be null nor empty")
#Pattern(regexp = "^[0-9]+$", message = "Customer ID must be a number")
#Min(value = 1L, message = "Customer ID must not be less than 1")
#Max(value = 9999999999L, message = "Customer ID must not be larger than 9999999999")
private long custId;
#Column(name = "account_num")
#Id
#NotEmpty(message = "Account number cannot be null nor empty")
#Pattern(regexp = "^[0-9]+$", message = "Account number must be a number")
#Min(value = 1L, message = "Account number must not be less than 1")
#Max(value = 9999999999L, message = "Account number must not be larger than 9999999999")
private long accountNum;
#Column(name = "account_name")
#NotEmpty(message = "Account name cannot be null nor empty")
#Size(min = 1, max = 30, message = "Account name must have length between 1 and 30")
private String accountName;
#Column(name = "account_type")
#NotEmpty(message = "Account type cannot be null nor empty")
#Size(min = 1, max = 7, message = "Account type must have length between 1 and 7")
private String accountType;
#Column(name = "balance_date")
#NotEmpty(message = "Balance date cannot be null nor empty")
private Timestamp balanceDate;
#Column(name = "currency")
#NotEmpty(message = "Currency cannot be null nor empty")
#Size(min = 3, max = 3, message = "Currency must have length exactly equal to 3")
private String currency;
#Column(name = "opening_available_balance", columnDefinition = "Decimal(20,2) default '0.0'")
#NotEmpty(message = "Opening available balance cannot be null nor empty")
#Pattern(regexp = "^[0-9.]+$", message = "Opening available balance must be a decimal number")
#DecimalMin(value = "0.0", message = "Opening available balance cannot be negative")
private float openingAvailableBalance;
}
Transaction.java
package com.somecompany.account.model;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import lombok.Data;
#Entity
#Data
public class Transaction {
#ManyToOne
#JoinColumn(name = "account_num", referencedColumnName = "account_num", insertable = false, updatable = false, nullable = false)
private Account account;
#EmbeddedId
private TransactionPK transactionPK;
#Column(name = "account_name")
#NotEmpty(message = "Account name cannot be null nor empty")
#Size(min = 1, max = 30, message = "Account name must have length between 1 and 30")
private String accountName;
#Column(name = "currency")
#NotEmpty(message = "Currency cannot be null nor empty")
#Size(min = 3, max = 3, message = "Currency must have length exactly equal to 3")
private String currency;
#Column(name = "debit_amt", columnDefinition = "Decimal(20,2) default '0.0'")
#NotEmpty(message = "Debit amount cannot be null nor empty")
#DecimalMin(value = "0.0", message = "Debit amount cannot be negative")
private float debitAmt;
#Column(name = "credit_amt", columnDefinition = "Decimal(20,2) default '0.0'")
#NotEmpty(message = "Credit amount cannot be null nor empty")
#DecimalMin(value = "0.0", message = "Credit amount cannot be negative")
private float creditAmt;
#Column(name = "debit_credit")
#NotEmpty(message = "Debit/Credit cannot be null nor empty")
#Size(min = 1, max = 6, message = "Debit/Credit must have length between 1 and 6")
private String debitCredit;
#Column(name = "transaction_narrative")
#Size(min = 0, max = 50, message = "Transaction narrative must have length between 0 and 50")
private String transactionNarrative;
}
TransactionPK.java
package com.somecompany.account.model;
import java.io.Serializable;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import lombok.Data;
#Embeddable
#Data
public class TransactionPK implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name = "account_num")
#NotEmpty(message = "Account number cannot be null nor empty")
#Pattern(regexp = "^[0-9]+$", message = "Account number must be a number")
#Min(value = 1L, message = "Account number must not be less than 1")
#Max(value = 9999999999L, message = "Account number must not be larger than 9999999999")
private long accountNum;
#Column(name = "value_date")
#NotEmpty(message = "Value date cannot be null nor empty")
private Timestamp valueDate;
}
H2 DB primary and foreign key info:
Sample DB data on SpringBoot app startup (data.sql):
INSERT INTO ACCOUNT (cust_id, account_num, account_name, account_type, balance_date, currency, opening_available_balance) VALUES
(1111111111, 1111111111, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Savings', TIMESTAMP '2020-11-01 11:01:01', 'SGD', 99999.99),
(2, 2, 'B', 'Savings', TIMESTAMP '2020-11-02 11:02:02', 'AUD', 0.0),
(1111111111, 3333333333, 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', 'Current', TIMESTAMP '2020-11-03 11:03:03', 'USD', 99999.99);
INSERT INTO TRANSACTION (account_num, account_name, value_date, currency, debit_amt, credit_amt, debit_credit, transaction_narrative) VALUES
(1111111111, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', TIMESTAMP '2012-11-01 11:01:01', 'SGD', 0.0, 99999.99, 'Credit', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'),
(2, 'Savings Account', TIMESTAMP '2012-11-02 11:02:02', 'USD', 0.1, 0.0, 'Debit', null),
(1111111111, 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', TIMESTAMP '2012-11-03 11:03:03', 'USD', 99999.99, 0.0, 'Debit', 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC');
After some investigation, I have made the following changes and the application run as expected finally.
Account.java
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonBackReference;
import lombok.Getter;
import lombok.Setter;
#Entity
#Getter
#Setter
/**
* The model class for "Account" table.
*
* #author patrick
*
*/
public class Account {
#OneToMany(mappedBy = "transactionPK.account")
#JsonBackReference
private List<Transaction> transactions = new ArrayList<>();
#Column(name = "cust_id")
#NotEmpty(message = "Customer ID cannot be null nor empty")
#Pattern(regexp = "^[0-9]+$", message = "Customer ID must be a number")
#Min(value = 1L, message = "Customer ID must not be less than 1")
#Max(value = 9999999999L, message = "Customer ID must not be larger than 9999999999")
private long custId;
#Column(name = "account_num")
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#NotEmpty(message = "Account number cannot be null nor empty")
#Pattern(regexp = "^[0-9]+$", message = "Account number must be a number")
#Min(value = 1L, message = "Account number must not be less than 1")
#Max(value = 9999999999L, message = "Account number must not be larger than 9999999999")
private long accountNum;
#Column(name = "account_name")
#NotEmpty(message = "Account name cannot be null nor empty")
#Size(min = 1, max = 30, message = "Account name must have length between 1 and 30")
private String accountName;
#Column(name = "account_type")
#NotEmpty(message = "Account type cannot be null nor empty")
#Size(min = 1, max = 7, message = "Account type must have length between 1 and 7")
private String accountType;
#Column(name = "balance_date")
#NotEmpty(message = "Balance date cannot be null nor empty")
private Timestamp balanceDate;
#Column(name = "currency")
#NotEmpty(message = "Currency cannot be null nor empty")
#Size(min = 3, max = 3, message = "Currency must have length exactly equal to 3")
private String currency;
#Column(name = "opening_available_balance", columnDefinition = "Decimal(20,2) default '0.0'")
#NotEmpty(message = "Opening available balance cannot be null nor empty")
#Pattern(regexp = "^[0-9.]+$", message = "Opening available balance must be a decimal number")
#DecimalMin(value = "0.0", message = "Opening available balance cannot be negative")
private float openingAvailableBalance;
}
Transaction.java
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
#Entity
#Getter
#Setter
/**
* The model class for "Transaction" table.
*
* #author patrick
*
*/
public class Transaction {
#EmbeddedId
private TransactionPK transactionPK;
#Column(name = "account_name")
#NotEmpty(message = "Account name cannot be null nor empty")
#Size(min = 1, max = 30, message = "Account name must have length between 1 and 30")
private String accountName;
#Column(name = "currency")
#NotEmpty(message = "Currency cannot be null nor empty")
#Size(min = 3, max = 3, message = "Currency must have length exactly equal to 3")
private String currency;
#Column(name = "debit_amt", columnDefinition = "Decimal(20,2) default '0.0'")
#NotEmpty(message = "Debit amount cannot be null nor empty")
#DecimalMin(value = "0.0", message = "Debit amount cannot be negative")
private float debitAmt;
#Column(name = "credit_amt", columnDefinition = "Decimal(20,2) default '0.0'")
#NotEmpty(message = "Credit amount cannot be null nor empty")
#DecimalMin(value = "0.0", message = "Credit amount cannot be negative")
private float creditAmt;
#Column(name = "debit_credit")
#NotEmpty(message = "Debit/Credit cannot be null nor empty")
#Size(min = 1, max = 6, message = "Debit/Credit must have length between 1 and 6")
private String debitCredit;
#Column(name = "transaction_narrative")
#Size(min = 0, max = 50, message = "Transaction narrative must have length between 0 and 50")
private String transactionNarrative;
}
TransactionPK.java
import java.io.Serializable;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotEmpty;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.Data;
#Embeddable
#Data
/**
* The model class for the EmbeddedId (i.e. primary key) of the "Transaction" table.
*
* #author patrick
*
*/
public class TransactionPK implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#ManyToOne
#JoinColumn(name = "account_num", referencedColumnName = "account_num", insertable = false, updatable = false, nullable = false)
#JsonManagedReference
private Account account;
#Column(name = "value_date")
#NotEmpty(message = "Value date cannot be null nor empty")
private Timestamp valueDate;
}
I have created an "account" field in TransactionPK replacing the "account_num" field (the account object already has the "account_num" info anyway), and annotated it with #ManyToOne.
This is because the releationship is "An account can have many transactions (i.e. list of transactions), but a transaction only belongs to one account". The releationship is at the object level but not field level.
For the "List transactions" in "Account" and "Account account" in "TransactionPK", they are for indicating the foreign key relationship only, they don't have to be existing in the JSON files.
And if we just leave it like that, it will give an infinite recursion error when serializing to JSON (since each has an element of the other, it can never finish generating the JSON).
To solve the issue, we may mark these fields with #JsonIgnore, which will skip serializing both fields.
Or if we need to show one but not the other (e.g. show "account" in Transaction JSON, but not showing "transactions" in Account JSON), then we can annotate the one which we want to keep with the #JsonManagedReference, and mark the one that we don't want to show in JSON with #JsonBackReference.
Referece:
https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
Another change I made is not using #Data. Avoid using #Data from lombok when you are working with entities if they will be used with ORM like JPA.The #Data's implementation of equals() and hashCode() may mess with the object comparison.
Referece:
https://thorben-janssen.com/lombok-hibernate-how-to-avoid-common-pitfalls/
I have a users table which contains the user details. i also have a authorities table which has the role of a user. The user and authorities table has one to many mapping. When i try to save the details using Jpa the foreign key column is blank no data is inserted in that field. i have a form in which i am specifying the role of the user along with other details.
package com.example.StarsProject.Model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
#Entity
#Table
#Getter
#Setter
public class Authorities {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String role;
#ManyToOne(cascade = CascadeType.PERSIST,fetch=FetchType.EAGER)
#JoinColumn(name = "users_id", referencedColumnName = "id")
private Users users;
public Authorities(String role){
this.role = role;
}
}
package com.example.StarsProject.Model;
import com.example.StarsProject.DTO.UserDTO;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
#Entity
#Table
#Getter
#Setter
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "first_name")
private String firstname;
#Column(name = "last_name")
private String lastname;
#Column(unique = true)
private String email;
private String password;
#OneToMany(fetch = FetchType.EAGER,targetEntity = Authorities.class,mappedBy = "users", cascade = CascadeType.PERSIST)
private Set<Authorities> authorities;
public Users(UserDTO userDTO) {
this.email = userDTO.getEmail();
this.firstname = userDTO.getFirstname();
this.lastname = userDTO.getLastname();
this.password = userDTO.getPassword();
// Authorities authorities = new Authorities();
// authorities.setRole(userDTO.getRole());
// Set<Authorities> set = new HashSet<>();
// set.add(authorities);
this.authorities = new HashSet<Authorities>(Arrays.asList(new Authorities(userDTO.getRole())));
}
}
package com.example.StarsProject.Service;
import com.example.StarsProject.DTO.UserDTO;
import com.example.StarsProject.Model.Users;
import com.example.StarsProject.Repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class UserDetailsServiceImpl implements UserDetailsServiceInterface{
#Autowired
UserRepository userRepository;
#Override
public void storeUserDetails(UserDTO userDTO) {
Users users = new Users(userDTO);
userRepository.save(users);
}
}
When i try to save the user details it doesn't insert any value in the foreign key column. Can someone tell me what i am doing wrong.
You need to setusers field in Authorities manually. Hibernate won't fill it for you.
I have a basic Spring boot app and I am trying to map a list of entities to list of DTOs using Mapstruct (version 1.3.0.Final).
Source:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.sql.Timestamp;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
#Table(name = "source")
#Entity(name = "Source")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Source implements Serializable {
private static final long serialVersionUID = 964150155782995534L;
#Id
#JsonIgnore
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SourceSeq")
#SequenceGenerator(sequenceName = "source_id_seq", allocationSize = 1, name = "SourceSeq")
private long id;
#NotNull
#Size(min = 36, max = 36)
#Column(name = "uuid", nullable = false, length = 36)
private String uuid;
#Column(name = "user_id")
private Long userId;
#Column(name = "username")
private String username;
#Column(name = "user_org_id")
private Long userOrgId;
#Column(name = "user_org_name")
private String userOrgName;
#Column(name = "account_number")
private Integer accountNumber;
#Column(name = "account_name")
private String accountName;
#Column(name = "billing_delay")
private Integer billingDelay;
#Column(name = "default_billing_delay")
private Integer defaultBillingDelay;
#Column(name = "billing_enabled")
private Boolean billingEnabled = true;
#JsonIgnore
#CreationTimestamp
#Column(name = "created_date")
private Timestamp createdDate;
#JsonIgnore
#UpdateTimestamp
#Column(name = "updated_date")
private Timestamp updatedDate;
}
Target:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Target implements Serializable {
private static final long serialVersionUID = 8939532280496355293L;
#ApiModelProperty(hidden = true)
private String uuid;
#ApiModelProperty(value = "user ID", example = "123456", dataType = "int64", position = 1)
private Long userId;
#ApiModelProperty(value = "username", example = "myUser", position = 2)
private String username;
#ApiModelProperty(hidden = true)
private String firstName;
#ApiModelProperty(hidden = true)
private String lastName;
#ApiModelProperty(value = "user organization ID", example = "71836", dataType = "int64", position = 3)
private Long userOrgId;
#ApiModelProperty(value = "user organization name", example = "Org Inc", position = 4)
private String userOrgName;
#ApiModelProperty(value = "account number", example = "987654", position = 5)
private Integer accountNumber;
#ApiModelProperty(value = "account name", example = "My Mapping Acc", position = 6)
private String accountName;
#ApiModelProperty(value = "billing delay (in days)", example = "60", position = 7)
private Integer billingDelay;
#ApiModelProperty(value = "default billing delay (in days)", example = "30", position = 8)
private Integer defaultBillingDelay;
#ApiModelProperty(value = "is billing enabled?", example = "true", position = 9)
private Boolean billingEnabled = true;
#ApiModelProperty(hidden = true)
private Date createdDate;
}
Mapper:
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
#Mapper
public interface MyMapper {
MyMapper MAPPER = Mappers.getMapper(MyMapper.class);
// Target toTarget(Source source);
// I have tried using this as well but my target mapped list only contains billingEnabled = true for every object in the response list. MapperImpl class also included below. Without toTarget method get a compilation error (also included below)
// Response:
/*[
{
"billingEnabled": true
},
{
"billingEnabled": true
},
{
"billingEnabled": true
}
]*/
List<Target> toTargets(List<Source> sources);
}
MapperImpl:
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2019-09-16T00:06:14-0700",
comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
public class MyMapperImpl implements MyMapper {
#Override
public Target toTarget(Source source) {
if ( source == null ) {
return null;
}
Target target = new Target();
return target;
}
#Override
public List<Target> toTargets(List<Source> sources) {
if ( sources == null ) {
return null;
}
List<Target> list = new ArrayList<Target>( sources.size() );
for ( Source source : sources ) {
list.add( toTarget( source ) );
}
return list;
}
}
Error:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.5.1:compile (default-compile) on project my-project: Compilation failure
[ERROR] /Users/user8812/workspace/my-project-/src/main/java/MyMapper.java:[17,23] Can't map Collection element "Source source" to "Target target". Consider to declare/implement a mapping method: "Target map(Source value)".
I'm looking to have Target list with the same field names mapped without another individual toTarget method as that has worked for me in another project with an older Mapstruct version.
The exception thrown by MapStruct during compilation is telling you how to fix it:
Can't map Collection element "Source source" to "Target target". Consider to declare/implement a mapping method: "Target map(Source value)".
You could even place this method signature inside the same interface you've shown us.
Edit
It seems like the default global configuration for MapStruct has been changed in the application. Try applying this annotation to you "source to target" method inside the interface:
#BeanMapping(ignoreByDefault = false)
Or you can explicitly map their fields with the #Mapping annotation.
Since the field names and types in Target type and in Source type are the same, wouldn't it be more beneficial and easier to use BeanUtils.copyProperties(source, target)? It is very straightforward and allows stating of ignored fields.
I think declaration should have annotation in MyMapper interface.
#Mapping(source = "sources", target = "targets")
List<Target> toTargets(List<Source> sources);
Reference: https://mapstruct.org/
I have a many to many relation between Permission and Role classes.
And there is a role_permission table to keep relation between role and permission
CREATE TABLE public.role_permissions
(
role_id bigint NOT NULL,
permission_id bigint NOT NULL,
CONSTRAINT role_permissions_pkey PRIMARY KEY (role_id, permission_id),
CONSTRAINT fkh0v7u4w7mttcu81o8wegayr8e FOREIGN KEY (permission_id)
REFERENCES public.permission (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fklodb7xh4a2xjv39gc3lsop95n FOREIGN KEY (role_id)
REFERENCES public.role (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE public.role_permissions
OWNER TO postgres;
When I want to delete Permission it throws following error
org.postgresql.util.PSQLException: ERROR: update or delete on table "permission" violates foreign key constraint "fkh0v7u4w7mttcu81o8wegayr8e" on table "role_permissions"
Detail: Key (id)=(6) is still referenced from table "role_permissions".
Here is my class implementations
package com.nova.stats.client.backend.auth.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.PreRemove;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.HashSet;
import java.util.Set;
#Data
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#EqualsAndHashCode(exclude = {"users", "roles"})
#ToString(exclude = {"users", "roles"})
#Entity
public class Permission implements GrantedAuthority {
#Transient
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 2, max=100, message = "Permission name must be between {min} and {max} characters long")
#Column(unique = true)
private String name;
#NotNull
#Size(min = 10, max=250, message = "Permission description must be between {min} and {max} characters long")
#Column
private String description;
#Getter(onMethod = #__(#Override))
#NotNull
#Size(min = 6, max=100, message = "Permission authority must be between {min} and {max} characters long")
#Column(unique = true)
private String authority;
#Getter(onMethod = #__(#JsonIgnore))
#ManyToMany(mappedBy = "permissions")
private Set<Role> roles;
#Getter(onMethod = #__(#JsonIgnore))
#ManyToMany(mappedBy = "permissions")
private Set<User> users;
public Permission(String name, String description, String authority) {
this.name = name;
this.description = description;
this.authority = authority;
}
}
package com.nova.stats.client.backend.auth.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.HashSet;
import java.util.Set;
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(exclude = {"users", "permissions"})
#ToString(exclude = {"users", "permissions"})
#Getter
#Setter
#Entity
public class Role implements GrantedAuthority {
#Transient
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 2, max=100, message = "Role name must be between {min} and {max} characters long")
#Column(unique = true)
private String name;
#NotNull
#Size(min = 10, max=250, message = "Role description must be between {min} and {max} characters long")
#Column
private String description;
#Getter(onMethod = #__(#Override))
#NotNull
#Size(min = 6, max=100, message = "Role authority must be between {min} and {max} characters long")
#Column(unique = true)
private String authority;
#Getter(onMethod = #__(#JsonIgnore))
#ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>(0);
#Getter(onMethod = #__(#JsonIgnore))
#Setter(onMethod = #__(#JsonProperty("permissions")))
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "role_permissions", joinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "permission_id", referencedColumnName = "id"))
private Set<Permission> permissions;
public Role(String name, String description, String authority, Set<Permission> permissions) {
this(name, description, authority);
this.permissions = permissions;
}
public Role(String name, String description, String authority) {
this.name = name;
this.description = description;
this.authority = authority;
}
}
Here is what I need; when I want to delete any permission then foreign key must be deleted in role_permission table but related role can't be delete. I mean when delete permission, just delete permission on table permission and relation on table role_permission
What should I do for that ?
The problem is that when try to delete a permission, it is still referenced on user. You need to delete the user first or remove it's permission before removing from permission table.
To delete in cascade you can try this:
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "permissions")
private Set<User> users;
I added "#OnDelete(action = OnDeleteAction.CASCADE)" annotation on User and Permission in Role class
And also added Role and User in Permission class
And now, It works as I want
Here is an Example
package com.asd.asd;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Set;
#Data
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#EqualsAndHashCode(exclude = {"users", "roles"})
#ToString(exclude = {"users", "roles"})
#Entity
public class Permission implements GrantedAuthority {
#Transient
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 2, max=100, message = "Permission name must be between {min} and {max} characters long")
#Column(unique = true)
private String name;
#NotNull
#Size(min = 10, max=250, message = "Permission description must be between {min} and {max} characters long")
#Column
private String description;
#Getter(onMethod = #__(#Override))
#NotNull
#Size(min = 6, max=100, message = "Permission authority must be between {min} and {max} characters long")
#Column(unique = true)
private String authority;
#Getter(onMethod = #__(#JsonIgnore))
#ManyToMany(mappedBy = "permissions")
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<Role> roles;
#Getter(onMethod = #__(#JsonIgnore))
#ManyToMany(mappedBy = "permissions")
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<User> users;
public Permission(String name, String description, String authority) {
this.name = name;
this.description = description;
this.authority = authority;
}
}
I'm new to Spring MVC and Roo, I was able to deploy a Roo project very easily. I created my data model and deployed it in a mysql database with their Json controllers. Everything is working perfectly and I can pull my data from my database via restful commands. However I'm having difficulty understanding how to create my own custom JSON controller where I can grab specific data from a query I define in one of my tables.
I have three tables; Patient, Doctor, and Careteam that connects the two.
The Careteam table holds all the records which associates a patient with many docotor and doctors with many patients.
The goal of the controller is to pass it a parameter PatientID, and then
Select Doctors from Careteam where PatientID = PatientID;
The query will return me all the doctors for that PatientID and then wraps it in JSON so I get grab it through a restful command.
Here is my code:
Careteam.java
package com.medroid.domain;
import javax.persistence.ManyToOne;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.tostring.RooToString;
import org.springframework.roo.addon.json.RooJson;
#RooJavaBean
#RooToString
#RooJpaActiveRecord
#RooJson
public class Careteam {
/**
*/
#ManyToOne
private Patient patient;
/**
*/
#ManyToOne
private Doctor doctor;
}
Doctor.java
package com.medroid.domain;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.tostring.RooToString;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.springframework.roo.addon.json.RooJson;
#RooJavaBean
#RooToString
#RooJpaActiveRecord
#RooJson
public class Doctor {
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "doctor")
private Set<DoctorMessages> DoctorMessages = new HashSet<DoctorMessages>();
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "doctor")
private Set<DoctorNotifications> DoctorNotifications = new HashSet<DoctorNotifications>();
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "doctor")
private Set<DoctorDiagnosis> DoctorDiagnosis = new HashSet<DoctorDiagnosis>();
#NotNull
#Column(name = "id", unique = true, insertable = false, updatable = false)
private int doctorID;
/**
*/
#NotNull
#Size(min = 8)
private String Password;
/**
*/
#NotNull
private String UserName;
/**
*/
#NotNull
#Size(min = 3)
private String FirstName;
/**
*/
#NotNull
#Size(min = 3)
private String LastName;
/**
*/
#NotNull
private String Specialty;
/**
*/
#NotNull
private String status;
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "doctor")
private Set<Careteam> attendingpatients = new HashSet<Careteam>();
}
Patient.java
package com.medroid.domain;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.tostring.RooToString;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.OneToMany;
import javax.persistence.Column;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.constraints.Max;
import org.springframework.roo.addon.json.RooJson;
#RooJavaBean
#RooToString
#RooJpaActiveRecord
#RooJson
public class Patient {
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "patient")
private Set<PatientMessages> PatientMessages = new HashSet<PatientMessages>();
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "patient")
private Set<PatientNotifications> PatientNotifications = new HashSet<PatientNotifications>();
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "patient")
private Set<PatientMedications> PatientMedications = new HashSet<PatientMedications>();
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "patient")
private Set<PatientAllergies> PatientAllergies = new HashSet<PatientAllergies>();
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "patient")
private Set<DoctorDiagnosis> Diagnosis = new HashSet<DoctorDiagnosis>();
/**
*/
#NotNull
#Column(name = "id", unique = true, insertable = false, updatable = false)
private int patientID;
/**
*/
#NotNull
private String UserName;
/**
*/
#NotNull
#Size(min = 8)
private String Password;
/**
*/
#NotNull
#Size(min = 3)
private String FirstName;
/**
*/
#NotNull
#Size(min = 3)
private String LastName;
/**
*/
#Size(min = 3)
private String MiddleName;
/**
*/
#NotNull
#Max(120L)
private int Age;
/**
*/
#NotNull
private String Sex;
/**
*/
#NotNull
private String DOB;
/**
*/
#NotNull
private String City;
/**
*/
#NotNull
private String Prov;
/**
*/
#NotNull
private String Bloodtype;
/**
*/
#NotNull
private String Status;
/**
*/
#NotNull
private String HC;
/**
*/
#NotNull
private String SymptomDesc;
/**
*/
#OneToMany(cascade = CascadeType.ALL, mappedBy = "patient")
private Set<Careteam > AttendingDoctors = new HashSet<Careteam>();
}
I created a new controller called JSONPatientDoctors and wrapped the controller with a web mvc controller so I will hook into my views.
JsonPatientDoctors.java
package com.medroid.domain;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import org.junit.runner.Request;
import com.medroid.domain.Careteam;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#RequestMapping("/jsonpatientdoctors/**")
#Controller
public class JsonPatientDoctors {
#RequestMapping(method = RequestMethod.POST, value = "{id}")
public void post(#PathVariable Long id, ModelMap modelMap, HttpServletRequest request, HttpServletResponse response) {
}
#RequestMapping(value ="/jsonpatientdoctors/GETPATIENTS", method = RequestMethod.GET)
public void GET() {
/* List<Careteam> result = Careteam.findAllCareteams();
System.out.println(result.get(0));*/
/*"SELECT o FROM Careteam o"*/
List<Careteam> careteam = Careteam.entityManager().createQuery("SELECT o FROM Careteam o where o.patient = '1'", Careteam.class).getResultList();
/* List<Careteam> careteam = Careteam.findAllCareteams(sortFieldName, sortOrder)*/
System.out.println(careteam.get(0));
}
#RequestMapping
public String index() {
return "jsonpatientdoctors/index";
}
}
I was able to pull the patient data by ID but the issue is I want to only select the doctor field where the patient ID = whatever,
I tried "SELECT o.doctor FROM Careteam o where o.patient = '1'"
But I get an error:
Type specified for TypedQuery [com.medroid.domain.Careteam] is incompatible with query return type [class com.medroid.domain.Doctor]; nested exception is java.lang.IllegalArgumentException: Type specified for TypedQuery [com.medroid.domain.Careteam] is incompatible with query return type [class com.medroid.domain.Doctor]
I solved the issue, here is the solution
#RequestMapping(value ="/jsonpatientdoctors/GetDoctorsByPatientID/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
#ResponseBody
public ResponseEntity<String> showJson(#PathVariable("id") String id) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
try {
String ID = id;
ArrayList<String> DoctorJson = new ArrayList<String>();
List<Careteam> careteam = Careteam.entityManager().createQuery("SELECT o FROM Careteam o where o.patient =" + ID, Careteam.class).getResultList();
if (careteam == null) {
return new ResponseEntity<String>(headers, HttpStatus.NOT_FOUND);
}
for(int i=0; i < careteam.size(); i++){
DoctorJson.add(careteam.get(i).getDoctor().toJson());
}
return new ResponseEntity<String>(DoctorJson.toString(),headers,HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<String>("{\"ERROR\":"+e.getMessage()+"\"}", headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
}