I have a rather complex query (too many nested levels for either HQL or Criteria queries), so I've written it as a SQLQuery. I'd really like to use the AliasToBeanResultTransformer to transform my results into a List, but I'm having some issues. I've included code snippets below of what I've got right now.
When I log the results for the transformed query, I can see that the transformer does create a List, however, all the fields in each AdvancedClauseSearchResultDTO are null. I assume that means that I'm doing something wrong with aliasing ... that the AliasToBeanResultTransformer can't find the correct setters to call. However, the AdvancedClauseSearchResultDTO class does have public setters for each of the columns that I've aliased in my sql string. If this was a Criteria query, I'd use projections to define an alias for each column to be returned, but I'm unsure of how to accomplish the same thing using a SQLQuery.
Any advice on how to get the aliases set so that the ResultTransformer can use them? I've seen some limited documentation that suggested that using the 'as aliasName' method should work, but it doesn't seem to be for me.
Beginning snippet of query string definition, note the 'as' alias definitions
StringBuffer clauseBaseQuery = new StringBuffer();
clauseBaseQuery.append("select ");
clauseBaseQuery.append(" clauseDetail.clause_detail_id as clauseDetailId,");
clauseBaseQuery.append(" clauseDetail.clause_id as clauseId,");
clauseBaseQuery.append(" providers.provider_name as provider, ");
clauseBaseQuery.append(" products.product_name as product, ");
SQLQuery creation & setting of resultTransformer
Query query = session.createSQLQuery(clauseBaseQuery.toString());
query.setResultTransformer(new AdvancedClauseSearchResultTransformer());
return (List<AdvancedClauseSearchResultDTO>)query.list();
AdvancedClauseSearchResultTransformer class (uses AliasToBeanResultTransformer and then does some extra processing):
class AdvancedClauseSearchResultTransformer implements ResultTransformer {
//Use the aliasTransformer to do most of the work
ResultTransformer aliasTransformer = Transformers.aliasToBean(AdvancedClauseSearchResultDTO.class);
#Override
public List transformList(List list) {
log.debug("transforming CLAUSE results");
List<AdvancedClauseSearchResultDTO> result = aliasTransformer.transformList(list);
//for each row, set the status field
for (AdvancedClauseSearchResultDTO dto : result) {
log.debug("dto = " + dto);
String status = null;
Date effectiveDate = dto.getEffectiveDate();
Date terminationDate = dto.getTerminationDate();
Date now = new Date(System.currentTimeMillis());
if (now.before(effectiveDate)) {
status = "Pending";
} else if (now.after(terminationDate)) {
status = "Terminated";
} else {
status = "Active";
}
dto.setStatus(status);
if (StringUtils.isNotEmpty(dto.getReasonForAmendment())){
dto.setAmended(Boolean.TRUE);
}else{
dto.setAmended(Boolean.FALSE);
}
}
return result;
}
#Override
public Object transformTuple(Object[] os, String[] strings) {
Object result = aliasTransformer.transformTuple(os, strings);
return result;
}
}
This depends on the backend you're using, which you don't mention in your post.
Various DB backends use case insensitive naming for the columns unless you properly escape them, so they end being retrieved as CLAUSEDETAILID or clausedetailid, even when you specify the column result name with the proper case.
With PostgreSQL (and I believe Oracle, too), you have to write your query like this (note the column quoting):
StringBuffer clauseBaseQuery = new StringBuffer();
clauseBaseQuery.append("select ");
clauseBaseQuery.append(" clauseDetail.clause_detail_id as \"clauseDetailId\",");
clauseBaseQuery.append(" clauseDetail.clause_id as \"clauseId\",");
clauseBaseQuery.append(" providers.provider_name as \"provider\", ");
clauseBaseQuery.append(" products.product_name as \"product\", ");
Query query = session.createSQLQuery(clauseBaseQuery.toString());
So that will allow Hibernate to properly recognize the property and map the result to a bean, provided you also specfied the tranformation:
query.setResultTransformer(Transformers.aliasToBean(AdvancedClauseSearchResultDTO.class));
as was suggested by #zinan.yumak.
I did some more research on this today, and finally noticed a good stack trace of the underlying error I was getting, and a hibernate forum entry that helped me get past this.
The Exception I was getting is:
Caused by: org.hibernate.PropertyNotFoundException: Could not find setter for CLAUSEDETAILID
It appears as if Hibernate is taking my camel case aliases & turning them into all uppercase, so it can't find the matching setters in my AdvancedClauseSearchResultDTO class.
Here's the forum entry that pointed me in the right direction:
https://forum.hibernate.org/viewtopic.php?f=1&t=1001608
I ended up using the approach detailed in that post for my own ResultTransformer, and that's working for me.
I think it is not a good method to write a result transformer to solve
your problem. Try something like this,
Query query = session.createSQLQuery(clauseBaseQuery.toString());
query.setResultTransformer(Transformers.aliasToBean(AdvancedClauseSearchResultDTO.class));
And in AdvancedClauseSearchResultDTO class, modify setter methods to set required
fields for you. For example,
class AdvancedClauseSearchResultDTO {
private Date effectiveDate;
private String status;
.
.
public void getEffectiveDate() {
return effectiveDate;
}
public void setEffectiveDate(Date aDate) {
Date now = new Date(System.currentTimeMillis());
if (now.before(effectiveDate)) {
this.status = "Pending";
} else if (now.after(terminationDate)) {
this.status = "Terminated";
} else {
this.status = "Active";
}
this.effectiveDate = aDate;
}
}
You got the idea...
The easiest fix is to put quotation marks for the column alias like:
select first_name as "firstName" from employee
Related
I am using spring boot repository with native query. While I am trying to update few fields by query and supply wrong values it is not throwing any exception.
here is my code,
#Modifying(clearAutomatically = true)
#Query(value = "update tbl_user set is_phone_Verified=true, mobile_verification_otp='XXX', updated_At=:updatedAt where " +
"phone_number=:registeredMobile and " +
"mobile_verification_otp=:phoneVerificationOtp", nativeQuery = true)
void updateMobileVerified(#Param("registeredMobile") String registeredMobile,
#Param("phoneVerificationOtp") String phoneVerificationOtp,
#Param("updatedAt") Date updatedAt);
Now, the issue is if I supply even wrong otp value, it is not throwing any exception. From service I am calling this method. and if there is no exception then I am returning true. Please find the service method.
#Override
public Boolean mobileVerification(String registeredMobile, String phoneVerificationOtp) {
try{
Date date = new Date();
userRepository.updateMobileVerified(registeredMobile,phoneVerificationOtp, date);
return true;
} catch(Exception e) {
return false;
}
}
Can someone please suggest me some way how can I determine if the update is successful.
Thanks.
If you would like to determine whether any rows were updated, the updateMobileVerified method can be defined to return int instead of void to indicate the number of rows updated
It seems that you are updating an unknown entity in the database. I highly recommend looking for a tbl_user entity by a unique id, like user_id or something like that. If the user doesn't exist, you either throw an exception or return false. In the case the given tbl_user was found, you basically update your desired attributes. This way, you force a strong control over updating users' data. I hope this might clarify your vision.
I was exploring the way to return map from Hibernate.
I came to know i can do it by two ways
First one was : using map
String HQL_QUERY =
"select new map(hp.col1 , hp.col2) from HP hp where hp.col1 in (:Ids)";
test =
getSession().createQuery(HQL_QUERY).setParameterList("Ids", ids).list();
Otherone is using setResultTransformer
String HQL_QUERY =
"select hp.col1 , hp.col2 from HP hp where hp.col1 in (:Ids)";
test =
getSession().createQuery(HQL_QUERY).setParameter("Ids", ids)
.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP).list();
In both ways i am getting same result .... no issues.
But i wanna ask if there is any advantage of using one over other.
For example if you are using map you have to write less code , but might be possible that result transformer is more efficient(not sure).
I tried to find it on google but didn't find much.
Please help
They are just two ways of expressing the same thing.
Here is a confirmation from hibernate sources : in QueryLoader.java which is the class parsing hql queries :
selectNewTransformer = HolderInstantiator.createSelectNewTransformer(
selectClause.getConstructor(),
selectClause.isMap(),
selectClause.isList());
Here the selectClause.isMap() will be true (if you want to confirm that, have a look at ConstructorNode.java)
And then in HolderInstantiator.java :
public static ResultTransformer createSelectNewTransformer(Constructor constructor, boolean returnMaps, boolean returnLists) {
if ( constructor != null ) {
return new AliasToBeanConstructorResultTransformer(constructor);
}
else if ( returnMaps ) {
return Transformers.ALIAS_TO_ENTITY_MAP;
}
else if ( returnLists ) {
return Transformers.TO_LIST;
}
else {
return null;
}
}
So i guess the map() inside the hql select clause being more readable and more concise make it the best choice. Apart from that, exactly the same thing will happen behind the scene.
I am using SolrJ to interact with Solr instance. I simply want to get search score of each document returned by Solr for a particular search query. If I add a score field in my POJO with #Field annotation it works totally fine while retrieving documents. When I try to index something through the same POJO, Solr returns an error saying unknown field "score", as I do not have any field named "score" in my Solr schema. But, if I add a field named "score" in my Solr schema it starts returning the default value of score instead of search score. Please suggest me how to solve this problem. I do not want to iterate through all the documents one by one and add the score to POJO. I am performing following steps :
public void getSolrResult(String query) {
SolrQuery query = new SolrQuery();
query.set("q", query);
query.set("fl", "*, score");
QueryResponse queryResponse = solrjClient.query(query);
solrResult = queryResponse.getBeans(PojoSolr.class);
}
Class PojoSolr {
//Other fields
#Field("score")
private float searchScore;
public float getSearchScore(){
return searchScore;
}
public void setSearchScore(float score) {
this.searchScore = score;
}
}
I solved a similar problem like this using inheritance.
Simply move the score field down to a sub-class. Use the base class for indexing and the sub-class for querying.
class PojoSolr {
//Other fields
}
class PojoSolrOutput extends PojoSolr {
#Field("score")
private float searchScore;
}
Note that there are also use cases for the other way round, e.g. fields that are not stored and can thus not be retrieved, but must be indexed.
In this case another sub-class PojoSolrInput containing these fields makes sense.
Hope this helps.
My 30-minutes search on this topic lead me only to this SO post and:
http://lucene.472066.n3.nabble.com/How-to-get-the-score-in-the-result-td493812.html
http://lucene.472066.n3.nabble.com/Scoring-using-POJO-SolrJ-td3235016.html
In the second it's suggested to remove the #Field annotation from score, but it does not work. In both places no valid solution has been proposed.
So I came up with the solution of cycling on the "raw" results, binding them as getBeans does, and adding the score:
SolrDocumentList sdl = response.getResults();
List<T> documents = new LinkedList<>();
for (SolrDocument sd : sdl) {
#SuppressWarnings("unchecked")
T sb = (T) getClient().getBinder().getBean(<T class>, sd);
sb.setScore((Float) sd.get("score"));
documents.add(sb);
}
everyone.
Sorry for some english mistakes.
I am using Vaadin to build a front-end search interface for some search tasks.
In front-end I have only one text field called "telefone" that should fill three fields in my entity bean (telefone1, telefone2 and telefone3), because the telephone number provided by user can be found in any "telefone" field. See the code below:
#SuppressWarnings("unchecked")
#Override
public List<Fornecedor> executaPesquisaAvancadaDeFornecedores(Fornecedor fornecedor) {
Map<Integer, Fornecedor> fornecedoresMap = new HashMap<Integer, Fornecedor>();
if(fornecedor.getTelefone1() != null) fornecedor.setTelefone1("%" + fornecedor.getTelefone1() + "%");
fornecedor.setTelefone2(fornecedor.getTelefone1());
fornecedor.setTelefone3(fornecedor.getTelefone1());
Example exampleFornecedor = Example.create(fornecedor).enableLike(MatchMode.ANYWHERE).ignoreCase();
Criteria criteria = super.getSession().createCriteria(Fornecedor.class, "fornecedor").add(exampleFornecedor);
List<Fornecedor> fornecedores = criteria.list();
for(Fornecedor f : fornecedores) {
fornecedoresMap.put(f.getId(), f);
}
return new ArrayList<Fornecedor>(fornecedoresMap.values());
}
In this case, there is no result provided by criteria.list() method. But if I remove "telefone2" and "telefone3" setters, like this...
if(fornecedor.getTelefone1() != null) fornecedor.setTelefone1("%" + fornecedor.getTelefone1() + "%");
... the criteria and the QBE returns the result correctly, but only if the telephone provided is saved in "telefone1" field.
My question is why this occurs? AFAIK, Hibernate Query By Example matches the results based on the given search fields filled, ignoring null attributes.
Thanks.
You are building a condition (with s = '%' + entered search string + '%'):
(telefone1 LIKE s) AND (telefone2 LIKE s) AND (telefone3 LIKE s)
I guess that you really want
(telefone1 LIKE s) OR (telefone2 LIKE s) OR (telefone3 LIKE s)
If that is true you can't use query by example which always produces and-connected conditions. Try to use HQL or the criteria API instead.
I have an object with 70 attributes. For ease of use I created 2 objects, a 'main' object and a 'details' object, with 1:1 relationship based on an auto-generated integer ID. I had a SEARCH screen that allowed searching on any of the main attributes, for which I build Restriction objects for whatever the user typed in. What was nice was that I did this all through iterating through the fields and building criterion - I didn't need ugly code to specifically handle each of the 30 attributes.
Now they want to search on the details fields as well. My previous screen-field-iterating code works perfectly with no changes (the whole reason for making it 'generic'), however I cannot get the JOIN to work to query on details fields.
class House {
Integer houseID;
String address;
. . .
HouseDetails houseDetails;
}
class HouseDetails {
Integer houseID;
String color;
. . .
}
I tried to create an alias and add it to the criteria :
criteria.createAlias("houseDetails", "houseDetails");
but I get this error :
org.hibernate.QueryException: could not resolve property: color of: House
Here's the thing - I know this would work if I prefix my restrictions with the alias name, but I do NOT want to have to know which table (House or HouseDetails) the field comes from. That would ruin all the automatic looping code and create specific code for each field.
Since SQL can do this as long as the column names are unique :
select * from house, housedetails where house.houseID = housedetails.houseID
and color = 'blue';
I'm wondering how can I get this to work using criteria??
As an aside, but related to this : Is there a way to perform something like Java's introspection on Hibernate HBM.XML mapping files? A number of times I've wanted to do this to solve problems but never found an answer. For the above problem, if I could easily find out which table contained each field, I could add the prefix to the Restriction. Something like this :
// Map of search keys (columns) to searching values
for ( String key : parms.keySet() ) {
String val = parms.get(key);
if ( HIBERNATE-SAYS-KEY-IS-FROM-DETAILS-TABLE ) {
key = "houseDetails." + key;
}
criteria.add(Restrictions.eq(key,val));
}
You can make method to find table name for passed column name.
By using SessionFactory.getClassMetaData() you can get all the information about that class. Once you have ClassMetaData then you can get all the property names. An demo method is shown below:
public String findTableName(String columnName)
{
boolean found=false;
Map<String, ClassMetadata> classMetaData = sessionFactory.getAllClassMetadata();
for (Entry<String, ClassMetadata> metaData : classMetaData.entrySet())
{
String[] propertyNames = metaData.getValue().getPropertyNames();
for (String property : propertyNames)
{
if(property == columnName)
{
return metaData.getKey() + "." + property;
found=true;
break;
}
}
if(found)
break;
}
}
The alias mechanism in hibernate and the Criteria API is pretty well specified. I suggest going through the documentation a little a bit.
I think what you want is something like this:
Criteria criteria = session.createCriteria(House.class);
criteria.createAlias("houseDetails.color", "houseColor");
criteria.add(Restrictions.eq("houseColor", "red"));