How specify a JPA criteria API with "MEMBER IN" and a many-to-many relationship in a mapped superclass? - jpa

I would like to query ConcreteEntity where AUser user IS MEMBER of ConcreteEntity.createdBy with
#Entity
public class AUser implements Serializable {
#Id
#GeneratedValue
private Long id;
private String property;
and
#MappedSuperclass
public class AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#ManyToMany
private Set<AUser> createdBy = new HashSet<>();
and
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
ppublc class ConcreteEntity extends AbstractEntity {
private String property1;
i.e.
EntityManager em = ...; //set to null or else
AUser user = ...; //set to null or else
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<ConcreteEntity> criteria = cb.createQuery(ConcreteEntity.class);
Root<ConcreteEntity> concreteRoot = criteria.from(ConcreteEntity.class);
criteria.select(concreteRoot);
SetAttribute<AbstractEntity, AUser> setAttribute = ConcreteEntity_.createdBy;
PluralAttribute<ConcreteEntity, Set<AUser>, AUser> pluralAttribute = setAttribute;
//richtercloud/jpa/criteria/api/metamodel/member/of/NewMain.java:[40,77] error: incompatible types: SetAttribute<AbstractEntity,AUser> cannot be converted to PluralAttribute<ConcreteEntity,Set<AUser>,AUser>
Expression<Set<AUser>> createdByExpression = concreteRoot.get(pluralAttribute);
criteria.where(cb.isNotMember(user, createdByExpression));
TypedQuery<ConcreteEntity> query = em.createQuery(criteria);
List<ConcreteEntity> result = query.getResultList();
I don't get the transition from SetAttribute to PluralAttribute. I know that I'm not supposed to post question with code which doesn't compile, but although I'd say I have a fair understanding of generics, I don't get it here.
I'm using Eclipselink 2.5.2 and JPA 2.1.

look at the Path.get methods:
<Y> Path<Y> get(SingularAttribute<? super X, Y> attribute);
<E, C extends Collection<E>> Expression<C> get(PluralAttribute<X, C, E> collection);
the second should be instead:
<E, C extends Collection<E>> Expression<C> get(PluralAttribute<? super X, C, E> collection);
So, IMHO, it's not your fault, the API is bugged.
A workaround is needed. Just erase the compile-time type (it's erased on run-time, anyway).
You have to do:
Expression<Set<AUser>> createdByExpression = ((Root<AbstractEntity>) (Object) root).get(AbstractEntity_.createdBy);
or
Expression<Set<AUser>> createdByExpression = root.get((SetAttribute<ConcreteEntity, AUser>) (Object) AbstractEntity_.createdBy);
or else
Expression<Set<AUser>> createdByExpression = root.get("createdBy");

Related

JPQL query on abstract mother class accessing subclasses properties

I'm facing a problem trying to make a JPA repository on an abstract class.
What I'm willing to do is a method that filters, paginates and returnes 3 differents kind of objects in a single list. Some have shared properties (grouped in AbstractClass).
Here are my different classes :
Mother abstract class
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "te_abstract_data")
#DiscriminatorColumn(name="data_type")
public abstract class AbstractData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "data_type")
#Enumerated(EnumType.STRING)
private DataType dataType;
}
AbstractClass
#MappedSuperclass
public class AbstractClass extends AbstractData {
#ManyToOne
#JoinColumn(name = "fk_obj_id")
private ObjEntity opbj;
}
ClassA & ClassB classes
#Entity
#DiscriminatorValue("CLASS_A")
#Table(name = "te_class_a")
#PrimaryKeyJoinColumn(name="fk_data_id")
public class ClassAEntity extends AbstractClass {
// some fields...
}
ClassC
#Entity
#DiscriminatorValue("CLASS_C")
#Table(name = "te_class_c")
#PrimaryKeyJoinColumn(name="fk_data_id")
public class ClassCEntity extends AbstractData {
// some fields...
}
And here is my repository :
#Repository
public interface DataDao extends JpaRepository<AbstractData, Integer> {
#Query(value =
"SELECT ad " +
"FROM AbstractData ad " +
"WHERE ad.obj.name = :objName " +
// some other filters on various fields
"ORDER BY ad.id ASC"
)
List<AbstractData> findFiltered(
Pageable pageable,
String objName
);
}
The current query in my repository ignores only returns ClassB object, even if ClassA and C's objects are well initialized. I have no idea about what could be wrong...
Any idea how I could solve this ?

Spring Data JPA order by value from OneToMany relation

I am trying to sort a result by nested collection element value. I have a very simple model:
#Entity
public class User {
#Id
#NotNull
#Column(name = "userid")
private Long id;
#OneToMany(mappedBy = "user")
private Collection<Setting> settings = new HashSet<>();
// getters and setters
}
#Entity
public class Setting {
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userid")
private User user;
private String key;
private String value;
// getters and setters
}
public interface UserRepository extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User> {
}
I want to have a result returned sorted by the value of one setting.
Is it possible to order by user.settings.value where settings.name = 'SampleName' using Spring Data JPA with QueryDSL?
I've used JpaSpecificationExecutor. let's see findAll for example.
Page<T> findAll(#Nullable Specification<T> spec, Pageable pageable);
Before call this method you can create your specification dynamically (where condition) and Pageable object with dynamic Sort information.
For example
...
Specification<T> whereSpecifications = Specification.where(yourWhereSpeficiation);
Sort sortByProperty = Sort.by(Sort.Order.asc("property"));
PageRequest orderedPageRequest = PageRequest.of(1, 100, sortByProperty);
userRepository.findAll(whereSpecifications, PageRequest.of(page, limit, orderedPageRequest));

JPA Exception- Can not find constructor for <Class> with argument types "[class java.lang.String, class java.lang.String]" to fill data

I use jBoss Fuse 6.1.0 with blueprint DSL with openJPA. I use Container Managed transaction (JTA) and transaction managed by Aspects that handles Commit and Rollback as of now
I have following Classes that are JPA entities.
#Entity
#Table(name="CLIENT")
#NamedQuery(name="Client.findAll", query="SELECT c FROM Client c")
public class Client implements Serializable {
private static final long serialVersionUID = 1L;
//Had to add this for avoiding exception. And it works as expected
//Dummy constructor for JPA - Workaround
public Client(String s1, String s2){}
#Column(name="requestid", unique=true,nullable=false)
private String requestId;
#Id
#Column(name="clientid", unique=true, nullable=false, length=128)
private String clientId;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="REQUESTID", nullable=false)
private RoccoRequest roccoRequest;
//bi-directional One-To-Many association to ClientGroup
#OneToMany(mappedBy="client",fetch=FetchType.LAZY)
private List<ClientGroup> clientGroups;
....
,...
...
}
#Entity
#Embeddable
#Table(name="CLIENTGROUP")
#NamedQuery(name="ClientGroup.findAll", query="SELECT c FROM ClientGroup c")
public class ClientGroup implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private ClientGroupPK id;
#Column(length=32)
private String type;
#Column(name="clientid", length=128)
private String clientId;
//bi-directional many-to-one association to Client
#ManyToOne(fetch=FetchType.EAGER)
#MapsId("clientid")
#JoinColumn(name="CLIENTID", nullable=true, insertable=false, updatable=false)
private Client client;
..
.
.
.
}
#Entity
#Table(name="ROCCOREQUEST")
#NamedQuery(name="RoccoRequest.CHECK_EXISISTING_CLIENT_DETAILS",
query="SELECT r FROM RoccoRequest r JOIN r.client c WHERE c.crmId = :crmId")
public class RoccoRequest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="requestid", unique=true, nullable=false, length=128)
private String requestId;
#OneToOne(mappedBy="roccoRequest", fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.REMOVE})
private Client client;
..
..
..
CriteriaQuery<Client> criteriaQuery = criteriaBuilder.createQuery(Client.class);
Root<Client> clientRoot = criteriaQuery.from(Client.class);
//Join the Client table with the RoccoRequest table
final Join<Client, RoccoRequest> clientRoccoJoin = clientRoot.join(Client_.roccoRequest,JoinType.INNER);
final Path<String> _requestStatus = clientRoccoJoin.get(RoccoRequest_.statusCode);
final Path<String> _requestId = clientRoccoJoin.get(RoccoRequest_.requestId);
final Predicate _crmIdPredicate = criteriaBuilder.equal(clientRoot.get(Client_.crmId), CRMId);
criteriaQuery.multiselect(_requestId,_requestStatus);
criteriaQuery.where(_crmIdPredicate);
//Get list of details of existing requests for the client with the request type as ACO
clientDetails = entityManager.createQuery(criteriaQuery).getResultList();
if(null != clientDetails) for(Client clientDetail : clientDetails){
StatusBO statusDetails = new StatusBO();
statusDetails.setCode((clientDetail.getRoccoRequest().getStatusCode()));
PreInitiationBO preinitiateDetails = new PreInitiationBO();
preinitiateDetails.getCaseHeader().setRequestId(requestId);
preinitiateDetails.setStatus(statusDetails);
exisitngRequestInfo.add(preinitiateDetails);
}
I have did some Criteria fetching of the entities. But I'm getting an exception as follows:
Can not find constructor for "class com.xxx.xxx.model.Client" with
argument types "[class java.lang.String, class java.lang.String]" to
fill data.
Why does JPA expect an argument Constructor? It has anything to do with the association? I tried removing the OneToMany relationship but I still get the error.
Please note that I have added a 2 argument constructor that makes no sense to me. But it works if it's given. log root level has Debug enabled. It has very less information on exception.
Please help.
As JBNizet pointed out,
I was making a dumb mistake by adding multiselect with two Strings but was having a CrtieriaQuery of type Client.class.
This can either be solved by removing the multiselect(Not in my case) or by Making the CriteriaQuery and other types with Tuples.class instead of Client.class and loop through the Tuples and get as tuple.get(0) etc.
Problem resolved. Thanks #Neil and #JBNizet

Hibernate-search search from any indexed entity

I am using Hibernate-search for searching data in my Jboss application. I have 3 JPA entity classes that all extend BaseEntity class and each are indexed by Lucene. For example:
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
#Temporal(TemporalType.TIMESTAMP)
private Date created;
public abstract Long getId();
}
#Entity
#Table(name = "DVD")
public class Dvd extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Field
private String title;
}
#Entity
#Table(name = "BOOK")
public class Book extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Field
private String author;
}
Now I would like to search for either DVD title or Book author by wildcard search query and get the result list as List. This is what I have this far:
public List<BaseEntity> search(String query, int firstResult, int maxResults) {
List<BaseEntity> results = null;
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
Query luceneQuery = new WildcardQuery(new Term("*", "*" + query + "*"));
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, BaseEntity.class);
fullTextQuery.setFirstResult(firstResult);
fullTextQuery.setMaxResults(maxResults);
results = fullTextQuery.getResultList();
return results;
}
But with this I am not getting any results. How is it possible to get this to work or is there even way without using buildQueryBuilder for each entity? Thanks!
You'll want to use the varargs-style method for the classes, like so:
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, DVD.class, Book.class);
This is because when Hibernate Search creates the search query, it adds the class name(s) to the query (for the _hibernate_class field, which is the indexed class' name).

Related entities not loaded - EAGER ignored?

Got GlassFish v3. I have an one-to-many entity. The problem is, that EclipseLink seems to ignore the fetch EAGER mode.
Here is my entities.
#Entity
public class Person implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Hobby> hobbies;
// getter and setter
}
A 1:n relationship
#Entity
public class Hobby
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
#JoinColumn
private Person person;
// getter and setter
}
And the bean
#javax.ejb.Remote
public interface Testing
{
public void addTestData();
public List<Person> getTestData();
}
#javax.ejb.Stateless
public class TestingBean implements Testing
{
#javax.persistence.PersistenceContext
private EntityManager entityManager;
public void addTestData()
{
Person p = new Person();
p.setName("JOE");
entityManager.persist(p);
Hobby h1 = new Hobby();
h1.setName("h1");
h1.setPerson(p);
entityManager.persist(h1);
}
public List<Person> getTestData()
{
TypedQuery<Person> gridQuery = entityManager.createQuery("SELECT e FROM Person e", Person.class);
return gridQuery.getResultList();
}
}
EDIT Client:
InitialContext context = new InitialContext();
Testing test = (Testing)context.lookup("java:global/dst2_1/TestingBean");
test.addTestData();
for(Person p: test.getTestData()) {
System.out.println(p.getName());
for(Hobby b : p.getHobbys()) {
System.out.println(b.getName());
}
}
context.close();
Using MySQL - Storing the data works. But if I fetch the data only the person is returned - not hobbies. Coudld you tell me what is wrong in my code?
EDIT sorry have tried so many things ... The code shown as above produces:
Exception Description: An attempt was made to traverse a
relationship using indirection that had a null Session. This often
occurs when a n entity with an uninstantiated LAZY relationship is
serialized and that lazy relationship is traversed after
serialization. To avoid this issue, ins tantiate the LAZY
relationship prior to serialization.
But the Person is returned correctly. Why does it specify LAZY while I am using EAGER?
You code looks correct. I can't see any way that the EAGER could be ignored.
Are you sure you get the error with this attribute, not another one?
Also ensure you recompile and deployed your code correctly. You most like have an old version deployed.
Make the eager object Serializable