Saving search history with many checks and validations inside I am absolutely confused by saveSearchHistory function. It seems dirty and unobvious.
Should I refactor it, and how?

SearchHistory.java

public class SearchHistory {
  public SearchHistory(Long userId, Long groupId, String searchString) {
        this.userId = userId;
        this.groupId = groupId;
        this.searchString = searchString;
    }
  ...
  //getters + setters
  ...
  public void validate() throws InvalidParameterException{
    if (searchString == null || searchString.length() &lt; MIN_WORD_SIZE) {
      throw new InvalidParameterException("Wrong search word min size: "+searchString);
    }

    if (searchString.length() &gt; MAX_WORD_SIZE) {
        searchString = searchString.substring(0, MAX_WORD_SIZE - 1);
    }
}
}


SearchHistoryController.java

@Repository
public class SearchHistorControllerImpl implements SearchHistoryController {

    @PersistenceContext(unitName = "userPU")
    private EntityManager userEm;

    @Override
    @Transactional
    public List&lt;SearchHistory&gt; getSearchHistories(Long userId) {
        return emptyIfNull(userEm.createNamedQuery("getSearchHistories")
                .setParameter("userId", userId)
                .getResultList());
    }

    @Override
    @Transactional
    public SearchHistory saveSearchHistory(Long userId, Long groupId, String word) throws InvalidParameterException{
        SearchHistory searchHistory = new SearchHistory(userId, groupId, word);
        searchHistory.validate(); //throws InvalidParameterException

        removeSameWord(userId, groupId, word);
        checkWordsSizeLimit(userId, groupId);

        searchHistory = userEm.merge(searchHistory);
        userEm.flush();
        return searchHistory;
    }

    private void removeSameWord(Long userId, Long groupId, String word) {
        userEm.createNamedQuery("removeSameWord")
                .setParameter("userId", userId)
                .setParameter("groupId", groupId)
                .setParameter("searchString", word)
                .executeUpdate();
    }

    private void checkWordsSizeLimit(Long userId, Long groupId){
        List&lt;SearchHistory&gt; searchHistoryIds = userEm.createNamedQuery("findOldSearchWordsIds")
                .setParameter("userId", userId)
                .setParameter("groupId", groupId)
                .setFirstResult(MAX_HISTORY_SIZE - 1)
                .getResultList();

        if (!searchHistoryIds.isEmpty()) {
            userEm.createNamedQuery("removeOldWords")
                    .setParameter("searchHistoryIds", searchHistoryIds)
                    .executeUpdate();
        }
    }
}


SearchHistory.html.xml

...
&lt;query name="findOldSearchWordsIds"&gt;
    SELECT id
     FROM SearchHistory as searchHistory
     WHERE
        searchHistory.userId = :userId
        AND searchHistory.groupId = :groupId
     ORDER BY searchHistory.searchTime DESC
&lt;/query&gt;

&lt;query name="removeOldWords"&gt;
    DELETE FROM SearchHistory as searchHistory
    WHERE searchHistory.id in ( :searchHistoryIds )
&lt;/query&gt;

&lt;query name="removeSameWord"&gt;
    DELETE FROM SearchHistory as searchHistory
    WHERE
        searchHistory.searchString = :searchString
        AND searchHistory.userId = :userId
        AND searchHistory.groupId = :groupId
&lt;/query&gt;
...


FullSearch.java

 ...
public void commandExecute() {
    buildSearchResult();

    if (whetherSaveRequestInHistory) {
        final String filteredString = StringUtils.removeEnd(filterString, "*");
        try {
            searchHistoryManager.saveSearchHistory(context.getUserId, groupId, filteredString);
        } catch (InvalidParameterException ipe) {
            log.info("Unable to save Search History to db: "+ ipe.getMessage());
        }
    }
} 

...
It seems dirty and unobvious. 


I wouldn't go that far. I think that the code generally looks fine.

Style

The indentation and spacing is generally fine, but sometimes it is inconsistent (2 vs 4 spaces, spaces around +).

validate

I would validate right in the constructor instead of in a public method, because someone might forget to call the method, and then you end up with an invalid object. 

And as validate has side-effects (it doesn't just check the string, it actually changes it), I would add a JavaDoc comment.

removeSameWord

Is this method really needed? Your database schema should be able to use a unique constraint to not allow duplicate rows. Or, since you are using merge, you should also be able to just remove it completely, since merge updates existing entries.

Naming


Sometimes, you use word or words and sometimes searchString for what seems to me to be the same thing. This is a bit confusing, you should use variable names consistently.
the query removeOldWords could just be removeByIds, because it doesn't care if words are old or new, it just deletes all words with the given ids.
checkWordsSizeLimit doesn't just check, it also deletes, the name should reflect this (maybe something like enforceWordsSizeLimit; or split the method up in getLastIds and removeByIds).
the query removeSameWord could be renamed to remove (the query doesn't really care if it's the same word as some other word, it just removes the given entry).
removeSameWord could also be remove (the parameters make it clear what will be removed) or removeExisting.