i like to create a (crtieria-) query against a dynamic model but i always get the exception
No [EntityType] was found for the key class [demo.DynamicResult] in the Metamodel
at the last line
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
DynamicHelper helper = new DynamicHelper(JpaHelper.getServerSession(emf));
Class<? extends DynamicEntity> rootclass = helper.getType("demo.DynamicResult").getJavaClass();
query.from(rootclass);
The DynamicResult gets generated with following code
ServerSession serverSession = JpaHelper.getEntityManager(entityManager).getServerSession();
DynamicClassLoader dcl = DynamicClassLoader.lookup(serverSession);
JPADynamicHelper jpaDynamicHelper = new JPADynamicHelper(entityManager);
Class<?> dynamicResult = dcl.createDynamicClass("demo.DynamicResult");
JPADynamicTypeBuilder dynamicResultBuilder = new JPADynamicTypeBuilder(dynamicResult, null, "DynamicResult");
dynamicResultBuilder.addDirectMapping("id", String.class, "id");
//Some more addDirectMappings ....
dynamicResultBuilder.setPrimaryKeyFields("id");
type = dynamicResultBuilder.getType();
jpaDynamicHelper.addTypes(false, false, type);
What did I missed ?
Is this kind of query possible at all ?
Regards
You may be hitting https://bugs.eclipse.org/bugs/show_bug.cgi?id=429760 bug. There was a problem with copying newly created dynamic entity metadata into current session.
Check this bug and attached diffs. jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/dynamic/simple/SimpleQueryTestSuite.java test suite contains test which does similar thing - creates dynamic entity and runs JPQL query against it within the same transaction (UnitOfWork).
I would try latest 2.7.0 or 2.6.1 (do not use .WAS) build from https://www.eclipse.org/eclipselink/downloads/nightly.php to see if it works fine for you.
Related
I am trying to make a universal search for my entity with criteria builder where a given text is matched with all the columns in the entity.
String likeSearchText = "%" + searchText + "%";
List<Customer> searchedCustomers = null;
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery query = builder.createQuery(Customer.class);
Root <Customer> root = query.from(Customer.class);
ArrayList<Predicate> conditions = new ArrayList<>();
conditions.add(builder.like(root.<String>get("firstName"), likeSearchText));
conditions.add(builder.like(root.<String>get("lastName"), likeSearchText));
conditions.add(builder.like(root.<String>get("middleName"), likeSearchText));
conditions.add(builder.like(root.<String>get("companyName"), likeSearchText));
conditions.add(builder.like(root.<String>get("industry"), likeSearchText));
query.where(builder.or(conditions.toArray(new Predicate[conditions.size()])));
query.select(root);
searchedCustomers = entityManager.createQuery(query).getResultList();
return searchedCustomers;
When I run this method I always get an empty list. I tried changing the like to notLike and that works perfectly fine by giving me a list containing elements which are not like the given search text so I am really confused as to what's wrong with my like method.
Any kind of help would be appreciated!
I had similar problems when I made some testing and had entities with the same (simple)name in the classpath. So for example there were entities like:
org.example.one.Customer
org.example.two.Customer
If you do not have explicitly set different table names like:
package org.example.one;
#Entity("customer_one")
public class Customer { ...
and
package org.example.two;
#Entity("customer_two")
public class Customer { ...
hibernate might:
mix stuff in the same table in db
try to find field from wrong table when constructing the query
Also I thibk you do not need this:
query.select(root);
I'm currently building a native SQL query with Spring Data JPA that uses a #SqlResultSetMapping. The query looks filters the rows based on certain parameters and then returns all of the columns of an entity table plus additionally it calculates another column on the fly. Something like this:
#Entity
#NamedNativeQuery(
name = "Entity.searchBySearchParams",
resultSetMapping = "SearchResultMapping",
query = "select entity.*, (*calculation*) as anotherField from ..."
)
#SqlResultSetMapping(
name = "SearchResultMapping",
entities = #EntityResult(entityClass = Entity.class),
columns = #ColumnResult(name = "anotherField", type = double.class)
)
public class Entity { ... }
And then I'm trying to call this native, named query from my repository (which extends JpaRepository<T, Long>) like that:
#Query(nativeQuery = true, name = "Entity.searchBySearchParams")
List<Object[]> searchBySearchParams( ... lots of params ... );
Where I'm expecting searchBySearchParams(...)[0] to be of type Entity and searchBySearchParams(...)[1] of type Double.
Unfortunately I'm getting this exception when trying to call the above repository method:
org.springframework.dao.InvalidDataAccessApiUsageException: Cannot create TypedQuery for query with more than one return; nested exception is java.lang.IllegalArgumentException: Cannot create TypedQuery for query with more than one return
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
....
Which is weird since calling the query and applying my result set mapping directly by invoking the EntityManage#createNamedQuery method worked perfectly fine.
Am I missing something or should I post this to the Spring Data Jira? Neither JPA's nor Spring's documentation provided information for my case ...
More Background Info:
Actually the additional column anotherField is a transitive property on my Entity. I haven't found a way of mapping that column to the transitive property, which is why my repository method currently returns Object[].
I am using JPA Criteria API with JPA meta model.
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<T> criteria = cb.createQuery(User.class);
final Root<T> root = criteria.from(User.class);
criteria.select(root).where(cb.equal(root.get(User_.username), value));
return em.createQuery(criteria).getResultList();
here the problem is root.get method throws NPE first time after the server starts (i.e first execution after server start), but the same code works fine from second execution onwards. It doesn't matter when the first execution executed after server started. It may be executed after few seconds or after few minutes.
Following is the exception thrown:
java.lang.NullPointerException: null
at org.apache.openjpa.persistence.criteria.PathImpl.get(PathImpl.java:245) ~[openjpa-2.4.2.jar:2.4.2]
If you look at the source code of PathImpl.get
public <Y> Path<Y> get(SingularAttribute<? super X, Y> attr) {
if (getType() != attr.getDeclaringType()) {
attr = (SingularAttribute)((ManagedType)getType()).getAttribute(attr.getName());
}
return new PathImpl<X,Y>(this, (Members.SingularAttributeImpl<? super X, Y>)attr, attr.getJavaType());
}
it access attr.getDeclaringType()
If I look at the JPA meta model which is auto generated it looks as follows:
public class User_ {
public static volatile SingularAttribute<User,String> username;
...
}
username is definitely null, however the code works well except first time.
It appears during runtime JPA set value to User_.username.
so question is how do prevent throwing NPE first time!
Using TomEE 7.0.4
i am trying to reuse a dynamic query as a named query as described here:
https://wiki.eclipse.org/EclipseLink/Release/2.5/JPA21#Add_Named_Query
the goal is to build the criteria-query only once and then reuse it as a namedquery if parameter did not change.
public static List<User>getUserByParameter(ParameterMap parameter){
EntityManager em = getEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> criteriaQuery = builder.createQuery(User.class);
Root<User> user = criteriaQuery.from(User.class);
List<Predicate> predicates = new ArrayList<Predicate>();
//...build up the query depending on parameter
if (null != parameter.getStatus()){
predicates.add(builder.equal(user.<Integer>get("status"), parameter.getStatus()));
}
//etc.
criteriaQuery.select(user).where(predicates.toArray(new Predicate[]{}));
Query query = em.createQuery(criteriaQuery);
//now register this query as a namedQuery
em.getEntityManagerFactory().addNamedQuery("userByParameter", query);
return query.getResultList();
}
i thought about something like:
public static List<User>getUserByParameter(ParameterMap parameter){
Query userByParameter = em.createNamedQuery("userByParameter");
if (null != userByParameter){
return userByParameter.getResultList();
}else {
//build the dynamic query as above
}
}
this results in a nullpointer as the namedQuery doesn't exist the first time.
how can i reuse the query in the same method or in other words, how can i check in a clean way (without using try-catch) if a namedquery exists?
I'm not sure I understand the problem you are looking to solve. The getUserByParameter method is something that should be built on the EntityManagerFactory, when it is first initialized or obtained. Feel free to add properties to your factory if you wish to keep track of what you have added already, but these should be done only once, upfront during initialization.
What is confusing is that you are expecting the query results to be reused - named queries are designed to help reduce the cost of parsing and preparing queries. EclipseLink has a query cache feature that can return the results for you if the same parameters are used, without you needing to cache the query, its parameters and the results yourself.
I always get the same error when using the criteria API:
Local Exception Stack:
Exception [EclipseLink-6075] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.QueryException
Exception Description: Object comparisons can only use the equal() or notEqual() operators. Other comparisons must be done through query keys or direct attribute level comparisons.
Expression: [
Relation operator LIKE
Query Key rootId
Base de.uni.entities.Diary
Constant Testaccount]
Query: ReportQuery(referenceClass=Diary )
The code with the criteria API is the following:
....
CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
// Restrictions
Predicate[] predicate = new Predicate[1];
// From-clause
Root<User> root2R = cq.from(User.class);
predicate[0] = cb.like(root2R.<String> get("rootId"), id);
Join<Clazz, Diary> friends2R;
friends2R = root2R.join("diaries");
cq.where(predicate);
// Select-clause
cq.select((Selection<? extends Clazz>) friends2R);
Query query = getEm().createQuery(cq);
List<Object> data = query.getResultList();
return data;
}
The problem exists because of the following two lines:
" Join friends2R;
friends2R = root2R.join("diaries");"
Without the mapping (with an adjusted select-clause) i would get the searched User with the fitting rootId, so that there occurs no error. But now i wanna map the User with the Diaries and show all diaries of the User-entity. But it always ends in the error ahead. Whether i use "like" or "equal", it doesn't work.
The generell code shouldn't have an error, because i'm using the same code already to get all mapped Users of a User (many to many relation) => no problem.
I just don't understand, why this error occurs and especially why it mentions the Diary as Base although the user should be mentioned as the base-class...and the id is right too and also appears so in the database...
I really hope that you can help me, thanks in advance!
How is rootId mapped, using a Basic, or OneToOne?
Seems to be a bug, please log the bug on EclipseLink.
Your code does not seem correct though, the
CriteriaQuery cq = cb.createQuery(User.class);
should be,
CriteriaQuery cq = cb.createQuery(Diary.class);
should it not?
Doesn't Diary also have a relationship to User? You could just query Diary where its user has the id.