How to check if a record exists using JPA - java

I want to know whether a given record is present in a database or not. so far I have achieved this by writing a JPA query and the running it by getSingleResult() method. this would throw a NoResultException if the record with the given parameter does not exist. Of course, it's not a must for the record to exist, so it's the normal behaviour sometimes, that's why I asked to myself, is it neccessary to throw an Exception which I have to handle by a catch block? As far as I know the cost of Exception handling is quite big, so I'm not very satisfied with this solution, also, I don't even need the object, I only need to know it's existence in the DB.
Is there a better way to check whether an object exist or not? eg. using getResultList() and checking it's size maybe?

If you just want to know whether the object exists, send a SELECT COUNT to your database. That will return 0 or 1.
The cost of the exception handling isn't that big (unless you do that millions of times during a normal operation), so I wouldn't bother.
But the code doesn't really reflect your intention. Since getSingleResult() calls getResultList() internally, it's clearer like so:
public boolean objExists(...) {
return getResultList(...).size() == 1;
}
If you query by object id and you have caching enabled, that will become a simple lookup in the cache if the object has already been loaded.

Try to avoid loading the entity into the session (getSingleResult()) just to check for it's existence. A count is better here. With the Criteria Query API it would look something like this:
public <E extends AbstractEntity> boolean exists(final Class<E> entityClass, final int id) {
final EntityManager em = getEntityManager();
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Long> cq = cb.createQuery(Long.class);
final Root<E> from = cq.from(entityClass);
cq.select(cb.count(from));
cq.where(cb.equal(from.get(AbstractEntity_.id), id));
final TypedQuery<Long> tq = em.createQuery(cq);
return tq.getSingleResult() > 0;
}

Simply use count(e) in your query, so no NoResultException will be thrown and you will avoid loading the entity object
So the Java code can be as follow:
public boolean isRecordExist() {
String query = "select count(e) from YOUR_ENTITY e where ....";
// you will always get a single result
Long count = (Long) entityManager.createQuery( query ).getSingleResult();
return ( ( count.equals( 0L ) ) ? false : true );
}
Hope that helps someone :)

If you are searching by primary key you can also use Entitymanger.find(entityClass, primaryKey) which returns null when the entity does not exist.

here is a generic approach to work with a type T and an arbitrary ID value
public boolean exists(Object key) {
EntityManager entityManager = getEntityManager();
Metamodel metamodel = entityManager.getMetamodel();
EntityType<T> entity = metamodel.entity(entityClass);
SingularAttribute<T, ? extends Object> declaredId = entity.getDeclaredId(key.getClass());
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery<T> cq = cb.createQuery(entityClass);
Root<T> from = cq.from(entityClass);
Predicate condition = cb.equal(from.get(declaredId), key);
cq.where(condition);
TypedQuery<T> q = entityManager.createQuery(cq);
return q.getResultList().size() > 0;
}

Try this
public boolean exists(Object id){
return getEntityManager().find(entityClass, id)!=null;
}

A much easier solution to this problem is expanding the repository with a function of the return type Optional<Datatype>. Using this within a stream with a filter applied, you can easily check if the object exists with .isPresent()
For example:
public interface ChatArchiveRepo extends JpaRepository<ChatArchive, Long> {
List<ChatArchive> findByUsername(String username);
Optional<ChatArchive> findByConversationUid(String conversationUid);
}
And within your function or stream:
.filter(conversation -> !chatArchiveRepo.findByConversationUid(conversation.getUid()).isPresent())

I would just add a custom method in jpa repository
#Query("SELECT COUNT(ID) FROM 'name' WHERE transactionId =:id")
int findIfTxnPresentById(#Param("id") Long txnId);

Related

How to I remove L from Long field's values in java spring

I put query param for my list services for example:
tablename/list?query=id:10
it is running but I added other param
'personTNo'
tablename/list?query=id:10&personTNo=101035678
id is Integer but personTNo is Long
when I try to this sql returns select * from TABLENAME WHERE personTNo=10L
but this I want to return without 'L' for Long value. It is my code's a bit section in RepositoryCustom class
public List<TABLENAME> getTable(Specification aTablenameSpec) {
CriteriaBuilder builder = mEntityManager.getCriteriaBuilder();
CriteriaQuery<Object> query = builder.createQuery();
Root<TABLENAME> root = query.from(TABLENAME.class);
String queryWhere = null;
org.hibernate.query.Query hibernateQuery = null;
Predicate predicate = aTablenameSpec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
query.select(root);
TypedQuery<Object> typedQuery = mEntityManager.createQuery(query);
hibernateQuery = typedQuery.unwrap(org.hibernate.query.Query.class);
String queryString = hibernateQuery.getQueryString();
This row returns with L result, How to remove 'L' value in sql
Use INTEGER() function in the sql query. You can also try CAST() or CONVERT() functions in the query
Based on the problem description and code, it seems safe to assume the tech stack includes: JPA and Spring Data JPA.
And I understand that you want to remove the Long value L suffixes, but it's not clear if that's because the suffixes are causing a problem or exactly why you want the suffixes removed.
I only say that because the example query string appears to be a valid JPA query:
select * from TABLENAME WHERE personTNo = 10L
JPA support for the use of literal values in queries includes support for standard Java numeric (integer/long/float/double) literal value syntax.
Which means the L suffix on the literal Long value of personTNo, as defined in your query (10L), is legitimate, valid, and should not cause a problem.
Please let me know if I've missed the point, made an incorrect assumption, or overlooked something, and I will follow up.

How to restrict a hibernate criteria to a specific root entity

I'm writing a dynamic query using hibernates criteria language. I'm stunned as I'm unable to find any information regarding restricting the root entity dynamically (i.e. without specifying the id property).
I have an interface IEntity. There are several entities implementing IEntity. Some of them have an ID-property id while others have another ID-property (shadowId).
I want to write a single method covering both cases. Here is what I got so far:
#Override
public boolean querySomething(final IEntity ent) {
final Criteria criteria =
currentSession().createCriteria(HibernateUtils.deproxy(ent.getClass()));
criteria.createAlias("sharedProperty", "prop");
//This does not work:
criteria.add(Property.forName("this").eq(ent));
criteria.setProjection(Projections.count("prop.anotherProperty"));
final Number result = (Number) criteria.uniqueResult();
return result != null && result.longValue() > 0;
}
I would like to avoid to have a if statement like
if (ent instanceof TypeWithPropertyId){
criteria.add(Property.forName("id").eq(ent));
} else {
criteria.add(Property.forName("shadowId").eq(ent));
}
Edit
Note: HibernateUtils.deproxy(Class<?> clazz) removes any Proxy and returns the original class.
As of now, I didn't find anything in the criteria api which could help me. But I found IdentifierEqExpression by accident. Sadly IdentifierEqExpression still needs the ID Value and can not extract it itself. So for the moment I'm using a custom extension:
public class ThisEqualsExpression extends IdentifierEqExpression {
public ThisEqualsExpression(final Object value, final SessionFactory sf) {
super(sf.getClassMetadata(deproxy(value.getClass())).getIdentifier(value, null));
}
}
I would prefer not using this hack-ish approach, but I still like this better than an if-statement:
public boolean querySomething(final IEntity ent) {
final Criteria criteria =
currentSession().createCriteria(HibernateUtils.deproxy(ent.getClass()));
criteria.createAlias("sharedProperty", "prop");
//This does work:
criteria.add(new ThisEqualsExpression(ent, currentSession().getSessionFactory()));
criteria.setProjection(Projections.count("prop.anotherProperty"));
final Number result = (Number) criteria.uniqueResult();
return result != null && result.longValue() > 0;
}
Edit 2:
As requested some clarification:
In pure SQL, i have something like
SELECT COUNT(bar.property)
FROM Foo foo
INNER JOIN Bar bar ON foo.bar_id=bar.id
But I want something like
SELECT COUNT(bar.property)
FROM Foo foo
INNER JOIN Bar bar ON foo.bar_id=bar.id
WHERE foo.id=<some ID> --This is the important part
Back to Java:
Foos ID-Property depends on its type. It may be id (which I used in the SQL example above), but it could also be some other property. I was wondering if there was a way to restrict a criterias root entity without knowing the propertys name.

How to obtain single result from Hibernate SQLQuery instead of list [duplicate]

Does a query execution always have to return a list ? How do I replace the code below if I am sure it will only return a single object ?
#Override
public List<DocInfo> findAllByDocId(String docId)
{
Query q = getCurrentSession().createQuery("from DocInfo item where item.id = :docId");
q.setString("docId", docId);
List<DocInfo> docInfoList = q.list();
return docInfoList;
}
You can use Query#uniqueResult() with Hibernate if I am not wrong. I think that is what you are looking for. In this case, you have to handle the NonUniqueResultException in your code if there is more than one row returned from your query.
You can use
query.getSingleResult();
when you are absolutely sure that query would return only one row and I am talking about
import javax.persistence.Query;
If you are loading by id (pk), as it appears you are here, you really should use Session.load/Session.get instead.

Hibernate restriction in causes an error if the list is empty

If my list is empty, I get the following error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')'
Below is my hibernate related method:
#Override
public List<SomeThing> findByIds(List<Integer> someIds) {
return sessionFactory.getCurrentSession().createCriteria(SomeClass.class)
.add(Restrictions.in("id", someIds))
.list();
}
What should I do to guard against this error?
I know I could short-circuit the call and return an empty list like:
if(someIds == null || someIds.size() == 0) {
return new List<SomeThing>();
}
But is there a more elegant way to do this?
I would say Hibernate needs to fix this issue, and give meaningful message.
I think its responsibility of the provider/hibernate to check for the empty/null List.
One can imagine the cause, it tries to construct where clause, something like id in (), somewhere in org.hibernate.loader.criteria.CriteriaQueryTranslator or similar..But because here the List is empty, it would be throwing an exception. But they already created query with ( and could not complete because of exception/empty List.
NO. If you execute the query with empty parameters for in clause, it will fail (you may verify this by running plain SQL). Better not to execute the query if the input param is null/empty.
Only thing I can advice is to use isEmpty() function and != null in if statement and little restructuring as:
#Override
public List<SomeThing> findByIds(List<Integer> someIds) {
List<Something> result = null; //you may initialize with empty list
if(someIds != null || !someIds.isEmpty() {
result = sessionFactory.getCurrentSession().createCriteria(SomeClass.class)
.add(Restrictions.in("id", someIds))
.list();
}
return result;
}
(This is mostly base on #Yogendra Singh's reply, with a twist to make it more adoptable to commonly-seen situation of multiple optional argument)
Criteria API aims to let you compose your query programmatically. Such kind of dynamic feature is expected to be handled in your code.
Normally we make optional criteria by this:
#Override
public List<SomeThing> findBySearchParams(SearchParam searchParam) {
// create criteria with mandatory search criteria
Criteria criteria = sessionFactory.getCurrentSession()
.createCriteria(SomeClass.class);
.add(Restriction("someField", searchParam.getSomeField()));
// add "id" only if "someId" contains value
if(searchParam.getSomeIds() != null && !searchParam.getSomeIds().empty()) {
criteria.add(Restrictions.in("id", searchParam.getSomeIds()));
}
// add "anotherField" only if "anOptionalField" is not null
if(searchParam.getAnOptionalField() != null) {
criteria.add(Restrictions.in("anotherField", searchParam.getAnOptionalField()));
}
return criteria.list();
}
Edit:
Although Hibernate does not (yet) provide a more elegant way for that, you can write something yourself to make it looks more elegant:
class SmartCriteriaBuilder {
private Criteria criteria;
SmartCriteriaBuilder (Criteria criteria) { this.criteria = criteria;}
SmartCriteriaBuilder in(String field, Collection values) {
if (!empty(values)) {
this.criteria.add(Restrictions.in(field,values));
}
}
// all other kind of restrictions ....
Criteria toCriteria() {
return this.criteria;
}
}
Then you can do something looks smarter:
SmartCriteriaBuilder criteriaBuilder =
new SmartCriteriaBuilder(sessionFactory.getCurrentSession().createCriteria());
criteriaBuilder .in("someField", listPossiblyNullOrEmpty);
return criteriaBuilder .toCriteria().list();

JPQL query: selecting a single Boolean which is the AND of two of object's fields

Suppose that I have a JPA-annotated class called MyData with a primary identifier field (BigDecimal type called "primaryID"), and two Boolean fields called "fieldA" and "fieldB". What I want to do is create a JPA query that will select fieldA AND fieldB for a given MyData instance.
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Boolean> boolQuery = builder.createQuery(Boolean.class);
final Root<MyData> data = boolQuery.from(MyData.class);
boolQuery.select(builder.isTrue(builder.and(data.<Boolean> get("fieldA"),
profile.<Boolean> get("fieldB"))));
final Predicate primaryIDPredicate = builder.equal(profile.<BigDecimal> get("primaryID"),
1000);
boolQuery.where(primaryIDPredicate);
final TypedQuery<Boolean> myQuery = entityManager.createQuery(boolQuery);
When my entityManager executes this query, I get: org.hibernate.hql.ast.QuerySyntaxException: unexpected AST node: and near line 1.... This leads me to believe that I need something different (other than the builder.isTrue method) to designate that I want to take a Boolean and of two fields of my object. Any ideas on how I should construct this query?
OK, what I ended up doing was simply moving the booleans I wanted to "AND" into the predicate instead, and just checking whether anything was returned. Of course, I could have simply retrieved the entire object (MyData) based on the primary ID, and done the "AND" in Java code, but the point was to avoid fetching the full object because it has a large number of nested joins to its referenced collections, and those would be unnecessary for this case.
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<BigInteger> primaryIDQuery = builder.createQuery(BigInteger.class);
final Root<MyData> data = primaryIDQuery.from(MyData.class);
primaryIDQuery.select(data.<BigInteger> get("primaryId"));
final Predicate primaryIDPredicate = builder.equal(profile.<BigInteger> get("primaryId"),1000);
final Predicate otherPredicate = builder.or(
builder.isTrue(profile.<Boolean> get("fieldA")),
builder.isTrue(profile.<Boolean> get("fieldB")));
primaryIDQuery.where(primaryIDPredicate , otherPredicate);
final TypedQuery<BigInteger> existenceQuery = entityManager.createQuery(primaryIDQuery);
boolean whatIWant = existenceQuery.getResultList().size() > 0;

Categories