Spring JPA Equal conditon with derived query for LocalDate not working - oracle

I have a Oracle DB with type of DATE my Entiy class have LocalDate parameter as shown in here
#Entity
#Table(name = "CONTRACT")
public class ContractEntity {
private Long id;
private LocalDate contractStartDate;
private LocalDate contractEndDate;
...
For the testing I created derived query to find Entity by contractStartDate
public interface ContractRepository extends JpaRepository<ContractEntity, Long>, JpaSpecificationExecutor<ContractEntity> {
ContractEntity getContractEntityByContractStartDateIs(LocalDate date)
}
Then I created Two Tests as follows;
#DisplayName("Should get Exact Date Entity")
#Test
void test1(){
LocalDate date=LocalDate.of(2021,03,9);
ContractEntity entity= searchService.getByDate(date); // getByDate is mapped to getContractEntityByContractStartDateIs() in service layer
assertEquals(date,entity.getContractStartDate());
}
#DisplayName("Should match ID:1 Entity's Date with Local Date")
#Test
void test2(){
ContractEntity entity=searchService.getById(1l).get();
assertEquals(1l,entity.getId());
LocalDate date=LocalDate.of(2021,03,9);
assertEquals(date,entity.getContractStartDate());
}
}
My problem is test1 fails with null error which means equality condition fails to give existence of that Entity even though test2 passed which date is equal to id:1 entity's Start-date
p.s: I have set Contract Table's ID:1 Contract Start Date value to 2021,03,9 as shown in image here
So what would be the reason for this Why test1 fails to get equal entity and why test2 pass that assert equity of date is true

LocalDate doesn't contain a time component, but the data type you used in the database does and it is not 0 for the data in the database.
In order to make the comparison (which happens in the database) the LocalDate gets converted with a date + time where the time is probably 0, which is not equal to the date+times present in the database.
When the entity gets loaded the time part of the database value gets dropped and the result is equal to your LocalDate value.

Related

Inserting a ZonedDateTime value to Oracle Timestamp With Timezone Column With JPA

In my SpringBoot project, I am trying to insert a ZonedDateTime value to an Oracle database where the column type is specified as TIMESTAMP WITH TIMEZONE. When I do that, the inserted value gets converted to the JVM timezone and gets inserted. However, what I expect is to insert the value in ZonedDateTime property as it is.
For example, if the ZonedDateTime value is set to 2020-05-16T12:12:24+02:30[US/Denver], then I expect to write to DB the same value and read from DB the same value. Is there any way I can do this with Hibernate and JPA
#Entity
class MyEntity{
ZonedDateTime myDateTimeWithTimeZone;
}
#Repository
public interface MyRepository extends JpaRepository<MyEntity, Long>
{
}
This is how i am going to use it,
MyEntity myEntity = new MyEntity();
myEntity.setMyDateTimeWithTimeZone(ZoneDateTime.now('US/Denver')) // This timezone can be anything depending on userInput
myRepository.save(myEntity);

Spring Data JPA Projection with select distinct

I have a database table which holds Metadata for documents. My task now is to get a list with documenttypes. The documenttypes are not unique in the database table but of course I want them to be in my list. The sql is very simple:
SELECT DISTINCT groupname, group_displayorder
FROM t_doc_metadata
ORDER BY group_displayorder;
I have learned that I can use projections to get a subset of fields from my entity DocMetadata. I solved this as follows. My Entity:
#Entity
#Table(name="T_DOC_METADATA")
#Data
public class DocMetadata {
..............
#Column(nullable=false)
private String displayname;
#Column(nullable=false)
private Integer displayorder;
#Column(nullable=false)
private String groupname;
#Column(name="GROUP_DISPLAYORDER",
nullable=false)
private Integer groupDisplayorder;
#Column(name="METADATA_CHANGED_TS",
nullable=false,
columnDefinition="char")
private String metadataChangedTimestamp;
..........
}
My inteface for projection:
public interface GroupnameAndOrder {
String getGroupname();
Integer getGroupDisplayorder();
void setGroupname(String name);
void setGroupDisplayorder(int order);
}
Now I thought I'd be extraordinary clever by adding these lines to my repository:
#Query("select distinct d.groupname, d.groupDisplayorder from DocMetadata d order by d.groupDisplayorder")
public List<GroupnameAndOrder> findSortedGroupnames();
Sadly, when iterating over the result list and calling getGroupname() the result is null.
So I changed the lines in my repository according to the documentation:
public List<GroupnameAndOrder> findBy();
Now I get the groupnames but of course they are not unique now. So it doesn't solve my problem.
Is there any way to receive a ordered list with unique groupnames?
You are trying to be too clever. Instead just write the proper find method and return the GroupnameAndOrder. Spring Data JPA will then only retrieve what is needed for the projection.
Something like this should do the trick.
List<GroupnameAndOrder> findDistinctByOrderByGroupDisplayorder();

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.

Initialize field with current year and assigned ID

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().

Select fews columns (DTO) with specification JPA

I am using spring-data-jpa version 1.5.1.RELEASE .
My domain is :
public class MyDomain{
....
....
private String prop1;
private String prop2;
......
......
}
My JPA Specification is:
public final class MyDomainSpecs {
public static Specification<MyDomain> search(final String prop1,final String prop2) {
return new Specification<MyDomain>() {
public Predicate toPredicate(Root<MyDomain> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// Some tests if prop1 exist .....
Predicate predicate1 = cb.equal(root.get("prop1"), prop1);
Predicate predicate2 = cb.equal(root.get("prop2"), prop2);
return cb.and(predicate1, predicate2);
}
};
}
}
My Repository :
public interface MyDomainRepository extends JpaRepository<MyDomain, Long>, JpaSpecificationExecutor<MyDomain> {
List<MyDomain> findAll(Specification<MyDomain> spec);
}
All is Working .
But my need (For performance DB tunning) is to not return and select all fields of MyDomain from DB .
I need to select only for example tree properties (prop1, prop2, prop3) , idealy in a DTO Object .
I don't want to convert My List<MyDomain> to List<MyDto> because i am tunning DB request .
So , I don't find any way to do that with spring-data-Jpa and Specification .
Any Idea ?
Thanks
This is not possible as for now. There is a ticket for this but no idea if it will be ever implmented: https://jira.spring.io/browse/DATAJPA-51
Create a special version of MyDomain (e.g. MyDomainSummary or LightMyDomain) that only includes the fields you want to map.
Basic example
Borrowed from the excellent JPA WikiBook.
Assume a JPA entity (i.e. domain class) like so:
#Entity
#Table(name="EMPLOYEE")
public class BasicEmployee {
#Column(name="ID")
private long id;
#Column(name="F_NAME")
private String firstName;
#Column(name="L_NAME")
private String lastName;
// Any un-mapped field will be automatically mapped as basic and column name defaulted.
private BigDecimal salary;
}
The SQL query generated will be similar to
SELECT ID, F_NAME, L_NAME, SALARY FROM EMPLOYEE
if no conditions (where clause) are defined. So, to generalize the basic case one can say that the number of queried columns is equal to the number of mapped fields in your entity. Therefore, the fewer fields your entity, the fewer columns included in the SQL query.
You can have an Employee entity with e.g. 20 fields and a BasicEmployee as above with only 4 fields. Then you create different repositories or different repository methods for both.
Performance considerations
However, I seriously doubt you'll see noticeable performance improvements unless the fields you want to omit represent relationships to other entities. Before you start tweaking here log the SQL that is currently issued against the data base, then remove the columns you want to omit from that SQL, run it again and analyze what you gained.

Resources