Construct JPA query for a OneToMany relation - jpa

I've those 2 entities
Class A {
#OneToMany(mappedBy="a")
private List<B> bs;
}
Class B {
#ManyToOne
private A a;
private String name;
}
1) I would like to construct a query that says get all A's that have at least one B with name ="mohamede1945"
2) I would like to construct a query that says get all A's that don't have any B with name = "mohamede1945"
Could anyone help me?

First of all, I think you can learn the answer by looking at this link and search for JOIN: http://download.oracle.com/docs/cd/E11035_01/kodo41/full/html/ejb3_langref.html
Second of all, here is my approach:
#Entity
#NamedQueries({
#NamedQuery(name="A.hasBName",query="SELECT a FROM A a JOIN a.b b WHERE b.name = :name"),
#NamedQuery(name="A.dontHasBName",query="SELECT a FROM A a JOIN a.b b WHERE b.name <> :name")
})
Class A { /* as you defined */ }
In you DAO, you can make the namedquery like this:
public List<A> findByHasBName( String name ){
Query q = em.createNamedQuery("A.hasBName")
.setParameter("name", name);
try{
return ( (List<A>) q.getResultList());
} catch ( IndexOutOfBoundsException e){
return null;
}
}

You can use the ANY and ALL constructs to filter the subquery. So something like
1. FROM A aEntity WHERE 'mohamede1945' = ANY (SELECT bEntity.name FROM aEntity.bs bEntity)
2. FROM A aEntity WHERE 'mohamede1945' <> ALL (SELECT bEntity.name FROM aEntity.bs bEntity)

Related

Entity Framework Core generates wrong query for MySQL when I use separate IQueryable generic method

I faced with a strange behaviour when entity framework core throwing away my Includes.
I need to make some generic methods to filter and combine my queries and those methods must receive some parameters and IQueryable filter subquery and return combined IQueryable result for further composition.
I simplified my code and made an example when you can see what I do mean:
public IQueryable<Tuple<TResult, TFilter>> Method1<TResult, TFilter>(IQueryable<TFilter> filters)
where TResult : ResultEntity
where TFilter : FilterEntity
{
var q = from state in _dbContext.Set<TResult>()
join f in filters on state.ID_Result equals f.ID
where ....
select new Tuple<TResult, TFilter>(state, f);
return q;
}
public void GetResult(int pageIndex, int pageSize)
{
IQueryable<Car> results = _dbContext.Cars.Include(x => x.Images);
// 1) All right, it has the images
var q1 = Method1<CarState, Car>(results).ToList();
// 2) Wrong, there is no images. And SQL query doesn't contain any joins to Images table
var q2 = Method1<CarState, Car>(results).Skip(pageIndex).Take(pageSize).ToList();
// 3) Without the method and with anonymous type it's ok.
var tmp = from state in _dbContext.Set<CarState>()
join f in results on state.ID_Result equals f.ID
where ....
select new { state, f };
var q3 = tmp.Skip(pageIndex).Take(pageSize).ToList();
}
What did I do wrong?
Do not use Builder methods or constructors if you want to use query later. It is because LINQ translator cannot trace back fields to generate correct SQL.
This part of query is problematic
new Tuple<TResult, TFilter>(state, f);
Better to create new class(es)
public class MTuple<T1, T2>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
And use them in your methods (similar to anonymous classes usage)
public IQueryable<MTuple<TResult, TFilter>> Method1<TResult, TFilter>(IQueryable<TFilter> filters)
where TResult : ResultEntity
where TFilter : FilterEntity
{
var q = from state in _dbContext.Set<TResult>()
join f in filters on state.ID_Result equals f.ID
where ....
select new MTuple<TResult, TFilter>
{
Item1 = state,
Item2 = f
};
return q;
}
SUGGESTION
Such classes can be generated by T4 template, or just copy generated code

How to use enum in #Query as a constant

I have tried to put a full class path (com.xxxx.State.Finish) after != but not helping.
#Query("select c from CustomOrder c where c.dealer = :roleName and
c.nextManager = null and c.currentState != Finish")
List<CustomOrder> findOpenOrder(#Param("roleName") String roleName);
Entity:
#Getter
#Enumerated(EnumType.STRING)
CustomOrderEnums.State currentState;
Enum:
public enum State {
Open, Finish
}
#Query("select c from CustomOrder c where c.dealer = :roleName and
c.nextManager = null and c.currentState != com.xxx.FooEnum.Finish")
FooEnum has to be a top class not an inner one. If it has to be an inner class, use ' quoted string (haven't tried it without ').
#Query("select c from CustomOrder c where c.dealer = :roleName and
c.nextManager = null and c.currentState != 'Finish'")
I have just found that instead of using #Query it could be simply used as:
List<User> findIdByRoleRoleAndProvinceType(String role, ProvinceEnum.ProvinceType provinceType);
and this is entity User:
#Entity
public class User {
Role role; // entity has a String field role;
Province province; // entity has a ProvinceEnum.ProvinceType field type.
...
}

Cast Entity inside a query

I'm looking for a way to cast an entity inside a jpql query.
Example:
#Entity
class Topic {
#OneToMany
List<AbstractMessage> messages;
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
abstract class AbstractMessage {
String content;
}
#Entity
class MessageType1 extends AbstractMessage {
String att1;
}
#Entity
class MessageType2 extends AbstractMessage {
Integer att2;
}
I'm trying to collect all Topic where one or more of its messages have the type MessageType2 and have att2 = 1.
Here is my suggestion as a jpql query:
select t from Topic t left outer join t.messages on (Type(t) = MessageType2)
where t.att2 = 1
I don't think this query works because JPA doesn't join the MessageType2 table.
Is there a way to do that or I have to make a native query?
Thanks!
You can simulate the CAST:
http://en.wikibooks.org/wiki/Java_Persistence/Querying#Joining.2C_querying_on_a_OneToMany_relationship
SELECT t FROM Topic t JOIN t.messages m, MessageType2 y WHERE m = y AND y.att2 = 1
Cleaner solution would be to use JPA 2.1 TREAT:
SELECT t FROM Topic t JOIN TREAT(t.messages AS MessageType2) m WHERE m.att2 = 1
http://hantsy.blogspot.cz/2013/12/jpa-21-treat.html

Best practices using JPA to load object from query with a list member

I'm getting started with JPA and I want to know the best way to achieve something like this:
I need to implement a service that returns a list of scripts, and each script has a list of parameters. I simplified the query, but it is something like this:
(SELECT
p.DESC
FROM
INPUT_PARAMETERS p
INNER JOIN SCRIPT_PARAMS sp ON p.PARAM_ID = sc.PARAM_I
INNER JOIN SCRIPT s ON s.SCRIPT_ID = sc.SCRIPT_ID
WHERE
s.NAME = 'name')
UNION
(SELECT
p.DESC
FROM
OUPUT_PARAMETERS p
INNER JOIN SCRIPT_PARAMS sp ON p.PARAM_ID = sc.PARAM_I
INNER JOIN SCRIPT s ON s.SCRIPT_ID = sc.SCRIPT_ID
WHERE
s.NAME = 'name')
And I want to return a list of POJO objects that are something like:
public class Script {
private String name;
private List<String> params;
public Script(){}
public String getName()
{
return name;
}
public void setName(String pName)
{
name = pName;
}
public List<String> getParams()
{
return params;
}
public void setParams(List<String> pParams)
{
params = pParams;
}
}
I want to know what is the best way to load the POJO object from a query. Is it best to build a JPQL query, or can I use a Native Named Query, and do I need to get a object[] and construct my POJOs manually, or can I use JPA to load the objects from the query?
You can only construct instances of Script manually. When querying with JPQL, reason is that constructor expression cannot take List as argument. Additionally JPQL does not have unions.
Also with SQL query you cannot construct Script directly, because result can be only scalars or entity.

Extending with filter queries with entity framework

I have 3 tables in my testdatabase, Customers <-> CustomersUsergroups <-> Usergroups
For customers I have a method that returns all Usergroups like this:
public IQueryable<Usergroup> GetUsergroups()
{
return from ug in _entities.UsergroupSet.Include("Customer")
select ug;
}
And to that I have a "filter-class" for class Usergroup
public static IQueryable<Usergroup> ByUsergroupID(this IQueryable<Usergroup> qry, int usergroupID)
{
return from ug in qry
where ug.UsergroupID == usergroupID
select ug;
}
so when I type:
return _repository.GetUsergroups().ByUsergroupID(usergroupID);
it works great, but the problem now is that I would like to extend so I can filter by CustomerID aswell, sort of like:
public static IQueryable<Customer> ByCustomerID(this IQueryable<Customer> qry, int customerID)
{
return from c in qry
where c.CustomerID == customerID
select c;
}
so I can use it like:
return _repository.GetUsergroups().ByCustomerID(customerID);
is that possible to do with just using "Include"? or isnt it an easy way to fix that since they are different classes?
Thanks in advance
/M
I'm not sure if I understand problem the way you described, but it is normal that you can't use method ByCustomerID because you defined extension on IQueryable<Customer> not on IQueryable<UserGroup>.
If you want to define it on IQueryable<UserGroup> and if you intended to get UserGroups assigned to one Customer that can be done like this:
public static IQueryable<UserGroup> ByCustomerID(this IQueryable<UserGroup> qry, int customerID)
{
return from ug in qry
from c in ug.Customer
where c.CustomerID == customerID
select ug;
}