not getting updatedBy field from table using JPA - mysql

I have a table entity_detail that has following structure:
id detail_id center_code Comments updated_by updated_on created_on created_by
1 121 0 Test user 2020-04-22 2020-04-21 user
2 122 1 Test user1 2020-04-22 2020-04-22 user
I have an entity corresponding to this table:
#Entity(name = "entity_detail")
public class EntityDetail extends AuditableEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long detailId;
private Boolean centerCode;
private String comments;
}
There is another class AuditableEntity which manages the Audit.
#EntityListeners(AuditingEntityListener.class)
#Data
public class AuditableEntity implements Serializable {
private static final long serialVersionUID = 1L;
#CreatedBy
private String createdBy;
#CreatedDate
private Date createdOn;
#LastModifiedBy
private String updatedBy;
#LastModifiedDate
private Date updatedOn;
}
Now when I try to fetch the data from the table by using id:
savedEntityDetailTest = entityDetailRepository.findById(id);
All the attributes which are coming from AuditableEntity get returned null.
which means when I try to fetch updatedBy field using the following line it returns null.
savedEntityDetailTest.getUpdatedBy();
while when I try to get comments in the same way I get value which is saved in the table.
savedEntityDetailTest.getcomments();
Please suggest me a fixture or workaround.

Add EnableJpaAuditing annotation above your main class.
#SpringBootApplication
#EnableJpaAuditing
public class H2Database2Application {
public static void main(String[] args) {
SpringApplication.run(H2Database2Application.class, args);
}
}
Add MappedSuperclass annotation to the AuditableEntity class.
#EntityListeners(AuditingEntityListener.class)
#MappedSuperclass
#Data
public class AuditableEntity implements Serializable {
private static final long serialVersionUID = 1L;
#CreatedBy
private String createdBy;
#CreatedDate
private Date createdOn;
#LastModifiedBy
private String updatedBy;
#LastModifiedDate
private Date updatedOn;
}
Add Data annotation to the EntityDetail class.
#Entity(name = "entity_detail")
#Data
public class EntityDetail extends AuditableEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long detailId;
private Boolean centerCode;
private String comments;
}
And Add AuditorAware like below:
#Component
public class SecurityAuditorAware implements AuditorAware<String> {
#Override
public Optional<String> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return Optional.empty();
}
return Optional.of(((User)authentication.getPrincipal()).getUsername());
}
}

Related

How to add multiple entity in a single entity in jpa spring boot application

I am trying to add multiple entities in a single entity, I don't know this way possible or not please refer to my below code and help
the below code are the entity tables
#Entity
#Table(name = "agent_employee")
public class AgentEmployee extends Agent implements Serializable{
private static final long serialVersionUID = 1L;
#OneToMany // unidirectional
#JoinColumn(name = "employment_id", referencedColumnName = "id")
List<Employment> employmnet = new ArrayList<Employment>();
#OneToMany(
mappedBy = "agent",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<Officess> officess = new HashSet<>();
public List<Employment> getEmploymnet() {
return employmnet;
}
public void setEmploymnet(List<Employment> employmnet) {
this.employmnet = employmnet;
}
public Set<Officess> getOfficess() {
return officess;
}
public void setOfficess(Set<Officess> officess) {
this.officess = officess;
}
}
and Employment class is
#Data
#Entity
public class Employment {
#Id
#Column(nullable = false, unique = true)
private Long id;
private String empName;
private String location;
#Override
public String toString() {
return "Employment [id=" + id + ", empName=" + empName + ", location=" + location + "]";
}
}
and Offices class is
#Data
#Entity
#Table(name = "officess")
public class Officess implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String officeName;
#ManyToOne
#JoinColumn(name = "agent_emp")
private AgentEmployee agent;
}
And I have used spring boot repositories to all the respective entities
#GetMapping(path = "/add")
public #ResponseBody String addAgentEmployee() {
try {
AgentEmployee agemp = new AgentEmployee();
agemp.setFirstName("harish");
agemp.setLastName("kadsuru");
agemp.setEmail("hari**********is.net");
Employment emp1 = new Employment();
Employment emp2 = new Employment();
Employment emp3 = new Employment();
emp1.setId(501l);
emp2.setId(502l);
emp3.setId(503l);
emp1.setEmpName("junior engineer");
emp2.setEmpName("senior engineer");
emp3.setEmpName("team leader");
emp1.setLocation("bengaluru");
emp2.setLocation("mumbai");
emp3.setLocation("UAE");
List<Employment> emps = Arrays.asList(emp1, emp2, emp3);
employmentRepository.saveAll(emps);
agemp.setEmploymnet(emps);
agentEmployeeRepository.save(agemp);
return "saved";
} catch (Exception e) {
return "unable to save data due to exception";
}
}
#GetMapping("addOffice")
public #ResponseBody String addAgentEmployeeOffice() {
AgentEmployee emp;
Optional<AgentEmployee> agemp = agentEmployeeRepository.findById(27l);
if (agemp.isPresent()) {
emp = agemp.get();
}
else {
emp =new AgentEmployee();
emp.setFirstName("garish");
emp.setLastName("tumkur");
emp.setEmail("garish.kr#cyclotis.net");
}
log.info("###### {}",agemp);
Officess off1 = new Officess();
Officess off2 = new Officess();
Officess off3 = new Officess();
off1.setOfficeName("Google");
off2.setOfficeName("facebook");
off3.setOfficeName("Instagram");
Set<Officess> offices = emp.getOfficess();
offices.add(off1);
offices.add(off2);
offices.add(off3);
agentEmployeeRepository.save(emp);
log.info("######## {}", offices);
return "saved";
}
I don't think any problem with code, but I think I have a problem while saving the data. Kindly any body refer the correct way to analyse this problem.
Looks like your mapping is not correct. Also verify you have a EMPID column.
You don't need to use the #JoinTable annotation in your case.
As you are saving data you should use #PostMapping
StatusReport - removed private BigInteger EMPID; as it is used in joining
#Entity
#Table(name="statusreport")
public class StatusReport {
private BigInteger COMPLIANCEID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private BigInteger STATUSRPTID;
private String COMMENTS;
private Date CREATEDDATE;
private BigInteger DEPARTMENT_ID;
#OneToOne
#JoinColumn(name = "EMPID")
private Employees employee;
//others methods
}
Employee - removed private BigInteger DEPARTMENT_ID; as it is used in joining
#Entity
public class Employees {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private BigInteger EMPID;
private String FIRSTNAME;
private String LASTNAME;
private Date DOB;
private String EMAIL;
#OneToOne
#JoinColumn(name = "DEPARTMENT_ID")
private Department department;
//others methods
}

JPA: Many to many relationship - JsonMappingException: Infinite recursion

I'm having trouble with a many to many relation with JPA.
My code looks as follows:
The Sensor class:
#Entity
#Table(name = "sensor")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Sensor {
#Id
private long chipId;
#OneToMany(mappedBy = "sensor")
#JsonBackReference
private Set<Link> userLinks;
private String firmwareVersion;
private long creationTimestamp;
private String notes;
private long lastMeasurementTimestamp;
private long lastEditTimestamp;
private double gpsLatitude;
private double gpsLongitude;
private double gpsAltitude;
private String country;
private String city;
private boolean indoor;
private boolean published;
}
The user class:
#Entity
#Table(name = "user")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonManagedReference
private int id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user")
private Set<Link> sensorLinks;
private int role;
private int status;
private long creationTimestamp;
private long lastEditTimestamp;
}
And the Link class (relation class):
#Entity
#Table(name = "link")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Link {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "user_id")
#MapsId("user_id")
private User user;
#ManyToOne
#JoinColumn(name = "sensor_id")
#MapsId("sensor_id")
private Sensor sensor;
private boolean owner;
private String name;
private int color;
private long creationTimestamp;
}
The controller:
...
#RequestMapping(method = RequestMethod.GET, path = "/user/{email}", produces = MediaType.APPLICATION_JSON_VALUE)
#ApiOperation(value = "Returns details for one specific user")
public User getUserByEmail(#PathVariable("email") String email) {
return userRepository.findByEmail(email).orElse(null);
}
...
The UserRepository:
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByEmail(String email);
#Modifying
#Query("UPDATE User u SET u.firstName = ?2, u.lastName = ?3, u.password = ?4, u.role = ?5, u.status = ?6 WHERE u.id = ?1")
Integer updateUser(int id, String firstName, String lastName, String password, int role, int status);
}
I want to achieve, that the user endpoint shows all linked sensors with that particular user.
What I get is only an error message:
JSON mapping problem:
com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"];
nested exception is
com.fasterxml.jackson.databind.JsonMappingException: Infinite
recursion (StackOverflowError) (through reference chain:
com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"])
How can I fix this issue?
Thanks in advance
Marc
------------------------------------ Edit -----------------------------------
According to Abinash Ghosh's answer, I added following DTOs:
UserDto:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class UserDto {
private int id;
private String firstName;
private String lastName;
private Set<LinkDto> sensorLinks;
private int role;
private int status;
private long creationTimestamp;
private long lastEditTimestamp;
}
LinkDto:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class LinkDto {
private Integer id;
private SensorDto sensor;
private boolean owner;
private String name;
private int color;
private long creationTimestamp;
}
And the mapper (I realized it a bit different, but it should be the same):
public UserDto getUserByEmail(#PathVariable("email") String email) {
User user = userRepository.findByEmail(email).orElse(null);
return convertToDto(user);
}
private UserDto convertToDto(User user) {
return mapper.map(user, UserDto.class);
}
This leads to following Exception:
2020-04-13 14:22:24.383 WARN 8176 --- [nio-8080-exec-2] o.h.e.loading.internal.LoadContexts : HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext#68ab57c7<rs=HikariProxyResultSet#2017009664 wrapping Result set representing update count of -1>
1) Error mapping com.chillibits.particulatematterapi.model.db.main.User to com.chillibits.particulatematterapi.model.io.UserDto
1 error] with root cause
java.lang.StackOverflowError: null
at com.mysql.cj.NativeSession.execSQL(NativeSession.java:1109) ~[mysql-connector-java-8.0.19.jar:8.0.19]
...
It's working!
This post helped: https://stackoverflow.com/a/57111004/6296634
Seems that you should not use Lombok #Data in such cases.
When User serialized for the response, all getter methods of User's fields are called.
So, User relational field sensorLinks's getter are also called to set value. This happened recursively. That's cause of infinite recursion.
It's better to not use Entity as a response. Create a DTO class for User then map User entity value into DTO then send response. Don't use any Enity class again into DTO then it will result same problem
For dynamically map one model to another you can use ModleMapper
public class UserDTO {
//Fields you want to show in response & don't use enity class
private Set<LinkDTO> sensorLinks;
}
public class LinkDTO{
//Fields you want to show in response &don't use enity class
}
public User getUserByEmail(#PathVariable("email") String email) {
User user = userRepository.findByEmail(email).orElse(null);
UserDTO userDto = merge(user,UserDTO.class)
return userDto;
}
public static <T> void merge(T source, T target) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.map(source, target);
}

Springboot - empty get rest response

I am building a simple get rest call from MySQL database, the problem is that it returns an empty object.
The call itself is takes in an email (I know this is not the best approach), here is my code:
Entity:
#Entity
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
private String email;
private String password;
private String firstName;
private String userName;
private String lastName;
private boolean active;
#Temporal(TemporalType.DATE)
private Date createDate;
#Temporal(TemporalType.DATE)
private Date updateDate;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Collection<Role> roles;
// constructor
// get and setter
}
Repository:
public interface UserRepository extends JpaRepository<User, Long> {
// User findById (Integer Id);
#Query("SELECT u.id FROM User u where u.id = :id")
User findById(#Param("id") Integer id);
User findByEmail (String email);
}
Service:
#Service("userService")
public class UserService {
private String status, message;
private final HashMap map = new HashMap();
#Autowired
private UserRepository userRepository;
// #Autowired
// private RoleRepository roleRepository;
public User findByUserEmail (String email) {
return userRepository.findByEmail(email);
}
}
Controller:
#RestController("userControllerService")
#RequestMapping("/user/account")
public class UserController {
#Autowired
private UserService userService;
#GetMapping("/test-get/{email}")
public User jj(#PathVariable("email") String email){
return userService.findByUserEmail(email);
}
}
And my database happens to have the following data:
And here is the response I get after hitting the URL
I have no clue why my response is empty!
You cannot have the #in the URI path. Encode it with %40.
Reference: Can I use an at symbol (#) inside URLs?
Also, right way is to use as a query param as that's more of a good identifier and allows # as it parses as string
#GetMapping("/test-get")
public User jj(#RequestParam("email") String email){
return userService.findByUserEmail(email);
}
Either ways, hit as encoded url as /test-get/email=a#b.com ? or /test-get/a%40b.com for your previous code.

Modifying objects list fileds saved as LONGBLOB in MySQL

I have a table "Project" in my MySQL Databse that contains an ArrayList saved as LONGBLOB (FiledDetailsData).
public class ProjectEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String projectName;
private String city;
private int nbFields;
private List<FiledDetailsData> FiledDetails;...
I already have alot of data saved and would like to keep it. The problem is that I had to change to structure of FiledDetailsData to add new fields
I've changed it from
#Embeddable
#XmlRootEllement
public class FiledDetailsData implements Serializable`{
private String id;
private String lot;
private String number;
private String street;
private String size;
private String status;
to
#Embeddable
#XmlRootEllement
public class FiledDetailsData implements Serializable`{
private String id;
private String lot;
private String number;
private String street;
private String size;
private String status;
private BigDecimal extraField;
private BigDecimal extraParking;
When I try to run the application with the previous data, I get this error
Could not deserialize object from byte array. Internal Exception: local class incompatible
I can fin it by deleting the LONGBLOB in my DAtaBase and recreating it, but then I lose all my data.
here are my save/edit methods
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
Thank you.

How to return a List of objects in JSON when using JAX-RS

I have it working... kind of. Except when the Json is printed out, it prints out a lot of duplicate code. I suspect it has something to do with the way its marshalled. In any case, this is my code:
User.java
#XmlRootElement(name="user")
#Entity
#Table(name="users")
public class User implements Serializable{
private static final long serialVersionUID = 1L;
#XmlElement(required=true)
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#XmlElement(required=true)
#Column(name="username", nullable=false)
private String userName;
#XmlElement(required=true)
#Column(name="phoneid", nullable=false)
private String phoneid;
#XmlTransient
#ManyToMany(
cascade={CascadeType.ALL},
mappedBy="voters",
targetEntity=Vote.class,
fetch=FetchType.EAGER
)
private List<Vote> votes = new ArrayList<Vote>();
Vote.java
#XmlRootElement(name="vote")
#Entity
#Table(name="votes")
public class Vote implements Serializable {
private static final long serialVersionUID = 1L;
#XmlElement(required=true)
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id", nullable=false)
private int id;
#XmlElement(required=true)
#Column(name="name", nullable=false)
private String name;
#XmlElement(required=true)
#Column(name="max", nullable=false)
private int max;
#XmlElement(required=true)
#Column(name="current", nullable=false)
private int current;
#XmlElement(required=true)
#Column(name="is_simple", nullable=false)
private int isSimple;
#XmlTransient
#ManyToMany(cascade = {CascadeType.ALL}, targetEntity=User.class, fetch=FetchType.EAGER)
#JoinTable(name = "vote_user",
joinColumns = {#JoinColumn(name = "vote_id")},
inverseJoinColumns = {#JoinColumn(name = "user_id")}
)
private List<User> voters = new ArrayList<User>();
GetRestfulVote.java
#Path("/votes")
public class GetRestfulVote {
#EJB
VoteBeanInterface bean;
#GET
#Path("/{userid}")
#Produces(MediaType.APPLICATION_JSON)
public List<Vote> getVotesJson(#PathParam("userid") String userId) {
//Will be getting using the user id to be able to set a flag if the calling user voted in it
List<Vote> listOfVotes = bean.getVotes();
return listOfVotes;
}
}
Now as is fairly obvious from my code I have a many to many relationship between my Vote and User classes. I believe thats where the issue is occurring. But the problem is I don't know how to keep it from being omitted from the "jsonification" process. More importantly, how can I force it only to turn the owning class list (Votes) into Json. Any help and clarification would be greatly appreciated!