Dates calculation with Entity Framework? - entity-framework

I have a table with a date as datetime and a month count as int.
How can I get all entities with EF that are
date + month_count > today's date - 7 days
table structure
id uniqueidentifier
date datetime
month_count int

For performance reasons you might want to consider executing the date calculations directly in SQL:
using (var dbContext = new DbContext())
{
var result = from entity in dbContext.Entities.SqlQuery(
#"SELECT * FROM entities
WHERE
DATEDIFF(d,
DATEADD(m, [month_count], [date]),
GETDATE()) > -7");
}
Read more about using SQL with EntityFramework here
An alternative is to just load the lot into memory, and sort it using non SQL aware LINQ:
using (var dbContext = new DbContext())
{
var oneWeekAgo = (DateTime.Now() - TimeSpan.FromDays(7)).Date();
var result = from entity in dbContext.Entities.ToList()
where entity.Date + TimeSpan.FromMonths(entity.MonthCount)
> oneWeekAgo;
}

Related

Rewrite query in JPA

I want to rewrite this SQL query in JPA.
String hql = "SELECT date(created_at) AS cdate, sum(amount) AS amount, count(id) AS nooftransaction "
+ "FROM payment_transactions WHERE date(created_at)>=date(now()- interval 10 DAY) "
+ "AND date(created_at)<date(now()) GROUP BY date(created_at)";
TypedQuery<Merchants> query = entityManager.createQuery(hql, Merchants.class);
List<Merchants> merchants = query.getResultList();
Is there a way to rewrite the queries into JPA or I should use it as it is?
In situations like these, more often than not the best approach is to write a plain SQL view:
CREATE OR REPLACE VIEW payment_transactions_stats AS
SELECT date(created_at) AS cdate, sum(amount) AS amount, count(id) AS nooftransaction
FROM payment_transactions
WHERE date(created_at)>=date(now()- interval 10 DAY)
AND date(created_at)<date(now()) GROUP BY date(created_at);
And map it to an #Immutable entity. This approach works well when:
you have read only data
the view does not need parameters (in this case there are solutions as well which span from hacky to nice)
You provide no details about the classes and entities but it could be something like:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = builder.createTupleQuery();
From<PaymentTransaction> tx = query.from(PaymentTransaction.class);
Expression<Long> sumAmount = builder.sum(tx.get("amount"));
Expression<Long> count = builder.count(tx.get("id"));
Expression<Date> createdAt = tx.get("created_at");
query.multiselect(createdAt, sumAmount, count);
query.where(builder.greaterThanOrEqualTo(createdAt, builder.function("DATEADD", "DAY", new Date(), builder.literal(-10))),
builder.lessThan(createdAt, new Date()));
query.groupBy(createdAt);
entityManager.createQuery(query).getResultList().stream()
.map(t -> new Merchants(t.get(0, Date.class), t.get(1, Long.class), t.get(2, Long.class)))
.collect(Collectors.toList());
It is better not to use JPA for complex queries like this. JPA are usually used for simple queries.
Since the question is tagged with spring-data-jpa, you could try using a Spring CRUDRepository on top of your table. In the CRUDRepository, write a custom method with the #Query annotation.
It's hard for me to formulate the entire query because I don't know the members of your Merchants class.
Alternatively you can set the nativeQuery = true for the #Query annotation and use actual DB query to solve your problem.
You can use below code
CriteriaBuilder qb = entityManager.getCriteriaBuilder();
CriteriaQuery cq = qb.createQuery();
Root paymentInstructionsRoot = cq.from(PaymentInstructions.class);
List<Predicate> predicates = new ArrayList<>();
predicates.add(qb.greaterThanOrEqualTo(path, fromDateRange));
predicates.add(qb.lessThanOrEqualTo(path, toDateRange));
Selection cdate = paymentInstructionsRoot.get(PaymentInstructions_.createdAt).alias("cdate");
Selection amount = qb.sum(paymentInstructionsRoot.get(PaymentInstructions_.amount))).alias("amount");
Selection nooftransaction = qb.count(paymentInstructionsRoot.get(PaymentInstructions_.id))).alias("nooftransaction");
Selection[] selectionExpression = {cdate, amount, nooftransaction};
Expression[] groupByExpression = {paymentInstructionsRoot.get(PaymentInstructions_.createdAt)};
cq.multiselect(selectionExpression).where(predicates.toArray(new Predicate[]{})).groupBy(groupByExpression).where(predicates.toArray(new Predicate[]{}));
List<PaymentInstructions> paymentInstructions = entityManager.createQuery(cq).getResultList();
In your Entity class that represents the 'payment_transactions' table, add the following:
#SqlResultSetMapping(
name = "PaymentTransaction.summaryMapping",
classes = {
#ConstructorResult(targetClass = PaymentTransactionSummary.class,
columns = {
#ColumnResult(name = "cdate")
, #ColumnResult(name = "amount")
, #ColumnResult(name = "nooftransaction")
})
}
)
Create a new pojo class named PaymentTransactionSummary (must match the name used above, or whatever name you choose, with member fields cdate, amount, and nooftransaction. Include a constructor that includes those three fields in the order listed above.
Then in your dao class, write this:
Query q = entityManager.createNativeQuery("your query string from above"
, "PaymentTransaction.summaryMapping");
List<PaymentTransactionSummary> results = q.getResultList();

JPA-Query for a Date in a Datetime column

I'm trying to build a query for all orders which were created today.
My Order-Entity has a datetime field like this:
#Column(name = "OrderCreationDate")
#Temporal(TemporalType.TIMESTAMP)
private Date orderCreationDate;
Named-Query:
#NamedQuery(name = "OrderHeader.findByOrderCreationDate", query = "SELECT o FROM OrderHeader o WHERE o.orderCreationDate = :orderCreationDate")
I tried to build the query like this:
public List<OrderHeader> findFromToday() {
Date dateToday = new Date();
TypedQuery<OrderHeader> query = em.createNamedQuery("OrderHeader.findByOrderCreationDate", OrderHeader.class).setParameter("orderCreationDate", dateToday);
return query.getResultList();
}
Of course the ResultList is empty since the date AND time would have to match.
Unfortunately I need the time in my database, so the orderCreationDate needs to stay datetime/timestamp.
So how can I query for a specific date, ignoring the time?
thanks!
Your call to setParameter needs to pass in the temporal type argument also, defining what to use for comparison.
http://www.datanucleus.org/javadocs/javax.persistence/2.1/javax/persistence/Query.html#setParameter-java.lang.String-java.util.Date-javax.persistence.TemporalType-

How to write JPA Query to the equivalent mysql query

I want to write equivalent JPA query to the following mysql query
select active_package,sum(duration),sum(charge),count(*)
from User
where call_type="MO"
and start_date between '2012-02-01' and '2012-02-09'
group by active_package;
For JPA Query the corresponding Attributes are below.
activePackage,callType,duration,charge,startDate
Entity class is User.
I want to use the createQuery() of JPA.
Can any one tell me or give me the link where can i find the solution for this.
Try this one, it should work, if not, please comment, we will get it work :).
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createQuery(Tuple.class);
Root<User> entity = cq.from(User.class);
cq.multiselect(entity.get("activePackage"),
cb.sum(entity.get("duration").as(Long.class)),
cb.sum(entity.get("charge").as(Double.class),
cb.count(entity).as(Long.class)));
cq.where(cb.equal(entity.get("callType"), "MO"),
cb.between(entity.get("startDate").as(Date.class),
new Date(), new Date()));
cq.groupBy(entity.get("activePackage"));
List<Tuple> resultList = entityManager.createQuery(cq).getResultList();
for (Tuple result : resultList) {
System.out.println(result.get(0) + " " + result.get(1)
+ " " + result.get(2) + " " + result.get(3));
}
Also if you want to filter only by date, but have timestamp in your model, you can check this Compare date's date part only with Timestamp in Hibernate answer.
Also JPA provides constructing result classes as return values, so you can group your columns. Read more.

Entity Framework - Select * from Entities where Id = (select max(Id) from Entities)

I have an entity set called Entities which has a field Name and a field Version. I wish to return the object having the highest version for the selected Name.
SQL wise I'd go
Select *
from table
where name = 'name' and version = (select max(version)
from table
where name = 'name')
Or something similar. Not sure how to achieve that with EF. I'm trying to use CreateQuery<> with a textual representation of the query if that helps.
Thanks
EDIT:
Here's a working version using two queries. Not what I want, seems very inefficient.
var container = new TheModelContainer();
var query = container.CreateQuery<SimpleEntity>(
"SELECT VALUE i FROM SimpleEntities AS i WHERE i.Name = 'Test' ORDER BY i.Version desc");
var entity = query.Execute(MergeOption.OverwriteChanges).FirstOrDefault();
query =
container.CreateQuery<SimpleEntity>(
"SELECT VALUE i FROM SimpleEntities AS i WHERE i.Name = 'Test' AND i.Version =" + entity.Version);
var entity2 = query.Execute(MergeOption.OverwriteChanges);
Console.WriteLine(entity2.GetType().ToString());
Can you try something like this?
using(var container = new TheModelContainer())
{
string maxEntityName = container.Entities.Max(e => e.Name);
Entity maxEntity = container.Entities
.Where(e => e.Name == maxEntityName)
.FirstOrDefault();
}
That would select the maximum value for Name from the Entities set first, and then grab the entity from the entity set that matches that name.
I think from a simplicity point of view, this should be same result but faster as does not require two round trips through EF to sql server, you always want to execute query as few times as possible for latency, as the Id field is primary key and indexed, should be performant
using(var db = new DataContext())
{
var maxEntity = db.Entities.OrderByDecending(x=>x.Id).FirstOrDefault()
}
Should be equivalent of sql query
SELECT TOP 1 * FROM Entities Order By id desc
so to include search term
string predicate = "name";
using(var db = new DataContext())
{
var maxEntity = db.Entities
.Where(x=>x.Name == predicate)
.OrderByDecending(x=>x.Id)
.FirstOrDefault()
}
I think something like this..?
var maxVersion = (from t in table
where t.name == "name"
orderby t.version descending
select t.version).FirstOrDefault();
var star = from t in table
where t.name == "name" &&
t.version == maxVersion
select t;
Or, as one statement:
var star = from t in table
let maxVersion = (
from v in table
where v.name == "name"
orderby v.version descending
select v.version).FirstOrDefault()
where t.name == "name" && t.version == maxVersion
select t;
this is the easiest way to get max
using (MyDBEntities db = new MyDBEntities())
{
var maxReservationID = _db .LD_Customer.Select(r => r.CustomerID).Max();
}

A LINQ extension that I wrote will not work with Entity Framework

I have an extension method defined like so:
public static TSource MaxBy<TSource, TResult>(this IEnumerable<TSource> collection, Func<TSource, TResult> func) where TSource : class
{
var comparer = Comparer<TSource>.Default;
TSource maxItem = null;
foreach (var item in collection)
{
if (comparer.Compare(item, maxItem) > 0)
maxItem = item;
}
return maxItem;
}
which I then use in the following LINQ-to-Entities query:
var balancesQuery = from t in db.Transactions
where t.UserId == userId
group t by t.CurrencyCode into tg
let tMaxDate = tg.MaxBy(i => i.TsCreate)
join c in db.Currencies on tg.Key equals c.CurrencyCode
select new { Currency = c, Balance = tMaxDate.Balance }
So what I'm doing is - get the newest transaction (MaxBy TsCreate) in each currency (group by CurrencyCode) and then select the balance against each of those transactions.
My problem is - this does not work with Entity Framework (LINQ-to-Entities; I get:
LINQ to Entities does not recognize the method 'Transaction MaxBy[Transaction,DateTime](System.Collections.Generic.IEnumerable'[Transaction], System.Func'2[Transaction,System.DateTime])' method, and this method cannot be translated into a store expression.
The same query works with LINQ to SQL.
My questions are:
Is there a way to make it work with Entity Framework?
Or maybe there is a better way of querying for the same information, which would work with Entity Framework?
Thanks in advance for any help!
Isn't the query same as:
var balancesQuery = from t in db.Transactions
where t.UserId == userId
group t by t.CurrencyCode into tg
join c in db.Currencies on tg.Key equals c.CurrencyCode
select new {
Currency = c,
Balance = tg.OrderByDescending(i => i.TsCreate).Take(1).Balance
};
You can't use extensions with LINQ queries that actually pull data from the database as it's impossible for this to be turned into SQL. However, you can do this by returning the results into memory using ToArray() or ToList() to trigger the database query executing and then call your extension functions on resultant real data in memory.