Initialize field with current year and assigned ID - spring

I've got an entity that I am persisting. Its ID is automatically assigned when storing it into the database via Spring Repository.
In the same entity, I have a field build from the Id and the current year: "<current_year>-<id>".
In a method annotated with #PrePersist, the ID has not been assigned yet, so I wrote some code in a #PostPersist method:
#PostPersist
protected void setupOrderNumber() {
this.orderNumber = Calendar.getInstance().get(Calendar.YEAR) + "-" + id;
}
This code does not store the orderNumber into the database, because the entity was stored already.
How can I achieve such a result with JPA directly within the entity?
If not possible with JPA, I could use Hibernate with a #Formula annotation, but I am not sure how to write it: #Formula("extract(year from current_date) + '-' + id") does not seem to work.

As you've already noticed: In #PrePersist a generated ID is not available - just because the ID is set afterwards when persisting into the database. And no changes made in #PostPersist are persisted, just because the persist has already taken place...
You can use a #Formula, as long you don't need the value in the database. But I wouldn't use extract(year from current_date) - as this would change the orderNumber when the year changes - what is different to your experiment with #PostPersist.
Instead use a year field, which you initialize in #PrePersist and reference that one in your formula:
#Entity
public class MyEntity {
#Id
#GeneratedValue(...)
private Long id;
private int year;
#Formula("concat(id, '-', year)")
private String orderNumber;
#PrePersist
private void prePersist() {
this.year = Calendar.getInstance().get(Calendar.YEAR);
}
#PostPersist
private void postPersist() {
this.orderNumber = id + "-" + year;
}
}
I initialize the orderNumber in postPersist() as well, to have a valid value immediately after EntityManager.persist().

Related

How to do findById for autogenerated id

I have a scenario where I am consuming an event and saving the details in the DB. Now the record that is being stored in the database has the id field autogenerated #GeneratedValue(strategy = GenerationType.IDENTITY).
In my test case I need to check if data is getting stored in the DB or not and is as per expectation.
But I am not sure how will I do findById() of SpringBoot Crud/JPA Repository since I do not know what value got generated.
Any help would be appreciated.
Take a look at save method from CrudRepository interface. Spring executes this method in transaction and after its completion Hibernate will generate identifier in returned entity.
Suppose your entity and repository looks as following:
....
public class SomeEntity {
#Id
#GeneratedValue
private Long id;
private String name;
public SomeEntity(String name){
this.name = name;
}
....
}
public interface SomeRepository extends CrudRepository<SomeEntity, Long> {
}
After saving entity:
SomeEntity someEntity = someRepository.save(new SomeEntity("Some entity"));
someEntity.getId() will contain actual record id which can be used further in your tests.
I think you are looking for annotation #DirtiesContext .
It is a Test annotation which indicates that the ApplicationContext associated with a test is dirty and should therefore be closed and removed from the context cache. - javadoc
Read Section 9.3.4 - Here
Check - Example ans below as well:
#Test
#DirtiesContext
public void save_basic() {
// get a course
Course course = courseJpaRepository.findById(10001L);
assertEquals("JPA ", course.getName());
// update details
course.setName("JPA - Updated");
courseJpaRepository.saveOrUpdate(course);
// check the value
Course course1 = courseJpaRepository.findById(10001L);
assertEquals("JPA - Updated", course1.getName());
}
BTW - how you can get the id : simply via getter method from the return type of save
EmployeeDetails employeeDetails = emaployeeService.saveEmployeeDetails(employee);
int temp = employeeDetails.getID()
Related Post : Here

How do i check if a record already exists in table in springboot JPA?

I have a table with 4 fields. And if i inserted a record that already exists i.e all field value matches with previous record in table. How do i return record only but not insert into database ?
My model look like this:
#Entity
public class QuestionDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String department;
private String year;
private String academic_year;
private String semester;
private String type;
private String subject;
private int points;
private int unit;
// getter, setter
And Controller look this:
#Autowired
public QuestionDetailsRepository qdRepository;
#PostMapping("/api/questionDetails")
public QuestionDetails addQuestion(#Valid #RequestBody QuestionDetails qDetails) {
// here i want to first check if qDetails object is already present in table .
If present i want to return that existed record instead of inserting into table.
QuestionDetails qd = qdRepository.save(qDetails); // save only new record
return qd;
}
Using postman i send data like this:
{
"department" : "IT",
"year" : "2020",
"academic_year" : "1st year",
"semester" : "first semester",
"type" : "objective",
"subject" : "JAVA",
"points" : 10,
"unit" : 5
}
Here, i am sending data that is already present in table. So, i want to check if this record already exist? If doesn't exist insert into table otherwise return that existed record.
How do i achieve that using springboot Jpa hibernate?
Implement a select method in QuestionDetailsRepository as below. Add all the criteria which make a record unique. I am using department and year but you can use all the parameters of the QuestionDetails entity.
#Query("select qd from QuestionDetails qd where qd.department = :#{#req. department} and qd.year = :#{#req.year}")
Optional<QuestionDetails> findQuestionDetails(#Param("req") QuestionDetails req);
Ensure to implement the equals() and hashCode() in QuestionDetails class as per the unique criteria.
Your pseudo-code would look like this:
Optinal<QuestionDetails> optRecord = qdRepository.findQuestionDetails(qDetails);
if(opt.isPresent()){
return opt.get();
}else{
qdRepository.save(qDetails);
}

Advantage of assigning the returned savedEntity in Spring Data

I see in most of the coders save data(using spring data) as:
savedEntity = repo.save(savedEntity);
Long id = savedEntity.getId();
I am confused about why most of them assign back the returned value to the saved Entity while the following code also works exact(I have tested myself):
repo.save(savedEntity);
Long id = savedEntity.getId();
Did I miss some benefit of assigning back?
for example, let the entity be:
#Entity
public class SavedEntity {
#Id
private int id;
private String name;
//getter, setters, all arg-constructor, and no-arg constructor
}
Consider the object of SavedEntity is
SavedEntity entity = new SavedEntity(1,"abcd");
now for your first question,
SavedUser entity1 = repo.save(entity);
Long id = entity1.getId();
this entity1 object is the return object getting from the database, which means the above entity is saved in the database succesfully.
for the Second Question,
repo.save(entity);
Long id = entity.getId();//which you got it from SavedEntity entity = new SavedEntity(1,"abcd");
here the value of id is the integer you mentioned in place of id(the raw value).
Most of the time the id (primary key) is generated automatically while storing the entity to the database using strategies like AUTO, Sequence etc. So as to fetch those id's or autogenerated primary key values we assign back the saved entity.
For example:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
}
In this case you'll not pass the id externally but it will create a value for it automatically while storing the data to DB.

spring boot - calculated field

So, I have an entity, that has field start_date (java.util.Date is the type).
I want to have another field, that would automatically populate with integer that corresponds to day of week (as a number 1 for sunday, 2 for monday, etc.) of starting date.
Here's fragment of my entity:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#DateTimeFormat(pattern = "yyyy-MM-dd")
#Temporal(TemporalType.DATE)
private Date start_date;
I've tried to add the calculated field in the following way:
#Column(name = "weekday")
#Formula("(select dayofweek(l.start_date) from Lesson l where l.id = id)")
private Integer weekDay;
However, when looking at the Lesson table in H2 console, there's no such column as "weekday"
I also tried other option - without #Formula annotation and with a setter that takes start_date parameter, but I guess this setter is never called, since the column "weekday" is populated with null.
Here's the setter I've tried to use as an alternative solution:
public void setWeekDay(Date start_date) {
Calendar c = Calendar.getInstance();
c.setTime(start_date);
this.weekDay = c.get(Calendar.DAY_OF_WEEK);
}
It's obvious that I'm missing something here, it's probably because I'm still learning Spring boot...
To sum up - I want to have a column in table Lesson, that is calculated from another column of the same table.
#Formula means your field to be calculated by your rules. This entity field is not column in db. This field is calculating for each entity in loading time by specified rule.
If annotation #Column(name = "weekday") would work near #Formula you would be really confused if you expect in loaded entity same value as in DB but here is calculated one and different (inconsistent situation).
If you want save here value from the Lesson table you should remove #Formula and use #EntityListeners({YourEntityJpaCallbacksListener.class}) In spring bean YourEntityJpaCallbacksListener you can define methods marked with #PreUpdate or #PrePersist and use correspond operations to set calculated value into weekday.
for example:
#EntityListeners({YourEntityJpaCallbacksListener.class})
#Entity
public class YourEntity{
// your code
}
#Component
public class YourEntityJpaCallbacksListener {
#Autowired
private LessonRepository lessonRepository;
#PreUpdate
void preUpdate(YourEntity yourEntity) {
if (recurrentRuleRepository.exists(yourEntity.getId())) {
Integer weekDay = lessonRepository.findOne(yourEntity.getId());
yourEntity.setWeekDay(weekDay);
}
}
}
Ok, so I think I have managed to solve this.
I have removed the #Formula annotation
I have created a method that calculates the day of the week and writes it to weekDay.
public void calculateDayOfWeek(){
Calendar c = Calendar.getInstance();
c.setTime(start_date);
this.weekDay = c.get(Calendar.DAY_OF_WEEK);
}
And I put it inside start_date setter:
public void setStart_date(Date start_date) {
this.start_date = start_date;
calculateDayOfWeek();
}
So now, evertime the start_date is created or updated, it updates (or creates) the value of weekDay.

I need help for persisting into oracle database

There is a problem about generating id while persisting into database.
I added the following code to my jpa entity file, however I'm getting 0 for personid.
#Id
#Column(unique=true, nullable=false, precision=10, name="PERSONID")
#SequenceGenerator(name="appUsersSeq", sequenceName="SEQ_PERSON", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "appUsersSeq")
private long personid;
EjbService:
#Stateless
public class EjbService implements EjbServiceRemote {
#PersistenceContext(name = "Project1245")
private EntityManager em;
#Override
public void addTperson(Tperson tp) {
em.persist(tp);
}
}
0 is default value for long type. The id will be set after invoking select query for the related sequence, which commonly is executed when you persist the entity. Are you persisting the entity? In case yes, post the database sequence definition to check it.

Resources