I use List objects in Restrictions.in methods, now I must use this case at Restrictions.like
But Restrictions.like doesn't receive List param. How Can I solve this problem?
My code is bottom:
public void setPhones(Set<String> phones) {
this.phones.addAll(phones);
if (!this.phones.isEmpty()) {
aliases.add(new QueryAlias("profile", "profile"));
criterions.add(Restrictions.like("profile.phone", this.phones));
}
}
Correcting my prior (and now edited) answer
According to documentation (https://docs.jboss.org/hibernate/orm/5.4/javadocs/org/hibernate/criterion/Restrictions.html) it seems you don't have a direct way to do this.
You can try iterating your List, then create a List of Restriction.like for each of your phones then converting this list into an array to use into a Restrictions.or:
public void setPhones(Set<String> phones) {
this.phones.addAll(phones);
if (!this.phones.isEmpty()) {
// Creates a list to store criterions
List<Criterion> criterionsPhoneNumbers = new ArrayList<>();
// For each number searched, it creates a criterion with a "Like Restriction" adding to criterionsPhoneNumbers List.
// Pay attention to match mode (in raw sql it'll be a "like" using %phoneNumber% - check the generated SQL by hibernate).
// You can change this to suit your needs.
for (String number : numbers) {
aliases.add(new QueryAlias("profile", "profile"));
criterionsPhoneNumbers.add( Restrictions.like("number", number, MatchMode.ANYWHERE) ) ;
}
// Here criterionsPhoneNumbers is being converted to array then added to the criteria with "Or Restriction"
criteria.add(Restrictions.or( criterionsPhoneNumbers.toArray(new Criterion[restrictionsPhoneNumbers.size()]) ));
}
}
My prior answer was wrong because adding each phone number as a Restriction.like (only) wasn't enough and was converted to a sql with a logical 'and' in 'where' clause. As I hadn't tested then I couldn't see the error.
I had implemented then I saw the error.
My apologies.
Related
This works for me, however I know that it is incorrect to use a parameter Raw type, so how am I supposed to do this correctly? The end goal is to check each key for correctness in a test suite.
I have other ideas but I want to learn the correct way to do this rather than guess.
ArrayList<HashMap<String,Object>> results = dbConn.getDataFromTable("select * from paf_extract_in.hcr_retro_chart_list WHERE payer = 'HEALTHTEAMADVANTAGE'");
for (HashMap result: results) {
if (!result.containsKey("hcr_retro_chart_list.packetid")) {
Assertions.fail();
}
}
I can do this as well, however this feels wrong and I can not do any other Assertions on the items.
for (Object result: results) {
if (!result.toString().contains("hcr_retro_chart_list.packetid")) {
Assertions.fail();
}
}
I have a section of code that used to utilize Optional<Department>, but due to some errors I worked out I am now converting it to List<Department>. Obviously this means I now have to change the return types and other method calls. Here are my questions:
I changed my returns to "new LinkedList<>()" (indicated in the code below) but is that correct?
There is a red error under ".isPresent" (indicated in the code below) with error message "The method isPresent() is undefined for the type List<Department>". What should I be changing that to?
Below is the updated code with comments indicating where errors are now occurring. Any help and explanations would be GREATLY appreciated!
public List<Department> delete(String department_ID) {
if ((department_ID == null) || (department_ID.isEmpty())) {
return new LinkedList<>(); //<<<<<<<<<<<<<<< used to be "return Optional.empty();"
}
List<Department> existing = get(department_ID);
if (existing.isPresent()) { //<<<<<<<<<<< red error under ".isPresent()"
String sql = "DELETE employee.*, department.* " + "FROM employee, department "
+ "WHERE employee.department_ID = :department_ID AND department.department_ID = :department_ID;";
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("department_ID", department_ID);
int rows = jdbcTemplate.update(sql, parameters);
if (rows > 0) {
return existing;
}
}
return new LinkedList<>(); //<<<<<<<<<<<<<<< used to be "return Optional.empty();"
}
I changed my returns to "new LinkedList<>()" (indicated in the code below) but is that correct?
I have googled the error message for my ".isPresent" error and cant find any explanations that fit
tl;dr
Change:
if (existing.isPresent()) { …
… to:
if ( ! list.isEmpty() ) { …
Details
You said:
red error under ".isPresent" (indicated in the code below) with error message "The method isPresent() is undefined for the type List".
The variable existing holds a reference to a List object. If you look at the Javadoc for List, you find no method named isPresent. So of course trying to call a non-existent method generates an error from the compiler.
That isPresent method was from the Optional class. The method checks to see if the optional holds a payload or if the optional is empty.
You seem to be switching to a style where you always expect a List, even if the list is empty (no elements).
If you want to be defensive, you can check that the list object exists.
if ( Objects.nonNull( existing ) ) { … }
But you can omit that null check if you are confident that such a condition cannot exist.
You may want to check if the list is empty, to avoid a needless call to your database. If so, change that code to:
if ( ! list.isEmpty() ) // Perform database work only if list has some elements.
You have other issues. Among them:
Generally the convention in Java is to avoid underscores in names. And generally best to avoid ALL-CAPS. So departmentId, not department_ID.
When returning lists, generally best to return an unmodifiable list. If the calling programmer needs a modifiable list, they can easily make from the returned unmodifiable list.
To get an unmodifiable list, use List.of rather than new LinkedList<>().
I cannot understand why your delete method would return a list. You may believe that you are reporting rows that got deleted, but technically you are not.
By the way, a tip: Text blocks can help with embedded SQL.
Instead of returning new LinkedList<>() you could return List.emptyList().
isPresent() is a method of Optional, but you assign the outcome of method get(department_ID) to an instance of List. You can check the List using
if(!(existing == null || existing.isEmpty())) {
I have 2 classes:
public class ChatGroup{
final public String name;
private List<ChatContact> contacts;
/* ---getters/setters/constructors-- */
}
public class ChatContact implements Parcelable, Comparable {
final public String name;
final public String jid;
public Status status;
/* ---getters/setters/constructors-- */
}
Then I have a list of ChatGroup items:
List<ChatGroup> chatGroupList = .....;
As you can see every ChatGroup has a list of ChatContact., and what I need is to search inside chatGroupsList, for ChatContacts that matches a query (search by username).
A way I'm doing, is do an auxilar list, search for every group, and look "inside" for ever chatContact, if exist I add the group with the contact:
private List<ChatGroup> searchContacts(String query) {
List<ChatGroup> filteredContacts = new ArrayList<>();
for (ChatGroup chatGroup : chatGroupList) {
ChatGroup auxChatGroup = new ChatGroup(chatGroup.name);
for (ChatContact chatContact : chatGroup.getContacts()) {
if (chatContact.name.toLowerCase().contains(query)) {
auxChatGroup.addContact(chatContact);
}
}
if (auxChatGroup.getContacts().size() > 0)
filteredContacts.add(auxChatGroup);
}
for (ChatGroup chatGroup : filteredContacts) {
Collections.sort(chatGroup.getContacts());
}
return filteredContacts;
}
All of this works perfect. But right now, this list has few groups with few contacts each one, but in a future will be a high number of elements, and this could be a "slow" solution.
So my question is, there is another faster way to do this type of search?
Unfortunately, if you are seriously going to search for something like "a" and want everyone who has the letter A at any point in their name, that type of search does not index well.
But looking at your algorithm, I see a few possible improvements.
Initialize ChatGroup auxChatGroup = null and only create the object when you find a result that matches the filter. This will avoid creating a few unnecessary objects if you have lots of rooms.
Sorting the list of contacts every time you do a search seems like a lot of wasted effort. Using a sorted collection such as TreeSet could offer you a huge time savings on each search.
If the number of groups becomes huge, as in millions, then consider using a multi-threaded search.
Depending on your use case, it may be possible to return a filtered "view" instead of a snapshot. However that may add some complexity and possible gotchas.
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"));
This question already has answers here:
PreparedStatement IN clause alternatives?
(33 answers)
Closed 3 years ago.
i have a list of names e.g.:
List<String> names = ...
names.add('charles');
...
and a statement:
PreparedStatement stmt =
conn.prepareStatement('select * from person where name in ( ? )');
how to do the following:
stmt.setParameterList(1,names);
Is there a workaround? can someone explain why this method is missing?
using: java, postgresql, jdbc3
This question is very old, but nobody has suggested using setArray
This answer might help https://stackoverflow.com/a/10240302/573057
There's no clean way to do this simply by setting a list on the PreparedStatement that I know of.
Write code that constructs the SQL statement (or better replaces a single ? or similar token) with the appropriate number of questions marks (the same number as in your list) and then iterate over your list setting the parameter for each.
this method is missing due to type erasure the parameter type of the List is lost at runtime. Therefore the need to add several methods arires: setIntParameters, setLongParameters, setObjectParameters, etc
For postgres 9 I have used this approach:
jdbcTemplate.query(getEmployeeReport(), new PreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setTimestamp(1, new java.sql.Timestamp(from.getTime()));
ps.setTimestamp(2, new java.sql.Timestamp(to.getTime()));
StringBuilder ids = new StringBuilder();
for (int i = 0; i < branchIds.length; i++) {
ids.append(branchIds[i]);
if (i < branchIds.length - 1) {
ids.append(",");
}
}
// third param is inside IN clause
// Use Types.OTHER avoid type check while executing query
ps.setObject(3, ids.toString(), **Types.OTHER**);
}
}, new PersonalReportMapper());
In case the questions' meaning is to set several params in a single call...
Because the type validation is already defined in a higher level, I think the only need is for setObject(...).
Thus, a utility method can be used:
public static void addParams(PreparedStatement preparedStatement, Object... params) throws SQLException {
for (int i = 0; i < params.length; i++) {
Object param = params[i];
preparedStatement.setObject(i+1, param);
}
}
Usage:
SqlUtils.addParams(preparedStatement, 1, '2', 3d);
Feel free converting this to a Java 8 lambda :)
I was reviewing code this morning and one of my colleagues had a different approach, just pass the parameter using setString("name1','name2','name3").
Note: I skipped the single quote at the beginning and end because these are going to be added by the setString.
After examining various solutions in different forums and not finding a good solution, I feel the below hack I came up with, is the easiest to follow and code. Note however that this doesn't use prepared query but gets the work done anyway:
Example: Suppose you have a list of parameters to pass in the 'IN' clause. Just put a dummy String inside the 'IN' clause, say, "PARAM" do denote the list of parameters that will be coming in the place of this dummy String.
select * from TABLE_A where ATTR IN (PARAM);
You can collect all the parameters into a single String variable in your Java code. This can be done as follows:
String param1 = "X";
String param2 = "Y";
String param1 = param1.append(",").append(param2);
You can append all your parameters separated by commas into a single String variable, 'param1', in our case.
After collecting all the parameters into a single String you can just replace the dummy text in your query, i.e., "PARAM" in this case, with the parameter String, i.e., param1. Here is what you need to do:
String query = query.replaceFirst("PARAM",param1); where we have the value of query as
query = "select * from TABLE_A where ATTR IN (PARAM)";
You can now execute your query using the executeQuery() method. Just make sure that you don't have the word "PARAM" in your query anywhere. You can use a combination of special characters and alphabets instead of the word "PARAM" in order to make sure that there is no possibility of such a word coming in the query. Hope you got the solution.
Other method :
public void setValues(PreparedStatement ps) throws SQLException {
// first param inside IN clause with myList values
ps.setObject(1 , myList.toArray(), 2003); // 2003=array in java.sql.Types
}