Given an entity with two date fields of type LocalDateTime:
class MyEntity {
private LocalDateTime start;
private LocalDateTime lastUpdate;
}
I want to write a query with criteria api that orders the result set by the time between these two dates.
/* ... */
Root<MyEntity> root = query.from(MyEntity.class);
Order order = ???
root.orderBy(cb.desc(order));
/* ... */
My ideas so far:
CriteriaBuilder#diff wants its arguments to be derived from Number, which LocalDateTime doesn't
CriteriaBuilder#treat maybe treat it as a Timestamp somehow? It is stored as timestamp in the database after all
p.s.:
using EclipseLink 2.7.3
looking for a solution that is not tied to a specific database
Your idea of cb.treat(..) that would do a downcast would not work because Number does not extend/inherit LocalDateTime. And even if it would be possible there would then be a problem because there would not be a way to cast database timestamp type to any Number either directly in database side.
I found this question which might help you. Accepted answer makes use of standard(?) SQL function TIMESTAMPDIFF and that should be quite implementation independent solution. You could make some generic sort based on that answer.
However, one option would also be adding a new calculated field for duration / interval like:
private Duration duration;
#PrePersist // update on each persist
private void calcDuration() {
duration = Duration.between(start, lastUpdate);
}
Then ordering would be like:
Path<Long> duration = root.get("duration"); // duration is stored as bigint or like
criteriaQuery.orderBy(criteriaBuilder.desc(duration)); // or .asc(..)
Naturally duration consumes extra space in db - and it should normally be just transient and/or calculated value - but it might also bring some performance boost.
Related
var res = Context.Exampletable
.Where(s => s.CompanyId == CompanyId &&
Convert.ToDateTime(s.TextDate) >= DateTime.Now)
.Select(x => new Exampletable { TextDate = x.TextDate })
.FirstOrDefault();
This is the Linq for one of my problem statements. I want to fetch records future date records from current date & timestamp, so I am converting and comparing it to Datetime but I get this error:
The LINQ expression 'DbSet
.Where(a => Convert.ToDateTime(a.TextDate) > Convert.ToDateTime(DateTime.Now))' > could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()
Note: in Postgresql DB TextDate column has string datatype and contains values like '4/1/2020 10:00 AM'.
Please provide a solution for this.
If you really can't change the underlying column type, then instead of unsupported Convert.ToDateTime use C# cast operator which maps to PostgreSQL CAST operator:
(DateTime)(object)s.TextDate >= DateTime.Now
Note that the "intermediate" cast to object is needed just to make the C# compiler happy.
P.S. I really have no idea why some methods of Convert like ToInt32 are supported, and other like ToDateTime are not. I guess just yet another EF Core inconsistency.
It always baffles me that people decide to store DateTime values as strings and then order users of the database to do calculations with the values. I can imagine you'd like to curse the person who decided to do this, especially because he decided to store it in this non-sortable fashion.
Best solution
If possible, change the database such that it stores DateTimes as DateTimes, or if your database language doesn't know how to do that, store the Ticks of the DateTimes as longs. Future users of the database will glorify your name!
long nowTicks = DateTime.Now.Ticks;
var result = Context.Exampletable
.Where(example => example.CompanyId == CompanyId && example.DateTicks >= nowTicks);
Almost best solution
if the decision to save DateTimes as strings is a decision of the developers of PostgreSQL, then try to find if they have functions to handle these datetimes, especially comparison
The "it's getting worse" solution
Try to find out if PostgreSQL has string manipulation functions, so you can translate 4/1/2020 10:00 AM into something IComparable. It is difficult if you want to write code to compare this value with for example 4/1/2019 10:00 AM, or 4/2/2020 10:00 AM, so I guess it will be hell of a job to do this in SQL
With EF Core 5+ you can use ValueConverters as a "workaround" for this scenario and use the built in StringDateTimeConverter (https://apisof.net/catalog/e0dd77d4-73c3-6bba-e51a-4842a59894d1).
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Exampletable>()
.Property(e => e.TextDate)
.HasConversion<string>();
}
public class ExampleTable
{
public DateTime TextDate {get; set;}
}
Then simply
DateTimeOffset someValue = some-value-here;
var query = DbContext.Entities.Where(e => e.TextDate <= someValue);
It is important that the parameter inside your LINQ query is a datetimeoffset for this to work as EF CORE doesn't generate a SQL with CAST operation for the column if you don't. See here for an example https://gist.github.com/dasiths/19b885c58442226d9fc8b89bc78511e4
The generated SQL will be like
((#__startSearch_0 >= CAST([s].[TextDate]) AS datetimeoffset))
Edit: To reiterate, using value converters is a "workaround" here. To see a full analysis of the options, I've written a full detailed analysis of the "work arounds" with the value converter hack here https://dasith.me/2022/01/23/ef-core-datetime-conversion-rabbit-hole/.
I want to uniquely identity an entity without using the primary key. So I thought about generating an unique and random value. Moreover, value must be easy to read / manually copy and is expected to be 6 or 7 characters long.
Design
My entity A:
public class A{
// ...
#Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
private String value;
// ...
public String getValue(){
return value;
}
protected void setValue(String value){
this.value = value;
}
}
represented in the database by the table
CREATE TABLE IF NOT EXISTS schema.mytable{
-- ...
value TEXT NOT NULL DEFAULT generate_unique_value_for_mytable(),
-- ...
CONSTRAINT "un_value" UNIQUE (value),
-- ...
}
I thought letting the database handling this and then fetch the value...
Problem
With the current design, value is correctly generated in the database but when JPA fetches A entities, value field is empty.
I cannot remove insertable=false otherwise, it will hit against the NOT NULL constraint
If I remove insertable=false and I put some dummy data, the data overrides the value generated by generate_unique_value_for_mytable()
If I remove everything in the Column annotation, I can save the A entity but value is still empty
Ugly solution
I couldn't find a proof but it looks like having the database generating a value is a bad idea. I do have the same problem for a non-primary key field which is generated by a sequence: I cannot fetch the value from the database.
So my ugly solution is to decorate the create() method of the EJB responsible for A entities:
public class Aejb{
public void create(A entity){
// method kind of ensures randomness
String value = MyUtil.generateRandomValue();
A isThereAnyoneHere = findByValue(value);
while(isThereAnyoneHere != null){
String value = MyUtil.generateRandomValue();
isThereAnyoneHere = findByValue(value);
}
// unicity is ensured
entity.setValue(value);
em.persist(entity);
}
}
Questions
Can I fetch a non-primary key value generated by the database from a JPA entity? Value can be generated by a function or a sequence.
Is there a more elegant solution than my ugly workaround to provide an unique and random value?
Yes.You haven't mentioned your database, but it is possible for
Oracle to return the value inserted via triggers, and have
Eclipselink obtain this value in your model - see
https://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_returninsert.htm
Set the value using a #PrePersist method that will get executed
before the entity is inserted, but if you are relying on one or more database queries, you will run into performance issues, as inserting a new A will be expensive. You might instead just insert the random value and deal with the occasional conflict, and pick some random that has less chance of overlaps, like a UUID.
If I understand correctly, #Generated annotation should do the trick. This annotation sets the value from database DEFAULT field value.
Example:
#Generated(GenerationTime.INSERT)
#Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
private String value;
However there is a drawback: if you decide to set value of your field in Java, it would be overwritten by Hibernate using the result from DEFAULT in your database.
Self-answer to mark question as closed
Final solution
We finally went for a combination of
Stored procedures: the database will generate the value. The procedure also ensures that the value is unique across the table
Named queries: to fetch the generated value by the procedure. I did not use NamedStoredProcedures because we are using PostgreSQL and PostgreSQL JDBC driver did not support name parameters which raised some problems.
With this configuration, the EJB is sure to have at most one database call to fetch the requested value.
Response to other answers
Here is a summary of the other answers feedback for self-reference and next readers:
Oracle trigger: we're using PostgreSQL :(
UUID: We had the constraint of having our unique and random code human-readable. An end-user is assumed to be able to manually rewrite it. Consequently, we could not have a long String such as an UUID.
PrePersist: Other business actions take place after the code generation in the same transaction which means that those actions need to be redone in case of collision. I'm not very confident about managing JPA exception (transaction scope and so on) so I preferred not to play with it.
#Generated: This is a Hibernate specific feature. We're using EclipseLink
Database Trigger: If code were purely generated at database level, I encountered the same problems of not fetching the value: the value is properly generated as database level but the entity will have the value as null
Using JPA with EclipseLink, I would like to track the timestamp of the last update made to an entity instance. Assuming that this would be easy to combine with optimistic locking, I defined the entity as follows:
import javax.persistence.Version;
[...]
#Entity
public class Foo {
#Id int id;
#Version Timestamp lastChange;
[...]
}
Updating a changed object is done with the following code:
EntityManager em = Persistence.createEntityManagerFactory("myConfiguration");
em.getTransaction().begin();
em.merge(foo);
em.getTransaction().commit();
I would expect that foo.lastChange would be set to the new timestamp each time an update to a changed instance is committed. However, while the field LASTCHANGE is updated in the database, it is not updated in the object itself. A second attempt to save the same object again thus fails with an OptimisticLockException. I know that EclipseLink allows to choose between storing the version-field in cache or directly in the object and I made sure that the configuration is set to IN_OBJECT.
The obvious question is: How to get the foo.lastChange field set to the updated timestamp value when saving to the database? Would
foo = em.find(Foo.class, foo.id);
be the only option? I suspect there must be a simpler way to this.
merge does not modify its argument. It copies the state from its argument to the attached version of its argument, and returns the attached version. You should thus use
foo = em.merge(foo);
// ...
return foo;
I have a large implementation of GWT-RPC and actually I'm evaluating the alternative to move to RequestFactory.
The basic reason why I'm doing that is because I'm not very happy with TypeSerializers solution that GWT-RPC produce, and the huge size of code generated for serialize/deserialize that actually represent in my case more than 60% of the whole JS resulting code.
So for a week I have been reading all about requestFactory and my first impression was that the API is confined to the management of persistent Entities, but it doesn't shows clearly how the API will support Query Results Proxys.
I read that it could be done by using ValueProxy but I couldn't found any good example of doing that.
What I mean is asume that I need to provide a Sales Ranking of Top 10 clients for last month.
Information like that is easy to provide via RPC, but with RequestFactory I'm not sure.
I don't have any Domain Object to proxy.
via GWT-RPC I will have a service method like:
List<ClientRankingDTO> getClientRanking(String clientCode, Date fromDate, Date untilDate);
My ClientRankingDTO will looks like:
public class ClientRankingDTO implements Serializable {
private String clientCode;
private String clientDescription;
private Integer rankingPosition;
private BigDecimal amount;
// Getters and setters are hidden for simplicity
}
So at my DAO layer I will have some method over the SalesStatistics Domain Model Entity that will calculate the corresponding ranking and will generates the List of ClientRankingDTO with the corresponding results.
That could be done by a simple query like:
Select top 10 client_code, sum(amount) from sales_stats A
where A.sales_date>=fromDate
and A.sales_date<=untilDate
group by client_code
order by amount desc
implemented with the ORM of your choice.
My question is how can I implement this kind of service with RequestFactory?
Simply use ValueProxy instead of EntityProxy, and you turn RequestFactory into a "simple RPC" mechanism, similar to GWT-RPC.
Proxy:
#ProxyFor(ClientRankingDTO.class)
interface ClientRankingProxy extends ValueProxy {
// getters for the properties, no need for setters if it's only server-to-client
}
Then, in the RequestContext (client-side):
Request<ClientRankingProxy> getClientRanking(String clientCode, Date fromDate, Date untilDate);
And in the service on the server-side:
public ClientRankingDTO getClientRanking(String clientCode, Date fromDate, Date untilDate) {
// your request to the database, mapping the result to ClientRankingDTO
}
I've found two ways of concurrency checking for my entities in EF 4.1:
TimeStamp attribute for byte array
ConcurrencyCheck attribute for another types
The first one is very simple. You just mark byte array property as TimeStamp, create additional column in database and voila...
I've got a problem with the second method. Enity Framework has started generate sql script for concurrency check, when I marked the LastUpdateDate property.
Property:
[ConcurrencyCheck]
public DateTime LastUpdateDate { get; set; }
Sql:
select
...
where (([Id] = #3) and ([LastUpdateDate] = #4))
...
#4='value'
But EF does not generate sql script for updating the value of LastUpdateDate?
Is it possible to say EF to update the LastUpdateDate after concurrency checking without triggers or something like this?
And the second question:
What is the best practice of using concurrency checking in EF when you have something like LastUpdateDate property(property will be displayed in UI)? Is it better to check concurency using LastUpdateDate and avoid creating of addtional column for TimeStamp in your tables or
create additional TimeStamp property and renounce of the using DateTime property for concurrency checking?
Have you tried to use a rowversion (timestamp) instead of the DateTime datatype to check for concurency?
I would use the timestamp, because you are sure that the system will update it for you. Further more the value will be very precice.
The following blog posts will give you more information about how to map a timestamp.
The first one shows how to use the timestamp as a concurrency check.
Code First Optimistic Concurrency with Fluent Assertions
Round tripping a timestamp field with EF4.1 Code First and MVC 3