I have a query
List<Integer> ids = sessionFactory.getCurrentSession()
.createSQLQuery("SELECT l.id FROM likes l INNER JOIN liketoanswers lta ON l.id = lta.id AND lta.answer_id = :a_id")
.setParameter("a_id",answer.getId())
.list();
List<Like> likes = new ArrayList<>();
for (Integer id : ids) {
likes.add(findById(id));
}
return likes;
I want to make this query like .createQuery("from Like l inner join fetch likes.answers la where la.id = answer.getId ..)
but word Like is registered in sql and a dont want to change entity class name.
I known that there a method to substitute class , but cannot find
Related
I have a query that uses a constructor expression. For the most part it works, but I wanted to reduce the number of queries on the database. So I tried FETCH and #EntityGraph, but I am getting
org.springframework.dao.InvalidDataAccessResourceUsageException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy ...
I tried removing the FETCH and just use JOIN to fix the problem, but it does not do the fetch operation.
Is this even allowed or possible with Hibernate/JPA?
Code wise it looks something like
#Query("select new example.BillingTargetAndStudent(bt,s) from Student s, BillingTarget bt where s.id = bt.targetID and bt.account.status = :accountStatus and bt.account.organization = :organization")
Stream<BillingTargetAndStudent> streamAllByOrganizationAndStatusForStudentList(Organization organization, AccountStatus accountStatus);
What I want to do is FETCH bt.account in the same query along with s.user.attributes. The bt.account isn't too hard to workaround, but the s.user.attributes would not be possible with the constructor expression as it is a collection
What I ended up doing is to use the Stream<Object[]> and select bt, s from ... then have a converter that changes Object[] to call the constructor expression
default Stream<BillingTargetAndStudent> streamAllByOrganizationAndStatusForStudentList(Organization organization, AccountStatus accountStatus) {
return streamAllByOrganizationAndStatusForStudentList0(organization, accountStatus)
.map(o -> new BillingTargetAndStudent((BillingTarget) o[0], (Student) o[1]));
}
#Query("select bt, s from Student s join fetch s.user u join fetch u.attributes, BillingTarget bt join fetch bt.account a where s.id = bt.targetID and bt.account.status = :accountStatus and bt.account.organization = :organization")
Stream<Object[]> streamAllByOrganizationAndStatusForStudentList0(Organization organization, AccountStatus accountStatus);
I can reduce the query slightly by putting in one #EntityGraph
#EntityGraph("Student.forStudentList")
#Query("select bt, s from Student s, BillingTarget bt left join fetch bt.billingContractTargets bct join fetch bt.account a where s.id = bt.targetID and bt.account.status = :accountStatus and bt.account.organization = :organization")
Stream<Object[]> streamAllByOrganizationAndStatusForStudentList0(Organization organization, AccountStatus accountStatus);
#NamedEntityGraph(
name = "Student.forStudentList",
attributeNodes = {
#NamedAttributeNode("schedules"),
#NamedAttributeNode(value = "user", subgraph = "user.attributes"),
},
subgraphs = {
#NamedSubgraph(name = "user.attributes",
attributeNodes = {#NamedAttributeNode("attributes")})
}
)
But I am limited to just one and it has to be the entity for the repository. I cannot say
#EntityGraph("BillingTarget.forStudentList")
#EntityGraph("Student.forStudentList")
When I try this I get the proper JSON as a result, but it takes a lot of time:
Criteria c = sessionFactory.getCurrentSession().createCriteria(User.class);
List<User> users = c.list();
List<User> specialUsers = new ArrayList<>();
for (User user : users) {
List<Perm> userPerms = user.getProfile().getPerms();
for (Perm perm : userPerms) {
if (perm.getId().equals(SPECIAL_ID)) {
specialUsers.add(user);
}
}
}
return specialUsers;
and the JSON is like:
[{"id":111,"name":"Name111"},{"id":222,"name":"Name222"}]
In attempt to improve performance I tried code below. In SQL app the results are OK, a few records of users:
String sql = "SELECT u.id, u.name FROM app.user u inner join app.perms p where u.profile = p.profile AND p.right= :rightId";
List<User> specialUsers= (List<User>)sessionFactory.getCurrentSession()
.createSQLQuery(sql)
.setParameter("rightId", SPECIAL_ID)
.list();
return specialUsers;
Now the 'JSON' however looks like this:
[[111,"Name111"],[222,"Name222"]]
I tried several things, like select *, criteria.add(Restrictions...) but to no effect. What I noticed is that in the first case specialUsers.toString returns proper data, in the second case it returns meaningless Strings like Ljava.lang.Object;#23e1469f.
Any hints how to solve this?
I managed to solve this in this way, may not be perfect:
// get ids of all special users
String sql = "SELECT u.id FROM app.user u inner join app.perms p where u.profile = p.profile AND p.right= :rightId";
List<Integer> intIds = sessionFactory.getCurrentSession()
.createSQLQuery(sql)
.setParameter("rightId", SPECIAL_ID)
.list();
// convert to long values
List<Long> longIds = intIds.stream()
.mapToLong(Integer::longValue)
.boxed().collect(Collectors.toList());
// get all special users
Criteria c = sessionFactory
.getCurrentSession()
.createCriteria(User.class)
.add(Restrictions.in("id", longIds));
List<User> specialUsers = c.list();
return specialUsers;
}
I have an #Entity class Company with several attributes, referencing a companies Table in my db. One of them represents a Map companyProperties where the companies table is extended by a company_properties table, and the properties are saved in key-value format.
#Entity
#Table(name = "companies")
public class Company extends AbstractEntity {
private static final String TABLE_NAME = "companies";
#Id
#GeneratedValue(generator = TABLE_NAME + SEQUENCE_SUFFIX)
#SequenceGenerator(name = TABLE_NAME + SEQUENCE_SUFFIX, sequenceName = TABLE_NAME + SEQUENCE_SUFFIX, allocationSize = SEQUENCE_ALLOCATION_SIZE)
private Long id;
//some attributes
#ElementCollection
#CollectionTable(name = "company_properties", joinColumns = #JoinColumn(name = "companyid"))
#MapKeyColumn(name = "propname")
#Column(name = "propvalue")
private Map<String, String> companyProperties;
//getters and setters
}
The entity manager is able to perform properly find clauses
Company company = entityManager.find(Company.class, companyId);
However, I am not able to perform JPQL Queries in this entity and retrieve the Map accordingly. Since the object is big, I just need to select some of the attributes in my entity class. I also do not want to filter by companyProperties but to retrieve all of them coming with the proper assigned companyid Foreign Key. What I have tried to do is the following:
TypedQuery<Company> query = entityManager.createQuery("SELECT c.id, c.name, c.companyProperties " +
"FROM Company as c where c.id = :id", Company.class);
query.setParameter("id", companyId);
Company result = query.getSingleResult();
The error I get is:
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [SELECT c.id, c.name, c.companyProperties FROM Company as c where c.id = :id]. [21, 40] The state field path 'c.companyProperties' cannot be resolved to a collection type.
org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1616)
org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1636)
com.sun.enterprise.container.common.impl.EntityManagerWrapper.createQuery(EntityManagerWrapper.java:476)
Trying to do it with joins (the furthest point I got was with
Query query = entityManager.createQuery("SELECT c.id, c.name, p " +
"FROM Company c LEFT JOIN c.companyProperties p where c.id = :id");
does not give me either the correct results (it only returns the value of the property and not a list of them with key-value).
How can I define the right query to do this?
Your JPA syntax looks off to me. In your first query you were selecting individual fields in the Company entity. But this isn't how JPA works; when you query you get back the entire object, with which you can access any field you want. I propose the following code instead:
TypedQuery<Company> query = entityManager.createQuery("from Company as c where c.id = :id", Company.class);
query.setParameter("id", companyId);
Company result = query.getSingleResult();
Similarly, for the second join query I suggest the following code:
Query query = entityManager.createQuery("SELECT c" +
"FROM Company c LEFT JOIN c.companyProperties p WHERE c.id = :id");
query.setParameter("id", companyId);
List<Company> companies = query.getResultList();
The reason why only select a Company and not a property entity is that properties would appear as a collection inside the Company class. Assuming a one to many exists between companies and properties, you could access the propeties from each Company entity.
You are expecting to get a complete Company object when doing select only on particular fields, which is not possible. If you really want to save some memory (which in most cases would not be that much of a success) and select only some field, then you should expect a List<Object[]>:
List<Object[]> results = entityManager.createQuery("SELECT c.id, c.name, p " +
"FROM Company c LEFT JOIN c.companyProperties p where c.id = :id")
.setParameter("id", companyId)
.getResultList();
Here the results will contain a single array of the selected fields. You can use getSingleResult, but be aware that it will throw an exception if no results were found.
Im working with hibernate and java. I have an Group class and a User class. They share a many-to-many relationship as shown in this ERD .
What Im trying to achieve is that I want to retrieve a list of groups with the condition that they contain a User with a certain User_id.
In the GroupDao I have defined a function retrieveForUser in which I tried to retrieve the list using hibernate query language:
public List<Group> retrieveForUser(int userid){
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
String hql = "select distinct g from Group g " +
"join g.allGroupMembers u " +
"where u.id = :id";
Query query = session.createQuery(hql).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
query.setParameter("id", userid);
List<Group> list = query.list();
session.getTransaction().commit();
return list;
}
When I try to loop throught the resulting list using:
for(Group g : groupDao.retrieveForUser(user1.getId())){
System.out.println(g.getName());
}
I get the following errormessage:
java.lang.ClassCastException: java.util.HashMap cannot be cast to nl.hu.jelo.domain.group.Group
Question
How can I achieve it so that I end up with an List<Group> with only groups that contain a User with an certain User_id
You do not need to set result transformer. ALIAS_TO_ENTITY_MAP is for other purpose.
Simply do
String hql = "select distinct g from Group g " +
"join g.allGroupMembers u " +
"where u.id = :id";
Query query = session.createQuery(hql);
query.setParameter("id", userid);
is good enough.
Something off topic, are you sure you want to handle transaction manually like that?
How I can get list of custom objects, like results below query:
SELECT p.category.id, count(p.id) FROM Product p left join p.category c WHERE p.seller.id=:id GROUP BY c.id
By example:
return getEntityManager().createQuery("SELECT p.category.id, count(p.id) FROM Product p left join p.category c WHERE p.seller.id=:id GROUP BY c.id").setParameter("id", id).getResultList();
I need a map with category id and number of products in category.
Unfortunately, JPA doesn't provide a standard way to retrieve the results in a Map. However, building up your map manually by walking through the result list is simple enough:
TypedQuery<Object[]> q = getEntityManager().createQuery(
"SELECT c.id, count(p.id) " +
"FROM Product p LEFT JOIN p.category c " +
"WHERE p.seller.id = :id " +
"GROUP BY c.id", Object[].class).setParameter("id", id);
List<Object[]> resultList = q.getResultList();
Map<String, Long> resultMap = new HashMap<String, Long>(resultList.size());
for (Object[] result : resultList)
resultMap.put((String)result[0], (Long)result[1]);
Assuming you are using hibernate(tagged), can try the below HQL query, I haven't tested.
SELECT new map(p.category.id as category_id, count(p.id) as id_count) FROM Product p left join p.category c WHERE p.seller.id=:id GROUP BY c.id
Using JPA 2.0 and EclipseLink impl
For the first question: list of custom objects(no table objects):
answer: create a custom model and use the #Entity and #Id
#Entity
public class QueryModelDTO implements Serializable{
#Id
private Integer categoryId;
private int count;
---gets and sets
}
create the query and execute
QueryModelDTO qm = (QueryModelDTO) em.createQuery(
"SELECT p.category.id as categoryId, count(p.id) as count FROM Product p
left join p.category c WHERE p.seller.id=:id
GROUP BY c.id",QueryModelDTO.class)
.setParameter("id", id).getSingleResult();
For the second: how to read the response on a map
answer: Use the QueryHints and ResultTypes (this is one variant for the #DannyMo answer)
Query q = em.createNativeQuery("SELECT * FROM Foo f");
q.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
List<Map> lm = q.getResultList();
for (Map map : lm) {
for (Object entry : map.entrySet()) {
Map.Entry<DatabaseField, Object> e = (Map.Entry<DatabaseField, Object>) entry;
DatabaseField key = e.getKey();
Object value = e.getValue();
log.debug(key+"="+value);
}
}
I hope this helps