Domanda

Im trying to write query, which returns me list of Drivers wich is not assigned to route.

My database a set up as following.

Route:
route_id
user_id//specified as driver

User:
user_id
role // need to select user, which is Driver role

Only route sees user(driver), user(driver) doesn`t see route.

This is my try to write such query.

public List<User> getUnsignedDrivers(){
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

    CriteriaQuery<User> query = criteriaBuilder.createQuery(User.class);
    Root<User> user = query.from(User.class);
    query.select(user);

    Subquery<Route> subquery = query.subquery(Route.class);
    Root<Route> subRootEntity = subquery.from(Route.class);
    Predicate correlatePredicate = criteriaBuilder.equal(subRootEntity.get("Route_.User"), user);
    subquery.where(correlatePredicate);
    query.where(criteriaBuilder.not(criteriaBuilder.exists(subquery)));

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    return typedQuery.getResultList();
}

I`m new to jpa, so that is the problem.

To be more specific, i need to select user with role driver, which are not set to any route

My entities are set up as following:

    @Entity
public class Route {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

@OneToOne(fetch = FetchType.EAGER, cascade = {})
@JoinColumn(name = "user_id", nullable = true)
private User driver;
.....
@Entity
public class User {

public static enum Role {
    ADMIN, MANAGER, DRIVER;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;

UPDATE: current query

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

    CriteriaQuery<User> query = criteriaBuilder.createQuery(User.class);
    Root<User> user = query.from(User.class);
    Predicate predicateRole = criteriaBuilder.equal(user.get("role"), User.Role.DRIVER);
    query.where(predicateRole);
    query.select(user);


    Subquery<Route> subquery = query.subquery(Route.class);
    Root<Route> subRootEntity = subquery.from(Route.class);
    Predicate correlatePredicate = criteriaBuilder.equal(subRootEntity.get("driver"), user);
    subquery.where(correlatePredicate);
    query.where(criteriaBuilder.not(criteriaBuilder.exists(subquery)));

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    return typedQuery.getResultList();

problem still exist

i get this exception:

java.lang.IllegalStateException: No explicit selection and an implicit one cold not be determined
at org.hibernate.ejb.criteria.QueryStructure.locateImplicitSelection(QueryStructure.java:296)
at org.hibernate.ejb.criteria.QueryStructure.render(QueryStructure.java:249)
at org.hibernate.ejb.criteria.CriteriaSubqueryImpl.render(CriteriaSubqueryImpl.java:282)
at org.hibernate.ejb.criteria.predicate.ExistsPredicate.render(ExistsPredicate.java:58)
at org.hibernate.ejb.criteria.QueryStructure.render(QueryStructure.java:258)
at org.hibernate.ejb.criteria.CriteriaQueryImpl.render(CriteriaQueryImpl.java:340)
at org.hibernate.ejb.criteria.CriteriaQueryCompiler.compile(CriteriaQueryCompiler.java:217)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:587)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
at $Proxy25.createQuery(Unknown Source)

throws at this line TypedQuery<User> typedQuery = entityManager.createQuery(query);

Work around this works for me quite well. I wrote this, because i can`t use inverse relationship.

public List<User> getUnsignedDrivers(){
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

    CriteriaQuery<User> query = criteriaBuilder.createQuery(User.class);
    Root<User> user = query.from(User.class);
    Predicate predicateRole = criteriaBuilder.equal(user.get("role"), User.Role.DRIVER);
    query.where(predicateRole);
    query.select(user);

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    List<User> allDrivers = typedQuery.getResultList();
    List<User> notAssignedDrivers = new ArrayList<User>();
    List<Route> haveDriverRoutes = getRouteWithNoDrives();
    for (User driver : allDrivers){
        if (!isDriverAssigned(haveDriverRoutes,driver.getId())){
            notAssignedDrivers.add(driver);
        }
    }
    return notAssignedDrivers;
}

private boolean isDriverAssigned(List<Route> haveDriverRoutes, long driverId){
    for(Route route : haveDriverRoutes){
        if (route.getDriver().getId() == driverId){
            return true;
        }
    }
    return false;
}

@SuppressWarnings("unchecked")
public List<Route> getRouteWithNoDrives() {
    Query query = entityManager.createQuery("SELECT o FROM " + type.getSimpleName() + " o WHERE o.driver != null");
    return  query.getResultList();
}
È stato utile?

Soluzione

You are missing the inverse OneToOne relationship in User entity:

@OneToOne(mappedBy="driver")
private Route route;

See this link about how to map OneToOne relationships.

And you have an error in this part: subRootEntity.get("Route_.User"). This is not a valid syntax, and you don't have a property called User in Route entity: the property is called driver (after reading your latest edit).

You have 2 ways to get that Path expression, either using:

Path<User> path = subRootEntity.get("driver");
// in a compact way: 
Predicate correlatePredicate = criteriaBuilder.equal(subRootEntity.get("driver"), user);

or by use of Metamodel:

Path<User> path = subRootEntity.get(Route_.driver);
// in a compact way: 
Predicate correlatePredicate = criteriaBuilder.equal(subRootEntity.get(Route_.driver), user);

You seem to have mixed the two approaches. See this article for further info about the use of Metamodel.

The rest of the query looks correct.

Altri suggerimenti

The line where you add the subquery should return something. So you should change: query.where(criteriaBuilder.not(criteriaBuilder.exists(subquery)));

to:

query.where(criteriaBuilder.not(criteriaBuilder.exists(subquery.select(subRootEntity))));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top