0

as the title suggests I am having a problem trying to use JPA Criteria with Spring Boot for a specific case.

Generally everything works but trying to search for stored data with a String property having newLine character embedded ( \n ) doesn't seem to work.

I am able to Get the data, edit them, save them through my front end, create new with multiple lines etc. but trying to search for them when for example a column is equals with 'hello\nworld' it wont work even though running this query in MySQL Workbench works returning the desired data :

select * from kerkinidb.ct_thhlastika_press_threats where description_en = 'hello\nworld';

To clarify, the way I do the search is by waiting in a Get request an argument called search which has all the properties that the user filtered. I am matching it with a Regex (which also has inside the Java 8.0 new Regex \\\\R for matching with multilines (and it works) ) then I am giving the the Service layer the Search Criteria that I matched which then passes to the Jpa Criteria Repository to parse them and generating the Predicates (matching again with Regex and \\\\R to create a final Predicate with OR and ANDs for the filtering) then triggering the query, then making another query called count to implement Pagination and finally mapping to a custom object and returning it.

I debugged every step and the final Predicate does generate the query I want, but the db doesn't return the expected data. So I am really confused since as I said the query does work in MySQL Workbench.

This is an example of the logging (I turned Spring Boot logging for MySQL logs on) generated when the Request is being triggered (in this case the stored data I have in my Table ct_thhlastika_press_threats in column description_en is a\ns\ndd so I am searching for this one as you can see instead of the example I said earlier hello\nworld :


2019-02-12 16:01:01.929 DEBUG 18368 --- [nio-8080-exec-2] org.hibernate.SQL                        : select ctthhlasti0_.id as col_0_0_, ctthhlasti0_.act_code as col_1_0_, ctthhlasti0_.description_en as col_2_0_, ctthhlasti0_.remarks as col_3_0_ from ct_thhlastika_press_threats ctthhlasti0_ where 1=1 and ctthhlasti0_.description_en=? order by ctthhlasti0_.id asc limit ?
2019-02-12 16:01:01.933 TRACE 18368 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [a\ns\ndd]
2019-02-12 16:01:01.944 DEBUG 18368 --- [nio-8080-exec-2] org.hibernate.SQL                        : select count(ctthhlasti0_.id) as col_0_0_ from ct_thhlastika_press_threats ctthhlasti0_ where 1=1 and ctthhlasti0_.description_en=?
2019-02-12 16:01:01.944 TRACE 18368 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [a\ns\ndd]
2019-02-12 16:01:01.946 TRACE 18368 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([col_0_0_] : [BIGINT]) - [0]

For anyone interested to look farther into the code you could find it Github repo. The project is for my University thesis. I made comments for adding to the Regexes (both of them) for ctThhlastikaPressThreats Controller and Repository. The db (the standard as well as the secondary for testing) need to be generated (can be generated by changing the auto-dll in application.properties).




Update

I tried using System.lineSeparator() (as suggested by @Hermann Steidel in the answer bellow) replacing the text newLine \n. But even though in the logs we can see now clearly that the value for the equals Predicate has line separations it still doesn't return the data from the DB.

Farther Explanation of my implementation

A correct Get request for the dynamic searching is like this :

http://localhost:8080/v1/ctThhlastikaPressThreats/search?search=descriptionEn~hello\nworld;@&size=10&page=0&sort=Asc

As you can see I am using a path variable called search (which has all the properties filtering requested from the user) along with 3 more for the size, page and sort.

For the search variable I am using 3 distinct characters to implement OR and AND Predicates for the final query. Those are the ~ which is to know that the property before it needs to use an equal predicate, the ; which is to be able to have multiple values for each property requested by the user and finally the @ which triggers the end of this property's filtering.

Those 3 characters are being Regexed in two places. In the Controller and in the SearchRepository of (for example since we specifically started with this one -> the CtThhlastasikaPressThreats)

Finally in the SearchRepository you can see that I am triggering 2 queries, one is to get the filtered data from the db and another is to get the count of the data for Pagination purposes while also in the end mapping to a custom DTO.

Steps to reproduce :

After generating the db, change for the 2 Regexes of CtThhlastikaPressThreats. For the Controller to be (\w+?)(~|<|>)([(!-/.\\\\R 0-9\p{L});]+)?@ and for the SearchRepository to be ([(!-/.\\\\R 0-9\p{L})]+).

Then you can use the example of the request I have above when having in the db saved for the specific table and for column descriptionEn with value of hello\nworld for example or whatever value you put (also change it into the request).

Final thing I tried but wasn't the solution :

Put in the CtThhlastikaPressThreatsSearchRepository in the method search (it is after line 61) above the :

predicate = builder.equal(root.get(param.getKey()), match.toString());

Make it as :

match = match.toString().replace("\\n", System.lineSeparator()); predicate = builder.equal(root.get(param.getKey()), match.toString());

This will basically change the value from being hellow\nworld to become hello\r\nworld so I guess it still isn't the desired solution. Thinking that in the db it is stored as \n for the lineSeparators.

Now in the logs you can see that when you trigger the Get Request again the VARCHAR value of descriptionEn is indeed now with line separations instead of the \n (which still should be recognized by MySQL) text.

Final thoughts

I believe since even this

select * from kerkinidb.ct_thhlastika_press_threats where description_en = 'hello\nworld';

works in MySQL Workbench, that something in between might be ruining the request when trying to also include newLine char or lineSeparators.




If there is any idea of why it doesn't work as intended please share to try it out.

Thank you for your time

  • So I downloaded your source, started a MySQL 8 instance, populated a single row on the 'kerkinidb.ct_thhlastika_press_threats' table with the description value 'hello\nworld' and I was able to get it to get that result when I made the call ```http://localhost:9080/v1/ctThhlastikaPressThreats/search?search=yo&size=1&page=0&sort=ASC```. I'm not sure why as "yo" is not in there. Anyways, I suggest you provide us a dump of your table and some URL/Request samples so we can reproduce it exactly as you are. – Hermann Steidel Feb 13 '19 at 03:13
  • Also as a small code review comment. The try/catches in your controller code is not desirable. I'm guessing you have them there to be able to debug if something goes wrong? I would look into the @RestControllerAdvice mechanism and clean those up. https://discuss.elastic.co/t/spring-boot-global-custom-exception-handling-mechanism-using-restcontrolleradvice/94938 You can then have a single point to catch ALL the exceptions you want. :) – Hermann Steidel Feb 13 '19 at 03:16
  • Hello, the try-catches were used for future possible use of custom Exceptions implemented but yes as is they are probably not necessary. The request is wrong thats why it returned (all the data basically of the table).Let me give you an example of a request : ```http://localhost:8080/v1/ctThhlastikaPressThreats/search?search=descriptionEn~a\ns\ndd;@&size=10&page=0&sort=Asc``` I am specifying also the property `descriptionEn` to make `equal Predicate` with it. Also I am using `;` and `@` as you can see to regex them and be able to do OR - AND Predicates. Thank you for the time you spent! :) – Alexandros Markovits Feb 13 '19 at 12:25
  • I also forgot to say that I am using the `~` after the `descriptionEn` as the equals pointer for the `SearchCriteria` Object (I am generating a List of Search Criterias in the Controller through the first `Regex`) to accomplish the dynamic searching for the user's desired filtering, generating this way both `AND` and `OR Predicates` (in the SearchRepository) depending if the user used multiple values of a property or not etc. :) So in your case you should try the : `http://localhost:8080/v1/ctThhlastikaPressThreats/search?search=descriptionEn~hello\nworld;@&size=10&page=0&sort=Asc` . – Alexandros Markovits Feb 13 '19 at 12:33
  • Also something I mentioned in the question, since you testing the code you also need to change the two `Regexes` (in the Controller and in the SearchRepository) to also have the `\\R` which allows also for `\n` newLines. Replace the Controller's one with `(\w+?)(~|<|>)([(!-/.\\\\R 0-9\p{L});]+)?@` And the SearchRepository one with `([(!-/.\\\\R 0-9\p{L})]+)` Otherwise searching with `\n` in the value wont pass through the Regexes. :) – Alexandros Markovits Feb 13 '19 at 12:42
  • 1
    Ok, I'm able to reproduce it now. I can also see how if you do it on a database tool, you'll get a hit. I think what's happening is that somewhere it's just treating the \n as just part of the text. Like if I was entering "hello\\nworld" to escape the newline. I added these to see more: `spring.jpa.show-sql=true spring.jpa.properties.hibernate.use_sql_comments=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.type=trace` – Hermann Steidel Feb 14 '19 at 04:10
  • Yes that is exactly what I thought was happening but it is weird isnt it? When as a text it works in MySQL :P Thank you so much for all your effort, honestly! I will try the answer. Before I do, what more do these applications properties give you for logging than just the previous one I had? I will definitely look into them for a betfer understanding of hibernate. – Alexandros Markovits Feb 14 '19 at 11:17
  • 1
    You are enabling logging from a logger perspective, the other, you are telling spring to setup your JPA/Hibernate properties to make it log differently. I believe your way is more efficient and the better way to go, I just wanted to get it to spit out as much as I could to see if I could catch anything. I edited my answer, sorry it was late for me. :) – Hermann Steidel Feb 14 '19 at 12:58

1 Answers1

1

I got it to work. Probably not the answer you wanted but, if you replace your incoming "\n"s with the system line separator, it will get you want you want.

 search = search.replace("\\n", System.getProperty("line.separator"));
 try {
    return ctThhlastikaPressThreatsService.searchCtThhlastikaPressThreats(producedSearchCriterias(search), size, page, sort);

If you look at the params value logging, you'll see the param is now

extracted value ([col_2_0_] : [VARCHAR]) - [hello
world]

instead of

extracted value ([col_2_0_] : [VARCHAR]) - [hello\nworld]

It's like I suspected, somewhere in search pipeline, it's escaping the "\n". I'm not suggesting you do what I suggest right there on the controller, I just wanted to show you that this is what needs to happen (somewhere) for it to work.

Hermann Steidel
  • 1,000
  • 10
  • 18
  • Awesome, I tried it and it is a step closer indeed but after trying it it still didn't return the data even though the equals Predicate now has the value with line separations instead of the replaced `\\n`. So, since we do see that in MySQL it works even with the VARCHAR having the `\n` in the value I now suspect somehow in between Hibernate ruins what we try to achieve (I might be wrong of course). I will update the question with what I changed and where. – Alexandros Markovits Feb 14 '19 at 14:05
  • Actually I just debugged it also and realized that doing the replace basically changed the value from lets say `hello\nworld` to `hello\r\nworld`. Just as a side info. – Alexandros Markovits Feb 14 '19 at 15:02