How do I query Hibernate for object where property may be null or specific value? - spring

I have the following objects I'm working with:
RawRead
RawRead.Checkpoint
Checkpoint.EndCustomer
Guard
Where Checkpoint and Guard are properties of RawRead, EndCustomer is a property of Checkpoint. All are objects.
My current Hibernate gubbins:
Criteria crit = sess.createCriteria(RawRead.class);
crit.add(
Restrictions.or(
Restrictions.eq("checkpoint", null),
Restrictions.in("checkpoint.parentEndCustomer",collectionOfEndCustomers)
)
);
So Checkpoint can be null, but if it is there I only want the RawRead objects where the parentEndCustomer object is in the checkpoint.parentEndCustomer property.
I hope that makes sense.
My guesstimate above produces an error that (to me) suggests that my criteria are incorrectly specified:
[Request processing failed; nested exception is org.hibernate.QueryException: could not resolve property: checkpoint.parentEndCustomer of: uk.co.romar.guardian.objects.RawRead] with root cause org.hibernate.QueryException:
could not resolve property: checkpoint.parentEndCustomer of: uk.co.romar.guardian.objects.RawRead at
org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:81) at
org.hibernate.persister.entity.AbstractPropertyMapping.toColumns(AbstractPropertyMapping.java:96) at
org.hibernate.persister.entity.BasicEntityPropertyMapping.toColumns(BasicEntityPropertyMapping.java:62) at
org.hibernate.persister.entity.AbstractEntityPersister.toColumns(AbstractEntityPersister.java:1457) at
org.hibernate.loader.criteria.CriteriaQueryTranslator.getColumns(CriteriaQueryTranslator.java:483)
Relevant bits of RawRead:
#ManyToOne
#JoinColumn(name="CHECKPOINT_OID")
#NotFound(action=NotFoundAction.IGNORE)
public Checkpoint checkpoint = null;
public void setCheckpoint(Checkpoint in) {this.checkpoint = in;}
public Checkpoint getCheckpoint() {return this.checkpoint;}
#ManyToOne
#JoinColumn(name="GUARD_OID")
#NotFound(action=NotFoundAction.IGNORE)
private Guard guard = null;
public void setGuard(Guard in) {this.guard = in;}
public Guard getGuard() {return this.guard;}
And from Checkpoint:
#ManyToOne
#JoinColumn(name="ENDCUSTOMER_OID")
#NotFound(action=NotFoundAction.IGNORE)
private EndCustomer parentEndCustomer = null;
public EndCustomer getParentEndCustomer() {return this.parentEndCustomer;}
public void setParentEndCustomer(EndCustomer ownerCustomer) {this.parentEndCustomer = ownerCustomer;}
EDIT Follows after implementing first answer from below.
If I have some data like this in the database (I hope the notation makes sense!):
RawRead {
ID=1
checkpoint={id=1,parentEndCustomer={ID=1}}
}
RawRead {
ID=2
checkpoint={id=4,parentEndCustomer={ID=4}}
}
RawRead {
ID=3
checkpoint={id=7,parentEndCustomer={ID=31}}
}
RawRead {
ID=4
checkpoint={null}
}
and the collectionOfEndCustomers given in the Restriction is like this:
EndCustomer={ID=31}
The I would want to retrieve RawReads 3 and 4 only. RawRead 1 & 2 are rejected because the parentEndCustomer of the child checkpoint property does't match the one passed in to the restriction in collectionOfEndCustomers.
RawRead.3 is should be selected because the parentEndCustomer matches one in the collection passed in. RawRead.4 should be selected because the checkpoint is null.
Following the guidance in the first answer below results in all of the above RawReads being returned rather than the subset I'm after.

You can't chain properties like you would do in HQL. You must use joins, with Criteria. And since checkpoint can be null, you must use a left join. Moreover, Restrictions.eq() can't be used to compare something with null (as in HQL and SQL). You must use Restrictions.isNull() to do that.
So your query should look like this:
Criteria crit = sess.createCriteria(RawRead.class, "rawRead");
crit.createAlias("rawRead.checkpoint", "checkpoint", Criteria.LEFT_JOIN)
crit.add(
Restrictions.or(
Restrictions.isNull("checkpoint.id"),
Restrictions.in("checkpoint.parentEndCustomer", collectionOfEndCustomers)
)
);

Related

How to retrieve data by property in Couchbase Lite?

My documents have the property docType that separated them based on the purpose of each type, in the specific case template or audit. However, when I do the following:
document.getProperty("docType").equals("template");
document.getProperty("docType").equals("audit");
The results of them are always the same, it returns every time all documents stored without filtering them by the docType.
Below, you can check the query function.
public static Query getData(Database database, final String type) {
View view = database.getView("data");
if (view.getMap() == null) {
view.setMap(new Mapper() {
#Override
public void map(Map<String, Object> document, Emitter emitter) {
if(String.valueOf(document.get("docType")).equals(type)){
emitter.emit(document.get("_id"), null);
}
}
}, "4");
}
return view.createQuery();
}
Any hint?
This is not a valid way to do it. Your view function must be pure (it cannot reference external state such as "type"). Once that is created you can then query it for what you want by setting start and end keys, or just a set of keys in general to filter on.

LDAP template search by multiple attributes

Trying to search for users details by using userid,emailid,firstname,lastname,GUID,etc...many more values that need to be added in future
The search should be performed using all the attributes which are not null.
Found this piece of code online *
String filter = "(&(sn=YourName)(mail=*))";
*
Is there any other predefined template or such to do the search, more optimal way without directly specifying values to be Null or using if else statements for each and every attribute? All values must be passed to the method and those not null must be used for search using LDAP. Anything? Please help.
You can effectively use the Filters at run time to specify what to use for search and what not depending on some rules or your NULL validations on attributes. Pls find sample code which fetches person name using filters in ldapTemplate :-
public static final String BASE_DN = "dc=xxx,dc=yyy";
private LdapTemplate ldapTemplate ;
public List getPersonNames() {
String cn = "phil more";
String sn = "more";
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new EqualsFilter("sn", sn));
filter.and(new WhitespaceWildcardsFilter("cn", cn));
return ldapTemplate.search(
BASE_DN,
filter.encode(),
new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs)
throws NamingException {
return attrs.get("cn").get();
}
});
}
As name suggests the AndFilters joins all individual filters used in lookup like EqualFilter which checks for equality of attributes while WhitespaceWildcardsFilter to perform wildcard search. So here like we got cn = phil more, it in turn uses *phil*more* for search.

Lucene not firing an exception when sorting on an unknown field

I am using "Hibernate search" to index my entities in a database. Everything is okay, and the lucene engine works great!
I tried, successfully now, to detect when there is an invalid field in the query. In this case, I found org.hibernate.search.SearchException, with a message that says "Unable to find field for..." I am trying to do the same on the sort field, but the Lucene engine accepts any strings without firing an exception.
How can I fire an exception or detect and invalid sort field?
I found a solution, get field list:
// get the full text entity manager
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
// search factory
SearchFactory searchFactory = fullTextEntityManager.getSearchFactory();
// valid field list
org.apache.lucene.index.Fields fields = MultiFields.getFields(searchFactory.getIndexReaderAccessor().open(className));
List<String> validFieldList = IteratorUtils.toList(fields.iterator());
...and more later...
if(validFieldList.contains(sortFilter)){
SortField sortField = new SortField(sortFilter, SortField.Type.STRING, reverseSort);
Sort sort = new Sort(sortField);
jpaQuery.setSort(sort);
} else
throw new NoSearchFieldException("Unable to find sort field for attribute "+searchQuery.getSortOn());
}
and throw an custom exception in this case
public class NoSearchFieldException extends Exception {
public NoSearchFieldException(String message) {
super(message);
}
/* Disable stack trace */
#Override
public Throwable fillInStackTrace()
{
return this;
}}

How to set a Http Session parameter in a JPA named query

I want to set a parameter in a named query (JPA 2.0), so my dataTable would render the respective dataSet. The parameter is obtained remotely and injected in a AbstractFacade class.
I've tried to achieve this through the code above, but it's not working.
Can someone help me?
AbstractFacade (main code):
private String prefDep;
public List<T> findByPrefDep() {
prefDep= FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("xPrefDep");
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).setParameter("prefDep", prefDep).getResultList();
}
The Entity class (main code):
#NamedQuery(name = "Capacitacao.findByPrefDep", query = "SELECT c FROM Capacitacao c WHERE c.prefDep = :prefDep"),
The AbstractController:
public Collection<T> getItems() {
if (items == null) {
items = this.ejbFacade.findByPrefDep();
}
return items;
}
There is no exception launched, but the dataSet rendered corresponds to a findAll named query.
Thanks in advance.
Your code doesn't use your named query at all. A named query has a name, and your code doesn't use that name anywhere.
Use
getEntityManager().createNamedQuery("Capacitacao.findByPrefDep", Capacitacao.class)
.setParameter("prefDep", prefDep)
.getResultList();
You could have found that yourself by simply reading the EntityManager javadoc.

Cannot map raw SQL query to DataRow

I am trying to get IEnumerable from linq query below. What am I doing wrong?
IEnumerable<DataRow> results =
context.Database.SqlQuery<DataRow>("SELECT * FROM Customer").AsEnumerable();
DataRow class does not have default (parameterless) constructor, so you can't use it as query parameter type. There is no generic constraints on type parameter, and nothing mentioned on MSDN(!), but column map factory will throw exception if parameter type does not have default constructor:
The result type 'System.Data.DataRow' may not be abstract and must
include a default constructor.
Here is a code which throws this exception:
internal static CollectionColumnMap CreateColumnMapFromReaderAndClrType(
DbDataReader reader, Type type, MetadataWorkspace workspace)
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
ConstructorInfo constructor = type.GetConstructor(flags, (Binder) null, Type.EmptyTypes, (ParameterModifier[]) null);
if (type.IsAbstract || (ConstructorInfo) null == constructor && !type.IsValueType)
throw EntityUtil.InvalidOperation(InvalidTypeForStoreQuery((object) type));
// ...
}
BTW Mapping to DataRow makes no sense, even if it would have default public constructor. Because it is not simple primitive type and it does not have properties which match the names of columns returned from the query (yes, mapping uses properties only).
Correct usage of Linq will be
IEnumerable<Customer> results = context.Customers;
That will generate SELECT * FROM Customer query, and map query results to customer entities. If you really want to use raw SQL:
IEnumerable<Customer> results =
context.Database.SqlQuery<Customer>("SELECT * FROM Customers");
I think we were trying to solve the same problem (Google led me here, anyway). I am executing a raw SQL command through SqlQuery<TElement>(string sql, params object[] parameters and wanted to assert individual properties of the results returned from the query in a unit test.
I called the method:
var result = (db.SqlQuery<Customer>("select * from customers").First();
and verified the data it returned:
Assert.AreEqual("John", result.FirstName);
I defined a private class Customer inside my test class (unfortunately, I'm not using Entity Framework):
private class Customer
{
public string FirstName { get; set; }
}
The properties of Customer must match the column names returned in the SQL query, and they must be properties (not just variables of the Customer class. You don't have to create properties for all of the columns returned from the query.

Resources