hibernate - Dynamic sort in a query - spring

I have a Dao method that returns list of "posts" and looks like this:
public List<PostDTO> getPosts() {
Session session = sessionFactory.getCurrentSession();
return postList = session
.createQuery("select new com.poster.app.dto.PostDTO(p.id, p.date, p.title, p.text, p.imageUrl, p.author, p.category, count(c.post.id)) "
+ "from Post as p left join Comment as c ON p.id = c.post.id group by p.id",
PostDTO.class).getResultList();
}
So it basically just creates query and returns the dto's in that case. The thing is, i need to fetch exact same list BUT with different sorting. like i need to sort it dynamically by "newest", "most popular" and by "comments number" and i want to do this in one method instead of creating 3 methods for each ("newest", "most popular" and by "comments number"), how can i do that in hibernate?

You have choice between :
Use api criteria to build your query and add dynamically your order by clause.
Add the order by clause at the end of your query depending on your param
ex with 2nd option:
public List<PostDTO> getPostsOrderBy(String orderParam)
{
Session session = sessionFactory.getCurrentSession();
String query = "select new com.poster.app.dto.PostDTO(p.id, p.date, p.title, p.text, p.imageUrl, p.author, p.category, count(c.post.id)) "
+ "from Post as p left join Comment as c ON p.id = c.post.id group by p.id order by "+ orderParam;
return postList = session.createQuery(query,PostDTO.class).getResultList();
}

Related

Spring JPA projection - selecting specific columns in nested object list

Why I get the following error:
query specified join fetching, but the owner of the fetched
association was not present in the select list
when I try to get List of ids?
#Query("SELECT at.id FROM Template at " +
"WHERE at.shipper.id = :companyId " +
"AND at.isActive = true")
#EntityGraph(attributePaths = {"shipper"})
List<Long> findTemplateIdsByCompanyId2(Long companyId, Pageable pageable);
but when I want to get list of objects - everything is OK?
#Query("SELECT at FROM Template at " +
"WHERE at.shipper.id = :companyId " +
"AND at.isActive = true")
#EntityGraph(attributePaths = {"shipper"})
List<Template > findTemplateIdsByCompanyId2(Long companyId, Pageable pageable);
Template entity has OneToOne relationship with shipper field and OneToMany relationship with warehouse field
You need to join in the query if you are not fetching the entity, something like this should do it:
SELECT at.id FROM Template at JOIN at.Shipper s WHERE s.id = :companyId and at.isActive = true

Sub query in linq to sql

I want to return the comma seperated of the sub query result in my sql command.
As follow:
Select [User].UserName,
(Select [Role].Name + ', '
From
Security.UserRole As UserRole
Inner Join
Security.[Role] As [Role]
On UserRole.RoleId = [Role].RoleId
Where UserRole.UserId = [User].UserId
For Xml Path('')
)
From Security.[User] As [User]
Basically each user has multiple roles, and I want to load the roles names in comma separated format for each user.
The result set in Sql is like :
Is there any way I can write this query in LINQ ?
Thanks
Try Code
Create a Method Get User roles Particular User
Public String GetuserRoles(int userid)
{
string strroles="";
(from UserRole in Security.UserRole
join Role in Security.Role on UserRole.RoleId equals Role.RoleId
where UserRole.UserId==userid
select new
{
strs = Role.Name + ","
}).ToList().ForEach(c => strroles = strroles + c.strs);
return strroles;
}
After Write Your Linq Query:
var result=( from p in Security.User select new {
UserName=p.UserName,
Roles=GetuserRoles(p.UserId)
}).ToList();

Automapper with linq how?

Ok, I'm really struggling with finding a good example of what I need to do. So, I'll ask here.
Let's say I have a entity class (EF) named Customer and a corresponding view-model class named CustomerViewModel.
Using AutoMapper, I have created the following mappings:
Mapper.CreateMap<CustomerViewModel, Customer>();
Mapper.CreateMap<Customer, CustomerViewModel>();
How would I modify the following code to make use of this mapping?
public static List<CustomerViewModel> GetCustomers()
{
using (var context = new GCSBaseEntities())
{
var query = from c in context.Customers
select new CustomerViewModel
{
CompanyName = c.CompanyName,
Id = c.Id,
EmailAddress = c.EmailAddress,
FirstName = c.FirstName,
LastName = c.LastName,
MiddleName = c.MiddleName,
ModifiedDate = c.ModifiedDate,
Phone = c.Phone,
SalesPerson = c.SalesPerson,
Suffix = c.Suffix,
Title = c.Title,
FullName = c.FirstName + " " + c.LastName
};
return query.ToList();
}
}
Thanks in advance.
When you register your mappings, you must provide any complex mapping operations that have to occur. In your case, I believe all your properties match up, except for FullName = c.FirstName + " " + c.LastName. Here's how your Customer-to-CustomerViewModel mapping should look:
Mapper.CreateMap<Customer, CustomerViewModel>()
.ForMember(custVm => custVm.FullName,
mapper => mapper.MapFrom(cust => cust.FirstName + " " + cust.LastName));
You'll have to figure out how to shove the FullName prop from the ViewModel back into the FirstName & LastName fields on the EF class, though. But when you decide how to implement it, follow the pattern from above for the other mapping.
Your query can now be MUUUCH smaller:
using (var context = new GCSBaseEntities())
{
return from c in context.Customers
select Mapper.Map<CustomerViewModel>(c);
}
Figured it out. In order to avoid the aforementioned error, you have to Add the call the .AsEnumerable() after Customers like so:
return from c in context.Customers.AsEnumerable()
select Mapper.Map<CustomerViewModel>(c);
I got this from this thread: LINQ and AutoMapper

Entity Framework query: object not set to an instance of an object

When calling the query.ToList() I get
object reference not set to an instance of an object
For x.Gallons, all the orders have this value set, it's not null. Also, there are 2 DProducts in the database table with proper ID. What could be wrong?
ProductSummaryCollection.Clear();
var query = from p in Repository.repository.ctx.DProduct
join fo in Repository.repository.ctx.DFuelOrder.Include("DProduct")
on p.ID equals fo.DProductID
group fo by fo.DProduct into Prod
select new DProductSummary
{
Product = fo.DProduct,
TotalGallons = (float)Prod.Sum(x => x.Gallons)
};
try
{
IList<DProductSummary> ps = query.ToList();
foreach (DProductSummary dps in ps)
ProductSummaryCollection.Add(dps);
}
catch (Exception exc)
{
}
It seems that you can't do the following 2 things:
Create a entity object, DProduct in my case inside a linq query
You cannot access a reference in a linq query even if you Include it
So you have to use the join table.Propery instead.
A working query:
var query = from fo in Repository.repository.ctx.DFuelOrder.Include("DProduct")
join p in Repository.repository.ctx.DProduct
on fo.DProductID equals p.ID
group fo by new { fo.DProductID, p.Name } into Prod
select new DProductSummary
{
ProductName = Prod.Key.Name,
TotalGallons = (float)Prod.Sum(x => x.Gallons)
};

Spring pageable with specification add more LEFT JOIN on order by joined column

Can anybody explain to me why Spring adds LEFT JOIN checking on pageable Order using its specification feature?
Here is the case:
I want to use Spring specification to generate dynamic query for filtering data and pageable to limit the data. The query is between UserEntity and UserGroupEntity, and I want to always fetch the UserGroupEntity on every query, therefore I add a checking block for COUNT and data query. If the query is a query COUNT then I use the Join object, otherwise I use the Fetch object.
public static Specification<UserEntity> filtered(final String searchTerm) {
return new Specification<UserEntity>() {
#SuppressWarnings("unchecked")
#Override
public Predicate toPredicate(Root<UserEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Join<UserEntity, UserGroupEntity> joinUserGroup = null;
if (QueryHelper.isQueryCount(query)) {
joinUserGroup = root.join("userGroup");
} else {
Fetch<UserEntity, UserGroupEntity> fetchUserGroup = root.fetch("userGroup");
joinUserGroup = (Join<UserEntity, UserGroupEntity>) fetchUserGroup;
}
if (searchTerm != null) {
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(cb.like(root.get("lowerUsername").as(String.class), "%" + searchTerm.toLowerCase() + "%"));
predicates.add(cb.like(root.get("lowerEmail").as(String.class), "%" + searchTerm.toLowerCase() + "%"));
predicates.add(cb.like(joinUserGroup.get("lowerName").as(String.class), "%" + searchTerm.toLowerCase() + "%"));
cb.or(predicates.toArray(new Predicate[predicates.size()]));
}
return null;
}
};
}
This is how I call the specification on UserService:
Order order = new Order(Direction.ASC, "lowerEmail");
Page<UserEntity> pages = userRepository.findAll(filtered(searchTerm), new PageRequest(page, size, new Sort(order)));
The query is fine when I add an ordering using UserEntity column. Here is the generated HQL:
select generatedAlias0 from com.example.entity.UserEntity as generatedAlias0
inner join fetch generatedAlias0.userGroup as generatedAlias1
order by generatedAlias0.lowerEmail asc
But the query becomes strange (because I don't know why) when I use the the column from joined entity for ordering which is userGroup.lowerName. By using that column, Spring adds more LEFT JOIN in my query and makes it like this:
select generatedAlias0 from com.example.entity.UserEntity as generatedAlias0
left join generatedAlias0.userGroup as generatedAlias1
inner join fetch generatedAlias0.userGroup as generatedAlias2
order by generatedAlias1.lowerName asc
As I come across the spring-data-JPA code on Github, I found that the translation from specification to criteria is done in method getQuery(). This method calls other method toOrders() from class QueryUtils to apply the sort order. Method toOrders() finally calls method getOrCreateJoin() or isAlreadyFetched() which will check the the previous join attribute (the one I made in the specification) which is userGroup, but because the join type is not LEFT JOIN then Spring adds more join using LEFT JOIN in my query.
private static Join<?, ?> getOrCreateJoin(From<?, ?> from, String attribute) {
for (Join<?, ?> join : from.getJoins()) {
boolean sameName = join.getAttribute().getName().equals(attribute);
if (sameName && join.getJoinType().equals(JoinType.LEFT)) {
return join;
}
}
return from.join(attribute, JoinType.LEFT);
}
That's what I got from my search on the spring-data-jpa code, CMIIW. What is the purpose of the JoinType.LEFT actually I still don't understand. Your explanation will be very helpful for me (and us).
Now I think that I will use custom repository to generate dynamic query using JPQL until I understand the reason for that generated query with additional LEFT JOIN using specification and pageable.
I had the same issue, and found the solution and answer in https://stackoverflow.com/a/43965633/7280115. You may check if it also works for you

Resources