how to create query in Criteria api - jpa-2.0

I have a class:
#Entity
public class Resume {
private Long id;
#Embedded
private DesiredPositionAndSalary desiredPositionAndSalary;
}
and class:
#Embeddable
public class DesiredPositionAndSalary {
#ManyToMany
private Set<Specialization> specializations;
}
and class ;)
#Entity
public class Specialization {
private Long id;
}
now i have some Specializations that i need filtered by.
For example i need to select all resume with one of specialization like programmer or manager. Something like
select * from resume r inner join resume_to_specialization rts on r.id = rts.id inner join specialization s on rts.spec_id in(1,2)
how can i write this query in Criteria api? If i miss some major details i can give more.

Ok, i handle it with this:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Resume> cq =cb.createQuery(Resume.class);
Root<Resume> root = cq.from(Resume.class);
cq.select(root);
Set<Specialization> filter = getFilter();
SetJoin<DesiredPositionAndSalary, Specialization> join = root.join(Resume_.desiredPositionAndSalary, JoinType.INNER).join(
DesiredPositionAndSalary_.specializations, JoinType.INNER);
cq.where(cb.and(cq.getRestriction(), join.in(filter)));
cq.distinct(true);/*it is major, or we get duplicate of resume for every
specialization overlap with filter*/
List<Resume> result = em.createQuery(cq).getResultList();
.

Related

Unable to make general purpose query, without Domain entity

I am new to springbootneo4j. I have difficulties making general purpose queries. I want to be able to make any kind of query and get result without domain entity.
I am making a query like this in repository class:
#Query("MATCH (p:Employee) RETURN ID(p) as id, p.name as name, p.salary as salary ")
that is not working, but the following query is working:
#Query("MATCH (p:Employee) RETURN p ")
My domain entity class is something like this:
#NodeEntity
public class Employee {
#Id
#GeneratedValue
private Long id;
private String name;
private int salary;
#Relationship(type = "IS_BOSSOF", direction = Relationship.UNDIRECTED) Set<Employee> reporties = new HashSet<>();
public Employee() {}
// some more code
}
Create a command is like this:
(laksmi:Employee{name:"Laksmi",salary:200}),(ashwini:Employee{name:"AshwiniV",salary:300}), (harish:Employee{name:"Harish",salary:400}), (jay)-[:IS_BOSSOF]->(mukesh), (xyz)-[:IS_BOSSOF]->(mukesh), (harish)-[:IS_BOSSOF]->(ashwini),
Whenever you are distributing properties you need to use #QueryResult annotation on your class
SDN

Map a discriminator column to a field with Doctrine 2

In my project I have several class table inheritances like this:
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/** #Entity */
class Employee extends Person
{
// ...
}
I have a method which converts entities to arrays based on the fields which have public getters. The problem here is that I lose the inheritance information in my array because the discriminator value isn't stored in a field.
So what I tried was the following, hoping doctrine would automatically set $disc:
class Person
{
// can I automatically populate this field with 'person' or 'employee'?
protected $discr;
public function getDiscr() { return $this->discr; }
public function setDiscr($disc) { $this->discr; }
// ...
}
Is there a way to make this work in doctrine? Or would I need to read the class metadata in my entity-to-array method?
Sadly, there is no documented way to map the discr column to an entity. That's because the discr column is really part of the database and not the entity.
However, it's quite common to just put the discr value directly in your class definition. It's not going to change and you will always get the same class for the same value anyways.
class Person
{
protected $discr = 'person';
class Employee extends Person
{
protected $discr = 'employee';
Here's a small example of what I have in one of my ZF2 projects (using Doctrine MongoDB ODM):
// an instance of your entity
$entity = ...;
/** #var \Doctrine\ODM\MongoDB\DocumentManager $documentManager */
$documentManager = $serviceManager->get('DocumentManager');
/** #var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory $factory */
$factory = $documentManager->getMetadataFactory()
/** #var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $metadata */
$metadata = $factory->getMetadataFor(get_class($object));
if ($metadata->hasDiscriminator()) {
// assuming $data is result of the previous extraction
$data[$metadata->discriminatorField] = $metadata->discriminatorValue;
}
What I have done is I've implemented a custom interface DiscriminatorAwareInterface and I only apply the checks to classes that implement it (in your case it would be the class that all "discriminated" classes extend.
As a result I end up with code that looks like this:
// add value of the discrinimator field to entities that support it
if ($object instanceof DiscriminatorAwareInterface) {
/** #var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $metadata */
$metadata = $factory->getMetadataFor(get_class($object));
if ($metadata->hasDiscriminator()) {
$data[$metadata->discriminatorField] = $metadata->discriminatorValue;
}
}
I'm pretty sure it will be the same if you use the standard ORM, except instead of a document manager you will have entity manager.
Just got this problem and solved it without defining the discriminator as a real member:
abstract class MyEntity {
const TYPE_FOO = 'foo';
const TYPE_BAR = 'bar';
const TYPE_BUZ = 'buz';
...
/**
* #return string
*/
public function getMyDiscriminator()
{
$myDiscriminator = null;
switch (get_class($this)) {
case MyEntityFoo::class:
$myDiscriminator = self::TYPE_FOO;
break;
case MyEntityBar::class:
$myDiscriminator = self::TYPE_BAR;
break;
case MyEntityBuz::class:
$myDiscriminator = self::TYPE_BUZ;
break;
}
return $myDiscriminator;
}
...
}
class MyEntityFoo extends MyEntity {}
class MyEntityBar extends MyEntity {}
class MyEntityBuz extends MyEntity {}
You can use the following solution:
`$`$metadata = \Doctrine\ORM\Mapping\ClassMetadata((string)$entityName);
print_r($metadata->discriminatorValue);`

Mapping Extra Attribute in a Join Table JPA 2

I am trying to model this relationship following this link http://www.javaworld.com/javaworld/jw-01-2008/images/datamodel.gif
Its the usual Many to Many relationship between Order and Products but I dont know how to add the extra columns in the Join table.
#Entity
#Table(name = "Orders")
public class Order {
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "ORDER_LINES", joinColumns = { #JoinColumn(name = "ORDER_ID") }, inverseJoinColumns = { #JoinColumn(name = "PROD_ID") })
private Set<Product> products;
}
#Entity
#Table(name="PRODUCTS")
public class Product {
#ManyToMany(mappedBy="products")
private Set<Order> orders;
}
How to add Join Table extra attribute in JPA 2.0?
Thanks
There is no concept of having additional persistent attribute in relation in JPA (2.0). That's why relation with property is actually intermediate entity.
From both Order and Product entities, you need one-to-many relationship to new entity. Because of bidirectional relationship, new entity will have many-to-one relationships to Order and Product.
You need to go for something like this (shows only relationships, id's and other mappings stripped away):
#Entity
#Table(name="order_item")
public class OrderItem {
#ManyToOne
private Order order;
#ManyToOne
private Product product;
}
#Entity
public class Order {
#OneToMany (mappedBy = "order")
private Set<OrderItem> orderItems;
}
#Entity
public class Product {
#OneToMany(mappedBy = "product")
private Set<OrderItem> orderItems;
}

How to load 2 tables into one object in Doctrine2?

For example, we have an external DB of Countries and Cities. We need to be able to read that external DB with the following conditions:
We can't alter or modify in any way the World DB. We can't add FK for example.
When using the external DB, we want to keep an internal reference, for example for our entity "User" we want to keep a reference such as User->city
We want to have an internal entity CustomCities where users can create their own cities.
What would be the best approach to do this?
We have tried several options but all of them break in one way or another. One recommendation was to use a #Table with an external reference readOnly but that didn't work.
The closest solution we have found for this is to use an in-between class that represents a City object, but doesn't really hold data, and then via native queries, we populate that fake object. Then using internal logic we determine if the requested item such as User->getCity() came from the City DB or came from the CityCustomDB...
Any ideas on how to approach this?
I've taken a guess at the possible schema, have you tried using Class Table Inheritance so that the country essentially becomes your interface.
interface CountryInterface
{
public function getName();
}
So your entities might look like this
/**
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="type", type="string")
* #DiscriminatorMap({
* "internal" = "InternalCountry"
* ,"external" = "ExternalCountryAlias"
* })
*/
abstract class AbstractCountry implements CountryInterface
{
protected $id;
}
class InternalCountry extends AbstractCountry
{
protected $name;
public function getName()
{
return $this->name;
}
}
The ExternalCountryAlias works like a proxy to ExternalCountry, but I named it Alias so not to confuse it with Data Mapper Proxies.
class ExternalCountryAlias extends AbstractCountry
{
/**
* #OneToOne(targetEntity="ExternalCountry")
* #JoinColumn(name="external_country_id"
* ,referencedColumnName="id")
*/
protected $externalCountry;
public function getName()
{
return $this->externalCountry->getName();
}
}
ExternalCountry doesn't have to extend from the base class.
class ExternalCountry
{
protected $name;
public function getName()
{
return $this->name;
}
}
So when you get a country you are referencing the base class. So lets say country.id = 1 is and internal country and country.id = 2 is an external country.
// returns an instance of InternalCountry
$entityManager->find('AbstractCountry', 1);
// returns an instance of ExternalCountryAlias
$entityManager->find('AbstractCountry', 2);
And because they both implement CountryInterface you don't have to worry where they came from, you still access the name by calling getName();

JPA2 Criteria queries on entity hierarchy

suppose i have the following entity domain:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
public abstract class Entity1 {
//some attributes
}
#Entity
#DiscriminatorValue("T1")
public class Entity2 extends Entity1 {
#OneToMany(fetch=FetchType.EAGER, cascade = { CascadeType.ALL }, mappedBy="parent")
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Entity1Detail> details = new HashSet<Entity1Detail>();
}
#Entity
public class Entity1Detail {
#ManyToOne
#JoinColumn(name="REF")
private Entity2 parent;
#Basic
private Integer quantity;
}
#Entity
#DiscriminatorValue("T2")
public class Entity3 extends Entity1 {
//some other attributes
}
when i do a JPQL query:
select e from Entity1 e left join e.details d where d.quantity > 1
it runs well (left join ;P). however when i try to construct the same query using JPA2 Criteria API:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery q = builder.createQuery();
Root r = q.from(Entity1.class);
q.select(r);
q.where(builder.gt(r.join("details", JoinType.LEFT).get("quantity"), 1));
i get NPE in "join" because the attribute "details" doesn't belong to Entity1 (which is actually true, i have to select on Entity2.class instead). the thing is that when i have to construct my dynamic query using Criteria API i don't really know anything about hierarchy, i'm just passed a Class.
i understand that Criteria API is typesafe and all that, but is there a way to work around this? with aliases maybe (as before i used Hibernate Criteria API, traversing joins with aliases):
Criteria c = session.createCriteria(Entity1.class);
c.createAlias("details", "d");
c.add(Restrictions.ge("d.quantity", 1));
You need to base your query on entity2.details. Since the criteria API is type-safe, it catches that entity1 has no field named "details"
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery q = builder.createQuery();
Root r = q.from(Entity2.class); // Must use subclass as root
q.select(r);
q.where(builder.gt(r.join("details", JoinType.LEFT).get("quantity"), 1));
Since Entity2 extends Entity1, you can cast your results as the parent type safely. For example:
CriteriaQuery<Entity1> q = builder.createQuery(Entity1.class);
Root r = q.from(Entity2.class); // Must use subclass as root
will return a list of Entity1
CriteriaQuery<Entity2> q = builder.createQuery(Entity2.class);
Root r = q.from(Entity2.class); // Must use subclass as root
will return a list of Entity2
EDIT:
I think I misunderstood the goal here. If you want all Entity1 UNLESS they are Entity2 with details.quantity <= 1, you need to do more.
You can't use a left join from Entity1Detail to Entity1, because that is not strictly type safe. Instead, you need to join Entity2 to Entity1Detail somehow. Probably the best tool to use here is a correlated subquery.
CriteriaQuery<Entity1> q = builder.createQuery(Entity1.class);
Root<Entity1> ent1 = q.from(Entity1.class);
SubQuery<Entity2> subq = q.subquery(Entity2.class);
Root<Entity2> ent2 = subq.from(Entity2.class);
Path<Integer> quantity = ent2.join("details", JoinType.LEFT).get("quantity");
Predicate lessThan = builder.lte(quantity,1);
Predicate correlatedSubqJoin = cb.equal(ent1,ent2)
subq.where(lessThan, correlatedSubqJoin);
q.select(ent1);
q.where(builder.exists(subq).not());
The criteria API does not know that you are single table inheritance, so you have to write your queries for all inheritance strategies, including a Joined inheritance strategy.