0

I am trying to compare a String against every column in the table. Is there an easier way to do this?

public class MySpec implements Specification<Tbl> {

    private Tbl searchCriteria;

    public MySpec(Tbl searchCriteria){
        this.searchCriteria = searchCriteria;
    }

    @Override
    public Predicate toPredicate(Root<Tbl> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

        List<Predicate> textPredicates = new ArrayList<>();
        String searchText = "%" + searchCriteria.getSearchText() + "%";

        textPredicates.add(cb.like(root.get("col1"), searchText));
        textPredicates.add(cb.like(root.get("col2"), searchText));
        textPredicates.add(cb.like(root.get("col3"), searchText));
        textPredicates.add(cb.like(root.get("col4"), searchText));
        textPredicates.add(cb.like(root.get("col5"), searchText));
        textPredicates.add(cb.like(root.get("col6"), searchText));
        textPredicates.add(cb.like(root.get("col7"), searchText));
        textPredicates.add(cb.like(root.get("col8"), searchText));
        textPredicates.add(cb.like(root.get("col9"), searchText));

        return query.where(cb.or(textPredicates.toArray(new Predicate[0])
                    .distinct(true).getRestriction();
    }
}

I don't want to have to change the specification code every time I add a new column to the table. Another option is a separate table that will hold a concatenated version of each column in a string. Such as:

TBL
Col1 | Col2    | Col3    | Col4
-----------------------------------------
1    | "Name"  | "Value" | "Other Value" 


TBL_CACHE
Col1 | Col2
------------------------------
1    | "Name Value OtherValue" 

But I don't want to create another table for each table I want to full text search on and I don't want the maintenance of populating this data or creating a SQL job to do it automatically.

wheeleruniverse
  • 1,511
  • 2
  • 20
  • 37

2 Answers2

1

For hibernate to retrieve list of columns from entity.

String[] columnNames = getSessionFactory().getClassMetadata(Entity.class).getPropertyNames();

For JPA you can go through the list of fields

for (Field field : entity.getClass().getDeclaredFields()) {
   Column column = field.getAnnotation(Column.class);
   if (column != null) {
      columnNames.add(column.name());
   }
}

And you can use the same approach if you annotate methods.

Also check this and this. One more way to get metadata.

Then using the columns list jusst add your conditions in a loop.

Community
  • 1
  • 1
StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • Thank you. I didn't consider using Reflection to grab the column names. I was stuck in the mindset that root would have a getAll solution. This should work fine. – wheeleruniverse May 10 '17 at 14:37
0

You could get the size() from the CriteriaBuilder and create a loop. This assumes the column starts at 1 and ends at number of columns.

int sz = cb.size();
for (int ii = 1; ii <= sz; ii++) {
    String textIndex = "" + ii;
    textPredicates.add(cb.like(root.get("col" + textIndex), searchText));
}
Dakoda
  • 175
  • 7
  • Hey Dakota, sorry for the confusion but col1, col2, col3 was just example data. The real column names are not like that. Real data is something like name, type, contact, id. How can I find the column names without hard coding them. The reason I don't want to hardcore the column names is because if I add a new column tomorrow I will need to again change this Specification. Is there anyway to just say something like cb.like(root, searchText) to compare every column inside root? – wheeleruniverse May 10 '17 at 14:21