I am fairly new to Spring and Hibernate. I'm creating an app to learn them.
What I want to do is post a message with a timestamp.
So far I was trying to have PostgreSQL take care of the TIMESTAMPTZ generation via:
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
However this leads to a SQL statement error when trying to insert a new Message due to the fact that the createdAt field of the Message class is null when trying to save with Hibernate.
I thought the solution would be to use the @Transient annotation above my createdAt field such that it is ignored on persistence. It does solve the problem of insertion, now the messages are correctly persisted with a TIMESTAMPTZ generated by PostgreSQL but somehow when retrieving messages from the DB, it seems the createdAt field is also ignored ? I get null for createdAt on database retrieval via hibernate.
How could I go about telling hibernate to ignore the createdAt field on insertion but not on retrieval?
If this is impossible, how would you achieve an automatically generated timestamptz that you can still retrieve and display on the front end.
Mention: I intend to use the field in a Thymeleaf template and I have a class Project which includes a message list as a OneToMany relationship with Message.
Here is my Message class:
@Entity
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "message_seq")
@SequenceGenerator(name = "message_seq", sequenceName = "message_seq", allocationSize = 1, initialValue = 1)
private long messageId;
@NotBlank(message = "* Must provide a title", groups = OnUpdate.class)
@Size(min = 2, max = 50, groups = OnUpdate.class)
private String title;
@NotBlank(message = "* Must provide a content", groups = OnUpdate.class)
@Size(min = 5, max = 255, groups = OnUpdate.class)
private String content;
@Transient
private Timestamp createdAt;
}
My Project class:
@Entity
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "project_seq")
@SequenceGenerator(name = "project_seq", sequenceName = "project_seq", allocationSize = 1, initialValue = 1)
private long projectId;
@NotBlank(message = "* Must provide a project name")
@Size(min = 2, max = 100)
private String name;
@NotBlank(message = "* Must provide a project stage")
@Size(min = 2, max = 20)
private String stage;
@NotBlank(message = "* Must provide a project description")
@Size(min = 2, max = 255)
private String description;
@NotNull
@Valid
private Date startDate;
@NotNull
@Valid
private Date endDate;
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST},
fetch = FetchType.LAZY)
@JoinTable(name = "project_employee",
joinColumns = @JoinColumn(name = "project_id"),
inverseJoinColumns = @JoinColumn(name = "employee_id"))
private List<Employee> employees;
@OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
private List<Message> messageList = new ArrayList<>();
My MessageController method to create:
@PostMapping("/create")
public String createMessage(Model model, Principal principal, @Valid Message message, BindingResult bindingResult, @RequestParam("projectId") long projectId) {
if (bindingResult.hasErrors()) {
return "messages/new-message";
}
Project targetProject = projectService.findByProjectId(projectId);
UserAccount targetUserAccount = userAccountService.findByUserName(principal.getName());
message.setProject(targetProject);
message.setUserAccount(targetUserAccount);
messageService.save(message);
return "redirect:/projects/details?id=" + projectId;
}
My PostgreSQL table:
CREATE SEQUENCE IF NOT EXISTS message_seq;
CREATE TABLE IF NOT EXISTS message (
message_id BIGINT NOT NULL DEFAULT nextval('message_seq') PRIMARY KEY,
title VARCHAR(50) NOT NULL,
content VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
user_id BIGINT NOT NULL REFERENCES user_account (user_id),
project_id BIGINT NOT NULL REFERENCES project (project_id)
);
HERE is my application.properties file in case this can help:
spring.datasource.url=${jdbcurl}
spring.datasource.username=${dbuser}
spring.datasource.password=${dbpassword}
spring.datasource.initialization-mode=never
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.show-sql=true
spring.thymeleaf.cache=false
logging.level.root=WARN
logging.level.com.am.pma=DEBUG
logging.level.org.springframework=INFO
logging.file=app.log
server.error.whitelabel.enabled=false
server.error.include-binding-errors=always
spring.jpa.properties.javax.persistence.validation.mode=none
UPDATE:
I even tried @JsonInclude() and @JsonProperty above the field like suggested in What is the easiest way to ignore a JPA field during persistence?
Still does not work.