I have service which is getting values from api and mapping it by model mapper to the unified entity.
The problem is that UUID is not working. I am getting null instead of any string id (I had to change name of if for "uniqueIdentifier" becouse objects from api had "id" field).
My entity:
#Entity
#Getter
#Setter
#Table(name = "unifiedOffers")
#AllArgsConstructor
#NoArgsConstructor
public class UnifiedOfferEntity {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "id", updatable = false, nullable = false)
private String uniqueIdentifier;
private String companyName;
private String city;
private String street;
private String title;
private LocalDateTime posted;
private String url;
}
Endpoint for tests:
#GetMapping("/test")
public String getOffers() throws ExecutionException, InterruptedException {
List<UnifiedOfferEntity> result = jobFinderService.getAllOffers();
for (UnifiedOfferEntity unifiedOfferEntity : result) {
System.out.println(unifiedOfferEntity.getUniqueIdentifier() + " " + unifiedOfferEntity.getTitle() + " " + unifiedOfferEntity.getUrl());
}
unifedOfferRepository.saveAll(result);
return String.valueOf(result.size());
}
In that foreach I am getting values like for example:
null JavaDeveloper anyLinkUrl
so only UUID is not generating ids.
have you defined the underline class to implement uuid generator?
in a similar case I used the follwing code that accepts a parameter "rangeName" you can remove
#GeneratedValue(generator="intRange")
#GenericGenerator(name="intRange",
strategy = "imp.framework.jerpBridge.IntRangeGenerator" ,
parameters = {
#Parameter( name = "rangeName", value="DOCMA01")
})
public String nrDocumento;
where
public class IntRangeGenerator implements IdentifierGenerator, Configurable {
public IntRangeGenerator(String rangeName) {
this.rangeName = rangeName;
}
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
return "generated ID";
}
Related
So. I'm trying to have a custom validator for my method. This one:
#PostMapping("/person")
public Person create(#Validated(value = IPersonValidator.class) #RequestBody Person person, Errors errors) {
LOGGER.info("Creating a person with the following fields: ");
LOGGER.info(person.toString());
if (errors.hasErrors()) {
LOGGER.info("ERROR. Error creating the person!!");
return null;
}
//return personService.create(person);
return null;
}
This is my Validator class:
#Component
public class PersonValidator implements Validator, IPersonValidator {
#Override
public boolean supports(Class<?> clazz) {
return Person.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Person person = (Person) target;
if (person.getName() == null || person.getName().trim().isEmpty()) {
errors.reject("name.empty", "null or empty");
}
}
}
And my interface:
public interface IPersonValidator {
}
And my class:
#Entity
#Table(name = "persons")
#Data
#Getter
#Setter
#NoArgsConstructor
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_sequence")
#SequenceGenerator(name = "person_sequence", sequenceName = "person_sequence", allocationSize = 1)
#Column(name = "id")
private Long id;
//#NotBlank(message = "Name cannot be null or empty")
#Column(name = "name")
private String name;
#NotBlank(message = "Lastname cannot be null or empty")
#Column(name = "last_name")
private String lastName;
#Column(name = "last_name_2")
private String lastName2;
public Person(String name, String lastName, String lastName2) {
this.name = name;
this.lastName = lastName;
this.lastName2 = lastName2;
}
}
What am I expecting is for it to enter the validate method since Im using the annotation (#Validate) with the class that I want. I tried it too using the #Valid annotation, but it still won't enter in my custom validate method.
What am I doing wrong?
This code #Validated(value = IPersonValidator.class) is use for "grouping" of validations. Yoy say to validator valid only these constrains with groups = {IPersonValidator.class}.
For better understanding of custom validators see: https://www.baeldung.com/spring-mvc-custom-validator
edit:
This code #Validated(value = IPersonValidator.class) doesn't use your validator. From #Validated documantation:
value() specify one or more validation groups to apply to the
validation step kicked off by this annotation.
Validation groups are use for turning on/off of some valdations for some fields in data class. Se more here: https://www.baeldung.com/javax-validation-groups
For creating you own custom valdation you must create custom constrain annotation and custom validator for this annotation. This custom constrain annotation you may use on some field in data class. I really strong recommend read this https://www.baeldung.com/spring-mvc-custom-validator
I have Spring Boot application (v3.0.2, Java 17), and in it, a simple entity ActivityType and corresponding ActivityDto.
//Entity (uses Lombok 1.18.24)...
#Getter
#Setter
#Entity
public class ActivityType {
#Id
#Column(name = "ActivityTypeId", nullable = false)
private Integer id;
#Column(name = "ActivityName", nullable = false, length = 30)
private String activityName;
#Column(name = "ActivityDescription")
private String activityDescription;
}
//DTO...
public record ActivityTypeDto(
Integer id,
String activityName,
String activityDescription) implements Serializable {
}
I'm using IntelliJ Idea (v2022.2.4) and JPA Buddy (v2022.5.4-222) to generate the Mapper Interface (MapStruct v1.5.3.Final). When I build the Mapper implementation, in the generated code, both the toEntity and toDto methods are incorrect.
#Component public class ActivityTypeMapperImpl implements ActivityTypeMapper {
#Override
public ActivityType toEntity(ActivityTypeDto activityTypeDto) {
if ( activityTypeDto == null ) {
return null;
}
ActivityType activityType = new ActivityType();
return activityType;
}
#Override
public ActivityTypeDto toDto(ActivityType activityType) {
if ( activityType == null ) {
return null;
}
// What's this all about?? Why not activityType.id, etc??
Integer id = null;
String activityName = null;
String activityDescription = null;
ActivityTypeDto activityTypeDto = new ActivityTypeDto( id, activityName, activityDescription );
return activityTypeDto;
}
#Override
public ActivityType partialUpdate(ActivityTypeDto activityTypeDto, ActivityType activityType) {
if ( activityTypeDto == null ) {
return activityType;
}
return activityType;
}
I've tried various alternatives, including using a class for the DTO instead of a record, but no success. Looks like I've missed something, but not sure what.
Update:
I can fix this by not using Lombok for the Entity getters/setters, which leads me on to final question, is there a setting on the MapStruct plugin to take Lomboz into account?
please define you entity like this,
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ActivityType {
#Id
#Column(name = "ActivityTypeId", nullable = false)
private Integer id;
#Column(name = "ActivityName", nullable = false, length = 30)
private String activityName;
#Column(name = "ActivityDescription")
private String activityDescription;
}
then define ActivityTypeDTO like this,
#Data
public class ActivityTypeDTO {
#JsonProperty("id")
private Integer id;
#JsonProperty("ActivityName")
private String ActivityName;
#JsonProperty("activityDescription")
private String activityDescription;
best practice to use MapStruct is like this,
#Mapper(componentModel = "spring", uses = {})
public interface ActivityMapper extends EntityMapper<ActivityTypeDTO, ActivityType> {
ActivityTypeDTO toDto(ActivityType activityType);
ActivityType toEntity(ActivityTypeDTO activityTypeDTO);
}
and EntityMApper in Mapper should be like this,
public interface EntityMapper<D, E> {
E toEntity(D dto);
D toDto(E entity);
}
Now I am sure you mapper work correctly.
I'm writing a program that changes a member's password, I fetched the user by id from the database when I test the endpoint on postman it returns 200 OK, but fails to update the password in the database to the new password, What is the right logic to use for this task? my code is below.
Member
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name ="member",
indexes = {
#Index(
columnList = "email_address",
name = "email_address_idx",
unique = true
),
},
uniqueConstraints = {
#UniqueConstraint(
columnNames = {"email_address", "phone_number"},
name = "email_address_phone_number_uq"
)
}
)
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "nationality_id")
private Country nationality;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "country_of_residence_id")
private Country countryOfResidence;
#Temporal(TemporalType.DATE)
#Column(name ="date_of_birth")
private Date dateOfBirth = new Date();
#Column(name ="current_job_title")
private String currentJobTitle;
#Column(name = "email_address", nullable = false)
private String emailAddress;
#Column(name = "username")
private String username;
#Column(name ="phone_number")
private String phoneNumber;
#Column(name ="city")
private String city;
#Column(name ="state")
private String state;
#Column(name ="password", nullable = false)
private String password;
}
PasswordDto
#Data
public class ChangePasswordDto {
private String password;
private String oldPassword;
private String newPassword;
private String reNewPassword;
PasswordService
#Slf4j
#Service
public class ChangePasswordServiceImpl implements ChangePasswordService {
#Autowired
private ModelMapper modelMapper;
#Autowired
private PasswordEncoder passwordEncoder;
private final PasswordJpaRepository jpaRepository;
public ChangePasswordServiceImpl(PasswordJpaRepository jpaRepository) {
this.jpaRepository = jpaRepository;
}
#Override
#Transactional
public Member changePassword(Long id, ChangePasswordDto password) {
final Member member = jpaRepository.findById(id);
Member getPassword = new Member();
getPassword = modelMapper.map(id, Member.class);
Member updatedPassword = new Member();
if (member.getPassword().equals(checkIfValidOldPassword(member, password.getOldPassword()))){
if (password.getNewPassword().equals(password.getReNewPassword())) {
updatedPassword = changPassword(member, password.getNewPassword());
}
}else{
return null;
}
return updatedPassword;
}
#Override
#Transactional
public boolean checkIfValidOldPassword(Member member, String oldPassword) {
return matches(oldPassword, member.getPassword());
}
#Override
#Transactional
public Member changPassword(Member member, String password) {
member.setPassword(password);
jpaRepository.save(member);
return member;
}
}
PasswordController
#RestController
#RequestMapping(
value = "password",
produces = { MediaType.APPLICATION_JSON_VALUE }
)
public class ChangePasswordController {
private ChangePasswordService service;
public ChangePasswordController(ChangePasswordService passwordService) {
this.service = passwordService;
}
#PostMapping("/change-password/{id}")
public Member changePassword(#Validated #RequestBody ChangePasswordDto password, #PathVariable(name = "id") Long id){
return service.changePassword(id, password);
}
}
Troubleshooting and Debugging
In the future, it would be helpful for you to post the request as a cURL command as well as the Catalina logs.
Your bug is in the following statement
if (member.getPassword().equals(checkIfValidOldPassword(member, password.getOldPassword()))){
// The above expression is always evaluating false
}
The member.getPassword() accessory method returns a String. However checkIfValidOldPassword method returns a boolean. Let's refactor the code for demonstration.
String pwd = member.getPassword();
String opwd = password.getOldPassword();
boolean isValud = checkIfValidOldPassword(member, opwd);
assert pwd.equals(isValid);
You are attempting to evaluate the equality of a String and a primitive boolean ( autoboxed to the Boolean wrapper class object ). Likely this statement always evaluates false thus you are returning null and not invoking the code that actually makes the update.
Autoboxing Explained
The reason this did not throw a compile time exception is due to a feature known as Autoboxing. Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes.
In your example, the equals method has a single parameter of type Object. So although you passed a primitive boolean as the first parameter in the equals method, the Java compiler converted it to an Object of type Boolean. Because Boolean is an object, and all objects inherit from Object, no exception is thrown.
Most likely you are comparing the response of ‘toString’ method on your Boolean object which returns the string “true” when the primitive boolean value corresponds with true and “false” otherwise.
Security Concerns
Please be extremely careful when you are attempting to roll your own authentication or authorization features. For the most part, a password should be salted and encrypted before storing the information at-rest. Therefore, you should only ever be able to compare one salted/encrypted string with another salted/encrypted string
I'm trying to create findBy JpaRepo it's about returning only the data where isDeleted attribute is false.
this is my Service :
public List<Customer> getAllCustomers() {
List<Customer> customers = cutomerRepository.findByIsDeletedFalse();
return customers;
}
and this is my Controller :
#GetMapping("/viewList")
#CrossOrigin("http://localhost:4200/")
public ResponseEntity<List<Customer>> getAllCustomers() {
List<Customer> customers = new ArrayList<>();
customers = customerService.getAllCustomers();
if (customers.isEmpty()) {
LOGGER.error("no content ");
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
LOGGER.info("calling list of customers");
return new ResponseEntity<>(customers, HttpStatus.OK);
}
and this is customer model :
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;
}
but when I run it in postman it's not working and return an error :
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not
exist: boolean = integer Hint: No operator matches the given name
and argument types. You might need to add explicit type casts.
Position: 315
How could I solve this issue?
Looks like the name for your query isn't created right.
However, in this case, the usage of #Query will be much clearer.
Code snippet:
public interface CustomerRepo extends JpaRepository<Customer, Integer> {
List<Customer> findAllByIsDeletedIsFalse();
#Query("from Customer c where c.isDeleted=false")
List<Customer> getAllCustomers();
}
Iinstead of:
cutomerRepository.findByIsDeletedFalse()
You missed one more Is at the name of the method.
Update your Domain:
public class Customer implements Serializable {
private final static long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "serial_number")
private Long serialNumber;
// ...
#Column(name = "is_deleted")
private Boolean isDeleted;
}
JPA fields should be Objects instead of primitives. And entity class should implement Serializable as well.
If the exception will be the same you could try to update #Query:
#Query("from Customer c where c.isDeleted=0")
If pure SQL works for your DB you could use native query:
#Query(
value = "select * from Customer where is_deleted = false",
nativeQuery = true)
List<Customer> getAllCustomers();
It's not working because it doesn't follow the naming conventions for a boolean field. Usually in Java the primitive booleans are named without is prefix and the getter would be using this is prefix.
So in your case your entity class should look like that:
public class Customer {
// ...
#Column(name = "is_deleted")
private boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
Also the naming of the spring repository method should be:
List<Customer> findAllByDeletedIsFalse();
In case you want to use a Boolean reference type you can name your field isDeleted, but then the class would look like that:
public class Customer {
// ...
#Column(name = "is_deleted")
private Boolean isDeleted;
public Boolean getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(Boolean isDeleted) {
this.isDeleted = isDeleted;
}
}
and the repository method:
List<Customer> findAllByIsDeletedIsFalse();
Boolean Java maps a bit datatype column. You are probably using int as datatype in your database.
I use GraphQL-SPQR Library
The problem is "Validation error of type SubSelectionRequired: Sub selection required for type Timestamp"
Maybe there is expression in query for timestamp
or format in Entity
{"query":
"{findUserPointByUserId(userId:73){rowNum userAccountPointUserId totalPoint pointTypeDescription point userAccountCreatedDate} findUserAccountImgByUserId(userId:73){imageId,userId,presentImgNum}}"
}
Error
{
"errors": [
{
"message": "Validation error of type SubSelectionRequired: Sub selection required for type Timestamp of field userAccountCreatedDate",
"locations": [
{
"line": 1,
"column": 103
}
]
}
]
}
Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "view_user_account_point", schema = "public", catalog = "corus")
public class ViewUserAccountPoint {
#Id
#Basic
#GraphQLQuery(name = "rowNum")
#Column(name = "row_num", nullable = true)
private Long rowNum;
#Basic
#Column(name = "user_account_point_userid", nullable = true)
#GraphQLQuery(name = "userAccountPointUserId")
private Integer userAccountPointUserId;
#Basic
#Column(name = "subject_id", nullable = true)
#GraphQLQuery(name = "subjectId")
private Integer subjectId;
#Basic
#Column(name = "point", nullable = true)
#GraphQLQuery(name = "point")
private Integer point;
#Basic
#Column(name = "user_account_point_typeid", nullable = true)
#GraphQLQuery(name = "userAccountPointTypeId")
private Integer userAccountPointTypeId;
#Basic
#Column(name = "date_created", nullable = true)
#GraphQLQuery(name = "userAccountCreatedDate")
private Timestamp userAccountCreatedDate;
Service
public List<ViewUserAccountPoint> findUserPointByUserId(#GraphQLArgument(name = "userId") Integer userId){
return viewUserAccountPointRepository.findByUserAccountPointUserIdOrderByUserAccountCreatedDateDesc(userId);
}
Controller
private final GraphQL graphQL;
public UserController(UserAccountService userAccountService) {
GraphQLSchema schema = new GraphQLSchemaGenerator()
.withResolverBuilders(
//Resolve by annotations
new AnnotatedResolverBuilder())
.withOperationsFromSingleton(userAccountService,UserAccountService.class)
.withValueMapperFactory(new JacksonValueMapperFactory())
.generate();
graphQL = GraphQL.newGraphQL(schema).build();
}
#PostMapping(value = "/graphql", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseBody
public Map<String, Object> graphql(#RequestBody Map<String, String> request, HttpServletRequest raw) {
ExecutionResult executionResult = graphQL.execute(ExecutionInput.newExecutionInput()
.query(request.get("query"))
.operationName(request.get("operationName"))
.context(raw)
.build());
return executionResult.toSpecification();
}
I search through all query timestamp format
However, i couldn't find
i hope to hear the solution.
thank you
For one reason or another, Timestamp got mapped incorrectly. It ended up being an object and not a scalar.
As mentioned in the issue you opened, it's unclear where is Timestamp in your code coming from.
java.sql.Timestamp is supported out of the box in recent versions of GraphQL SPQR, so you might be on an older version.
If that's not the case, it would mean Timestamp is some other than java.sql.Timestamp, and you'd need to register a custom mapper for it.
public class TimestampMapper implements TypeMapper {
// Define the scalar as needed, see io.leangen.graphql.util.Scalars for inspiration
private static final GraphQLScalarType TIMESTAMP = ...;
#Override
public GraphQLOutputType toGraphQLType(AnnotatedType javaType, OperationMapper operationMapper, Set<Class<? extends TypeMapper>> mappersToSkip, BuildContext buildContext) {
return TIMESTAMP; //it's important to always return the same instance
}
#Override
public GraphQLInputType toGraphQLInputType(AnnotatedType javaType, OperationMapper operationMapper, Set<Class<? extends TypeMapper>> mappersToSkip, BuildContext buildContext) {
return TIMESTAMP; //same as above
}
#Override
public boolean supports(AnnotatedType type) {
return ClassUtils.isSuperClass(Timestamp.class, type);
}
}
Then register your mapper:
generator.withTypeMappers(new TimestampMapper())
It's incorrect query body for my case, make sure you have the right one.