JPA native sql query mapping error? - java

I have a JPA entity MyEntity which includes a composite primary key in a #Embeddable class MyEntityPK.
I am using a native sql query in method getThreeColumnsFromMyEntity():
public List<MyEntity> getThreeColumnsFromMyEntity() {
List<MyEntity> results = em.createNativeQuery("select pid,name,dateofbirth from (select pid,name, dateofbirth,max(dateofbirth) "
+ "over(partition by pid) latest_dateofbirth from my_entity_table) where"
+ " dateofbirth = latest_dateofbirth;","myEntityMapping").getResultList();
return results;
My #SqlResultSetMapping:
#SqlResultSetMapping(
name = "myEntityMapping",
entities = {
#EntityResult(
entityClass = MyEntityPK.class,
fields = {
#FieldResult(name = "PID", column = "pid"),
#FieldResult(name = "NAME", column = "name")}),
#EntityResult(
entityClass = MyEntity.class,
fields = {
#FieldResult(name = "dateofbirth", column = "dateofbirth")})})
My JPA columns are named : #Column(name="DATEOFBIRTH"), "PID" and "NAME".
I tested my sql statement straight on the db and it works fine.
When i run it on Eclipse I get an Oracle error:
ORA-00911 and "Error code 911 , Query: ResultSetMappingQuery [..]
My guess is there is something wrong with the mapping but I cannot find out what it is.

I assume you get this error because you are missing the alias name for the subquery, so instead you can try this :
select
pid,
name,
dateofbirth
from
(
select
pid,
name,
dateofbirth,
max(dateofbirth) over(partition by pid) AS latest_dateofbirth
from
my_entity_table
) second_result
-- ^--------------- use an aliase name to the subquery
where
second_result.dateofbirth = latest_dateofbirth
-- ^----use the aliase name to reference to any of its fields, in your case 'dateofbirth'
Take a look about the error meaning here ORA-00911: invalid character tips

Related

JPQL include elementCollection map in select statement

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.

JPA mapping entities with hql column names

I am trying to use a feature like RowMapper which provides me with ResultSet, so that I can set the attributes of my pojo by taking resultSet.getString("column_name") in JPA.
But JPA doesn't seems to provide such a feature.
StringBuffer rcmApprovalSqlString = new StringBuffer(QueryConstants.APPROVAL_DETAILS_SQL);
List<ApprovalDTO> finalApprovalList = null;
Query rcmApprovalTrailQuery = getEntityManager().createQuery(rcmApprovalSqlString.toString());
rcmApprovalTrailQuery.setParameter(1,formInstanceId);
List<?> approvalList = rcmApprovalTrailQuery.getResultList();
finalApprovalList = new ArrayList<ApprovalDTO>();
for(Object approvalObj : approvalList){
Object[] obj = (Object[]) approvalObj;
ApprovalDTO approvalDTO = new ApprovalDTO();
approvalDTO.setDeptName(obj[0]!=null? obj[0].toString() : NAPSConstants.BLANK_STRING);
approvalDTO.setUserId(obj[1]!=null? obj[1].toString()+" "+obj[2].toString() : NAPSConstants.BLANK_STRING);
approvalDTO.setComment(obj[6]!=null? obj[6].toString() : NAPSConstants.BLANK_STRING);
finalApprovalList.add(approvalDTO);
}
So instead of doing approvalDTO.setComment(obj[6]) which is the 6th element of array, can I do something like approvalDTO.setComment(rs.getString("comments")); ?
So if in future my column position change in the query, I will not have to change my DAO code to match the column number.
My hql query = select ad.departmentid.departmentname, ad.userid.userfirstname, ad.userid.userlastname, ad.napsroleid.napsrolename,
ad.approvalstatus, ad.approvaltimestamp, ad.approvalcomments
from ApprovaldetailsTbl ad
where ad.forminstanceid.forminstanceid = ?1
order by approvallevelid asc
With JPA 2.1 you have a great possibility to use SqlResultSetMapping. You can find out more for example here:
http://www.thoughts-on-java.org/2015/04/result-set-mapping-constructor.html
http://www.thoughts-on-java.org/2015/04/result-set-mapping-basics.html
http://www.thoughts-on-java.org/2015/04/result-set-mapping-complex.html
The idea is that instead of doing it as you used to do:
List<Object[]> results = this.em.createNativeQuery("SELECT a.id, a.firstName, a.lastName, a.version FROM Author a").getResultList();
results.stream().forEach((record) -> {
Long id = ((BigInteger) record[0]).longValue();
String firstName = (String) record[1];
String lastName = (String) record[2];
Integer version = (Integer) record[3];
});
you can introduce an annotation:
#SqlResultSetMapping(
name = "AuthorMapping",
entities = #EntityResult(
entityClass = Author.class,
fields = {
#FieldResult(name = "id", column = "authorId"),
#FieldResult(name = "firstName", column = "firstName"),
#FieldResult(name = "lastName", column = "lastName"),
#FieldResult(name = "version", column = "version")}))
and afterwards use the mapping (by specifying mapping name) in your query:
List<Author> results = this.em.createNativeQuery("SELECT a.id as authorId, a.firstName, a.lastName, a.version FROM Author a", "AuthorMapping").getResultList();
I am able to fetch the desired result only with Native query and not with NamedNativeQuery -
Query rcmApprovalTrailQuery = getEntityManager().createNativeQuery(rcmApprovalSqlString.toString(),"ApprovalMapping");
rcmApprovalTrailQuery.setParameter(1,formInstanceId);
List<ApprovaldetailsTbl> approvalList = rcmApprovalTrailQuery.getResultList();
My native query -
String RCM_APPROVAL_DETAILS_SQL = "select * "+
" from ApprovalDetails_TBL ad " +
" where ad.ApprovalDetailsId = ? ";
SqlResultSetMapping -
#SqlResultSetMapping(name="ApprovalMapping",
entities=#EntityResult(entityClass=ApprovaldetailsTbl.class
))
Note that you need to map all the column names to the entity field names if you are not using * in select query e.g -
fields = {
#FieldResult(name = "col1", column = "alais1"),
#FieldResult(name = "col2", column = "alais2")})

EclipseLink JPA: list entities with reference variables

I am using JPA's eclipseLink to perform CRUD operations on my entities. I am facing following problem:
I have two tables in DB:
CREATE TABLE User (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(30) NOT NULL UNIQUE,
email VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
signUpDate timestamp NOT NULL DEFAULT NOW()
);
CREATE TABLE Friendship (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
friendsSince timestamp NOT NULL DEFAULT NOW(),
user1_Id INTEGER NOT NULL REFERENCES User(id),
user2_Id INTEGER NOT NULL REFERENCES User(id)
);
The corresponding Entities
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
private String password;
#Temporal(value = TemporalType.DATE)
private Date signUpDate;
// constructors & setters & getters ...
}
#Entity
public class Friendship {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name="user1_Id", referencedColumnName = "id")
private User user1;
#ManyToOne
#JoinColumn(name="user2_Id", referencedColumnName = "id")
private User user2;
#Temporal(value = TemporalType.DATE)
private Date friendsSince;
// constructors & setters & getters ...
}
If I want to retrieve a list of some entities, according to "WHERE" clause of a query I get this "unknown state or association field [user1_Id] of class [com.filip.xxx.Friendship]" error.
Specifically:
I try to build this query:
Query query = mgr.createQuery("select f.id ,f.friendsSince, f.user1_Id, f.user2_Id from Friendship f where f.user1_Id = :user1Id and f.user2_Id = :user2Id or f.user1_Id = :user11Id and f.user2_Id = :user12Id");
and recieve this exception:
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Error compiling the query [select f.id ,f.friendsSince, f.user1_Id, f.user2_Id from Friendship f where f.user1_Id = :user1Id and f.user2_Id = :user2Id or f.user1_Id = :user11Id and f.user2_Id = :user12Id], line 1, column 31: unknown state or association field [user1_Id] of class [com.filip.xxx.Friendship].
It seems like there is a problem with mapping attributes back to the entities, because I have no problem with persisting these two entities.
And interesting is that, if I run this query:
Query query = mgr.createQuery("select f from Friendship f");
It returns me the correct list of all friendships entities.
Notice that the reference variable's name in friendship entity(user1, user2) are not the same as corresponding table's variables (user1_Id, user2_Id). Before I have used the same variable names in entity as in table, but recieved this error at persisting friendship entity:
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461):
org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'USER1_ID' in 'field list'
Error Code: 1054
Call: INSERT INTO FRIENDSHIP (FRIENDSSINCE, USER1_ID, USER2_ID) VALUES (?, ?, ?)
bind => [3 parameters bound]
Basically I don't understand, why eclipse link renames the entity's reference variables (user1 -> USER1_ID, user2 -> USER2_ID) when creating sql query, when it has than problems to map it back to the entities.
I have already tried these solutions:
Build query and return user1_Id column as user1 and user2_Id as user2
select f.id ,f.friendsSince, f.user1_Id as user1, f.user2_Id as user2 from Friendship f where f.user1_Id = :user1Id and f.user2_Id = :user2Id or f.user1_Id = :user11Id and f.user2_Id = :user12Id
but recieved the same IllegalArgumentException as above.
Could you help me solve this problem ?
Thanks
The exception
unknown state or association field [user1_Id] of class [com.filip.xxx.Friendship]
is received because you are using user1_Id name which is a database column name.
From the other hand ElementManager.createQuery() method expects JPQL string which accepts the entity's field name user1. Try to replace your query string with:
select f.id, f.friendsSince, f.user1, f.user2
from Friendship f
where f.user1 = :user1Id and
f.user2 = :user2Id or
f.user1 = :user11Id and
f.user2 = :user12Id

hibernate "where" query only works for id field

I have a problem with a Hibernate query that looks as follows:
List persons = getList("FROM creator.models.Person p WHERE p.lastName="+userName);
(the getList(String queryString) method just executes the query using a session factory.)
This is my person class:
#Entity
#Table(name="persons")
public class Person{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name="first_name", nullable=false, updatable=true)
private String firstName;
#Column(name="last_name", nullable=false, updatable=true)
private String lastName;
/// etc
And this is the table:
CREATE TABLE persons(
id INTEGER NOT NULL AUTO_INCREMENT,
first_name CHAR(50),
last_name CHAR(50),
abbreviation CHAR(4),
PRIMARY KEY (id)
);
Searching for a person with the name TestName, I get an exception with this message:
org.hibernate.exception.SQLGrammarException: Unknown column 'TestName' in 'where clause'
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:82)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
//etc
The query created by Hibernate looks like this:
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select person0_.id as id8_, person0_.abbreviation as abbrevia2_8_, person0_.first_name as first3_8_, person0_.last_name as last4_8_ from persons person0_ where person0_.last_name=TestName
Dec 10, 2012 5:14:26 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
Searching for the id (...WHERE p.id="3") works fine, by the way!
I hope somebody knows what is going wrong because for me the query looks right and I can't find out why the lastName is seen as a column name suddenly.
You need to put userName in quotes:
"FROM creator.models.Person p WHERE p.lastName='"+userName+"'";
Or (which is much better) to use parameters
replace your hql with:
Query query = session.createQuery("from creator.models.Person p where p.lastName = ?")
.setParameter(0, userName);
List persons = query.list();
that way you also prevent sql-injection.
you need to wrap your parameter with single quotes:
List persons = getList("FROM creator.models.Person p WHERE p.lastName='"+userName+"'");
but much better with a parameterized query:
String hql = "FROM creator.models.Person p WHERE p.lastName= :userName";
Query query = session.createQuery(hql);
query.setString("userName",userName);
List results = query.list();

Query using alias on column give an error

When i use alias for column i get error. Without alias everytinig works good. What is the problem with that ? This is simple example, but need to use more aliases in real project to wrap results in some not-entity class, but can't because of this error. How to solve this ?
NOT WORKING (with alias on id column):
public List<Long> findAll(Long ownerId) {
String sql = "select id as myId from products where ownerId = "+ownerId;
SQLQuery query = getSession().createSQLQuery(sql);
return query.list();
}
Error:
WARN [JDBCExceptionReporter:77] : SQL Error: 0, SQLState: S0022 ERROR
[JDBCExceptionReporter:78] : Column 'id' not found.
WORKING (without alias):
public List<Long> findAll(Long ownerId) {
String sql = "select id from products where ownerId = "+ownerId;
SQLQuery query = getSession().createSQLQuery(sql);
return query.list();
}
If your "product" is mapped, hibernate probably don't know about "myId" and therefore can't select it.
You can try something like:
getSession().createSQLQuery(sql).addScalar("myId", Hibernate.LONG)

Categories

Resources