JPA Native Query select and cast object - java

I have got an Object Admin which extends User. By default both Objects are in the table User_ of my Derby Database (included fields from Admin). Normally I'd select an User like this:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root user= query.from(User.class);
Predicate predicateId = cb.equal(category.get("id"), id);
query.select(user).where(predicateId);
return em.createQuery(query).getSingleResult();
However due to the complexity of my query I'm using a native query like this:
Query query = em.createNativeQuery("SELECT USER.* FROM USER_ AS USER WHERE ID = ?");
query.setParameter(1, id);
return (User) query.getSingleResult();
Though this throws a cast exception. I figure this is due to any fields from Admin.
My question is, how can I select a User using a native query with an equal result as the first example (including the same values for #LOB and #ManyToOne (et cetera) as the JPQL query would return)?

You might want to try one of the following ways:
Using the method createNativeQuery(sqlString, resultClass)
Native queries can also be defined dynamically using the EntityManager.createNativeQuery() API.
String sql = "SELECT USER.* FROM USER_ AS USER WHERE ID = ?";
Query query = em.createNativeQuery(sql, User.class);
query.setParameter(1, id);
User user = (User) query.getSingleResult();
Using the annotation #NamedNativeQuery
Native queries are defined through the #NamedNativeQuery and #NamedNativeQueries
annotations, or <named-native-query> XML element.
#NamedNativeQuery(
name="complexQuery",
query="SELECT USER.* FROM USER_ AS USER WHERE ID = ?",
resultClass=User.class
)
public class User { ... }
Query query = em.createNamedQuery("complexQuery", User.class);
query.setParameter(1, id);
User user = (User) query.getSingleResult();
You can read more in the excellent open book Java Persistence (available in PDF).
───────
NOTE: With regard to use of getSingleResult(), see Why you should never use getSingleResult() in JPA.

The accepted answer is incorrect.
createNativeQuery will always return a Query:
public Query createNativeQuery(String sqlString, Class resultClass);
Calling getResultList on a Query returns List:
List getResultList()
When assigning (or casting) to List<MyEntity>, an unchecked assignment warning is produced.
Whereas, createQuery will return a TypedQuery:
public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass);
Calling getResultList on a TypedQuery returns List<X>.
List<X> getResultList();
This is properly typed and will not give a warning.
With createNativeQuery, using ObjectMapper seems to be the only way to get rid of the warning. Personally, I choose to suppress the warning, as I see this as a deficiency in the library and not something I should have to worry about.

When your native query is based on joins, in that case you can get the result as list of objects and process it.
one simple example.
#Autowired
EntityManager em;
String nativeQuery = "select name,age from users where id=?";
Query query = em.createNativeQuery(nativeQuery);
query.setParameter(1,id);
List<Object[]> list = query.getResultList();
for(Object[] q1 : list){
String name = q1[0].toString();
//..
//do something more on
}

Please refer JPA : How to convert a native query result set to POJO class collection
For Postgres 9.4,
List<String> list = em.createNativeQuery("select cast(row_to_json(u) as text) from myschema.USER_ u WHERE ID = ?")
.setParameter(1, id).getResultList();
User map = new ObjectMapper().readValue(list.get(0), User.class);

The best solution I found is using Interface projection .
At the beginning, I created a DTO class but it just didn't work, replacing the class with an interface like this works great:
#Query(value = "SELECT vat as vatRate, SUM(...) as amount from ...", nativeQuery = true)
List<VatReportLine> getSalesVats();
public interface VatReportLine {
double getVatRate();
long getAmount();
}

First of all create a model POJO
import javax.persistence.*;
#Entity
#Table(name = "sys_std_user")
public class StdUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "class_id")
public int classId;
#Column(name = "user_name")
public String userName;
//getter,setter
}
Controller
import com.example.demo.models.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import java.util.List;
#RestController
public class HomeController {
#PersistenceUnit
private EntityManagerFactory emf;
#GetMapping("/")
public List<StdUser> actionIndex() {
EntityManager em = emf.createEntityManager(); // Without parameter
List<StdUser> arr_cust = (List<StdUser>)em
.createQuery("SELECT c FROM StdUser c")
.getResultList();
return arr_cust;
}
#GetMapping("/paramter")
public List actionJoin() {
int id = 3;
String userName = "Suresh Shrestha";
EntityManager em = emf.createEntityManager(); // With parameter
List arr_cust = em
.createQuery("SELECT c FROM StdUser c WHERE c.classId = :Id ANd c.userName = :UserName")
.setParameter("Id",id)
.setParameter("UserName",userName)
.getResultList();
return arr_cust;
}
}

Related

Hibernate native query also gets JPA select

I have a two database tables, "A" and "B" with #OneToMany(mappedBy = "a") on a List<B> field in entity A, and a #ManyToOne on field B.a. I ran into the "N+1" problem when doing default queries on A, so I am trying a native query such as:
#Query(value="select * from A as a left join B as b " +
"on a.ID = b.b ",
nativeQuery=true)
This works in the sense that the data is mapped back to the entities as expected.
My problem is that I can see that Hibernate is doing a separate select for each B rather than using the results of the join. That is, I see in the console a sequence of:
The select that I specified
For each instance of A, another select for B using the ID from A
In other words, I've still got the "n+1" problem.
I figured that the #OneToMany and #ManyToOne annotations might be causing Hibernate to do these extra selects, but when I take them out, my IDE (IntelliJ) says:
'Basic' attribute should not be a container
... on the List property in A.
How can I get it to map the results back in a single select with join? Should I just give up on Hibernate and JPA?
I am using spring-boot-start-data-jpa.2.5.4
Native #Query doesn't have sufficient mapping power, so it seems that Hibernate native query must be needed.
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.hibernate.Session;
import org.hibernate.transform.BasicTransformerAdapter;
import org.springframework.stereotype.Repository;
// https://docs.spring.io/spring-data/jpa/docs/2.5.6/reference/html/#repositories.custom-implementations
#Repository
public class CustomizedARepositoryImpl implements CustomizedARepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<A> getAll() {
// https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#sql-entity-associations-query
final Session sess = (Session) entityManager.getDelegate();
final List<A> res = sess
// If no duplicate column names, original sql can be used, too.
.createNativeQuery("select {a.*},{b.*} from A as a left join B as b on a.ID = b.a ")
.addEntity("a", A.class)
.addJoin("b", "a.bs")
.setResultTransformer(DistinctResultTransformer.INSTANCE)
.list();
return res;
}
// https://stackoverflow.com/q/12071014/4506703
static class DistinctResultTransformer extends BasicTransformerAdapter {
private static final long serialVersionUID = 1L;
static final DistinctResultTransformer INSTANCE = new DistinctResultTransformer();
#Override
public List transformList(final List collection) {
final List<Object> res = new ArrayList<>();
for (final Object[] obj : (List<Object[]>) collection) {
if (!res.contains(obj[0])) {
res.add(obj[0]);
}
}
return res;
}
}
}
Above code executes 1 query:
select a.id as id1_0_0_, a.name as name2_0_0_,b.a as a3_1_0__, b.id as id1_1_0__, b.id as id1_1_1_, b.a as a3_1_1_, b.name as name2_1_1_
from A as a left join B as b on a.ID = b.a
full sample code
You can use some methods avoiding N+1 problem.
Using JPQL fetch, instead of native-query:
#Query("select distinct a from A a left join fetch a.bs")
List<A> getAllJpqlFetch();
Above code executes 1 query:
select distinct a0_.id as id1_0_0_, bs1_.id as id1_1_1_, a0_.name as name2_0_0_, bs1_.a as a3_1_1_, bs1_.name as name2_1_1_, bs1_.a as a3_1_0__, bs1_.id as id1_1_0__
from a a0_ left outer join b bs1_ on a0_.id=bs1_.a
diff
Using JPA Criteria fetch, is equivalent to above JPQL:
#Repository
public class CustomizedARepositoryImpl implements CustomizedARepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<A> getAllCriteria() {
// https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#criteria-from-fetch
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<A> criteria = builder.createQuery(A.class);
final Root<A> root = criteria.from(A.class);
root.fetch("bs", JoinType.LEFT);
criteria.select(root).distinct(true);
return entityManager.createQuery(criteria).getResultList();
}
Above code executes 1 query:
select distinct a0_.id as id1_0_0_, bs1_.id as id1_1_1_, a0_.name as name2_0_0_, bs1_.a as a3_1_1_, bs1_.name as name2_1_1_, bs1_.a as a3_1_0__, bs1_.id as id1_1_0__
from a a0_ left outer join b bs1_ on a0_.id=bs1_.a
diff
Using #Fetch(FetchMode.SUBSELECT):
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
// ...
#Entity
public class A {
#OneToMany(mappedBy = "a")
#Fetch(FetchMode.SUBSELECT)
private List<B> bs;
// ...
}
// findAll() method implementation is auto-generated by Spring Data JPA
// https://docs.spring.io/spring-data/jpa/docs/2.5.6/reference/html/#repositories.core-concepts
repository.findAll();
Above code executes 2 queries(root entities and their relational entities):
select a0_.id as id1_0_, a0_.name as name2_0_ from a a0_
select bs0_.a as a3_1_1_, bs0_.id as id1_1_1_, bs0_.id as id1_1_0_, bs0_.a as a3_1_0_, bs0_.name as name2_1_0_
from b bs0_ where bs0_.a in (select a0_.id from a a0_)
diff
I ended up using the following solution, given by DEWA Kazuyuki, above. I'm copying it here because DEWA suggested several answers and I thought it useful to identify the particular one that worked for me. Thanks, DEWA.
#Repository
public class CustomizedARepositoryImpl implements CustomizedARepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<A> getAllCriteria() {
// https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#criteria-from-fetch
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<A> criteria = builder.createQuery(A.class);
final Root<A> root = criteria.from(A.class);
root.fetch("bs", JoinType.LEFT);
criteria.select(root).distinct(true);
return entityManager.createQuery(criteria).getResultList();
}

Spring Data JPA: query ManyToMany

I have entities User and Test
#Entity
public class User {
private Long id;
private String userName;
}
#Entity
public class Test {
private Long id;
#ManyToMany
private Set<User> users;
}
I can get all tests by User entity:
public interface TestRepository extends JpaRepository<EventSettings, Long> {
List<Test> findAllByUsers(User user);
}
But which query can I use for finding all tests by userName?
The following method signature will get you want to want:
List<Test> findByUsers_UserName(String userName)
This is using the property expression feature of Spring Data JPA. The signature Users_UserName will be translated to the JPQL x.users.userName. Note that this will perform an exact match on the given username.
Other answer shows how to achieve desired functionality using function naming technique. We can achieve same functionality using #Query annotation as follows:
#Query("select t from Test t join User u where u.username = :username")
List<Test> findAllByUsername(#Param("username")String username);
I was using #JoinTable and I got it working with this :
#Query("select t from Test t join t.users u where u.username = :username")
List<Test> findAllByUsername(#Param("username") String username);
t.users u instead of User u

Why I obtain this HibernateException related to a named query in this Spring project?

I am absolutly new in Hibernate and Spring development and I have the following problem
I have this entity class named KM_ProjectInfoStatus that map a DB table named KM_PROJECT_INFO_STATUS, so I have this class:
package egp.prc.km.model;
import javax.persistence.*;
import java.io.Serializable;
#NamedQueries({
#NamedQuery(name = "kmProjectInfoStatusList", query = "select status from KM_PROJECT_INFO_STATUS status order by status.idProjectInfoStatus") ,
#NamedQuery(name = "kmProjectInfoStatusById", query = "SELECT status FROM KM_PROJECT_INFO_STATUS status where lower(status.idProjectInfoStatus) = :statusId")
})
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#Column(name = "foldertech")
private Long foldertech;
#Column(name = "folderproject")
private Long folderproject;
public Long getIdProjectInfoStatus() {
return idProjectInfoStatus;
}
public void setIdProjectInfoStatus(Long idProjectInfoStatus) {
this.idProjectInfoStatus = idProjectInfoStatus;
}
public Long getFoldertech() {
return foldertech;
}
public void setFoldertech(Long foldertech) {
this.foldertech = foldertech;
}
public Long getFolderproject() {
return folderproject;
}
public void setFolderproject(Long folderproject) {
this.folderproject = folderproject;
}
}
As you can see in the previous snippet, uppon the class are definied 2 named queries, these:
#NamedQueries({
#NamedQuery(name = "kmProjectInfoStatusList", query = "select status from KM_PROJECT_INFO_STATUS status order by status.idProjectInfoStatus") ,
#NamedQuery(name = "kmProjectInfoStatusById", query = "SELECT status FROM KM_PROJECT_INFO_STATUS status where lower(status.idProjectInfoStatus) = :statusId")
})
1) The first one is named kmProjectInfoStatusList and select a list of rows from my KM_PROJECT_INFO_STATUS table.
2) The second one is named kmProjectInfoStatusById and select a single row from my KM_PROJECT_INFO_STATUS table using the id in the where condition.
Then I have to implement a Spring service that realize a DAO.
So I first create an interface named as KMProjectInfoStatusService in which I declare the method of my DAO:
public interface KMProjectInfoStatusService {
#Transactional
public List<KM_ProjectInfoStatus> getProjectInfoStatusList();
#Transactional
public KM_ProjectInfoStatus getProjectInfoStatus(Long idProjectInfoStatus);
}
I am absolutly new in Hibernate and Spring development and I have the following problem
I have this entity class named KM_ProjectInfoStatus that map a DB table named KM_PROJECT_INFO_STATUS, so I have this class:
package egp.prc.km.model;
import javax.persistence.*;
import java.io.Serializable;
#NamedQueries({
#NamedQuery(name = "kmProjectInfoStatusList", query = "select status from KM_PROJECT_INFO_STATUS status order by status.name") ,
#NamedQuery(name = "kmProjectInfoStatusById", query = "SELECT status FROM KM_PROJECT_INFO_STATUS status where lower(status.idProjectInfoStatus) = :statusId")
})
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#Column(name = "foldertech")
private Long foldertech;
#Column(name = "folderproject")
private Long folderproject;
public Long getIdProjectInfoStatus() {
return idProjectInfoStatus;
}
public void setIdProjectInfoStatus(Long idProjectInfoStatus) {
this.idProjectInfoStatus = idProjectInfoStatus;
}
public Long getFoldertech() {
return foldertech;
}
public void setFoldertech(Long foldertech) {
this.foldertech = foldertech;
}
public Long getFolderproject() {
return folderproject;
}
public void setFolderproject(Long folderproject) {
this.folderproject = folderproject;
}
}
As you can see in the previous snippet, the class contains also 2 named queries, these:
#NamedQueries({
#NamedQuery(name = "kmProjectInfoStatusList", query = "select status from KM_PROJECT_INFO_STATUS status order by status.name") ,
#NamedQuery(name = "kmProjectInfoStatusById", query = "SELECT status FROM KM_PROJECT_INFO_STATUS status where lower(status.idProjectInfoStatus) = :statusId")
})
Then I have to implement a Spring service that realize a DAO.
So I first create an interface named as KMProjectInfoStatusService in which I declare the method of my DAO:
public interface KMProjectInfoStatusService {
#Transactional
public List<KM_ProjectInfoStatus> getProjectInfoStatusList();
#Transactional
public KM_ProjectInfoStatus getProjectInfoStatus(Long idProjectInfoStatus);
}
Finally I create the KMProjectInfoStatusServiceImp class that implement the previous interface and that represent my concrete DAO:
#Repository("kmProjectInfoStatusService")
public class KMProjectInfoStatusServiceImpl extends AbstractService implements KMProjectInfoStatusService {
public List<KM_ProjectInfoStatus> getProjectInfoStatusList() {
//return new KM_ProjectInfoStatus[0]; //To change body of implemented methods use File | Settings | File Templates.
return getHibernateTemplate().execute(
new HibernateCallback<List<KM_ProjectInfoStatus>>() {
public List<KM_ProjectInfoStatus> doInHibernate(Session session) throws HibernateException, SQLException {
return getStatusList(session);
}
}
);
}
public KM_ProjectInfoStatus getProjectInfoStatus(Long idProjectInfoStatus) {
//return null; //To change body of implemented methods use File | Settings | File Templates.
final Long id = idProjectInfoStatus;
return getHibernateTemplate().execute(
new HibernateCallback<KM_ProjectInfoStatus>() {
public KM_ProjectInfoStatus doInHibernate(Session session) throws HibernateException, SQLException {
return getStatus(id, session);
}
}
);
}
private List<KM_ProjectInfoStatus> getStatusList(Session session) {
//create query:
Query query = session.getNamedQuery("kmProjectInfoStatusList");
List<KM_ProjectInfoStatus> statusList = query.list();
return statusList;
}
private KM_ProjectInfoStatus getStatus(Long idProjectInfoStatus, Session session) {
//create query:
Query query = session.getNamedQuery("kmProjectInfoStatusById");
query.setParameter("statusId", idProjectInfoStatus);
KM_ProjectInfoStatus status = (KM_ProjectInfoStatus) query.uniqueResult();
return status;
}
}
As you can see in this class I implement the 2 methods getProjectInfoStatusList() and getProjectInfoStatus() declared in the interface. These 2 methods use respectivelly the getStatusList() to perform the first named query to obtain the list and getStatus() that perform the second query to obtain an object from its id.
The problem is that when I try to execute my project an HibernateException is throwns and I obtain the following error message in the stacktrace:
2014-12-29 16:25:44,210 [[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'] ERROR impl.SessionFactoryImpl.<init>(363) - Error in named query: kmProjectInfoStatusById
org.hibernate.hql.ast.QuerySyntaxException: KM_PROJECT_INFO_STATUS is not mapped [SELECT status FROM KM_PROJECT_INFO_STATUS status where lower(status.idProjectInfoStatus) = :statusId]
at org.hibernate.hql.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:158)
at org.hibernate.hql.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:87)
at org.hibernate.hql.ast.tree.FromClause.addFromElement(FromClause.java:70)
at org.hibernate.hql.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:255)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3056)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:2945)
................................................................................
................................................................................
................................................................................
................................................................................
<29-dic-2014 16.25.44 CET> <Error> <Deployer> <BEA-149202> <Encountered an exception while attempting to commit the 1 task for the application 'KM_Web_war_exploded'.>
<29-dic-2014 16.25.44 CET> <Warning> <Deployer> <BEA-149004> <Failures were detected while initiating deploy task for application 'KM_Web_war_exploded'.>
<29-dic-2014 16.25.44 CET> <Warning> <Deployer> <BEA-149078> <Stack trace for message 149004
weblogic.application.ModuleException: :org.hibernate.HibernateException:Errors in named queries: kmProjectInfoStatusById, kmProjectInfoStatusList
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:365)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1300)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:859)
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:860)
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:779)
Truncated. see log file for complete stacktrace
Why? What could be the problem? What am I missing? How can I solve this issue?
Your in the Hibernate world, so you need to use Hibernate entity names and no longer SQL table names. By default, an entity name is the simple name of a class.
So you should write instead something like:
select status from KM_ProjectInfoStatus status order by status.name
Google more info on HQL/JPQL for the details.
HQL needs classname and SQL needs table name
use KM_ProjectInfoStatus in your HQL instead of KM_PROJECT_INFO_STATUS.
#NamedQueries({
#NamedQuery(name = "kmProjectInfoStatusList", query = "select status from KM_ProjectInfoStatus status order by status.name") ,
#NamedQuery(name = "kmProjectInfoStatusById", query = "SELECT status FROM KM_ProjectInfoStatus status where lower(status.idProjectInfoStatus) = :statusId")
})
HQL thus should not use table names, but entity class names
This is supposed to be HQL query (not SQL):
SELECT status FROM KM_PROJECT_INFO_STATUS status where lower(status.idProjectInfoStatus) = :statusId
So you shouldn't use the table name (KM_PROJECT_INFO_STATUS) but rather the entity class name (KM_ProjectInfoStatus) like this:
SELECT status FROM KM_ProjectInfoStatus status where lower(status.idProjectInfoStatus) = :statusId
Here is a link to Hibernate documentation related to the HQL FROM clause.
Also please try following standard Java naming conventions. It will make your code more readable :)

How to retrieve specific columns in Paginated way using Spring Data JPA?

I have One entity class, its service and repository as follows:
#Entity
#Table(name = "user")
public class User implements Serializable{
#Id
#Column(name = "id", unique = true)
private String userId;
#Column(name = "user_name")
private String userName;
#Column(name = "emp_code")
private String empCode;
// ... other properties
}
Repository
#Repository
public interface UserRepository extends PagingAndSortingRepository<User, String>
{
// .... working
#Query("select u.userName from User u")
Page<User> findAllUserName(Pageable pageable);
//... not working
#Query("select u.userName, u.empCode from User u")
Page<User> findAllUserNameAndEmpCode(Pageable pageable);
}
When I am trying to execute findAllUserName it works properly. but when using findAllUserNameAndEmpCode.. it throws following exceptions while starting tomcat:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found ',' near line 1, column 29 [select count(u.userName,u.empCode) from com.entity.User u]
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:54)
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:47)
at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:79)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:182)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:138)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:105)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:168)
at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:221)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:199)
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1778)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:291)
... 63 more
I dont know why, and how its converting this query to SELECT count(..) ? What is meaning of expecting CLOSE, found ',' ??
Please help.. Thanks
You should specify the count query. The Page return value of your select function needs to know how many results there will be. So it sends a COUNT query that is probably made from your select query and looks like this:
select count(u.userName,u.empCode) from com.entity.User u
which is wrong because COUNT function takes only one parameter. So you should create your custom count query (probably like this):
select count(u.userName) from com.entity.User u
and place it into #Query annotation:
#Query(
value = "select u.userName, u.empCode from User u",
countQuery = "select count(u.userName) from com.entity.User u"
)
Page<User> findAllUserNameAndEmpCode(Pageable pageable);

JPA: How to get entity based on field value other than ID?

In JPA (Hibernate), when we automatically generate the ID field, it is assumed that the user has no knowledge about this key. So, when obtaining the entity, user would query based on some field other than ID. How do we obtain the entity in that case (since em.find() cannot be used).
I understand we can use a query and filter the results later. But, is there a more direct way (because this is a very common problem as I understand).
It is not a "problem" as you stated it.
Hibernate has the built-in find(), but you have to build your own query in order to get a particular object. I recommend using Hibernate's Criteria :
Criteria criteria = session.createCriteria(YourClass.class);
YourObject yourObject = criteria.add(Restrictions.eq("yourField", yourFieldValue))
.uniqueResult();
This will create a criteria on your current class, adding the restriction that the column "yourField" is equal to the value yourFieldValue. uniqueResult() tells it to bring a unique result. If more objects match, you should retrive a list.
List<YourObject> list = criteria.add(Restrictions.eq("yourField", yourFieldValue)).list();
If you have any further questions, please feel free to ask. Hope this helps.
if you have repository for entity Foo and need to select all entries with exact string value boo (also works for other primitive types or entity types). Put this into your repository interface:
List<Foo> findByBoo(String boo);
if you need to order results:
List<Foo> findByBooOrderById(String boo);
See more at reference.
Basically, you should add a specific unique field. I usually use xxxUri fields.
class User {
#Id
// automatically generated
private Long id;
// globally unique id
#Column(name = "SCN", nullable = false, unique = true)
private String scn;
}
And you business method will do like this.
public User findUserByScn(#NotNull final String scn) {
CriteriaBuilder builder = manager.getCriteriaBuilder();
CriteriaQuery<User> criteria = builder.createQuery(User.class);
Root<User> from = criteria.from(User.class);
criteria.select(from);
criteria.where(builder.equal(from.get(User_.scn), scn));
TypedQuery<User> typed = manager.createQuery(criteria);
try {
return typed.getSingleResult();
} catch (final NoResultException nre) {
return null;
}
}
Best practice is using #NaturalId annotation. It can be used as the business key for some cases it is too complicated, so some fields are using as the identifier in the real world.
For example, I have user class with user id as primary key, and email is also unique field. So we can use email as our natural id
#Entity
#Table(name="user")
public class User {
#Id
#Column(name="id")
private int id;
#NaturalId
#Column(name="email")
private String email;
#Column(name="name")
private String name;
}
To get our record, just simply use 'session.byNaturalId()'
Session session = sessionFactory.getCurrentSession();
User user = session.byNaturalId(User.class)
.using("email","huchenhai#qq.com")
.load()
This solution is from Beginning Hibernate book:
Query<User> query = session.createQuery("from User u where u.scn=:scn", User.class);
query.setParameter("scn", scn);
User user = query.uniqueResult();
I solved a similar problem, where I wanted to find a book by its isbnCode not by your id(primary key).
#Entity
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String isbnCode;
...
In the repository the method was created like #kamalveer singh mentioned. Note that the method name is findBy+fieldName (in my case: findByisbnCode):
#Repository
public interface BookRepository extends JpaRepository<Book, Integer> {
Book findByisbnCode(String isbnCode);
}
Then, implemented the method in the service:
#Service
public class BookService {
#Autowired
private BookRepository repo;
public Book findByIsbnCode(String isbnCode) {
Book obj = repo.findByisbnCode(isbnCode);
return obj;
}
}
Write a custom method like this:
public Object findByYourField(Class entityClass, String yourFieldValue)
{
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery(entityClass);
Root<Object> root = criteriaQuery.from(entityClass);
criteriaQuery.select(root);
ParameterExpression<String> params = criteriaBuilder.parameter(String.class);
criteriaQuery.where(criteriaBuilder.equal(root.get("yourField"), params));
TypedQuery<Object> query = entityManager.createQuery(criteriaQuery);
query.setParameter(params, yourFieldValue);
List<Object> queryResult = query.getResultList();
Object returnObject = null;
if (CollectionUtils.isNotEmpty(queryResult)) {
returnObject = queryResult.get(0);
}
return returnObject;
}
Edit: Just realized that #Chinmoy was getting at basically the same thing, but I think I may have done a better job ELI5 :)
If you're using a flavor of Spring Data to help persist / fetch things from whatever kind of Repository you've defined, you can probably have your JPA provider do this for you via some clever tricks with method names in your Repository interface class. Allow me to explain.
(As a disclaimer, I just a few moments ago did/still am figuring this out for myself.)
For example, if I am storing Tokens in my database, I might have an entity class that looks like this:
#Data // << Project Lombok convenience annotation
#Entity
public class Token {
#Id
#Column(name = "TOKEN_ID")
private String tokenId;
#Column(name = "TOKEN")
private String token;
#Column(name = "EXPIRATION")
private String expiration;
#Column(name = "SCOPE")
private String scope;
}
And I probably have a CrudRepository<K,V> interface defined like this, to give me simple CRUD operations on that Repository for free.
#Repository
// CrudRepository<{Entity Type}, {Entity Primary Key Type}>
public interface TokenRepository extends CrudRepository<Token, String> { }
And when I'm looking up one of these tokens, my purpose might be checking the expiration or scope, for example. In either of those cases, I probably don't have the tokenId handy, but rather just the value of a token field itself that I want to look up.
To do that, you can add an additional method to your TokenRepository interface in a clever way to tell your JPA provider that the value you're passing in to the method is not the tokenId, but the value of another field within the Entity class, and it should take that into account when it is generating the actual SQL that it will run against your database.
#Repository
// CrudRepository<{Entity Type}, {Entity Primary Key Type}>
public interface TokenRepository extends CrudRepository<Token, String> {
List<Token> findByToken(String token);
}
I read about this on the Spring Data R2DBC docs page, and it seems to be working so far within a SpringBoot 2.x app storing in an embedded H2 database.
No, you don't need to make criteria query it would be boilerplate code you just do simple thing if you working in Spring-boot:
in your repo declare a method name with findBy[exact field name].
Example-
if your model or document consist a string field myField and you want to find by it then your method name will be:
findBymyField(String myField);
All the answers require you to write some sort of SQL/HQL/whatever. Why? You don't have to - just use CriteriaBuilder:
Person.java:
#Entity
class Person {
#Id #GeneratedValue
private int id;
#Column(name = "name")
private String name;
#Column(name = "age")
private int age;
...
}
Dao.java:
public class Dao {
public static Person getPersonByName(String name) {
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Person> cr = cb.createQuery(Person.class);
Root<Person> root = cr.from(Person.class);
cr.select(root).where(cb.equal(root.get("name"), name)); //here you pass a class field, not a table column (in this example they are called the same)
Query query = session.createQuery(cr);
query.setMaxResults(1);
List<Person> resultList = query.getResultList();
Person result = resultList.get(0);
return result;
}
}
example of use:
public static void main(String[] args) {
Person person = Dao.getPersonByName("John");
System.out.println(person.getAge()); //John's age
}
Have a look at:
JPA query language: The Java Persistence Query Language
JPA Criteria API: Using the Criteria API to Create Queries
I've written a library that helps do precisely this. It allows search by object simply by initializing only the fields you want to filter by: https://github.com/kg6zvp/GenericEntityEJB
Refer - Spring docs for query methods
We can add methods in Spring Jpa by passing diff params in methods like:
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
In my Spring Boot app I resolved a similar type of issue like this:
#Autowired
private EntityManager entityManager;
public User findByEmail(String email) {
User user = null;
Query query = entityManager.createQuery("SELECT u FROM User u WHERE u.email=:email");
query.setParameter("email", email);
try {
user = (User) query.getSingleResult();
} catch (Exception e) {
// Handle exception
}
return user;
}
This is very basic query :
Entity : Student
#Entity
#Data
#NoArgsConstructor
public class Student{
#Id
#GeneratedValue(generator = "uuid2", strategy = GenerationType.IDENTITY)
#GenericGenerator(name = "uuid2", strategy = "uuid2")
private String id;
#Column(nullable = false)
#Version
#JsonIgnore
private Integer version;
private String studentId;
private String studentName;
private OffsetDateTime enrollDate;
}
Repository Interface : StudentRepository
#Repository
public interface StudentRepository extends JpaRepository<Student, String> {
List<Student> findByStudentName(String studentName);
List<Student> findByStudentNameOrderByEnrollDateDesc(String studentName);
#Transactional
#Modifying
void deleteByStudentName(String studentName);
}
Note:
findByColumnName : give results by criteria
List findByStudentName(String studentName)
Internally convert into query : select * from Student where name='studentName'
#Transactional
#Modifying
Is useful when you want to remove persisted data from database.
Using CrudRepository and JPA query works for me:
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface TokenCrudRepository extends CrudRepository<Token, Integer> {
/**
* Finds a token by using the user as a search criteria.
* #param user
* #return A token element matching with the given user.
*/
#Query("SELECT t FROM Token t WHERE LOWER(t.user) = LOWER(:user)")
public Token find(#Param("user") String user);
}
and you invoke the find custom method like this:
public void destroyCurrentToken(String user){
AbstractApplicationContext context = getContext();
repository = context.getBean(TokenCrudRepository.class);
Token token = ((TokenCrudRepository) repository).find(user);
int idToken = token.getId();
repository.delete(idToken);
context.close();
}

Categories

Resources