I am working on a Spring-MVC project in which I am working on creating a backup of GroupNote object every night. Before creating a backup, I am checking if boolean flag indicating that the object was modified is true. If yes, then only a backup is created, and date is already set when the object was created.
Now, when a duplicate GroupNote object is created, it has the latest date set.
This way, the user can select a particular date and then retrieve the data-set for that day.
Now, the problem is :
If the object was not modified on Day 3,4,5, but directly on 6th, and then on 7th, once that is done, what if the User tries to retrieve the object from Day-3, it was not modified, but it was on 6th, so how I can implement this logic, where I have to retrieve the next best one.
The other problem I am facing is, GroupNotes have many-to-one mapping with GroupSection. How can I retrieve all GroupNote objects for a given GroupSection for a given date, which wont give all entities which are after Day-3, but only the 6th one.
Here is how every-night I am creating duplicates :
#Override
public void retrieveModifiedNotes() {
List<GroupNotes> groupNotesList = this.groupNotesDAO.listModifiedNotesForYesterday();
for(GroupNotes yesterdayNotes : groupNotesList){
yesterdayNotes.setLatestNote(false);
this.groupNotesDAO.editGroupNote(yesterdayNotes,yesterdayNotes.getOwnedSectionId());
GroupNotes newDayNotes = new GroupNotes();
BeanUtils.copyProperties(yesterdayNotes, newDayNotes);
newDayNotes.setLatestNote(true);
newDayNotes.setMnoticesid(0);
newDayNotes.setGroupNoteHistorySet(yesterdayNotes.getGroupNoteHistorySet());
newDayNotes.setNoteActivitySet(yesterdayNotes.getNoteActivitySet());
newDayNotes.setNoteSelectionSet(yesterdayNotes.getNoteSelectionSet());
newDayNotes.setUnreadNotesSet(null);
newDayNotes.setPrimaryNote(yesterdayNotes);
this.groupNotesDAO.addGroupNote(newDayNotes,yesterdayNotes.getOwnedSectionId());
}
}
The DAO me to get modified notes :
#Override
public List<GroupNotes> listModifiedNotesForYesterday() {
Session session = this.sessionFactory.getCurrentSession();
Calendar cal = Calendar.getInstance(); // Todays date
Query query = session.createQuery("from GroupNotes as gs where gs.noteModified=true and gs.latestNote=true and " +
"gs.noteSavedDate<:loadDate");
query.setParameter("loadDate", cal.gethodtTime());
return query.list();
}
Model GroupNotes :
#Entity
#Table(name = "groupnotes")
public class GroupNotes implements Serializable {
#Column(name = "note_modified", columnDefinition = "boolean default false")
private boolean noteModified;
#Column(name = "latest_note", columnDefinition = "boolean default true")
private boolean latestNote;
#Column(name = "note_save_date", columnDefinition = "date")
private Date noteSavedDate;
#ManyToOne
#JoinColumn(name = "msectionid")
#JsonIgnore
private GroupSection ownednotes;
}
Problematic DAO method :
#Override
#Transactional(readOnly = true)
public List<GroupNotes> listGroupNotesBySectionAndDate(int sectionId, Date dateToLoad) {
Session session = this.sessionFactory.getCurrentSession();
org.hibernate.Query query = session.createQuery("From GroupNotes as n where n.ownednotes.msectionid=:msectionid and ((n.noteDisabled=false and n.noteInActive=false "
+ "and n.privateNoteUser is null and n.noteSavedDate>:dateToLoad)) order by n.noteSavedDate asc");
query.setParameter("msectionid", sectionId);
query.setParameter("dateToLoad", dateToLoad);
return query.list();
}
As you can see in the above method, I have no way of limiting the query to only the object found on a specific date, I have a comparator operator, which will give data for any date after/before the one specified.
I hope the problem is clear, if there are any doubts, kindly let me know. Thank you. :-)
As far as I understand your problem, your dao method is perfectly fine, but you want to get only the best result (with closest date) not whole list, right?
In that case you should jpa hibernate's paging mechanism to limit the query result to only one row.
This question should be a help.
Related
I have problem with Hibernate Envers.
I have classes like:
#Entity
#Table(name = "REVINFO")
#RevisionEntity(MyRevisionEntityListener.class)
public class RevEntity {
#Id
#RevisionNumber
#Column(name = "REV", nullable = false, updatable = false)
private Integer id;
#RevisionTimestamp
#Column(name = "REVTSTMP", nullable = false, updatable = false)
private Date timestamp;
#Column(name = "MODIFIED_BY", length = 100)
private String modifiedBy;
#Column(name = "COMMENT", length = 100)
private String comment;
public class MyRevisionEntityListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
RevEntity a = (RevEntity) revisionEntity;
a.setComment("Some value");
}
}
How can i select every change for entity ID and their "REVINFO" object?
I've got something like this:
List resultList = AuditReaderFactory.get(entityManager)
.createQuery()
.forRevisionsOfEntityWithChanges(ClientType.class, true)
.add(AuditEntity.id().eq(entityId))
.getResultList();
And it's almost work good. I received every "change" but REVINFO looks strange. All fields are null - and there are 1 more object $$_hibernate_interceptor which actually hold "information" but i cannot acces it via code (or i dont know how). See example at the image.
So my question is:
1 - How can i get REVINFO values ?
2 - Do i realy have to use entityManager, or can it be achived with different approach ?
Edit 2:
Correct me if i am wrong, but does forRevisionsOfEntityWithChanges works as Lazy Initialization? I mean, if i try to receive for example modifiedBy field i actually get my data. Debugger log make me confused.
The call to forRevisionsOfEntityWithChanges returns an object array that contains:
Entity instance
Revision Entity
Revision Type
Property names that were changed.
How can i get REVINFO values ? 2 - Do i realy have to use entityManager, or can it be achived with different approach ?
So in your code, to get the revision info attributes, you would do the following. Note that in this code, the type of the revision-info object will depend on your configuration or if you're using a custom revision-info entity class in your deployment. Just be sure to cast it to the proper type.
for (Object entry : resultList) {
final Object[] row = (Object[]) entry;
final TheRevisionEntityClassType revisionInfo = row[1];
// now you can get the revision entity attributes from revisionInfo using getters
}
Correct me if i am wrong, but does forRevisionsOfEntityWithChanges works as Lazy Initialization? I mean, if i try to receive for example modifiedBy field i actually get my data. Debugger log make me confused.
Depending on the query, yes Hibernate may use proxies and its important to understand that in this case, the visual representation you get in the debugger may or may not be accurate depending if the object's internal state gets initialized by the debugger window or not.
I have a Table in MySQL which has it's column definitions as below:
CREATE TABLE APPOINTMENT(
CD_APPOINTMENT BIGINT NOT NULL,
-- omitted for brevity
APPOINT_DATE DATE NOT NULL
);
My JPA entity is defined as:
#Entity
#Table(name = "APPOINTMENT")
public class Appointment {
protected Long id;
protected Date date = new Date();
// other atributes omitted for brevity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CD_APPOINTMENT")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Temporal(TemporalType.DATE)
#Column(name = "APPOINT_DATE", columnDefinition = "DATE")
public Date getDate() {
return date;
}
}
As I'm using Spring, I have benefits of Spring Data JPA. Following that line, I'm using Spring Data JPA Repositories.
I'm testing in 2019-07-12 (at my timezone [UTC-3]).
When I run:
appointmentRepository.save(appointment);
the Appointment is successfully (more or less) saved.
Fine! The column APPOINT_DATE has the value of 2019-07-12, yes? Well, it's seems ok.
When I run:
SELECT * FROM APPOINTMENT;
the retrieved rows looks as expected:
CD_APPOINTMENT|APPOINT_DATE
--------------|------------
1| 2019-07-12
The strange part appears when I try to filter BETWEEN dates.
If I run my JPQL:
SELECT ap FROM Appointment AS ap WHERE ap.date BETWEEN :startDate AND :endDate
startDate and endDate are parameters received in a #Param annotation in Spring and both of them have the value of 2019-07-12
I get 0 rows, but I was expecting to get one (the above inserted Appointment). Firstly, I thought it was a problem with the JPQL, but it's not. If I execute the same JPQL with a different RDBMS (like H2, for an example), the query works perfectly.
And if I run the same JPQL but in SQL, directly on the MySQL database:
SELECT * FROM APPOINTMENT where APPOINT_DATE BETWEEN '2019-07-12' AND '2019-07-12'
just like the JPQL it returns 0 rows.
If I run the now(); command at MySQL database, it return the CORRECT date time.
How can I fix it?
Has anybody seen something like that already? Because I have not.
BETWEEN '2019-07-12' AND '2019-07-13'
It is best not to use between for date/times. One reason is because there might be a time component that throws off the comparison.
I would suggest:
SELECT *
FROM APPOINTMENT
WHERE APPOINT_DATE >= '2019-07-12' AND
APPOINT_DATE < '2019-07-13'
This logic works with an without a time component. And it can take advantage of an index on the date column.
My MySQL instance is from Amazon RDS.
Their default Time Zone is UTC. Switched from UTC to Brazil/East and now it's working as expected.
I have a class like this
#Entity
#Table(name = "Event")
public class Event {
#Transient
public static final long MAX_TIMESTAMP = 253402214400000L; // 9999-12-31 00:00:00
private Date creationTime;
private Date expiryTime;
private String otherValue;
public Event(int timeout, String otherValue){
this.creationTime = new Date();
this.expiryTime = (timeout == 0 ? new Date(MAX_TIMESTAMP) : new Date(creationTime.getTime() + SECONDS.toMillis(timeout)));
this.otherValue = otherValue;
}
}
I call save() methed in CrudRepository and save this data.
and I have a ScheduledExecutorService to find out some timeout events:
#Query("SELECT t FROM Event t WHERE t.expiryTime < CURRENT_TIMESTAMP")
List<Event> findTimeoutEvents();
this CURRENT_TIMESTAMP is database's time, but expiryTime is not. It means that I must make their time is same.sometimes, the application and database are not in the same machine, I can not make sure their time is same.
Can I set "expiryTime" generated by database? How can I pass the parameter "timeout" to database.
the database maybe postgresql or mysql.
thank you very much.
First of all I am not sure your code works, since instance of java.util.Date (if expiry time is java.util.Date object) can not be compared to int 0.
As for generating an expiryTime, yes, you obviously can. Check out how do triggers work.
Also I would like to add, that if you use spring-boot-starter-data-jpa, you may annotate creationTime field with #CreationTimestamp annotation. But I would personally set default value to CURRENT_TIMESTAMP() on db side.
I am using spring-data-neo4j V4 and look for solution how to fetch entities which are not directly connected to the loaded entity. To explain:
I do have 3 entities in my neo4j database.
#NodeEntity(label="membership")
public class Membership extends AbstractEntity{
public Membership(){ }
private String membershipId;
#Relationship(type = "IN_YEAR", direction = Relationship.OUTGOING)
private Set<Year> year = new HashSet<>();
//getter+setter
}
#NodeEntity(label="year")
public class Year extends AbstractEntity{
public Year(){}
private String name;
private String membershipId;
#Relationship(type = "IN_MONTH", direction = Relationship.OUTGOING )
private Set<Month> month = new HashSet<>();
//getter+setter
}
#NodeEntity(label="month")
public class Month extends AbstractEntity{
private String name;
//getter+setter
}
When i call my MembershipRepository and load a Membership by Id:
membershipRepository.findByMembershipId(id);
the year entities will be fetched but the month entities not.
Can anybody tell what is the best or recommended way to load the month entities when loading the membership entity? As written in http://docs.spring.io/spring-data/neo4j/docs/current/reference/html/ the #Fetch is obsolete since version 4 so I need another solution.
EDIT:
I read in http://docs.spring.io/spring-data/neo4j/docs/current/reference/html/ the workaround for fetch, just use the load methods from the Neo4jTemplate.
So I load the months for every year by:
Set<Year> fetchedYear = new HashSet<>();
for(Year year : ms.getYear()){
fetchedYear.add(neo4jTemplate.load(Year.class, year.getId(), 1));
}
ms.setYear(fetchedYear);
But is there a better solution?
The first option would be to use loadByProperty and set the loading depth to 2.
Example:
neo4jTemplate.loadByProperty(Membership.class, "membershipId", value, 2);
This is available for SDN-4.1.0-Snapshot
But if you do not want to load a Membership with depth 2 because too much of your graph will be loaded (from other relationships) then I think you could construct a cypher query (using OPTIONAL MATCH), execute it with the neo4jTemplate and retrieve the Object which will then be automatically mapped due to the “smartObjectMapping".
Example:
String query = "MATCH (n:Membership{membershipId:{id})-[r]-(m) OPTIONAL MATCH (m:Year)-[e]-(x) RETURN n,r,m,e,x";
Map<String,Object> map = new HashMap<>();
map.put("id",value);
Result result = neo4jTemplate.query(query,map);
now n in the Result should contain all mapped relationships
I am kind of new to JPA and have read a bit about second level caching in JPA. And I think it should apply correctly to my scenario.
I have a table (say A) whose content will never change unless a new release is applied.
I need to query some data from the database : for that I have a JPQL working correctly and that uses a join between table A and an additional table (say B).
Since the contents of table A never change through the life of application, I could essentially mark this table A as Cacheable and reuse the content from Cache - rather than going to the database for that.
I have read about #NamedQuery that enables a one time JPQL to SQL translation for the life of the application. And it is not what I am looking for.
I want to know how should I go about using the Cacheable property for my purpose.
This is what I have done so far :
Marked tableA as cacheable -
#Entity
#Cacheable
#Table(name = "TableA")
public class Table{
#Id
#NotNull
#Column(updatable = false)
private String uuid;
#NotNull
#Size(min = 1)
private String description;
.
.
.
}
2. There is a DAO that does a find using the JPQL -
public Collection findAll(String description) {
final Cache cache = entityManager.getEntityManagerFactory().getCache();
if (cache.contains(TableA.class, "abc")) {
System.out.println("cached");
} else {
System.out.println("not cached");
}
final Query query = entityManager
.createQuery("Select distinct A from TableA A, IN(A.TableB) B where A.description = :description"); //$NON-NLS-1$
query.setParameter("description", description); //$NON-NLS-1$
return query.getResultList();
}
</code>
Can I take advantage of using Cacheable property in my scenario. If so, then could you suggest how?
Also, "not cached" gets printed always no matter what the value of string I use (from table) instead of "abc".
Appreciate your help. Thanks