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
Related
I'm trying to implement the equivalent of this SQL query using Spring Data JPA Specifications:
SELECT * FROM product WHERE category_id IN (....)
The two entities involved in this query have a OneToMany and ManyToOne relationship:
ProductEntity:
#Data
#NoArgsConstructor
#Entity
#Table(name = "PRODUCT")
public class ProductEntity extends AbstractAuditableEntity {
// skipped other properties for simplicity sake
#ManyToOne
private CategoryEntity categoryEntity;
}
CategoryEntity:
#Entity
#Table(name = "PRODUCT_CATEGORY")
#Data
public class CategoryEntity extends AbstractBaseEntity {
// skipped other properties for simplicity sake
#OneToMany(mappedBy = "categoryEntity")
private List<ProductEntity> productEntities;
}
My JPA Specifications query compiles and runs without any error, but doesn't return any result:
Specification definition:
public static Specification<ProductEntity> inCategories(List<Long> categories) {
return (root, query, builder) -> {
if (categories != null && !categories.isEmpty()) {
final Path<CategoryEntity> category = root.get("categoryEntity");
return category.get("id").in(categories);
} else {
// always-true predicate, means that no filtering would be applied
return builder.and();
}
};
}
Client code:
Page<ProductEntity> productEntityPage = productRepository.findAll(Specification
.where(ProductSpecifications.inCategories(filterCriteria.getCategories()))
, pageRequest);
Why doesn't it work? I get results when querying the database using SQL statements, so there must be something wrong with either my JPA Specifications query or my entities mapping.
What am I missing?
I think you should use join here
Join<ProductEntity, CategoryEntity> categoryJoin = root.join("categoryEntity");
return categoryJoin.get("id").in(categories);
instead of
Path<CategoryEntity> category = root.get("categoryEntity");
return category.get("id").in(categories);
since root.get("categoryEntity").get("id"); will give you nothing as no such path (product.categoryEntity.id) exists in product table.
Am trying to create a Query that either matches all rows that equal tier or are NULL. Using Query Methods as described in Spring JPA Docs. The Default implementation below works if I just pass in the tier:-
#Entity
#Table(name = "tier")
class UuTier {
Long id;
Long tierId;
}
#Entity
#Table(name = "user")
class User {
#OneToOne
#JoinColumn(name="tier_id")
UuTier uuTier;
// Other Relationships
}
public interface UserRepository extends Repository<User, Long> {
List<User> findByTier_Id(#Param("tier")Long tier);
}
What I need is something like this, which is throwing an error " No property null found for type User". Can I achieve this ask using Query Methods?:-
public interface UserRepository extends Repository<User, Long> {
List<User> findByTierOrNull_Id(#Param("tier")String tier);
}
Following up from one of the responders (who for some reason deleted her post) - I got this to work!!
#Query("SELECT entity FROM User entity LEFT JOIN UuTier uuTier ON entity.uuTier.tier = uuTier.tier"
+ " WHERE entity.uuTier.tier = :tier OR entity.uuTier.tier IS NULL")
public List<User> findByTierOrNull_Id(#Param("tier") Long tier);
I'm using Spring, Hibernate and the JPA Criteria API.
Let's say I have a Vehicle class hierarchy and a Tire class. Each Vehicle can have multiple Tires.
Vehicle -- Tanker, Motorcylce
Tire
I want to query for small tires that are on Tankers with a capacity over 100 gallons.
But the vehicle property for Tire is of type Vehicle. Only one of the Vehicle subclasses (Tanker) has the "capacity" property.
How do I tell the criteria api to join ONLY on Tankers?
Thanks!
I'm sure this is has been asked/answered before, but I think I'm missing the right terminology to have a successful search.
Try this:
#Entity
public class Vehicle {
#OneToMany
private Set<Tire> tires;
}
#Entity
public class Tank extends Vehicle {
private int gallons;
}
#Entity
public class Motorcycle extends Vehicle {
}
#Entity
public class Tire {
}
public interface VehicleRepo extends JpaRepository<Vehicle, Long> {
#Query("select v.tires from Vehicle v where v.gallons > ?1")
List<Tire> findByGallonsIsGreaterThan(int gallons);
}
If you need select only subclass type in JPQL query you can use this approach:
select v from Vehicle v where type(v) = Tank
select v from Vehicle v where v.class = Tank
Example and test.
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)
Basically i have a named query "findServerWithNic" which refuses to work:
#Entity
#Table(name = "vnm_server")
#DiscriminatorValue("S")
#NamedQueries({
#NamedQuery(
name="findServerWithNic",
query="SELECT s FROM Server s, Nic n "+
"WHERE n.id = :nicId AND n member of s.nics"
)
})
public class Server extends NetworkedDevice implements Serializable
{...}
nics is not defined in Server but in it's superclass NetworkedDevice:
#Entity
#Table(name = "vnm_networked_device")
#DiscriminatorColumn(name = "c_device_type", discriminatorType = DiscriminatorType.CHAR)
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class NetworkedDevice extends AbstractIdentifyable implements Serializable
{
#OneToMany(mappedBy = "connectedHost", cascade = CascadeType.ALL)
#OrderColumn(name = "c_index")
protected List<Nic> nics = new ArrayList<Nic>();
public List<Nic> getNics() {
return nics;
}
public void setNics(List<Nic> nics) {
this.nics = nics;
}
}
now i have a testcase that creates a Nic instances a adds it to a Server instance (s.getNics() contains the instance, i checked) but the invocation of the query
public Server findVirtualServerOfNic(Long nicId) {
final Server network = em.createNamedQuery("findServerWithNic", Server.class)
.setParameter("nicId", nicId)
.getSingleResult();
return network;
}
results in an NoResultException
Caused by: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.throwNoResultException(EJBQueryImpl.java:1246)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.getSingleResult(EJBQueryImpl.java:750)
at com.profitbricks.provisioning.vnm.jpa.impl.VNManagementJPAImpl.findVirtualServerOfNic(VNManagementJPAImpl.java:101)
originally the nics-member was private but even setting it to protected didn't work.
We use eclipselink 2.2.0-M4 as our JPA 2-provider. Is this an eclipselink bug or is the query wrong?
Did you commit or flush the transaction? If you did not, then the database may not have the value (depending on your flush mode), so your query will not return anything.
Enable logging and include the SQL. If the SQL correct?
"member of" is generaly old JPQL syntax, normally "join" is used now instead
"SELECT s FROM Server s join s.mics n WHERE n.id = :nicId "