Is there best solution for get entity from PostgreSQL - postgresql

I have two entities
Author entity with atributes Id, firstName, secondName, lastName and transiet attribute numberBooks
Book entity with attirbutes Id, name and authorIds which is array of uuid
Question:
Is there best solution to get list of authors with numberBooks. NumberBooks need calculate from book entity
My solution is
//Author service
fun getListAuthors(): List<Author> {
val result = repository.findAllByDeletedAtIsNull()
setNumberBooksToAuthors(result)
return result
}
private fun setNumberBooksToAuthors(authors: List<Author>) {
val authorNumberBooks = repository.getAuthorsByDeletedAtIsNull(authors.map { it.id!! }.toList())
.map {
UUID.fromString(it["id"]) to Integer.parseInt(it["count"])
}.toMap()
for (author in authors) {
if (authorNumberBooks.containsKey(author.id)) {
author.numberBooks = authorNumberBooks[author.id]!!
}
}
}
//Author repository
#Query(
"SELECT CAST(author.id as varchar), CAST((SELECT COUNT(*) FROM books as b " +
"WHERE author.id = ANY(b.author_ids) AND b.deleted_at IS NULL) as varchar) " +
"from book_authors as author " +
"WHERE author.deleted_at IS NULL AND author.id in :authorIds", nativeQuery = true
)
fun getAuthorsByDeletedAtIsNull(authorIds: List<UUID>): MutableList<MutableMap<String, String>>

Related

How to make native query for nested projection in Spring Data JPA

I need to write a native query for my projection with nested interfaces.
My TransactionView interface:
public interface TransactionView {
Long getId();
TransactionType getType();
LocalDate getDate();
AccountProjection getAcc1();
AccountProjection getAcc2();
interface AccountProjection {
String getName();
CurrencyName getCurrencyCode();
BigDecimal getBalance();
}
BigDecimal getAmount();
PartnerView getPartner();
interface PartnerView {
String getName();
}
String getComment();
CategoryView getCategory();
interface CategoryView {
String getName();
}
}
JpaRepository:
public interface TransactionsRepository extends JpaRepository<Transaction, Long> {
List<TransactionView> findByAcc1PersonIdOrderByDateDesc(int personId);
}
This approach works good and I get JSON like this:
[{
"id":34,
"type":"TRANSFER",
"comment":"test comment",
"date":"2022-12-23",
"amount":200.00,
"acc2":
{
"name":"cash",
"currencyCode":"USD",
"balance":200.00
},
"acc1":
{
"name":"test acc",
"currencyCode":"USD",
"balance":700.00
},
"partner":null,
"category":null
},
{
"id":20,
"type":"EXPENCE",
"comment":"",
"date":"2022-12-13",
"amount":33.07,
"acc2":null,
"acc1":
{
"name":"cash",
"currencyCode":"BYN",
"balance":322.33
},
"partner":
{
"name":"bmw"
},
"category":
{
"name":"auto"
}
}]
But Hibernate generates a very complex query with a lot of extra columns fetching.
My native query returns null nested objects:
#Query(value = "SELECT t.id AS id, " +
"t.transaction_type AS type, " +
"t.transaction_date AS date, " +
"t.amount AS amount, " +
"t.comment AS comment, " +
"a1.balance AS acc1Balance, " +
"a1.currency_code AS acc1CurrencyCode, " +
"a1.name AS acc1Name, " +
"a2.balance AS acc2Balance, " +
"a2.currency_code AS acc2CurrencyCode, " +
"a2.name AS acc2Name, " +
"par.name AS partnerName, " +
"cat.name AS categoryName, " +
"cat.category_type AS categoryType " +
"FROM transaction t " +
"LEFT OUTER JOIN account a1 ON t.acc1_id=a1.id " +
"LEFT OUTER JOIN person per ON a1.person_id=per.id " +
"LEFT OUTER JOIN account a2 ON t.acc2_id=a2.id " +
"LEFT OUTER JOIN partner par ON t.partner_id=par.id " +
"LEFT OUTER JOIN category cat ON t.category_id=cat.id " +
"WHERE per.id=?1 ORDER BY t.transaction_date DESC", nativeQuery = true)
List<TransactionView> findByAcc1PersonIdOrderByDateDescTest(int personId);
[{
"id":34,
"type":"TRANSFER",
"comment":"test comment",
"date":"2022-12-23",
"amount":200.00,
"acc2":null,
"acc1":null,
"partner":null,
"category":null
},
{
"id":20,
"type":"EXPENCE",
"comment":"",
"date":"2022-12-13",
"amount":33.07,
"acc2":null,
"acc1":null,
"partner":null,
"category":null
}]
Also I tried approach from Peter Gyschuk, but it doesn't work.
How can I solve it using native query?

I have created a employee id tabview list i have to change it to dictionary

First image
i have used list with mvvm but my seniors told me use
dict<string, Employee> =
{
{"Employee 1" ,{id, emailid , name , father name , employee id , adddress}}
{"Employee 2" ,{id, emailid , name , father name , employee id , adddress}}
{"Employee 3" ,{id, emailid , name , father name , employee id , adddress}}
{"Employee 4" ,{id, emailid , name , father name , employee id , adddress}}
{"Employee 5" ,{id, emailid , name , father name , employee id , adddress}}
}
i am not able to change it to dictionary can anyone do it or give me some reference
Dictionaries are key-value collections. For every item, you have to specify a unique id.
For example:
var EmployeesDictionary = new Dictionary<string, Employee>();
EmployeesDictionary["empId"] = new Employee();
In your case you could populate your collection like that:
private record Employee(
int Id,
string EmpTab,
string EmailId,
string Name,
string FatherName,
string EmpId,
string Address);
private void LoadEmployees()
{
var EmployeesDictionary = new Dictionary<int, Employee>();
for (int i = 0; i < 5; i++)
{
EmployeesDictionary.Add(i, new Employee(
Id: i,
EmpTab: "EmptTab",
EmailId: "Email",
Name: "Name",
FatherName: "FatherName",
EmpId: "EmpId",
Address: "Address"));
}
}
You also could convert your list using LINQ:
private record Employee(
int Id,
string EmpTab,
string EmailId,
string Name,
string FatherName,
string EmpId,
string Address);
private void LoadEmplEmployees()
{
var EmployeesDictionary = new List<Employee>();
for (int i = 0; i < 5; i++)
{
EmployeesDictionary.Add(new Employee(
Id: i,
EmpTab: "EmptTab",
EmailId: "Email",
Name: "Name",
FatherName: "FatherName",
EmpId: "EmpId",
Address: "Address"));
}
EmployeesDictionary.ToDictionary(e => e.Id, e => e);
}
Dictionary Reference
LINQ ToDictionary Reference

How to write a generic LINQ query for filtering of data based on first name middle name and last name

I have one table which contains employees records like first , middle, last name. I have 3 textboxes for entering all these.
Now on .net side I want to write a single LINQ query to filter data based on first name middle name and last name. Any of these 3 fields can be blank.
Is these any way to write a single generic LINQ query?
public IList<Employee> GetEmployees(string first, string middle, string last)
{
var query = context.Employees.AsQueryable();
if (!string.IsNullOrWhiteSpace(first))
{
query = query.Where(x => x.FirstName == first);
}
if (!string.IsNullOrWhiteSpace(middle))
{
query = query.Where(x => x.MiddleName == middle);
}
if (!string.IsNullOrWhiteSpace(last))
{
query = query.Where(x => x.LastName == last);
}
return query.Select(x =>
new Employee
{
FullName = string.Join(" ", new string[] { x.FirstName, x.MiddleName, x.LastName}.Where(y => !string.IsNullOrWhiteSpace(y)))
})
.ToList();
}
You can write one linq query with or condition like bellow
_context.tablename.where(p=>p.firstName.contains(txtFirstName) || p.middleName.contains(txtMiddleName) || p.lastName.contains(txtLastName)).ToList();
Change tablename with you database table in above linq query
when iterating just verify if the criteria in use has value
var criteria = new
{
FirstName = default(string),
MiddleName = default(string),
LastName = "Doe",
};
var query = from record in Context()
where !string.IsNullOrEmpty(criteria.FirstName)
&& record.FirstName == criteria.FirstName
|| !string.IsNullOrEmpty(criteria.MiddleName)
&& record.MiddleName == criteria.MiddleName
|| !string.IsNullOrEmpty(criteria.LastName)
&& record.LastName == criteria.LastName
select record;
Try something like that;
public List<Employee> RetrieveEmployees(string firstName, string lastName, string middleName)
{
var query = from employees in context.Employees
where (string.IsNullOrEmpty(firstName) || employees.FirstName == firstName) &&
(string.IsNullOrEmpty(lastName) || employees.LastName == lastName) &&
(string.IsNullOrEmpty(middleName) || employees.MiddleName == middleName)
select employees;
return query.ToList();
}

An anonymous type cannot have multiple properties with the same name

I want to bind gridview through entity framework but it throws error
like-
An anonymous type cannot have multiple properties with the same name Entity Framwrok
Here is my method.
public void UserList(GridView grdUserList)
{
using (TreDbEntities context = new TreDbEntities())
{
var query =( from m in context.aspnet_Membership
from u in context.aspnet_Users
join usr in context.Users
on new { m.UserId, u.UserId }
equals new { usr.MembershipUserID, usr.UserId }
into UserDetails
from usr in UserDetails
select new {
CreationDate = m.CreateDate,
email = m.Email,
UserName = u.LoweredUserName,
Name = usr.FirstName + usr.LastNameLastName,
Active=usr.IsActive
}).ToList();
}
}
It shows error here. usr.UserId.
The direct issue is in the anonymous type new { m.UserId, u.UserId }: the same name twice. You can fix that by giving explicit property names, for example: new { u1 = m.UserId, u2 = u.UserId }.
But then the next issue will be that both anonymous types that define the join will not have the same property names, so the final fix is this:
public void UserList(GridView grdUserList)
{
using (TreDbEntities context = new TreDbEntities())
{
var query =( from m in context.aspnet_Membership
from u in context.aspnet_Users
join usr in context.Users
on new { u1 = m.UserId, u2 = u.UserId }
equals new { u1 = usr.MembershipUserID, u2 = usr.UserId }
into UserDetails
from usr in UserDetails
select new { CreationDate = m.CreateDate,
email = m.Email,
UserName = u.LoweredUserName,
Name = usr.FirstName + " " + usr.LastName,
Active = usr.IsActive
}
).ToList();
}
}
#Gert answer is correct. Just want to show simpler solution - give name only to first UserId property:
on new { MembershipUserID = m.UserId, u.UserId }

Convert datetime to a formatted string inside a LINQ-to-entities query

How can I convert DateTime into a formatted string?
This is the line in the following query that needs help:
StartDate = string.Format("{0:dd.MM.yy}", p.StartDate)
The whole query:
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new Offer()
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer = value,
ID = p.ID,
LocationID = q.BranchID,
LocationName = q.CustomerBranch.BranchName,
OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate = string.Format("{0:dd.MM.yy}", p.StartDate)
}).First();
I get the following error message:
LINQ to Entities does not recognize the method 'System.String ToString(System.String)' method, and this method cannot be translated into a store expression.
Another option is using SqlFunctions.DateName, your code will be like this:
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer = value,
ID = p.ID,
LocationID = q.BranchID,
LocationName = q.CustomerBranch.BranchName,
OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate = SqlFunctions.DateName("day", p.StartDate) + "/" + SqlFunctions.DateName("month", p.StartDate) + "/" + SqlFunctions.DateName("year", p.StartDate)
})
I found it useful if you don't want to add an extra select new block.
EDIT: Now that I understand the question, I'm giving it another shot :)
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer=value,
ID=p.ID,
LocationID=q.BranchID,
LocationName=q.CustomerBranch.BranchName,
OriginalPrice=SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice=SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate=p.StartDate
})
.ToList()
.Select(x => new Offer()
{
Title = x.OfferTitle,
Description = x.Description,
BestOffer=value,
ID=x.ID,
LocationID=x.BranchID,
LocationName=x.CustomerBranch.BranchName,
OriginalPrice=x.OriginalPrice,
NewPrice=x.NewPrice,
StartDate=x.StartDate.ToString("dd.MM.yy")
}).First();
I know it's a bit long, but that's the problem with Linq To SQL.
When you use linq, the database call isn't executed until you use something such as ToList() or First() that results in actual objects. Once that SQL call is executed by the first .First() call, you're now working with .NET types, and can use DateTime stuff.
I ended up using the sql function FORMAT; here's a simplified version of this implementation:
https://weblogs.asp.net/ricardoperes/registering-sql-server-built-in-functions-to-entity-framework-code-first
First you need to define the function in EF:
public class FormatFunctionConvention : IStoreModelConvention<EdmModel>
{
public void Apply(EdmModel item, DbModel model)
{
var payload = new EdmFunctionPayload
{
StoreFunctionName = "FORMAT",
Parameters = new[] {
FunctionParameter.Create("value", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.DateTime), ParameterMode.In),
FunctionParameter.Create("format", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.In)
},
ReturnParameters = new[] {
FunctionParameter.Create("result", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.ReturnValue)
},
Schema = "dbo",
IsBuiltIn = true
};
item.AddItem(EdmFunction.Create("FORMAT", "CodeFirstDatabaseSchema", item.DataSpace, payload, null));
}
}
Then define it as C# methods:
public static class SqlFunctions
{
[DbFunction("CodeFirstDatabaseSchema", "FORMAT")]
public static String Format(this DateTime value, string format)
{
return value.ToString(format);
}
[DbFunction("CodeFirstDatabaseSchema", "FORMAT")]
public static String Format(this DateTime? value, string format)
{
return value?.ToString(format);
}
}
Register it in your DbContext:
public class SqlDb : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Add(new FormatFunctionConvention());
}
}
And finally, you can call it like so:
var x = db.MyItems.Select(i => new { FormattedDate = SqlFunctions.Format(i.MyDate, "MM/dd/yyyy") }).ToArray();
That is what we did, we added a new function to the class and we query the date as normal in the query:
[ComplexType]
public class Offer
{
public DateTime StartDate
{
get;
set;
}
public String Title
{
get;
set;
}
/*Other fields*/
.
.
.
public string FormattedDate(string format)
{
return Date.ToString(format);
}
}
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new Offer()
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer = value,
ID = p.ID,
LocationID = q.BranchID,
LocationName = q.CustomerBranch.BranchName,
OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate = p.StartDate
}).First();
Then you can just call the FormattedDate field passing the desired format.
edit1.Text = offer.FormattedDate("dd.MM.yy");
Or can can define it as a field with just the getter:
public string FormattedDate
{
get { return Date.ToString("dd.MM.yy") };
}
edit1.Text = offer.FormattedDate;
In case you class is an Entity, you need to declare a new partial of that class and add the field.
Hope this help someone.
In vb (valid to c# too changing syntax):
Imports System.Data.Entity
...
query.Select(Function(x) New MyObject With {
...
.DateString = DbFunctions.Right("00" & x.DateField.Day, 2) & "/" & DbFunctions.Right("00" & x.DateField.Month, 2) & "/" & x.DateField.Year
...
}).ToList()
Note: ToList(), ToEnumerable() are not the way because its executes a query, the user wants linq to sql..
The easiest and most efficient way I have found to do string formats on numeric or datetime objects is by using string interpolation. It will bring back the actual DateTime/int/float/double/etc.. objects in the SQL query, and then client side it will do the string format during projection. I modified your query below, note how OriginalPrice, NewPrice, and StartDate are converted:
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new Offer()
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer = value,
ID = p.ID,
LocationID = q.BranchID,
LocationName = q.CustomerBranch.BranchName,
OriginalPrice = $"{p.OriginalPrice:C2}",
NewPrice = $"{p.NewPrice:C2}",
StartDate = $"{p.StartDate:dd.MM.yy}"
}).First();
if it's a datetime you need to use the .ToShortDateString(). But you also need to declare it AsEnumerable().
var offer = (from p in dc.CustomerOffer.AsEnumerable()
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer=value,
ID=p.ID,
LocationID=q.BranchID,
LocationName=q.CustomerBranch.BranchName,
OriginalPrice=SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice=SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate=p.StartDate
})
.ToList()
.Select(x => new Offer()
{
Title = x.OfferTitle,
Description = x.Description,
BestOffer=value,
ID=x.ID,
LocationID=x.BranchID,
LocationName=x.CustomerBranch.BranchName,
OriginalPrice=x.OriginalPrice,
NewPrice=x.NewPrice,
StartDate=x.StartDate.ToShortDateString()
}).First();