I have an entity 'Parent' which has a Set of enums
private Set<MyEnum> myEnums = EnumSet.noneOf(MyEnum.class);
#CollectionOfElements(targetElement=MyEnum.class)
#JoinTable
(name="PARENT_MY_ENUM",
joinColumns=#JoinColumn(name="PARENT_ID"))
#Enumerated(EnumType.STRING)
#Column (name="MY_ENUM", nullable=false)
public Set<MyEnum> getMyEnums(){
return myEnums;
}
public MyEnum {
ENUM_A,
ENUM_B,
ENUM_C,
ENUM_D;
}
Now I want to search for this entity with a collection of MyEnums. Only entities where all enums are set as in the search collection should be returned.
So if entity A has ENUM_A, ENUM_B and ENUM_C and entity B has ENUM_B, ENUM_C, ENUM_D a search with the search collectoin ENUM_A, ENUM_B, ENUM_C should only return entity A. A search for ENUM_B and ENUM_C should return nothing.
How would I do that in Hibernate?
if I do
select p from Parent p where p.myEnums IN (:searchCollection) and size(p.myEnums) = size(:searchCollection)
then this would return both entities for the first search.
Any ideas?
Update: I got a step further by figuring out how to do it in MySQL but applying this to Hibernate generates invalid SQL.
You would use a subquery with EXISTS for it like:
WHERE EXISTS(
SELECT pa.PARENT_ID, count(pme.MY_ENUM) FROM PARENT pa, PARENT_MY_ENUM pme
where pa.PARENT_ID = pme.PARENT_ID
AND pme.MY_ENUM IN ('ENUM_A','ENUM_B')
GROUP BY pa.PARENT_ID HAVING count(pme.MY_ENUM) = 2
)
But when I try to do the same in Hibernate:
select pa.ParentId, count(pa.myEnums) from Parent pa
WHERE pa.myEnums IN ('ENUM_A','ENUM_B')
GROUP BY pa.ParentId HAVING count(pa.myEnums) = 2
Hiberante creates this SQL statement:
select pa.CONTAINER_RELEASE_REFERENCE_ID as col_0_0_, count(.) as col_1_0_ from PARENT pa, PARENT_MY_ENUM enum1, PARENT_MY_ENUM enum2, PARENT_MY_ENUM enum3
where pa.PARENT_ID=enum1.PARENT_ID and pa.PARENT_ID=enum2.PARENT_ID and pa.PARENT_ID=enum3.PARENT_ID
and (. in ('ENUM_A' , 'ENUM_B'))
group by pa.PARENT_ID having count(.)=2
MySQL complains about the '.', Where is that coming from and why is Hibernate using 3 joins to PARENT_MY_ENUM?
Is this a Hibernate bug or what am I doing wrong?
Give the following a try for you exists subselect
select pa.ParentId, count(en) from Parent pa join pa.myEnums as en
WHERE en IN ('ENUM_A','ENUM_B')
GROUP BY pa.ParentId HAVING count(en) = 2
Otherwise, I wonder if something like that might not do the job
select p from Parent p join p.myEnums em
where (:results) = elements(em)
or
select p from Parent p join p.myEnums em
where (:results) in elements(em)
I think you can do that in java. Execute your initially proposed query, iterate the result and exclude (iterator.remove()) the false positives. It should be O(n), and I believe MySQL will require the same time to filter your result.
Related
I have three object types with corresponding tables:
class Order:
suborders = relationship('Suborder', lazy='dynamic')
class Suborder:
...
class PurchaseOrder:
suborder = relationship('Suborder', foreign_keys=[suborder_id], lazy='joined')
Now I need to get all PurchaseOrder instances matching Order's suborders. In pure SQL I'd write something like this:
SELECT po.*
FROM purchase_orders AS po JOIN suborders AS so ON po.suborder_id = so.id
WHERE so.order_id = 'order-007'
How do I do it using SQLAlchemy? I tried this:
o = Order.query.get('order-007')
PurchaseOrder.query.join(o.suborders)
But this gave me an error:
AttributeError: 'AppenderQuery' object has no attribute 'is_selectable'
What is the right way to do it?
Getting required data with just one query:
query = (
PurchaseOrder.query
.join(Suborder)
.filter(Suborder.order_id == 'order-007')
)
If you already have the Order instance o, you can do the following:
o = Order.query.get('order-007') # already have the order instance
query = (
session.query(PurchaseOrder)
.join(Suborder)
.with_parent(o)
)
, but you will still need to join on Suborder.
But again, the first one would correspond to the SQL query you provided as the SQL implementation.
I'm using Spring data JPA like this:
public interface SampleRepository extends CrudRepository<Sample, Integer>{
#Query("SELECT s FROM Sample s WHERE ((:ids) IS NULL OR s.id IN (:ids))")
List<Sample> queryIn(#Param("ids") List<Integer> ids);
}
But when ids is not empty, I got (1,2,3) is null which is incorrect in mysql.
If I write like this:
public interface SampleRepository extends CrudRepository<Sample, Integer>{
#Query("SELECT s FROM Sample s WHERE s.id IN (:ids)")
List<Sample> queryIn(#Param("ids") List<Integer> ids);
}
When ids is empty, I got s.id in (null)
If I must use native query to do this. Any suggestions?
I guess you want to use ISNULL function to see if :ids has a value or not
SELECT s FROM Sample s WHERE s.id IN (:ids)) or ISNULL(:ids) = 1
I use spring-jpa with hibernate implementation.
I use mariadb
I try to do an update with a join.
My object structure
#Entity
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long roomId;
#ManyToOne
#JoinColumn(name = "appartment_id")
private Appartment appartment;
}
#Entity
public class Appartment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long appartmentId;
#OneToMany
#JoinColumn(name="appartment_id")
private Set<Room> roomList;
}
My sql query
update Room r1 set r1.available = :availability
where r1.roomId in (
SELECT r2.roomId
from Room r2
JOIN r2.appartment a1
WHERE a1.appartmentId = :appartmentId
tried also
update Room r1 set r1.available = :availability
where exists
( SELECT r2
from Room r2
JOIN r2.appartment a1
where a1.appartmentId= :appartmentId
)
I get this error
java.sql.SQLException: Table 'room' is specified twice, both as a target for 'UPDATE' and as a separate source for data
Seem like update and join with mysql seem impossible?
Everytime you get the unexpected token exception, look for syntax errors.
In your case the update query should look like this:
UPDATE Room r1
SET r1.available = :availability
WHERE r1.roomId in
( SELECT r2.roomId FROM Room r2 JOIN r2.appartment a1 WHERE a1.appartmentId = :appartmentId )
EDIT (follow-up issue):
Here you may find some help for your follow-up question
The accepted answer features a MySQL example on how to perform an update with a JOIN. I'm quite sure that's what you're after. Now it would look something like this:
UPDATE Room r1 JOIN r1.appartment a1
SET r1.available = :availability
WHERE a1.appartmentId = :appartmentId
try it with your dialect but if it doesn't work, the following one should do the trick:
UPDATE Room r1 SET r1.available = :availability WHERE r1.appartment.appartmentId = :appartmentId
IN ( SELECT ... ) has poor performance characteristics. Since you are looking at an UPDATE, I recommend a multi-table UPDATE. Or EXISTS is good -- but not that EXISTS. You have not tied the two instances of Room together; the UPDATE will change all or none of the rows!
Since you have not explained what the query is supposed to do, I cannot advise you on how to do it.
before I return "set.select" I would like to include fields from another table but I canot join this table because it has no fields in common with the other two tables. How may I adjust my code below to achieve this? Iam using vs2012 sql and in MVC c#
var set =
(from m in managers
from t in context.tblCompany
join tsc in context.tblStyling on t.ccID equals tsc.ccID
select new { tsc.ccID,LogoIcon = tsc.Icon , tsc.style1, tsc.style2, t.Desc })
.ToList();
return set.Select(c => new Settings(c.ccID, c.style1, c.style2, c.Desc, c.LogoIcon , m.firstName , m.lastName));
Okay, it looks like you want to join context.tblCompany and context.tblStyle and get the cross product of the resulting set and managers. If that is correct, then you are already there. You just need to include the fields from manager that you want in your select statement:
var set =
(from m in managers
from t in context.tblCompany
join tsc in context.tblStyling on t.ccID equals tsc.ccID
select new
{
tsc.ccID,
LogoIcon = tsc.Icon,
tsc.style1,
tsc.style2,
t.Desc,
m.firstName,
m.lastName })
.ToList();
return set;
I am storing a Map in JPA , which stores a keyword translation in each language . such as one object stores Locale.ENGLISH -> "Father" , Locale.CHINESE -> "PaPa". And another object stores Locale.ENGLISH -> "Mother" , Locale.CHINESE -> "MaMa";
Here is my working design :
public class Relation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ElementCollection
#MapKeyColumn(name="locale")
#Column(name="value")
#CollectionTable(name = "RelationName", joinColumns = #JoinColumn(name = "relation_id"))
private Map<Locale, String> langMap = new HashMap<>();
// other fields skipped
}
It works well , I can store many keyword translations to DB. But when query with JPQL , it has some problems :
For example , I want to find which Relation has English key with value "Father" :
This is my code :
Relation r = em.createQuery("select r from Relation r join r.langMap m where ( KEY(m) = :locale and VALUE(m) = :value ) " , Relation.class)
.setParameter("locale" , locale)
.setParameter("value" , value)
.getSingleResult();
It generates this strange / not-working SQL :
Hibernate:
select
relation0_.id as id1_18_
from
Relation relation0_
inner join
RelationName langmap1_
on relation0_.id=langmap1_.relation_id
where
langmap1_.locale=?
and (
select
langmap1_.value
from
RelationName langmap1_
where
relation0_.id=langmap1_.relation_id
)=?
00:16:12.038 WARN o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1242, SQLState: 21000
00:16:12.038 ERROR o.h.e.jdbc.spi.SqlExceptionHelper - Subquery returns more than 1 row
I don't know why it generates that strange subquery.
I can solve this problem by Criteria :
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Relation> criteria = builder.createQuery(Relation.class);
Root<Relation> root = criteria.from(Relation.class);
criteria.select(root);
MapJoin<Relation , Locale , String> mapJoin = root.joinMap("langMap");
criteria.where(builder.and(
builder.equal(mapJoin.key(), locale) ,
builder.equal(mapJoin.value() , value))
);
return em.createQuery(criteria).getSingleResult();
It generates correct SQL ( where langmap1_.locale=? and langmap1_.value=? ) and works well.
But I feel Criteria is too complicated. And I wonder why the JPQL failed? How to correct the JPQL ?
Thanks.
Env :
JPA2 , Hibernate 4.2.3 , MySQL dialect
I had the same problem. It looks like accessing map by ref (without VALUE()) already gives you a map entry value, i.e. the next JPQL should be transformed to a valid SQL:
select r from Relation r join r.langMap m where ( KEY(m) = :locale and m = :value )
I had a similar problem using the JPQL VALUE() operator with Hibernate. It seems that Hibernate implements the VALUE() operator like the java.util.Map.values() method in Java. It generates a subquery that returns all values in the map, i.e. all rows of the mapping table that are related to the entity holding the Map attribute. As soon as you have more then one key/value pair in the map, a comparison expression, which expects scalar expressions as operands, will fail.
What you can do is to turn the comparison expression around and convert it to an IN expression.
Instead of:
select r from Relation r join r.langMap m
where ( KEY(m) = :locale and VALUE(m) = :value )
You can write:
select r from Relation r join r.langMap m
where ( KEY(m) = :locale and :value in (VALUE(m)) )
I hope this way your query will work.
The correct JPQL could be like this:
SELECT r FROM Relation r JOIN r.langMap map
WHERE map[:locale] = :value