I have created custom annotations that I add to my Java class fields.
When I create object of that class, I want my custom annotated fields to have value: null or "" or "customAnnotation"
For example:
#Entity
public class User implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "address_id")
private Long id;
#MyCustomAnnotation
private String firstname;
private String lastname;
....
And when I create the object somewhere in the project:
User user = new User();
user.setFirstname("John");
user.setLastname("Smith");
I want it to be:
String firstname = user.getFirstname() // firstname is "" or null
String lastname = user.getLastname() // lastname is "Smith"
How to create method that will find all annotated fields in all classes in the project and do the same for all of them? Where to store this method?
I am working on the Maven, Spring Boot project.
At some point in your code you will have to implement something like this:
Class<User> obj = User.class;
if (obj.isAnnotationPresent(MyCustomAnnotation.class)) {
//your business logic
}
Sensible places for something like this would be either immediately when creating users, when getting users (for whatever reason the annotation is being used) or at an interval in a timed routine.
edit: See this tutorial: https://www.mkyong.com/java/java-custom-annotations-example/
Related
I am trying to develop a REST API using Java and Spring boot. I have an entity class User.
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "mobile_number")
private String mobileNumber;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "created_date")
private Date createdDate;
}
My requirement is to select some specific fields from this entity. These fields can be specified
dynamically in REST API url like
http://localhost:8080/getUserDetails?start=2021-01-01&end=2021-05-05&fields=firstName
http://localhost:8080/getUserDetails?start=2021-01-01&end=2021-05-05&fields=firstName,lastName
When fields=firstName , I want to return only firstName in JSON.
Similary, firstName and lastName in JSON when fields = firstName,lastName.
If i use criteria query query.select(root.get("firstName"),root.get("lastName"))
than i have to create constructors in Entity / POJO class.
There can only be one constructor with 2 String parameters. So this approach will fail when i need to select mobileNumber and email.
If i use spring Projections than i am not able to get how to select dynamic fields in Projections without adding projections = "some value" in rest endpoint url. Also if i use interface or class projections i will have to define required fields there. Fields should be dynamic in my case.
Also i am trying to avoid fetching of all columns and than filtering it out based on requested fields as it is inefficient.
So can anyone please help me out?
I am using spring data jpa, spring boot and h2 database.
My repository looks like this
public interface IComponentStorageRepository extends JpaRepository<ComponentData, String> {
}
My domain object looks like this
#Entity
#Table(name = "component_data")
public class ComponentData {
#Column(name = "component_id")
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String componentId;
private String description;
with overriden equals and hashcode.
I have a service method that looks like this.
#Override
public void updateComponent(String componentId, String description) {
if (repository.existsById(componentId)) {
ComponentData stored = repository.getOne(componentId);
stored.setDescription(description);
repository.saveAndFlush(stored);
} else {
ComponentData stored = new ComponentData();
stored.setComponentId(componentId);
stored.setDescription(description;
repository.saveAndFlush(newResult);
}
}
It firstly performs a check if the object exists (to prevent EntityNotFound exception) and then is trying to read one. If no object found it supposed to create a new one.
But each time I am trying to invoke the method - it is never able to find the entity. Every time it creates a new one.
I originally used repository.save and had #Transactional over the method but it didn't help either. I also haven't specified any properties and use everything default.
What is the problem
#GeneratedValue(strategy = GenerationType.IDENTITY) can't be used with String type and what i know it's not supported by the H2 inmemory DB.
When you want to use the key as String you have to assign it manually before persisting
without #GeneratedValue anotation or define the #GeneratedValue for String type properly.
There is an example from #VladMichalcea
https://vladmihalcea.com/hibernate-and-uuid-identifiers/ .
Another option would be to change the type of the primary key to be #GeneratedValue supported types
long (Long), int(Integer).
Newbie Alert !
I just installed mongodb 2 days back and started creating REST api's over spring.
So i have a collection, userinfo, where a sample document would look like
{"_id":"5c62588e5e1fbc37dc9746d3","name":{"first":"rajan","last":"rawat"},"age":32}
I created the field name as Object type in the collection.
Now creating the entity class for it in java
#Document(collection = "userinfo")
public class UserInfo {
#Id
private String id;
private Name name;
private int age;
}
where the Class Name is
public class Name {
private String firstName;
private String lastName;
}
On running the API, the response I get is
{"id":"5c62588e5e1fbc37dc9746d3","name":{"firstName":null,"lastName":null},"age":32}
If I change the type in UserInfo class to string like,
#Document(collection = "userinfo")
public class UserInfo {
#Id
private String id;
private String name;
private int age;
}
The response changes to
{"id":"5c62588e5e1fbc37dc9746d3","name":"{ \"first\" : \"rajan\",
\"last\" : \"rawat\" }","age":32}
which basically gives a string representation of the object from collection.
My Questions.
Is there something wrong with the way I designed the collection in mongoDB. I am assuming my use case is a reason why the Object type would have been introduced.
How do I map this collection in java i.e #Document. What am I missing ? Is there Something else I need to configure in the Class Name
In your document your attributes name are "first" and "last", so in your class Name you need to use the same names so that the object can be mapped by spring.
just try this:
public class Name {
private String first;
private String last;
}
I want to save an employee object in couchbase using spring boot java. I am using reactive couchbase driver. My requirement is to save the employee object with employeeId suffixed with hard coded string "-EMPLOYEETYPE".
Example:
Object to couchbase from Java Application:
{ "employeeId" : "12345", "lname" :"ltest", "fname" : "ftest"}
While saving to couch base, key supposed to be generated like
"12345-EMPLOYEETYPE"
Below code is not working, kindly guide me how to achieve it.
Note: I am using lombok so there are no getters and setters.
#Document
public final class Employee {
#Id #GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES,delimiter="-EMPLOYEETYPE")
private String id;
#IdAttribute
private String employeeId;
}
Found a solution. We need to create an instance variable with suffix string literal assigned to it, and annotate with #IdSuffix. (For prefix, #IdPrefix). This field will not be persisted into couchbase and only used to generate id for document.
#Document
public final class Employee {
#Id #GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES,delimiter="-")
private String id;
#IdAttribute
private String employeeId;
#IdSuffix
private String suffix = "EMPLOYEETYPE";
}
Reference Doc: https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/#couchbase.autokeygeneration.configuration
I have to copy the properties from dto to entity class.
I am using BeanUtils.copyProperties().
In request body I am sending like below:
{
"userName":"test",
"userStatus": "I",
}
DTO class:
public class UserDto {
private String userName;
private String userStatus;
public User buildUser() {
User user = new User();
BeanUtils.copyProperties(this, user);
return user;
}
}
Entity class:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column(name = "user_name")
private String userName;
#Enumerated(EnumType.STRING)
#Column(name = "user_status")
private UserStatus userStatus;
}
note: userStatus can be nullable field in table.
Service code:
User user = userDto.buildUser();
I am getting userStatus value as null in User entity class.
When I changed UserDto.userStatus to enum type, then request body is not accepting empty value.
How do I convert from String to enum during BeanUtils.copyProperties() ?
Spring BeanUtils is not designed for such customizations.
You should set the field manually with.
While MapStruct or Dozen are.
As alternative to keep BeanUtils and no explicit setter invocation you have :
defining a factory method for the enum Jackson processing (a static method annotated #JsonCreator in the enum class such as :
#JsonCreator public static UserStatus getValue(String name) {
return
Stream.of(UserStatus.values())
.findAny(s -> s.name().equals(name))
.orElse(null);
}
In most of cases, this is the best solution as it handles the issue at the root.
setting the flag to ignore unknown value for any field of the class:
public class UserDto {
#JsonIgnoreProperties(ignoreUnknown = true)
//...
}
Fastest solution but I don't like a lot as it may hide some other serialization/deserialization issues.
adding an enum value representing the emptiness. You could so define the enum in the DTO.
In order to not store it in the database, the mapping of this enum value to null should be done in the entity itself.
For example :
public void setUserStatus(UserStatus userStatus){
if (userStatus != UserStatus.EMPTY){
this.userStatus = userStatus;
}
}
It should work but I am not a big fan either...
Enums cannot be null because their underlining values are int but you can set the FIRST value in the enum as a default value. tyou can also define your field in DTO as an Enum type instead of String.
UserStatus
public enum UserStatus {
NULL,
ACTIVE,
INACTIVE;
}
Service code:
userDto.setUserStatus(UserStatus.NULL);
userDto.buildUser();
OR If you want to set this override of copyProperties method to ignore userStatus field while converting:
public static void copyProperties(Object source, Object target,
#Nullable Class<?> editable,
#Nullable String... ignoreProperties);