Query by other entity's id in JPA repository - java

I have an SQL database with two related tables, my_entities and custom_entities.
The my_entities table has the columns id, custom_entity_id.
In my Groovy code, both are handled using Entity classes:
#Entity
class MyEntity {
#ManyToOne(fetch = EAGER)
#JoinColumn(name = "custom_entity_id")
CustomEntity customEntity
[...]
}
and
class CustomEntity {
#Id
#GeneratedValue(generator = "pooled")
#GenericGenerator(name = "pooled", strategy = "org.hibernate.id.enhanced.TableGenerator", parameters = [
#Parameter(name = "value_column_name", value = "sequence_next_hi_value"),
#Parameter(name = "prefer_entity_table_as_segment_value", value = "true"),
#Parameter(name = "optimizer", value = "pooled"),
#Parameter(name = "increment_size", value = "100"),
#Parameter(name = "initial_value", value = "100")])
Long id
[...]
}
I have a JPA repository MyEntityRepository class for querying the my_entity table:
#Repository
interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
[...]
}
In MyEntityRepository, I am trying to implement a findByCustomEntityIds() method like this:
#Query("""
select e
from MyEntity e
where e.deleted = false
and e.customEntity in :ids
""")
List<TestCollectionQuery> findByCustomEntityIds(#Param("ids") List<Long> ids)
In MySQL, the query is rather simple (for instance with id 2400), clearly no joins required:
SELECT t.* FROM my_entities t WHERE custom_entity_id in (2400);
Apart from the automatic translation from underscores to camel case, the trailing _id in the SQL table column name is stripped; otherwise, the code does not compile.
I also have a custom Groovy class which calls that method:
class MyClass {
#Autowired
MyEntityRepository myEntityRepository
List<MyEntity> getEntities(List<Long> customEntityIds) {
return myEntityRepository.findByCustomEntityIds(customEntityIds)
}
The call raises the following exception:
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value element [2400] did not match expected type [...CustomEntity (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value element [2400] did not match expected type [...CustomEntity (n/a)]
[...]
Caused by: java.lang.IllegalArgumentException: Parameter value element [2400] did not match expected type [...CustomEntity (n/a)]
From the error message, I understand that the input should be a list of CustomEntity objects, instead of IDs (Long).
A potential workaround seems to be querying the custom_entities table in order to convert the custom entity ids from Long to CustomEntity objects.
However, that would require adding a JPA CustomEntityRepository, and seems very inefficient. It would require an additional database call just for the purpose of converting IDs into objects, so that I can eventually query for the IDs (which I already have had to begin with) in the my_entities table.
The reason why I have the IDs for the custom entities, but not the objects, is that they are provided by user input further up the line.
My question is thus: how can I implement a method in the MyEntityRepository that implements the MySQL query stated above based on a list of custom entity IDs, without converting them from Long into CustomEntity objects?
Or is there a more fundamental flaw in my design?

The query should end in "and e.customEntity.id in :ids" not in "and e.customEntity in :ids".

Related

Dynamic Query Instead of #subselect when giving an Entity data

I got the following entity:
#Entity
#Immutable
#Subselect("select distinct r.code, r.dsc\r\n"
+ "from roles r, accesses a\r\n"
+ "where r.code = '12'\r\n")
#MasterEntity(labelCode = "Scenario")
public class Scenario implements java.io.Serializable {
I want to use a dynamic query (or parameterized) instead of this static query.
for example: r.code should be equal to a parameter variable not "12"
is there a replacement for #Subselect while I use #Immutable?

HashMap to SQL when Key is an enum

I have the following entity class:
public class SportData {
//other attributes
#Column(name = "POINTS")
#ElementCollection
private Map<NBAEvent, Integer> points;
}
public enum NBAEvent {
THREE_POINT,
TWO_POINT,
FREE_THROW,
//..
}
When I generate the corresponding tables using ddl-auto: validate it creates the NBAEvent(which is the k) column with the data type as an integer(points_key int4 NOT NULL).
How can I generate a varchar type column for the points key column?
If I manually write the SQL like points_key CHARACTER VARYING(255) how would it affect?
I'm using Spring boot along with Spring JPA, DB is PostgreSQL.
When the key of your Map is an enum then you must also add the MapKeyEnumerated annotation.
The key is to declare it with the EnumType.STRING type.
#ElementCollection
#CollectionTable(name = "colltable")
#MapKeyEnumerated(EnumType.STRING)
#Column(name="POINTS")
private Map<NBAEvent, Integer> map;
Otherwise as per javadocs it defaults to: javax.persistence.EnumType.ORDINAL. And that results in an integer table while ddl creation.
you may also need the #CollectionTable annotation as above.

How to convert returned object to required bean/pojo

My requirement is say I have two table:
T1: id,name,email
T2: id,address
To get data from both table I have done like:
Collection ls=null;
EntityManager em=ConnectionUtils.getEntityManager();
tx= em.getTransaction();
tx.begin();
Query q=em.createQuery("select t1.name,t2.address from T1 t1, T2 t2");
ls=(List<T1T2>)q.getResultList();
OP:[[Ljava.lang.Object;#e836bd1, [Ljava.lang.Object;#561b6dc8,
[Ljava.lang.Object;#22c491a2, [Ljava.lang.Object;#17353483,
[Ljava.lang.Object;#260a905c, [Ljava.lang.Object;#7f8b9b86,
[Ljava.lang.Object;#268fbbd5, [Ljava.lang.Object;#2674b0ba,
[Ljava.lang.Object;#36fe970f, [Ljava.lang.Object;#46f75fe,
[Ljava.lang.Object;#31ab78f8, [Ljava.lang.Object;#7092fb41,
[Ljava.lang.Object;#41ada224, [Ljava.lang.Object;#6e700b2b]
ya its annoying.
I am getting data but its an normal Object.
I have created pojo as:
T1T2: String name;String address; to get returned object in this
format. but getting proper format instead Entity error and that pojo
is not error.
I want same type of concept as marshalling of json string to corresponding pojo.
If you want to use JPA for mapping to POJO's use #SqlResultSetMapping annotation
Assuming T1T2 has a constructor T1T2(String name, String address) add this to any of your entity class definition
#SqlResultSetMapping(name = "CUSTOM_MAPPING", classes = #ConstructorResult(
targetClass = T1T2.class,
columns = {#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "address", type = String.class)}))
Now you can use this mapping:
Query q=em.createNativeQuery("select t1.name,t2.address from T1 t1, T2 t2","CUSTOM_MAPPING");
List<T1T2> = q.getResultList();
Note that it work only on native queries. I'm assuming that there is no association defined at JPA entity level between T1 and T2, otherwise whole process is obsolete. If there is an association, use #JoinTable annotation to declare it and JPA will make sure to fetch association along with entity.
In case you don't need full ORM functionality (or do not know how to do it properly), you can accomplish your task with Spring's JdbcTemplate:
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
List<T1T2> pojos = jdbcTemplate.query(
"SELECT t1.name, t2.address FROM T1 t1 JOIN T2 t2 ON t1.id=t2.id",
new BeanPropertyRowMapper(T1T2.class));
BeanPropertyRowMapper maps result set columns to POJO fields that have the same name, respecting field types.
Mapping JPQL Query Result to POJO
When you execute the query
Query q=em.createQuery("select t1.name,t2.address from T1 t1, T2 t2");
Collection c = q.getResultList();
you'll get a collection of zero or more instances of arrays of type Object. If you want the result of the query be mapped to a POJO, you can use the so called Construcot Expression. To use it, first define your POJO as follows with an appropriate constructor:
package com.myproject.dto;
public class T1T2 {
private String name;
private String address;
public T1T2() {}
public T1T2(String name, String address) {
this.name = name;
this.address = address;
}
// getters + setters
}
Then you can formulate your query as follows:
String queryString = "SELECT NEW com.myproject.dto.T1T2(t1.name, t2.address) FROM T1 t1, T2 t2";
TypedQuery<T1T2> q = em.createQuery(queryString, T1T2.class);
Collection<T1T2> result = q.getResultList();
Now you should have a collection of zero or more POJO instances and you don't need to cast.
Here is an extract from the JPA 2.0 Spec if you want to understand the details:
4.8.2 Constructor Expressions in the SELECT Clause
A constructor may be used in the SELECT list to return an instance of a Java class. The specified class is not required to be an entity or to be mapped to the database. The constructor name must be fully qualified.
If an entity class name is specified as the constructor name in the SELECT NEW clause, the resulting entity instances are in the new state.
If a single_valued_path_expression or identification_variable that is an argument to the constructor references an entity, the resulting entity instance referenced by that single_valued_path_expression or identification_variable will be in the managed state.
Note: Your query is building a x-product, do you really want that? As of now, you'll get any name with any address.

HQL query with several joins

I have the following relation of three classes:
#Entity
public class User{
#OnetoMany
List<Attribute> attributes = new ArrayList<Attribute>();
}
#Entity
public class Attribute{
#ManyToOne
AttributeType attributeType;
}
#Entity
public class AttributeType{
#Column
String type;
}
One user can have n attributes of m types.
I need to create HQL query which will return all Atribute Types List<AttributeType> of specific user attributes.
For example user has attribute a of type t, atribute b of type t and attribute c of type t1.
I need to return List<AttributeType> which will contain t and t1.
Please help. I just got lost in this query.
You shall map Attribute to User many to one relation, so the following query is what you need:
select distinct atr.attributeType
from Attribute atr
where atr.user = :user
I think the following query will work too:
select distinct atrs.attributeType
from User as user
join user.attributes as atrs
where user.id = :user_id

How to use CreateNativeQuery to query single value from SQL database value?

using a CRUD and want o access a certain value in the databse. what I tried is:
Query query = em.createNativeQuery("select o from Table o where o.IDfield= '938'");
Table o = (Table)query.getSingleResult();
but this fails.
Any suggestions?
Your select looks like a jpql query so it won't work when passed directly to the database as a native query. You can use the createQuery method instead. Even better would be to use a named parameterized query declared on your entity like this:
#Entity
#NamedQueries({#NamedQuery(name="MyEntity.findById", query="select o from MyEntity o where o.IDfield = :id"})
public class MyEntity {
#Id
private String IDfield;
}
...
MyEntity entity = (MyEntity)em.createNamedQuery("MyEntity.findById").setParameter("id", "938").getSingleResult();

Categories