null values inserted while auditing - spring-boot

My AuditListener
public class EmployeeAuditListeners {
#PrePersist
public void prePersist(Employee employee){
perform(employee,Action.INSERTED);
}
#PreUpdate
public void preUpdate(Employee employee){
perform(employee,Action.UPDATED);
}
#PreRemove
public void preRemove(Employee employee){
perform(employee,Action.DELETED);
}
#Transactional
public void perform(Employee emp, Action action){
EntityManager em = BeanUtil.getBean(EntityManager.class);
CommonLogs commonLogs = new CommonLogs();
commonLogs.setQuery("new query");
em.persist(commonLogs);
}
}
and My Auditable.class
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<U> {
#CreatedBy
protected U createdBy;
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date createdDate;
#LastModifiedBy
protected U lastModifiedBy;
#LastModifiedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date lastModifiedDate;
}
My CommonLogs.class
#Entity
#EntityListeners(AuditingEntityListener.class)
public class CommonLogs extends Auditable<String> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String query;
public CommonLogs() {
}
public CommonLogs(String query) {
this.query = query;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
My Employee.java class
#Entity
#EntityListeners(EmployeeAuditListeners.class)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
and I have a simple Rest Controller
#RestController
#RequestMapping("/api")
public class EmployeeController {
#Autowired
private EmployeeRepository employeeRepository;
#PostMapping("/employees")
public Employee createEmployee(#RequestBody Employee employee){
return employeeRepository.save(employee);
}
}
I want to log it on my table (common_logs) every time i perform some crud operations on my Employee Entity.
the above given example is working to some extent as it successfully stores employee and invokes EmployeeAuditListeners.
but now while saving CommongLog entity i expect it's parent class Auditable to automatically insert createdBy, createdDate etc. for now only query and id is inserted on common_logs table and remaining columns are null.

You can review the documentation for Auditing in here.
To enable the automatic Auditing, you must add the annotation #EnableJpaAuditing in your Application class:
#SpringBootApplication
#EnableJpaAuditing
class Application {
static void main(String[] args) {
SpringApplication.run(Application.class, args)
}
}
If you want the fields #CreatedBy and #LastModifiedBy too, you will also need to implement the AuditorAware<T> interface. For example:
class SpringSecurityAuditorAware implements AuditorAware<User> {
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return ((MyUserDetails) authentication.getPrincipal()).getUser();
}
}

Related

Why the class attributes in the quarku panache example are PUBLIC instead of PRIVATE

Referring to the getting started link below
https://quarkus.io/guides/hibernate-orm-panache
The example uses a Entity class with public attributes.
class Person{
public String name;
}
and used as
person.name = "Synd";
so is it simply a lazy example (!! in official doc ? ) or it means something else.
According to the documentation, it might be related to a single difference (extending PanacheEntityBase)
If you don’t want to bother defining getters/setters for your entities, you can make them extend PanacheEntityBase and Quarkus will generate them for you. You can even extend PanacheEntity and take advantage of the default ID it provides.
Therefore they are making them Public for Quarkus to generate getters/setters for you automatically.
#Entity
public class Person extends PanacheEntity {
public String name;
public LocalDate birth;
public Status status;
public static Person findByName(String name){
return find("name", name).firstResult();
}
public static List<Person> findAlive(){
return list("status", Status.Alive);
}
public static void deleteStefs(){
delete("name", "Stef");
}
}
vs
#Entity
public class Person {
#Id #GeneratedValue private Long id;
private String name;
private LocalDate birth;
private Status status;
public Long getId(){
return id;
}
public void setId(Long id){
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getBirth() {
return birth;
}
public void setBirth(LocalDate birth) {
this.birth = birth;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}

Null value in primary key of hibernate entity

I faced with problem of null value in PK.
Here's an entity:
#Entity
#Table(name="space")
public class Space implements Serializable {
#Id
#GeneratedValue
#Column(nullable = false, unique = true)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="user_id")
private UserAccount user;
private String name;
private String description;
private Date createdTime;
private Date modifiedTime;
#OneToMany(mappedBy="space")
private Set<SpaceAccess> spaceAccesses = new HashSet<>();
public Set<SpaceAccess> getSpaceAccesses() {
return spaceAccesses;
}
public void setSpaceAccesses(Set<SpaceAccess> spaceAccesses) {
this.spaceAccesses = spaceAccesses;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Space() {}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public UserAccount getUser() {
return user;
}
public void setUser(UserAccount user) {
this.user = user;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
}
I wrote strategy to generate PK properly but I always get Null in id field when I create new instance of the Space:
Space space = new Space();
Here's content of the object:
What i should do to generate id of instance properly using hibernate/spring mechanisms?
application.properties:
spring.datasource.url="some_url"
spring.datasource.username=name
spring.datasource.password=password
spring.jpa.generate-ddl=true
P.S. I use spring-boot-starter-data-jpa with version: 2.3.4.RELEASE.
Use:
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}

how to Fix spring boot one to many bidirectional infinity loop?

i am try to create a one to many bidirectional mapping using spring boot and spring data jpa please look the below entity
Employer Entity
#Entity
public class Employer
{
private Long id;
private String employerName;
private List<Employee> employees;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getEmployerName()
{
return employerName;
}
public void setEmployerName(String employerName)
{
this.employerName = employerName;
}
#OneToMany(cascade=CascadeType.ALL, mappedBy="employer")
public List<Employee> getEmployees()
{
return employees;
}
public void setEmployees(List<Employee> employees)
{
this.employees = employees;
}
}
Employee Entity
#Entity
public class Employee
{
private Long id;
private String employeeName;
private Employer employer;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getEmployeeName()
{
return employeeName;
}
public void setEmployeeName(String employeeName)
{
this.employeeName = employeeName;
}
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
public Employer getEmployer()
{
return employer;
}
public void setEmployer(Employer employer)
{
this.employer = employer;
}
}
Employer Repo
public interface EmployerServices extends JpaRepository<Employer, Long> {
}
Employee Repo
public interface EmployeeServices extends JpaRepository<Employee, Long> {
}
REST Controller is
#RestController
public class Controller {
#Autowired EmployeeServices employeeServices;
#Autowired EmployerServices employerServices;
#GetMapping("/getempr")
public Object getempr(){
return employerServices.findOne(1L);
}
}
now the problem begin start see my out put
its look like a infighting loop and my server throwing error getOutputStream() has already been called for this response.
I used #JsonBackReference & #JsonManagedReference
annotation but the problem is its working like one to many
{
"id":1,
"employerName":"employer",
"employees":[
{"id":1,"employeeName":"emp1"},
{"id":2,"employeeName":"emp2"}
]
}
if I am trying to get in the concern of many to one like all employee with employer. the output is
[
{
"id":1,
"employeeName":"emp1"
},
{
"id":2,
"employeeName":"emp2"}
]
its not showing me the employer details.
please suggets me guys what i am doing wrong. thanks in advance!!
Instead of using #JsonBackReferenceand #JsonManagedReference try to use annotation #JsonIgnoreProperties:
#JsonIgnoreProperties("employer")
private List<Employee> employees;
#JsonIgnoreProperties("employees")
private Employer employer;
It prevents Jackson from rendering a specified properties of associated objects.
with the JSON its a problem with bi-directional mapping. Use the below properties.
#JsonIgnoreProperties("employer")
#JsonIgnoreProperties("employees")
please keep fetching type as eager.
hope this will work.
You can solve your issue with two modification with annotations.
Employer.class
#Entity
public class Employer {
private Long id;
private String employerName;
#OneToMany(cascade = CascadeType.ALL,
mappedBy = "employer",
orphanRemoval = true)
private List<Employee> employees;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmployerName() {
return employerName;
}
public void setEmployerName(String employerName) {
this.employerName = employerName;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
Employee.class
#Entity
public class Employee {
private Long id;
private String employeeName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "employer_id")
private Employer employer;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public Employer getEmployer() {
return employer;
}
public void setEmployer(Employer employer) {
this.employer = employer;
}
}
For more information please visit this link.
Change your getEmployer Method like this:
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public Employer getEmployer()
{
return employer;
}
use
#JsonProperty(access = Access.WRITE_ONLY)
private List<Employee> employees;
So that it will ignore employees while printing to JSON in the response (and thus prevents the looping), but will still consider the JSON data (employee list) you pass in the request body so that it is available for persistence.

Can't save many-to-many relations by form in JSP

The Context
I have a simple workshop application with three entities - Job, Employee and Customer. I am trying to create web interface which will add new Job in this case. Job has many to many relations with Employee and Customer. In Job entity there are lists of Employee and Customer as well.
The Problem
When I try to post my request with new Job through HTTP I get Bad Request 400 with description:
The server cannot or will not process the request due to something
that is perceived to be a client error (e.g., malformed request
syntax, invalid request message framing, or deceptive request
routing).
I don't know where excactly is bug.
Forms for adding Customer and Employee work fine.
The Code
Entities:
Employee
#Component
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Pattern (regexp="[a-zA-Z]+")
#NotEmpty
private String employeeName;
#Pattern (regexp="[a-zA-Z]+")
#NotEmpty
private String employeeSurname;
#ManyToMany(mappedBy = "employeeList")
private List<Job> jobList;
public Employee() {
}
public Employee(int id, String employeeName, String employeeSurname) {
this.id = id;
this.employeeName = employeeName;
this.employeeSurname = employeeSurname;
}
public Employee(String employeeName, String employeeSurname) {
this.employeeName = employeeName;
this.employeeSurname = employeeSurname;
}
public List<Job> getJobList() {
return jobList;
}
public void setJobList(List<Job> jobList) {
this.jobList = jobList;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getEmployeeSurname() {
return employeeSurname;
}
public void setEmployeeSurname(String employeeSurname) {
this.employeeSurname = employeeSurname;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}}
Customer
#Component
#Entity
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Pattern(regexp="[a-zA-Z]+")
#NotEmpty
private String CustomerName;
#Pattern (regexp="[a-zA-Z]+")
#NotEmpty
private String CustomerSurname;
#Pattern (regexp = "\\w+")
#NotEmpty
private String car;
private int phonenumber;
#ManyToMany(mappedBy = "customerList")
private List<Job> jobList;
public Customer(String customerName, String customerSurname, String car, int phonenumber) {
CustomerName = customerName;
CustomerSurname = customerSurname;
this.car = car;
this.phonenumber = phonenumber;
}
public Customer(int id, String customerName, String customerSurname, String car, int phonenumber) {
this.id=id;
CustomerName = customerName;
CustomerSurname = customerSurname;
this.car = car;
this.phonenumber = phonenumber;
}
public Customer() {
}
public List<Job> getJobList() {
return jobList;
}
public void setJobList(List<Job> jobList) {
this.jobList = jobList;
}
public String getCustomerName() {
return CustomerName;
}
public void setCustomerName(String customerName) {
CustomerName = customerName;
}
public String getCustomerSurname() {
return CustomerSurname;
}
public void setCustomerSurname(String customerSurname) {
CustomerSurname = customerSurname;
}
public int getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(int phonenumber) {
this.phonenumber = phonenumber;
}
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}}
Job
#Component
#Entity
public class Job {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#NotEmpty
#Pattern(regexp="[a-zA-Z]+")
private String jobName;
#ManyToMany(fetch = FetchType.EAGER)
private List<Employee> employeeList;
#LazyCollection(LazyCollectionOption.FALSE)
#ManyToMany
private List<Customer> customerList;
public Job() {
}
public Job(String jobName, List<Employee> employeeList, List<Customer> customerList) {
this.jobName = jobName;
this.employeeList = employeeList;
this.customerList = customerList;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public List<Employee> getEmployeeList() {
return employeeList;
}
public void setEmployeeList(List<Employee> employeeList) {
this.employeeList = employeeList;
}
public List<Customer> getCustomerList() {
return customerList;
}
public void setCustomerList(List<Customer> customerList) {
this.customerList = customerList;
}}
DaoImpl
JobDaoImpl
#Repository
public class JobDaoImpl implements JobDao {
#PersistenceContext
EntityManager entityManager;
#Override
public List<Job> findAllJobs() {
return entityManager.createQuery("select j from Job j order by j.id", Job.class)
.getResultList();
}
#Override
public Job addJob(Job job) {
entityManager.persist(job);
entityManager.flush();
entityManager.refresh(job);
return job;
}}
Service
JobService
#Service
#Transactional
public class JobService {
private JobDao jobDao;
public List<Job> findAllJobs(){
return jobDao.findAllJobs();
}
public Job addNewJob(Job job){return jobDao.addJob(job);}
public JobService(JobDao jobDao) {
this.jobDao = jobDao;
}
}
Controller
JobController
#Controller
public class JobController {
JobService jobService;
EmployeeService employeeService;
CustomerService customerService;
public JobController(JobService jobService, EmployeeService employeeService, CustomerService customerService) {
this.jobService = jobService;
this.employeeService = employeeService;
this.customerService = customerService;
}
//JOB INDEX
#RequestMapping("job-index.html")
public ModelAndView getJobIndex() {
ModelAndView modelAndView = new ModelAndView("jobViews/jobIndex");
return modelAndView;
}
//SHOW EMPLOYEES
#RequestMapping("show-jobs.html")
public ModelAndView getAllJobs() {
ModelAndView modelAndView = new ModelAndView("jobViews/jobs");
modelAndView.addObject("jobs", jobService.findAllJobs());
return modelAndView;
}
//ADD NEW JOB GET METHOD
#GetMapping(value = "add-job.html")
public ModelAndView addNewJob(){
return new ModelAndView("jobViews/addJob","job", new Job());
}
//ADD NEW JOB POST METHOD
#PostMapping(value = "add-job.html")
public ModelAndView addNewJob(#ModelAttribute Job job){
return new ModelAndView("jobViews/addJobConfirmation","job",job);
}
#ModelAttribute("employeeInit")
public List<Employee> initializeEmployees() {
return employeeService.findAllEmployee();
}
#ModelAttribute("customerInit")
public List<Customer> initializeCustomer(){ return customerService.findAllCustomer();}}
JSP Views
addJob.jsp
<f:form method="post" modelAttribute="job">
<p>Job name:<f:input path="jobName"/></p>
<f:hidden path="id"/>
<f:select path="employeeList" multiple="true">
<f:options items="${employeeInit}" itemLabel="employeeSurname" itemValue="id"></f:options>
</f:select>
<f:select path="customerList" multiple="true">
<f:options items="${customerInit}" itemLabel="customerSurname" itemValue="id"></f:options>
</f:select>
<button type="submit">Add</button>

Repeated column in mapping for entity: Shipper column: SHIPPER_ID (should be mapped with insert="false"

I have been going around in circles with this error and not sure why I am getting this.
Here is the mapping of Shipper class
#Entity
#Table(schema="SALONBOOKS",name="SHIPPER")
#AttributeOverride(name="id", column=#Column(name="SHIPPER_ID"))
public class Shipper extends SalonObject {
private static final long serialVersionUID = 1L;
private ShipperType name;//ShipperType.WALKIN;
#Column(name="SHIPPER_NAME")
#Enumerated(EnumType.STRING)
public ShipperType getName() {
return name;
}
public void setName(ShipperType name) {
this.name = name;
}
#Override
public Long getId(){
return id;
}
}
Here is Order class which references Shipper
#Entity
#Table(schema="SALONBOOKS",name="ORDER")
#AttributeOverride(name="id", column=#Column(name="ORDER_ID"))
public class Order extends SalonObject {
private static final long serialVersionUID = 1L;
private BigDecimal total= new BigDecimal(0.0);
private int numOfItems=0;
private BigDecimal tax= new BigDecimal(0.0);;
private String currency="USD";
private BigDecimal subTotal= new BigDecimal(0.0);
private PaymentMethod paymentMethod;
private Shipper shipper;
private OrderStatusType status;
private Appointment appointment ;
private Person person;
#Column(name="TOTAL")
public BigDecimal getTotal() {
return total;
}
public void setTotal(BigDecimal total) {
this.total = total;
}
#Column(name="NUM_OF_ITEMS")
public int getNumOfItems() {
return numOfItems;
}
public void setNumOfItems(int numOfItems) {
this.numOfItems = numOfItems;
}
#Column(name="TAX")
public BigDecimal getTax() {
return tax;
}
public void setTax(BigDecimal tax) {
this.tax = tax;
}
#Column(name="CURRENCY")
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
#Column(name="SUBTOTAL")
public BigDecimal getSubTotal() {
return subTotal;
}
public void setSubTotal(BigDecimal subTotal) {
this.subTotal = subTotal;
}
#ManyToOne
#JoinColumn(name="PAYMENT_METHOD_ID", insertable=false,updatable=false)
public PaymentMethod getPaymentMethod() {
return paymentMethod;
}
public void setPaymentMethod(PaymentMethod paymentMethod) {
this.paymentMethod = paymentMethod;
}
#ManyToOne
#JoinColumn(name="SHIPPER_ID", insertable=false,updatable=false)
public Shipper getShipper() {
return shipper;
}
public void setShipper(Shipper shipVia) {
this.shipper = shipVia;
}
#Column(name="STATUS")
#Enumerated(EnumType.STRING)
public OrderStatusType getStatus() {
return status;
}
public void setStatus(OrderStatusType status) {
this.status = status;
}
#ManyToOne
#JoinColumn(name="APPOINTMENT_ID", insertable=false,updatable=false)
public Appointment getAppointment() {
return appointment;
}
public void setAppointment(Appointment appointment) {
this.appointment = appointment;
}
#ManyToOne
#JoinColumn(name="PERSON_ID", insertable=false,updatable=false)
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#Override
public Long getId(){
return id;
}
}
each of these extends:
#MappedSuperclass
public abstract class SalonObject implements Entity, Serializable {
private static final long serialVersionUID = 1L;
protected Long id;
protected DateTime createDate;
protected DateTime updateDate;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof SalonObject
&& obj !=null){
return ObjectUtils.equals(this.id, ((SalonObject) obj).getId()) ;
}
return false;
}
#Column(name="CREATE_DATE")
public DateTime getCreateDate() {
return createDate;
}
public void setCreateDate(DateTime dateTime) {
this.createDate = dateTime;
}
#Column(name="UPDATE_DATE")
public DateTime getUpdateDate() {
return updateDate;
}
public void setUpdateDate(DateTime updateDate) {
this.updateDate = updateDate;
}
}
The stackTrace is ::
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: salonbooks.model.Shipper column: SHIPPER_ID (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:709)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:731)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:753)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:506)
at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1358)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1849)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1928)
at org.springframework.orm.hibernate4.LocalSessionFactoryBuilder.buildSessionFactory(LocalSessionFactoryBuilder.java:343)
at salonbooks.core.HibernateConfiguration.sessionFactory(HibernateConfiguration.java:109)
removing the following method from Shipper and from Order worked to resolve this error
#Override
public Long getId(){
return id;
}
Because you are using property access, by overriding the base method (containing the mapping configuration) you will replace your base method mapping configuration with no config at all.
Using field access wouldn't have caused this issue, but the override would have been useless anyway. The id field should have private access too, so this method wouldn't compile if you change the access modifier.

Resources