I've got a value object for which i want to map my native query resultset.
I'm using #SQLResultSetMapping to map object fields but when value of any column is null, exception is throw:
Exception [EclipseLink-6177] (Eclipse Persistence Services - 2.6.1.v20150916-55dc7c3): org.eclipse.persistence.exceptions.QueryException
Exception Description: The column result [custom_properties] was not found in the results of the query.
My Entity Class and Mapping
#Entity
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#Table(name = "account_master")
#SqlResultSetMapping(name = "DTO_MAPPING", classes = #ConstructorResult(
targetClass = AccountDTO.class,
columns = {#ColumnResult(name = "id"),
#ColumnResult(name = "name"),
#ColumnResult(name = "custom_properties")
})
)
public class Account implements Serializable {
// fields, setters and getters
}
Value Object:
public class AccountDTO {
public AssetDTO(){
}
public AssetDTO(int id, String name, String customProperties) {
this.id = id;
this.name = name;
this.customProperties = customProperties;
}
}
And the execution statement
List<AccountDTO> accList = entityManager.createNativeQuery("SELECT id, name, custom_properties FROM account_master WHERE acc_hierarchy <# 2.3", "DTO_MAPPING").getResultList()
If custom_properties (which is nullable) is replaced by a static value in query, mapping works perfectly fine. Is there something wrong with the implementation? As mapping null value seems like a common scenario.
This was submitted as a bug in EclipseLink: https://bugs.eclipse.org/bugs/show_bug.cgi?id=484276
As a workaround, you can specify the type in the
#ColumnResult
annotation.
So you should change your code to:
#Entity
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#Table(name = "account_master")
#SqlResultSetMapping(name = "DTO_MAPPING", classes = #ConstructorResult(
targetClass = AccountDTO.class,
columns = {#ColumnResult(name = "id"),
#ColumnResult(name = "name"),
#ColumnResult(name = "custom_properties", type=String.class)
})
)
public class Account implements Serializable {
// fields, setters and getters
}
Related
I have these entities, from which I want get List of Tuples containing information from both of them:
which should look like this:
+--------+-----+---------+
|name |login|user_type|
+--------+-----+---------+
| |admin|admin |
|John Doe|john |user |
|Jane Doe|janed|user |
|........|.....|.........|
The thing is, my JPA skill got quite rusty and I forgot how to use Entity Managers. I know how to make basic JPQL queries or build-in stuff, but sadly it's not enough for me (since I need to use that list for filling up table in my UI). So, how did I should use Entity Managers? (if I should use that at all ;) )
Edit: now I think that using DTO projection is better; here's my mapping class
public class PersonUserMap {
private Integer personID;
private String name;
private String login;
private UserType userType;
public Integer getPersonID() {
return personID;
}
public String getName() {
return name;
}
public String getLogin() {
return login;
}
public UserType getUserType() { //custom Enum
return userType;
}
}
my annotation in People class:
#SqlResultSetMapping(
name = "PersonUserMapping",
classes = #ConstructorResult(
columns = { #ColumnResult(name = "personID", type=Integer.class),
#ColumnResult(name = "name"),
#ColumnResult(name = "login"),
#ColumnResult(name = "userType",type = UserType.class)},
targetClass = PersonUserMap.class))
and when using native query like this:
Query q = entityManager.createNativeQuery("Select p.personid, p.first_name || ' ' || p.last_name as name, u.login, u.user_type from people p join users u on p.user_idusers = u.idusers","PersonUserMapping");
it throws exception Could not resolve column name in result set [userType]
Thanks for #Chris help I finally got somewhere; my set mapping is looking like this:
#SqlResultSetMapping(
name = "PersonUserMapping",
classes = #ConstructorResult(
columns = { #ColumnResult(name = "personid", type=Integer.class),
#ColumnResult(name = "name"),
#ColumnResult(name = "login"),
#ColumnResult(name = "user_type",type = UserType.class)},
targetClass = PersonUserMap.class))
and my query looks like this
Query q = entityManager.createNativeQuery("select personid, concat(first_name, ' ', last_name) as 'name', users.login, users.user_type from aspirejestracja.people"
+ " full join aspirejestracja.users on user_idusers = users.idusers ", "PersonUserMapping");
now I can display all users which I need :)
I am working with spring boot. I want to apply a join in #Query and get the results in List.
objDto.java
#ToString
#Setter
#Getter
#AllArgsConstructor
public class objDto {
public int id;
private String title;
}
ObjDomain.java
#ToString
#Getter
#Setter
#Entity
#NoArgsConstructor
public class ObjDomain implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int id;
public String title;
}
ShowDetailsRepository.java
#Repository
public interface ShowDetailsRepository extends JpaRepository<ObjDomain, Long> {
#Query(value ="//JOIN QUERY HERE//", nativeQuery = true)
List<ObjDto> showDetails();
}
My Join query is working well when I used it in MYSQL CLI.
Error : No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.example.com.data.transfer.objects.ObjDto]
Expected Result : [{id: "1", title: "DEF"}]
You can create an interface projection instead of a DTO and in that interface you can only have the getter methods and pass that interface as the type of List.
#Query(value ="//JOIN QUERY HERE//", nativeQuery = true)
List<YourInterface> showDetails();
and your interface will look like this:
public interface YourInterface {
int getid();
String gettitle();
}
And make sure the getter methods in interface are in the same sequence as the values query results.
You will have to create sqlResultsetMapping..by doing that you are telling the sql in which class you want map the resultset. And also u can create the projection with help of interface. which can take less efforts compare to sqlResultsetMapping.
Below is the example
Lets say i want fetch few data from multiple table using native join query.
For Example- i will have to fetch empname and his dept from emp and dept table so my native query looks like below:
#Query(value = "SELECT e.name as empName,d.name as deptName FROM emp e left join dept d on d.empid=e.id WHERE e.id=:Id ,nativeQuery = true)
List<CustomData> findByRestaurantMenuGrouping(#Param("menuId") Long menuId);
Now put below resultsetmapping above any entity i can either emp or dept.
#SqlResultSetMapping(name = "customDataMapping",
classes = #ConstructorResult(
targetClass = CustomData.class,
columns = {
#ColumnResult(name = "empName", type = String.class),
#ColumnResult(name = "deptName", type = String.class),
}
)
)
-And create CustomData class as response class with 2 fields and parameterized constructer.
After you can call query using entitymanager by providing resultsetmapping which we created above.
List<CustomData> customdata= (List<CustomData>) em.createNativeQuery(quiStringBuilder.toString().trim(), "customDataMapping").getResultList();
You should use the combine of annotation SqlResultSetMapping and NamedNativeQuery.
For example:
In your entity:
#ToString
#Getter
#Setter
#Entity
#NoArgsConstructor
#SqlResultSetMappings({
#SqlResultSetMapping(
name = "ObjDtoMapping",
classes = #ConstructorResult(
targetClass = objDto.class,
columns = {
#ColumnResult(name = "id"),
#ColumnResult(name = "title")
}
)
)
})
#NamedNativeQuery(name = "showDetails", query = "SELECT a.id, a.title from
ObjDomain a", resultSetMapping = "ObjDtoMapping")
public class ObjDomain implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int id;
public String title;
}
And then, in your repository:
#Repository
public interface ShowDetailsRepository extends JpaRepository<ObjDomain, Long> {
#Query(name ="showDetails", nativeQuery = true)
List<ObjDto> showDetails();
}
change List<ObjDto> to
List<Object[]>
then in Service layer
List<Object[]> objectList = repositoryObj.showDetails();
for (Object[] obj : objectList) {
//.......
}
So I am writing native queries because they are very complex to get all the needed data and so on. I am currently facing a problem which is normally done by Hibernate/JPA etc.
Imagine the following:
#Entity
FooEntity{
#Id
public Long id;
#ManyToMany
public List<FeeEntity> feeEntities;
}
#Entity
FeeEntity{
#Id
public Long id;
#Column
public String name;
}
And some DTO
FooDTO{
private final Long id;
private final List<FeeDTO> someStrings;
public FooDTO(Long id, List<FeeDTO> feeDtos){
...
}
}
My #SqlResultSetMapping looks basically like
#SqlResultSetMapping(name = "FooDTO",
classes = #ConstructorResult(targetClass = FooDTO.class, columns = {
#ColumnResult(name = "id", type = Long.class),
//TODO #ColumnResult(name = "feeDtos", type = FeeDtos.class)
})
)
The named native query looks something like:
#NamedNativeQueries({
#NamedNativeQuery(
name = "FooData",
query = "SELECT MAINSELECT.ID AS id, " +
"???" +
" FROM Foo MAINSELECT WHERE ... " +
...,
resultSetMapping = "FooDTO")
})
How do I have to write the native query? Is this even possible without a subquery or do I have to do/execute a subquery for each datarow of the result? I was not able to find something on beloved google.
That's not possible.
The result of SQL query is always a table. So there are no nested tables and hence you cannot map it to a Collection.
I have a complex native query and I am trying to map its result to a non-entity DTO class. I am trying to use JPA's SqlResultSetMapping with ConstructorResult
My DTO class
#Data
public class Dto {
private Long id;
private String serial;
private Long entry;
private int numOfTasks;
}
My entity class, which has the repository interface that I will call this native query result.
#SqlResultSetMapping(
name = "itemDetailsMapping",
classes = {
#ConstructorResult(
targetClass = Dto.class,
columns = {
#ColumnResult(name = "ID"),
#ColumnResult(name = "SERIAL"),
#ColumnResult(name = "ENTRY"),
#ColumnResult(name = "TASKS")
}
)
}
)
#NamedNativeQuery(name = "getItemDetails", query = "complex query is here", resultSetMapping = "itemDetailsMapping")
#Entity
#Data
public class Item {}
Repository
#Repository
public interface ItemRepository extends JpaRepository<Item, Long> {
...
List<Dto> getItemDetails();
}
When I call the getItemDetails() from ItemRepository I have the following error:
org.springframework.data.mapping.PropertyReferenceException: No
property itemDetails found for type Item
What is the proper way to use SqlResultSetMapping and ConstructorResult
and solve this problem.
Any help would be appreciated.
To use named queries the name of the named query must have the entity name as prefix:
#NamedNativeQuery(name = "Item.getItemDetails",
query = "complex query is here", resultSetMapping = "itemDetailsMapping")
Then the interface method must have the same name as the named query without the prefix:
List<Dto> getItemDetails();
-
Read more about Spring Data JPA and named queries in the reference doc https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.named-queries
Sorry for long title..
I have the following error when querying a TableColumn entity:
Could not set field value
[org.comp.domain.data.ConstantParameterType#18c81fe5] value by
reflection : [class org.comp.data.AnalogParameter.analogParameterType]
setter of org.comp.data.AnalogParameter.analogParameterType; nested
exception is org.hibernate.PropertyAccessException: Could not set
field value [org.comp.data.ConstantParameterType#18c81fe5] value by
reflection : [class
org.comp.domain.data.AnalogParameter.analogParameterType] setter of
org.comp.domain.data.AnalogParameter.analogParameterType
My model contains two distincts 'single table per class' hierarchies having Parameter and ParameterType as superclasses. Each subclasse of Parameter hierarchy is mapped with a subclass of ParameterType hierarchy through #ManyToOne associations.
Here is an extract of my model with involved entities (unrelated fields ommitted):
// `Parameter` Single Table Per Class hierarchy
#Entity
#Table(name="parameters")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
public abstract class Parameter {
}
#Entity
#DiscriminatorValue(value="analog")
public class AnalogParameter extends Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id")
private AnalogParameterType analogParameterType;
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
public void setAnalogParameterType(AnalogParameterType analogParameterType) {
this.analogParameterType = analogParameterType;
}
}
#Entity
#DiscriminatorValue(value="constant")
public class ConstantParameter extends Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id")
private ConstantParameterType constantParameterType;
public ConstantParameterType getConstantParameterType() {
return constantParameterType;
}
public void setConstantParameterType(ConstantParameterType constantParameterType) {
this.constantParameterType = constantParameterType;
}
}
// `ParameterType` Single Table Per Class hierarchy
#Entity
#Table(name="parameters_types")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
public abstract class ParameterType { }
#Entity
#DiscriminatorValue(value="analog")
public class AnalogParameterType extends ParameterType { }
#Entity
#DiscriminatorValue(value="constant")
public class ConstantParameterType extends ParameterType {
}
Here is the TableColumn which is mapped with Parameter superclass through a #ManyToOne association:
#Entity
#Table(name="tables_columns")
public class TableColumn {
#ManyToOne
#JoinColumn(name="parameter_id")
private Parameter parameter;
public Parameter getParameter() {
return parameter;
}
}
And here is the generated SQL when querying A TableColumn Entity:
select tablecolum0_.id as id1_12_0_, tablecolum0_.created_at as
created_2_12_0_, tablecolum0_.is_optional as is_optio3_12_0_,
tablecolum0_.parameter_id as paramete6_12_0_, tablecolum0_.position as
position4_12_0_, tablecolum0_.updated_at as updated_5_12_0_,
parameter1_.id as id2_8_1_, parameter1_.category as category1_8_1_,
parameter1_.created_at as created_3_8_1_, parameter1_.device_id as
device_14_8_1_, parameter1_.effective_date as effectiv4_8_1_,
parameter1_.expiry_date as expiry_d5_8_1_, parameter1_.fqn as
fqn6_8_1_, parameter1_.height_from_the_ground as height_f7_8_1_,
parameter1_.label as label8_8_1_, parameter1_.name as name9_8_1_,
parameter1_.resolution_label as resolut10_8_1_, parameter1_.updated_at
as updated11_8_1_, parameter1_.parameter_type_id as paramet15_8_1_,
parameter1_.data_validity_period as data_va12_8_1_,
parameter1_.resolution as resolut13_8_1_, device2_.id as id1_1_2_,
device2_.created_at as created_2_1_2_, device2_.device_type_id as
device_t8_1_2_, device2_.fqn as fqn3_1_2_, device2_.label as
label4_1_2_, device2_.name as name5_1_2_, device2_.notes as
notes6_1_2_, device2_.parent_device_id as parent_d9_1_2_,
device2_.plant_id as plant_i10_1_2_, device2_.updated_at as
updated_7_1_2_, constantpa3_.id as id2_9_3_, constantpa3_.created_at
as created_3_9_3_, constantpa3_.description as descript4_9_3_,
constantpa3_.is_signed as is_signe5_9_3_, constantpa3_.label as
label6_9_3_, constantpa3_.name as name7_9_3_, constantpa3_.updated_at
as updated_8_9_3_ from tables_columns tablecolum0_ left outer join
parameters parameter1_ on tablecolum0_.parameter_id=parameter1_.id
left outer join devices device2_ on parameter1_.device_id=device2_.id
left outer join parameters_types constantpa3_ on
parameter1_.parameter_type_id=constantpa3_.id where tablecolum0_.id=1
I'm using Hibernate 5.0.11 with MySQL in a Spring Boot 1.4.1 / Data Rest project
EDIT
I tried in a vanilla Maven/Hibernate project using the same database. I have the same error. If i query Parameter objects directly, it's OK, but i get the error if i query TableColumn:
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" );
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// The following works
List<Parameter> ps = entityManager.createQuery("from Parameter", Parameter.class).getResultList();
for (Parameter p: ps) {
System.out.println(p);
}
// But if i replace with following, i get the same error as reported
// in the beginning of this question
List<TableColumn> tcs = entityManager.createQuery("from TableColumn", TableColumn.class).getResultList();
for (TableColumn tc: tcs) {
System.out.println(tc);
}
entityManager.getTransaction().commit();
entityManager.close();
1) Your category is of size=7, but you are setting a value of 8 characters i.e, constant
#DiscriminatorColumn(name="category", length=7)//change to more than 7
Because, #DiscriminatorValue(value="constant")//here the length is 8
You can use the below code for Discriminator column:
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)//no need of length attribute
2) verify that for the property private AnalogParameterType analogParameterType; you have below setter and getter methods.
//setter
public void setAnalogParameterType(AnalogParameterType analogParameterType) {
this.analogParameterType = analogParameterType;
}
//getter
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
3) Instead of
#ManyToOne
#JoinColumn(name="parameter_type_id")
use #ManyToOne(cascade=CascadeType.ALL) on getters.
For example:
#ManyToOne(cascade = CascadeType.ALL)
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
So i solved my issue by mapping the two superclasses Parameter and ParameterType with insertable/updatable=false:
#Entity
#Table(name="parameters")
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id", insertable = false, updatable = false)
private ParameterType parameterType;
}
It appears that enforcing the association at the superclass level does the trick. I don't know if it is a pure side effect or a design need. I have not found any resources on the subject.